Design question (related to Listeners in class hierarchies)


#1

distilling the code I have to deal with, I have a base class more or less like this:

(the constructors and methods for adding/removing listeners have been omitted)

class BaseClass
{
public:
    class Listener
    {
        virtual ~Listener() { }
        virtual basePropertiesChanged (BaseClass* source) { }
    };


    void setProperties (int b1, int b2, int b3)
    {
        base1 = b1; base2 = b2; base3 = b3;
        listeners.call (&Listener::basePropertiesChanged, this);
    }

private:
    int base1, base2, base3;
    ListenerList <Listener> listeners;
}

 

Now I want to extend it by deriving from it my derived class, which adds some members, and also want to add a callback to the listener that gets called when those values are set

 

class DerivedClass
{
public:
    class Listener
    {
        virtual ~Listener() { }
        virtual derivedPropertiesChanged (DerivedClass* source) { }
    };


    void setDerivedProperties (String s1, String s2)
    {
        string1 = s1; string2 = s2;

        // OBVIOUSLY I CAN'T DO THIS.. SO WHAT?
        listeners.call (&Listener::derivedPropertiesChanged, this);
    }

private:
    String string1, string2;
}

My question is the one in the code as a comment in the setDerivedProperties() method:

I can't call the callback for the derived class because of two problems:

1) the listener list is private in the base class (that's a minor problem, I can make it protected)

2) the type of listeners in the list is BaseClass::Listener, while the callback method I want to call in my derived class is DerivedClass::Listener::derivedPropertiesChanged. Is there a cast/workaround/magic trick around this?

Or, if not, what is the recommended design in this case? I'd like to avoid having a list of registered listeners for the base class AND another for the derived one..


#2

I think the normal way would be not to have a new Listener class in the derived class, but rather for your implementors of BaseClass::Listener to do the heavy lifting of figuring out what to do.

Taking an example from the JUCE classes, TextButton, DrawableButton, ToggleButton etc. do not have their own listeners. Instead, classes that want to listen to them simply implement Button::Listener. If you need to figure out whether the broadcasting object was of a specific subclass, you can do this:

 

void MyListener::buttonClicked (Button* button)
{
   if (DerivedButton* derived = dynamic_cast&lt;DerivedButton*&gt;(button))
   {
       derived-&gt;derivedSpecificMethod();
   }
}