Feature request: optional virtual inheritance from Component


#1

Hi --

I'm working on a project that defines a subclass of juce::Component that adds in some features that are useful for the project.  The project naturally also uses a number of other subclasses of juce::Component that are defined in Juce itself.  I'd like to be able to subclass those as well and have them inherit from the project's Component subclass.  Something like this:

namespace Foo
{
    class Component : virtual public juce::Component
    {
        // ...
    };

    class TextButton : public juce::TextButton, public Component
    {
        // ...
    };
}

That's made impossible though since internally Juce doesn't use virtual inheritance for the component class.  At least thus far, it seems the Component class is the only obvious one where it'd be especially useful for virtual inheritance to be an option.  It would be relatively easy to incorporate that into Juce so that it could be switched on or off trivially -- something like this:

#ifndef JUCE_COMPONENT_INHERITANCE
#define JUCE_COMPONENT_INHERITANCE
#endif

namespace juce
{
    class TextButton : JUCE_COMPONENT_INHERITANCE public Component
    {
        // ...
    };
}

That would make it so that there'd be no effect or performance hit on existing projects, but where projects that would like to make use of virtual inheritance could do so easily (without ending up with duplicate copies of juce::Component in the inheritance tree).  Thoughts on the suggestion?  (I could easily add this to a local copy of Juce, but it'd naturally be ideal if it were added upstream so that I don't have to update our copy of Juce with each version update.)


#2

NOOOOOOOOO.....

Virtual inheritance is a terrible code smell. I've never found a place where it was genuinely the best solution to anything, and have never used it in any production code I've ever written! There's always a better way to do things!


#3

Hmm, how would you approach this problem then?  The other alternative that occurs to me would be something like this:


namespace Foo
{
    class ComponentHelper
    {
    public:
        ComponentHelper(juce::Component *component);
        // ...
    };

    class Component : public juce::Component, public ComponentHelper
    {
    public:
        Component() : ComponentHelper(this) {}
        // ...
    };


    class TextButton : public juce::TextButton, public ComponentHelper
    {
        // ...
    };
}

That would work, but it seems to basically be duplicating what virtual inheritance does manually and would make the application code less readable.  I've very rarely used virtual inheritance in the past, but this is kind of the text-book case where it'd usually be applied.


#4

I can't really see what the purpose of these classes would be from your code outline. It seems like a confusing decision to create your own classes with the names Component and TextButton and then to mix them with the juce ones of the same names.

Basically, I don't really see what you're trying to do, but I doubt whether there isn't a simpler way of approaching whatever it is!


#5

The specific case that I'm working on at the moment is adding additional meta-data to components that's later fed to the StretchableLayoutManager (though I assume there will be other things which I'll want to add to the overridden component later as well).  Basically it makes components know if they're stretchable and how they should be stretched.  I've got a wrapper around the StretchableLayoutManager that makes it just so that I can throw widgets into that wrapper and they're automatically laid out.  It, however, at the moment only works for things that inherit from the project's Component.


#6

If it's just small metadata why don't you add it to the existing NamedValueSet every Component contains? You can get it with juce::Component::getProperties()


#7

Even if you're adding a new virtual class to provide this extra metadata, I can't see why you'd need virtual inheritance..? E.g. you could just have a class Stretchable with a pure virtual method isStretchable(), and implement it in whichever components you want to, using dynamic_cast when you need to find out whether a Component supports this interface. I do that kind of trick with classes like DragAndDropContainer, and so far have never needed the "diamond of death"!