patterncppMinor
Very simple events implementation
Viewed 0 times
implementationsimpleeventsvery
Problem
I've been thinking about implementing events and wrote some abstract code:
It compiles and works as expected, but... I have to write more code to inheri
#include
#include
#include
template
class Dispatcher
{
public:
class Listener
{
public:
virtual void OnEvent(Event& event, Dispatcher& sender) {};
};
private:
typedef std::shared_ptr ListenerPtr;
typedef std::vector Listeners;
Listeners _listeners;
public:
void Reg(ListenerPtr listener)
{
if( _listeners.end() != std::find(_listeners.begin(), _listeners.end(), listener) )
return;
_listeners.push_back(listener);
}
void Unreg(ListenerPtr listener)
{
Listeners::iterator iter = std::find(_listeners.begin(), _listeners.end(), listener);
if( _listeners.end() == iter )
return;
_listeners.erase(iter);
}
void Dispatch(Event& event)
{
for( Listeners::iterator iter = _listeners.begin(); iter != _listeners.end(); ++iter )
(*iter)->OnEvent(event, *this);
}
};
struct SomeEvent
{
int someParam;
SomeEvent(int someParam) : someParam(someParam) {}
};
class SomeDispatcher : public Dispatcher
{
};
struct OtherEvent
{
int otherParam;
OtherEvent(int otherParam) : otherParam(otherParam) {}
};
class OtherDispatcher :
public SomeDispatcher, public Dispatcher
{
};
class Consumer :
public Dispatcher::Listener,
public Dispatcher::Listener
{
virtual void OnEvent(SomeEvent& event, Dispatcher& sender)
{
std::cout & sender)
{
std::cout consumer(new Consumer());
dispatcher.Dispatcher::Reg( consumer );
dispatcher.Dispatcher::Reg( consumer );
dispatcher.Dispatcher::Dispatch(SomeEvent(1));
dispatcher.Dispatcher::Dispatch(OtherEvent(2));
dispatcher.Dispatcher::Unreg( consumer );
dispatcher.Dispatcher::Dispatch(SomeEvent(3));
dispatcher.Dispatcher::Dispatch(OtherEvent(4));
return 0;
}It compiles and works as expected, but... I have to write more code to inheri
Solution
Listner needs a virtual destructor
and the
You need the virtual destructor because delete will more than likely be called via a pointer to the base class (Listener) at some point.
No point in implementing
Personally I would prefer to use
Be careful of using '_` on the front of identifiers. The rules are not obvious so best avoided to make sure you don't actually break them. Underscore Rules
Personally not a fan of the following:
Nothing wrong with it. But reversing the order of the test just makes you pause a second. See Yoda Conditionals.
Prefer to use standard algorithms rather than hand coded loops.
If you want to have listeners that only deal with a specific type of event. You can write your Listner to be a filtering lsitener.
Change to:
and the
OnEvent() method should probably be pure virtual:class Listener
{
public:
virtual void OnEvent(Event& event, Dispatcher& sender) {};
};
// Try
class Listener
{
public:
virtual ~Listener() {}
virtual void OnEvent(Event& event, Dispatcher& sender) = 0;
};You need the virtual destructor because delete will more than likely be called via a pointer to the base class (Listener) at some point.
No point in implementing
OnEvent() to do nothing if you need sombody to over-ride it.Personally I would prefer to use
boost::ptr_vector<> rather than a std::vector>. The boost vector has a couple of advantages (less lcoks required). Also its members are provided via references rather than pointers so it makes it more natural to use with the standard algorithms (not such a big deal in C++11 (with lambdas) but it makes C++03 much easier to use).typedef std::shared_ptr ListenerPtr;
typedef std::vector Listeners;
Listeners _listeners;
// Replace with:
boost::ptr_vector listeners;Be careful of using '_` on the front of identifiers. The rules are not obvious so best avoided to make sure you don't actually break them. Underscore Rules
Personally not a fan of the following:
if( _listeners.end() != std::find(_listeners.begin(), _listeners.end(), listener) )Nothing wrong with it. But reversing the order of the test just makes you pause a second. See Yoda Conditionals.
Prefer to use standard algorithms rather than hand coded loops.
for( Listeners::iterator iter = _listeners.begin(); iter != _listeners.end(); ++iter )
(*iter)->OnEvent(event, *this);
// Try
std::for_each(listeners.begin(), listeners.end(),
std::bind2nd(Listner::OnEvent, event)
); // Assume boost::ptr_vector<>
// Or go all C++11 on us
std::for_each(std::begin(listeners), std::end(listeners),
[&event](std::shared_ptr& lis) {lis->OnEvent(event);}
);If you want to have listeners that only deal with a specific type of event. You can write your Listner to be a filtering lsitener.
class Listener
{
public:
virtual void OnEvent(Event& event, Dispatcher& sender) {};
};Change to:
template
class Listener
{
public:
~Listener() {}
// This is the method called by the dispatcher.
void eventFilter(OnEvent& event, Dispatcher& sender)
{
// dynamic_cast to a pointer type.
// Result is NULL if not the correct type.
// If you used a value type then it would throw an exception.
ForEvent* specialEvent = dynamic_cast(&event);
if (specialEvent != NULL)
{
// Only call the OnEvent for the types we want.
// put back to a references (so OnEvent does not need to
// check for NULL).
this->OnEvent(*specialEvent, sender);
}
}
virtual void OnEvent(ForEvent& event, Dispatcher& sender) = 0;
};Code Snippets
class Listener
{
public:
virtual void OnEvent(Event& event, Dispatcher& sender) {};
};
// Try
class Listener
{
public:
virtual ~Listener() {}
virtual void OnEvent(Event& event, Dispatcher& sender) = 0;
};typedef std::shared_ptr<Listener> ListenerPtr;
typedef std::vector<ListenerPtr> Listeners;
Listeners _listeners;
// Replace with:
boost::ptr_vector<Listener> listeners;if( _listeners.end() != std::find(_listeners.begin(), _listeners.end(), listener) )for( Listeners::iterator iter = _listeners.begin(); iter != _listeners.end(); ++iter )
(*iter)->OnEvent(event, *this);
// Try
std::for_each(listeners.begin(), listeners.end(),
std::bind2nd(Listner::OnEvent, event)
); // Assume boost::ptr_vector<>
// Or go all C++11 on us
std::for_each(std::begin(listeners), std::end(listeners),
[&event](std::shared_ptr<Listener>& lis) {lis->OnEvent(event);}
);class Listener
{
public:
virtual void OnEvent(Event& event, Dispatcher& sender) {};
};Context
StackExchange Code Review Q#27007, answer score: 6
Revisions (0)
No revisions yet.