This is the game changer right here, a simple functor that iterates every Component in a hierarchy recursively and calls a member function of a specific class, if the Component also derives from that interface. Implemented using dynamic_cast and boost::bind (or std::bind / std::tr1::bind).
Simple implementation, powerful results. You could use this to save and load settings, or to broadcast “events” to components that expose the interface, without having to manage a list of “event Ids” or manually registering a Listener. You don’t need to know who is listening for these member calls, which means you can re-parent components, add and remove them, and in general not care where they are located or if they are there at all.
// Calling this functor will recursively traverse a Component and all of its
// children, and call a member function for each Component that exposes the
// interface (determined via dynamic_cast).
//
// This requires boost::bind, std::tr1::bind, or std::bind to work.
//
/* Usage example
struct PersistentObject
{
virtual void onSaveSettings (XmlElement* xml) = 0;
virtual void onLoadSettings (XmlElement* xml) = 0;
};
void saveAllWindowSettings (XmlElement* xml)
{
for (int i = 0; i < Desktop::getInstance().getNumComponents(); ++i)
{
Component* c = Desktop::getInstance().getComponent (i);
// Any Component of any open window that derives from PersistentObject
// will have its onSaveSettings() member called by this broadcast.
//
componentBroadcast (c, &PersistentObject::onSaveSettings, xml);
}
}
*/
class componentBroadcast
{
public:
template <class C>
componentBroadcast (Component* c, void (C::*f)())
{ call <C> (c, boost::bind (f, _1)); }
template <class C, class T1>
componentBroadcast (Component* c, void (C::*f)(T1), T1 t1)
{ call <C> (c, boost::bind (f, _1, t1)); }
template <class C, class T1, class T2>
componentBroadcast (Component* c, void (C::*f)(T1, T2), T1 t1, T2 t2)
{ call <C> (c, boost::bind (f, _1, t1, t2)); }
template <class C, class T1, class T2, class T3>
componentBroadcast (Component* c, void (C::*f)(T1, T2, T3), T1 t1, T2 t2, T3 t3)
{ call <C> (c, boost::bind (f, _1, t1, t2, t3)); }
template <class C, class T1, class T2, class T3, class T4>
componentBroadcast (Component* c, void (C::*f)(T1, T2, T3, T4), T1 t1, T2 t2, T3 t3, T4 t4)
{ call <C> (c, boost::bind (f, _1, t1, t2, t3, t4)); }
template <class C, class T1, class T2, class T3, class T4, class T5>
componentBroadcast (Component* c, void (C::*f)(T1, T2, T3, T4, T5), T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
{ call <C> (c, boost::bind (f, _1, t1, t2, t3, t4, t5)); }
template <class C, class T1, class T2, class T3, class T4, class T5, class T6>
componentBroadcast (Component* c, void (C::*f)(T1, T2, T3, T4, T5, T6),
T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
{ call <C> (c, boost::bind (f, _1, t1, t2, t3, t4, t5, t6)); }
template <class C, class T1, class T2, class T3, class T4, class T5, class T6, class T7>
componentBroadcast (Component* c, void (C::*f)(T1, T2, T3, T4, T5, T6, T7),
T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
{ call <C> (c, boost::bind (f, _1, t1, t2, t3, t4, t5, t6, t7)); }
template <class C, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8>
componentBroadcast (Component* c, void (C::*f)(T1, T2, T3, T4, T5, T6, T7, T8),
T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
{ call <C> (c, boost::bind (f, _1, t1, t2, t3, t4, t5, t6, t7, t8)); }
private:
template <class Interface, class Functor>
void call (Component* component, Functor const& f)
{
Interface* const object = dynamic_cast <Interface*> (component);
if (object != nullptr)
f (object);
for (int i = 0; i < component->getNumChildComponents (); ++i)
call <Interface> (component->getChildComponent (i), f);
}
};
For the usage example, all you need to do in order to receive the “save” and “load” callbacks, is change your existing Component-derived class to also derive from PersistentObject, and implement the two functions:
class MyComponent : public Component, public PersistentObject
{
public:
void onSaveSettings (XmlElement* xml);
void onLoadSettings (XmlElement* xml);
};
Components in the hierarchy that don’t expose the PersistentObject interface are simply skipped.
Syntax:
// supports up to 8 parameters.
componentBroadcast (component, memberFunction, param1, param2, ..., param8);