Search code examples
c++inheritancesfmldiamond-problem

C++ Multiple Inheritance With Proprietary Base Class


I am working with SFML (Simple Fast Media Library) on a hobbyist game design project, and ran into quite an annoying conundrum working with sf::Sprite, sf::Drawable, and cloning. Here's the issue:

As a major fan of composite and factory design pattern, many of the objects in my program contain members which are pointers to abstract bases that may be a single primitive, or a composite of sf::Drawable. To solve the issue of copy-construction/factory creation with abstract objects, I have been using type covariance in the form of a 'clone' method inherited from a widely used 'Clonable' base class. As a specific example, I created the abstract class 'Graphic' which represents a clonable drawable primitive:

    class Clonable{
    public:
        virtual Clonable* clone() const = 0;
    };

    class Graphic: public sf::Drawable, public Clonable{
    public:
        virtual Graphic* clone() const = 0;
        // Derivatives define as: {return new MyClass(*this);}
    };

With this, I have designed multiple composites of Graphic which are also Clonable. The problem is, I want these composite members to able to be an sf::Sprite as well, which derives from sf::Drawable. This creates a diamond as both sf::Sprite and Graphic inherit from sf::Drawable. Normally, virtual inheritance would come to the rescue, but sf::Sprite is owned by SFML, not me.

I considered editing their source and recompiling the library, but this feels like butchery. Whenever SMFL decides to update their library, I will need to hack and recompile their source each time.

In summary: how do you solve the diamond problem when one (or both) of the first derived classes are proprietary/inaccessible?

Another possible avenue: how else might you promise c++ that an object will fulfill multiple requirements (draw and clone) without multiple inheritance?

Thanks,

Jordan


Solution

  • Whenever I run into inheritance issue, I consider replacing it by composition, which is another valid way to add behavior to existing classes.

    class Graphic : public Clonable {
    public:
        const sf::Drawable& getDrawable() const { return *drawable;  }
    private:
        sf::Drawable* drawble;
    };
    

    A Graphic is then a Clonable, but it also exposes a drawable interface through its getDrawable() method.