1

I have base classes with virtual methods fun. I need to redefine all fun methods in a derived class. I know of SO: C++ virtual override functions with same name, but it does not work for me.

Function processDerived accepts v1::Derived *. If I pass v2::Derived * there it does not work as expected.

class Base1 {
public:
    virtual ~Base1() {}
    virtual void funB1_1() {}
    virtual void funB1_2() {}
    virtual void fun() { std::cout << "Base1::fun()" << std::endl; }
};

class Base2 {
public:
    virtual ~Base2() {}
    virtual void fun() { std::cout << "Base2::fun()" << std::endl; }
    virtual void funB2() {}
};

namespace v1 {

    class Derived : public Base1, public Base2 {
        int x_;
    public:
        Derived() : x_(10) {}
        virtual ~Derived() {}
        virtual void funD1(int i) { std::cout << "Derived::funD1(" << i << ")" << std::endl; }
    };

}

namespace v2 {

    class Base1Proxy : public Base1 {
    public:
        virtual ~Base1Proxy() {}
        virtual void fun() override { fun_Base1(); }
    protected:
        virtual void fun_Base1() = 0;
    };

    class Base2Proxy : public Base2 {
    public:
        virtual ~Base2Proxy() {}
        virtual void fun() override { fun_Base2(); }
    protected:
        virtual void fun_Base2() = 0;
    };

    class Derived : public Base1Proxy, public Base2Proxy {
        int x_;
    public:
        Derived() : x_(10) {}
        virtual ~Derived() {}
        virtual void funD1(int i) { std::cout << "Derived::funD1(" << i << ")" << std::endl; }
    protected:
        virtual void fun_Base1() override { std::cout << "Derived::Base1::fun(), x = " << x_ << std::endl; }
        virtual void fun_Base2() override { std::cout << "Derived::Base2::fun(), x = " << x_ << std::endl; }
    };

}

void processDerived(v1::Derived * d, int i) {
    d->funD1(i);
}

int main() {
    v1::Derived d;
    Base1 * b1 = &d;
    Base2 * b2 = &d;
    b1->fun();
    b2->fun();
    processDerived(&d, 100);

    v2::Derived d2;
    b1 = &d2;
    b2 = &d2;
    b1->fun();
    b2->fun();
    processDerived(reinterpret_cast<v1::Derived *>(&d2), 200);
}

Output:

Base1::fun()
Base2::fun()
Derived::funD1(100)
Derived::Base1::fun(), x = 10
Derived::Base2::fun(), x = 10
Derived::Base1::fun(), x = 10  // expected Derived::funD1(200)

VTABLES (generated by MSVC):

;   COMDAT ??_7Derived@v2@@6BBase2Proxy@1@@
CONST   SEGMENT
??_7Derived@v2@@6BBase2Proxy@1@@ DQ FLAT:??_R4Derived@v2@@6BBase2Proxy@1@@ ; v2::Derived::`vftable'
    DQ  FLAT:??_EDerived@v2@@W7EAAPEAXI@Z
    DQ  FLAT:?fun@Base2Proxy@v2@@UEAAXXZ
    DQ  FLAT:?funB2@Base2@@UEAAXXZ
    DQ  FLAT:?fun_Base2@Derived@v2@@MEAAXXZ
CONST   ENDS
;   COMDAT ??_7Derived@v2@@6BBase1Proxy@1@@
CONST   SEGMENT
??_7Derived@v2@@6BBase1Proxy@1@@ DQ FLAT:??_R4Derived@v2@@6BBase1Proxy@1@@ ; v2::Derived::`vftable'
    DQ  FLAT:??_EDerived@v2@@UEAAPEAXI@Z
    DQ  FLAT:?funB1_1@Base1@@UEAAXXZ
    DQ  FLAT:?funB1_2@Base1@@UEAAXXZ
    DQ  FLAT:?fun@Base1Proxy@v2@@UEAAXXZ
    DQ  FLAT:?fun_Base1@Derived@v2@@MEAAXXZ
    DQ  FLAT:?funD1@Derived@v2@@UEAAXH@Z
CONST   ENDS

;   COMDAT ??_7Base2Proxy@v2@@6B@
CONST   SEGMENT
??_7Base2Proxy@v2@@6B@ DQ FLAT:??_R4Base2Proxy@v2@@6B@  ; v2::Base2Proxy::`vftable'
    DQ  FLAT:??_EBase2Proxy@v2@@UEAAPEAXI@Z
    DQ  FLAT:?fun@Base2Proxy@v2@@UEAAXXZ
    DQ  FLAT:?funB2@Base2@@UEAAXXZ
    DQ  FLAT:_purecall
CONST   ENDS

;   COMDAT ??_7Base1Proxy@v2@@6B@
CONST   SEGMENT
??_7Base1Proxy@v2@@6B@ DQ FLAT:??_R4Base1Proxy@v2@@6B@  ; v2::Base1Proxy::`vftable'
    DQ  FLAT:??_EBase1Proxy@v2@@UEAAPEAXI@Z
    DQ  FLAT:?funB1_1@Base1@@UEAAXXZ
    DQ  FLAT:?funB1_2@Base1@@UEAAXXZ
    DQ  FLAT:?fun@Base1Proxy@v2@@UEAAXXZ
    DQ  FLAT:_purecall
CONST   ENDS

;   COMDAT ??_7Derived@v1@@6BBase2@@@
CONST   SEGMENT
??_7Derived@v1@@6BBase2@@@ DQ FLAT:??_R4Derived@v1@@6BBase2@@@ ; v1::Derived::`vftable'
    DQ  FLAT:??_EDerived@v1@@W7EAAPEAXI@Z
    DQ  FLAT:?fun@Base2@@UEAAXXZ
    DQ  FLAT:?funB2@Base2@@UEAAXXZ
CONST   ENDS
;   COMDAT ??_7Derived@v1@@6BBase1@@@
CONST   SEGMENT
??_7Derived@v1@@6BBase1@@@ DQ FLAT:??_R4Derived@v1@@6BBase1@@@ ; v1::Derived::`vftable'
    DQ  FLAT:??_EDerived@v1@@UEAAPEAXI@Z
    DQ  FLAT:?funB1_1@Base1@@UEAAXXZ
    DQ  FLAT:?funB1_2@Base1@@UEAAXXZ
    DQ  FLAT:?fun@Base1@@UEAAXXZ
    DQ  FLAT:?funD1@Derived@v1@@UEAAXH@Z
CONST   ENDS

;   COMDAT ??_7Base2@@6B@
CONST   SEGMENT
??_7Base2@@6B@ DQ FLAT:??_R4Base2@@6B@          ; Base2::`vftable'
    DQ  FLAT:??_EBase2@@UEAAPEAXI@Z
    DQ  FLAT:?fun@Base2@@UEAAXXZ
    DQ  FLAT:?funB2@Base2@@UEAAXXZ
CONST   ENDS

;   COMDAT ??_7Base1@@6B@
CONST   SEGMENT
??_7Base1@@6B@ DQ FLAT:??_R4Base1@@6B@          ; Base1::`vftable'
    DQ  FLAT:??_EBase1@@UEAAPEAXI@Z
    DQ  FLAT:?funB1_1@Base1@@UEAAXXZ
    DQ  FLAT:?funB1_2@Base1@@UEAAXXZ
    DQ  FLAT:?fun@Base1@@UEAAXXZ
CONST   ENDS

As you can see method (1) is called instead of (2)

; old Derived::`vftable'
DQ  FLAT:??_EDerived@v1@@UEAAPEAXI@Z
DQ  FLAT:?funB1_1@Base1@@UEAAXXZ
DQ  FLAT:?funB1_2@Base1@@UEAAXXZ
DQ  FLAT:?fun@Base1@@UEAAXXZ
DQ  FLAT:?funD1@Derived@v1@@UEAAXH@Z

; new Derived::`vftable'
DQ  FLAT:??_EDerived@v2@@UEAAPEAXI@Z
DQ  FLAT:?funB1_1@Base1@@UEAAXXZ
DQ  FLAT:?funB1_2@Base1@@UEAAXXZ
DQ  FLAT:?fun@Base1Proxy@v2@@UEAAXXZ
DQ  FLAT:?fun_Base1@Derived@v2@@MEAAXXZ   ; <== (1)
DQ  FLAT:?funD1@Derived@v2@@UEAAXH@Z      ; <== (2)

Is there a solution for my case?

P.S. I know of MSVC-specific solution but I need it to work for GCC, Clang, Apple-Clang.

Community
  • 1
  • 1
Dmitry Sokolov
  • 3,118
  • 1
  • 30
  • 35
  • Rather than the cast between 2 completely unrelated types, what's the matter with a new class D1FunProvider? – UKMonkey Apr 19 '17 at 15:08
  • They are not unrelated. Consider `v2::Derived` as fixed `v1::Derived`. – Dmitry Sokolov Apr 19 '17 at 15:26
  • I can not extract any functionality from `v1::Derived` into new class. I must not change API. – Dmitry Sokolov Apr 19 '17 at 15:28
  • Why do you need `Base1Proxy` and `Base2Proxy` at all? – Michael Nastenko Apr 20 '17 at 01:37
  • @MichaelNastenko, see http://www.gotw.ca/gotw/039.htm – Dmitry Sokolov Apr 20 '17 at 15:06
  • @DmitrySokolov This approach can't work in your case. Adding virtual functions in base classes you changing you vtable, thus your `reinterpret_cast` can't be valid. Only possible way I can see is to override those functions in `Base1Proxy` and `Base2Proxy`. And still, there is no guaranty it'll work. AFAIK, standard does not define way vtables constructed for MI, wich means you `reinterpret_cast` is UB. – Michael Nastenko Apr 21 '17 at 01:14
  • @MichaelNastenko, I know that. The question is not about casting and UB. How can I change `class Derived` to have both derived `virtual void fun()` methods re-implemented and to have `process(Derived*)` working as expected (new `class Derived` is binary compatible with the previous one)? – Dmitry Sokolov Apr 21 '17 at 21:35

0 Answers0