Since most of my daily work is done in C++, every now and then I miss some niceties which are available in C#. One such example are .NET events, which are really an elegant solution for implementing observer design pattern.
Idiomatic C++ implementations of observer pattern are based on defining abstract interfaces, which have to be implemented by subject and observer classes. Sometimes it would be nicer to bind to an event just based on event/method signature instead of building all the needed scaffolding.
Here is a quick take at this problem. This is by no means a complete solution, but can be a basis for more elaborate one.
Events are defined and fired like this:
class Subject
{
public:
event<int> ValueChanged; // same template is used, regardless of number of arguments
event<int, const char*> ValueAndNameChanged; // no need for having event1<A> and event2<A, B>
void SetValue(int x)
{
// .. do something
ValueChanged(x); // then fire ValueChanged event
}
void SetValueAndName(int x, const string& name)
{
// do something...
ValueAndNameChanged(x, name.c_str()); // fire ValueAndNameChanged event
}
};
Clients consume events like this:
// implementation of observer object
class Observer
{
public:
void OnValueChanged(int i)
{
printf(" - non-static handler for SetValue called, new value = %ld\n", i);
}
static void OnValueChangedStatic(int i)
{
printf(" - static handler for SetValue called, new value = %ld\n", i);
}
};
// global functions work, too:
void OnValueChangedGlobal(int i)
{
printf(" - global handler for SetValue called, new value = %ld\n", i);
}
// test
int _tmain(int argc, _TCHAR* argv[])
{
Subject subject;
Observer observer;
subject.ValueChanged += Observer::OnValueChangedStatic; // static member method as handler
subject.ValueChanged += OnValueChangedGlobal; // global function as handler
subject.ValueChanged += event_handler(&observer, &Observer::OnValueChanged); // non-static member
subject.SetValue(3); // this will fire the event
// detach from event. This is not necessary when lifetime of observer is shorter than lifetime
// of the subject, but if subject outlives observer, handler must be unregistered
subject.ValueChanged -= Observer::OnValueChangedStatic;
subject.ValueChanged -= OnValueChangedGlobal;
subject.ValueChanged -= event_handler(&observer, &Observer::OnValueChanged);
return 0;
}
Output of this snippet is:
d:\dev\events>events.exe
- static handler for SetValue called, new value = 3
- global handler for SetValue called, new value = 3
- non-static handler for SetValue called, new value = 3
It is possible to use static and non-static members as handlers, as well as global functions. Note, however, that there is one difference compared to event behavior in .NET: if you register same handler twice, it will be called only once. This can easily be changed in the code, but it seems more logical to me.
Attached archive contains a Visual Studio solution with implementation of event class (events.h) and some additional examples.
events.zip (5.14 kb)