Reputation: 33589
I have a callback class that is used in some places throughout my application. It's constructed like this:
struct A {
void f();
}
A * callee = new A;
Callback_0 callback = CCallback_0(callee, &A::f);
callback
can now be stored and invoked later. The problem with this is if callee
is destroyed between callback creation and callback invocation, the program crashes (or worse).
The only solution I can think of so far: any class that wants to be a callee must be inherited from a base Callee
class:
class Callee {
public:
void registerCallback(const Callback& c) {
_callbacks.push_back(c);
}
~Callee() {
std::for_each(_callbacks.begin(), _callbacks.end(), [](Callback& c){c.disable();});
}
private:
std::vector<Callback> _callbacks;
}
The downside of this design is that I have to inherit every class that might be required to be called. Is there any better way? I'd prefer a C++03 solution, I only have limited C++11 support (limited by MSVC2010 and clang on Mac).
Upvotes: 1
Views: 517
Reputation: 4022
If we think the generic problem faced by you, you are in the process of sequencing two independent object's creation and deletion activity, and in between these states (C & D
), you want to have assosiation between them.
This is not very specific to callback
class design, and it is very generic to any such problem area. callback
class design is just a sub-class of the whole set of problem - which re-occurs under differenet nomenclature in our system designs.
AFAI, there are only two ways to handle such design issue -
a. Give the ownership of the creation/destruction to the caller object. You can achieve this by passing the attribute of the callee to the callback and give the resposibility of Life Cycle of callee to the caller. This can ensure that - the object does not accidentally get deleted during the operational process - such as callback
in current design.
b. Other way is to have a shared_ptr
or object counter
mechasim (as mentioned in comment) which will ensure that only after the last instance of use-up
does the callee gets released.
Ofcourse, these are technicall ways, now how does it suite and make sense to our deisgn is to be taken into account. From that perspective, Option b
seems to make more sense than Option a
.
That said, From my experience, I have not seen much of code where either a or b
options are used. We just leave the callee
and caller
independent and ensure that callee
is not destroyed by careful coding :)
Upvotes: 1
Reputation: 25613
The first solution is to manage all callers by the classes itself. This could be done by derive from some kind of a manager class as you already describe.
The second one is to set up some manager class which owns all callable instances and caller lists. Instances must created and deleted by this manager class and all registered callbacks must be owned by this manager instance.
Next one could be some kind of smart/auto_ptr solution. Each of the pointers have a connection to some kind of manager class which unregister callbacks.
Basically you must know when a instance die and it doesn't matter how you want to do this.
For me this sounds like a basic design problem. The outside world must know the existence of some, maybe unknown, instances of any class and there is no well defined interface to interact between the different classes and their instances. Maybe proxies can help to manage the interface. But without any knowledge of the rest of your program we would not find the best solution.
Upvotes: 0