Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Virtual Inheritance and dreaded diamond

I am having a hard time with a dreaded diamond problem. For a reminder, here is the classical class hierarchy of this problem:

    B
   / \
 C1   C2
   \ /
    D

To solve it, the standard solution is to make C1 and C2 use virtual inheritance to inherit from B.

My problem is that B and C1 are from an SDK that I cannot modify. Example below where I cannot make SubClassB inherit virtually from Base. Classes: PureVirtualBase, Base and SubClassB are from the SDK I use. I cannot modify them. SubClassA and Leaf are my custom classes. I can change them.

     PureVirtualBase(SDK)
           |
        Base(SDK)
       /        \
 SubClassA   SubClassB(SDK)
       \        /
          Leaf

In such a situation where SubClassB cannot be changed to use virtual inheritance from Base. How what should so that:

  • Leaf instance only contains one Base
  • Avoid the ambiguity when trying to access functions defined pure virtual in PureVirtualBase and implemented in Base
class PureVirtualBase
{
public:
    PureVirtualBase()
    {
        cout<<"<<PureVirtualBase::PureVirtualBase" << endl;
        cout<<">>PureVirtualBase::PureVirtualBase" << endl;
    }
    virtual int f_PureVirtualBase()=0;
};
class Base : public PureVirtualBase
{
public:
    Base(std::string id) {
        cout<<"<<Base::Base:"<<id << endl;
        m_id=id;
        cout<<">>Base::Base:"<<m_id << endl;
    }
    virtual int f_PureVirtualBase() {
       cout<<"Base::f_PureVirtualBase" << endl;
       return 1;
    }
private:
    std::string m_id;
};
class SubClassA:  public virtual Base
{
public:
    SubClassA(): Base("From SubClassA") {
        cout<<"<<SubClassA::SubClassA" << endl;
        cout<<">>SubClassA::SubClassA" << endl;
    }
};
class SubClassB:  public Base
{
public:
    SubClassB():Base("From SubClassB") {
        cout<<"<<SubClassB::SubClassB" << endl;
        cout<<">>SubClassB::SubClassB" << endl;
    }
};    
class Leaf:  public SubClassA, public SubClassB
{
public:
    Leaf():SubClassA(),  SubClassB(), Base("From Leaf") {
        cout << "<<Leaf::Leaf" << endl;
        cout << ">>Leaf::Leaf"<< endl;
    }
};
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    Leaf myleaf;
    myleaf.f_PureVirtualBase();
    return a.exec();
}
  • If I comment the call to f_PurevirtualBase it compiles but I have a warning that virtual base 'Base' inaccessible in 'Leaf' due to ambiguity If I uncomment this call: I get this error : request for member 'f_PureVirtualBase' is ambiguous
  • If I prefix this call by the class name (myleaf.SubClassA::f_PureVirtualBase() then it works, but something is obviously wrong as there are 2 Base contained in the Leaf Object).

Any hint?

More info to answer comments

My target architecture is slightly more complex that the sample I provided in the original question:

 PureVirtualBase(SDK)
       |
     Base(SDK)
        |
        --SubClassA
        --SubClassB(SDK)
        --SubClassC(SDK)
        --SubClassD(SDK)

LeafOne : inherits from SubClassA and SubClassB(SDK)

LeafTwo : inherits from SubClassA and SubClassC(SDK)

LeafThree : inherits from SubClassA and SubClassD(SDK)

SubClassA is my own private code. It provides custom functions. It should be able to be treated like a Base instance by SDK methods. This class won't be instantiated but it is here to be able to handle LeafOne, LeafTwo and LeafThree in the same when performing some treatment.

like image 865
Marc Avatar asked Apr 05 '12 10:04

Marc


2 Answers

This indicates a problem with your design, for which the simplest answer is avoid the diamond in the first place. Your choice of names for the example code is bad enough to make it hard to reason about what you might actually be wanting to do, but at any rate reconsider whether you need to inherit from both parents, and whether that makes sense.

Inheritance is one of the most abused constructs there is in OO languages, it does solve a problem, but it is used as a golden hammer everywhere else. Many times what you have in your hand is a screw, not a nail and the correct tool is not a hammer.

like image 143
David Rodríguez - dribeas Avatar answered Oct 06 '22 21:10

David Rodríguez - dribeas


If you're really stuck with these design constraints I'd definitely look to subclassing from B directly with an class that took C1 and C2 as composite components. Unfortunately that requires manually mirroring their interface (hopefully it's small or you can limit it to what you need) and proxying through to the subcomponents. It's not pretty, but unless you can enforce some give on the design elsewhere, then you're not really left with much choice.

One disadvantage of course is that you don't have the type identity you're looking for (the subclass won't satisfy "isa" of C1 or C2) which may be enough to blow this approach out of the water.

It's not pretty. But I expect that given your constraints it might be the "least bad" solution.

like image 21
Michael Wilson Avatar answered Oct 06 '22 22:10

Michael Wilson