Hello,
I'm facing a new issue with my event library.
Almost all is working very well by now with that lib, but today I faced a problem I did not anticipated : What to do if the dispatchEvent function call for a member function using a given instance that doesn't exist anymore, while dispatching the entire registered callbacks list ? Indeed in that case, it does crash (or it should)... but as I was working on a completely different part, I did not realized immediately that the problem came from the Event library...
So I'm searching for a nice way to handle this case.
Indeed the best practice is to not forget to unregister the event before deleting the given instance of an object that has registered interest in an another object event dispatch... but it would be nice to find a way to make an assert or to auto unregister callbacks pointing to instances which does not exist anymore.
I could use boost::shared_pointer too, but I try to avoid use of others libs as much as possible (both for learning purpose and to minimize dependencies).
Here is a very simple working example :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
class AAA
{
//constructors with string argument for name etc....
public:
void AnEventHandler( Event evt )
{ std::cout << "Called from " << m_name << std::endl; }
};
class BBB
{
//constructors with string argument for name etc...
public:
void AnAnotherEventHandler( Event evt )
{ std::cout << "Called from " << m_name << std::endl; }
};
class CCC : public Eventdispatcher
{
//constructors etc...
};
main()
{
AAA myAinstance("AAA1");
BBB myBinstance("BBB1");
CCC myDispatcherClassInstance;
myDispatcherClassInstance.addEventListener( Event::ON_XXX, myAinstance, &AAA::AnEventHandler );
myDispatcherClassInstance.addEventListener( Event::ON_XXX, myBinstance, &BBB::AnAnotherEventHandler);
myDispatcherClassInstance.dispatchEvent( Event( Event::ON_XXX, arg1, arg3, etc) );
// Output :
// Called from AAA1
// Called from BBB1
}
|
And this work very well (not this example as this is a simplified example).
But if I do the following, the dispatch function call for an unexisting registered instance and make a segfault error.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
|
// Same class AAA, BBB and CCC as ine the previous example ...
CCC myDispatcherClassInstance;
void MyCrahsFunction()
{
AAA anotherAinstance("AAA2");
myDispatcherClassInstance.addEventListener( Event::ON_XXX, anotherAinstance, &AAA::AnEventHandler );
/* At that point the anotherAinstance will be deleted as the function is complete
but the myDispatcherClassInstance has registered the callback and doesn't know
that the AAA instance doesn't exist anymore. Except indeed if I do not forget to
call the following removeEventListener function :*/
//myDispatcherClassInstance.removeEventListener( Event::ON_XXX, anotherAinstance, &AAA::AnEventHandler );
/* The point is exactly to know how to avoid crash if this removeEventListener
was forgotten, or at least to get an assert or something to alert about the problem */
}
main()
{
AAA myAinstance("AAA1");
BBB myBinstance("BBB1");
myDispatcherClassInstance.addEventListener( Event::ON_XXX, myAinstance, &AAA::AnEventHandler );
myDispatcherClassInstance.addEventListener( Event::ON_XXX, myBinstance, &BBB::AnAnotherEventHandler);
MyCrahsFunction();
/* The following call will crash the program, because the registered callback
pointing to anotherAinstance can't find it as it does not exist anymore.*/
myDispatcherClassInstance.dispatchEvent( Event( Event::ON_XXX, arg1, arg3, etc) );
// Output :
// Called from AAA1
// Called from BBB1
// ---Crash while trying to call AAA2
}
|
It does not systematically crash as the address is freed but not necessarily overwritten, except for virtual member function which immediately know if the member doesn't exist anymore as the virtual function doesn't exist anymore too.
Anyhow, it's better to avoid this situation even with non virtual member functions.
So the question is :
Is there any way to verify if the pointer (pointing to the callback instance) is still valid ?
Here is a shortened version of the MemberEventDelegate struct which handle the member function callbacks. Another class keep a list of all MemberEventDelegate and FunctionEventDelegate and call Invoke for each one on dispatch call.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
template< class C, class T >
struct MemberEventDelegate : IEventDelegate
{
typedef void(C::*eventMethod)(T);
MemberEventDelegate( C& _target, eventMethod _method ) : target(_target), method(_method)//, target2(&_target)
{ IEventDelegate::count_ = 0; }
virtual ~MemberEventDelegate(){};
virtual void Invoke( void* sender ) const
{
/* Here is the crash. If target point to an instance that
doesn't exist anymore the function call will crash the program.*/
//(target2->*(method))(sender); // pointer version
(target.*(method))(sender); // reference version
}
private:
eventMethod method;
//C* target2;
C& target;
};
|