I am trying to create a generic “callback” object that will hold arbitrary data and invoke member functions of related classes. Due to internal policy, I cannot use Boost.
The callback object looks like this:
template<typename Object, typename Data>
class Callback
{
public:
typedef void (Object::*PHandler)(Callback*);
Callback(Object* obj, PHandler handler) : pObj(obj), pHandler(handler) {}
Callback& set(PHandler handler) { pHandler = handler; return *this; }
void run() { (pObj->*pHandler)(this); }
public:
Data data;
protected:
Object* pObj;
PHandler pHandler;
};
And the class it works on:
struct Object1
{
struct Data { int i; };
typedef Callback<Object1, Data> Callback1;
void callback(Callback1* pDisp) { printf("%cb\n", pDisp->data.i); }
void test()
{
Callback1 cb(this, &Object1::callback);
cb.data.i = 1;
cb.run();
}
};
The following test works as expected:
Object1 obj1;
obj1.test();
So far so good.
However, when a coworker tried to derive from the Callback class instead of using a typedef, they got compilation errors due to incompatible pointers:
struct Object2
{
struct Data { int i; Data(int j) { i = j; } };
class Callback2 : public Callback<Object2, Data>
{
Callback2(Object2* obj, PHandler handler, int i) : Callback(obj, handler) { data.i = i; }
};
void callback(Callback2* pDisp) { printf("%cb\n", pDisp->data.i); }
void test()
{
Callback2 cb(this, &Object2::callback, 2);
cb.run();
}
};
I tried using the “curiously recurring template pattern” in the Callback class and managed to get derived classes working, but it broke code that used the typedef method.
My question is:
How can I modify the Callback class to work with both cases, and without requiring extra work on the part of the user of the class?
You have to pass the class type of the derived. To not break the typedef-way, you can can give that parameter a default value. Something like the following should work
Alternatively you can simply have two classes for this. One for inheritance and one if you don’t inherit. The first is for inheritance
And the second is for non-inheritance. You can make use of the base-class for this