I'm writing a set of simple widgets for a microcontroller application. I'd like to use the observer pattern to pass around events: when a button is clicked, when a timeout occurs - there are many uses which I'm currently implementing with very ugly glue code, with similar code occuring again and again.
The whole system is more or less static, i.e. widgets are created on startup, not dynamically. I'd like to get away with as little heap usage as possible, just to be able to tell (at compile time) if I'm running out of RAM (16 kB). This is the reason why I've not implemented any delegate destructor code. It would never be used while the system is running.
When an event occurs, a Signal
is fired, calling all connected observers. These are delegates that call the actual free function or method on some object. Here's my current code:
#include <iostream>
#include <functional>
#include <list>
// Interface for delegates with the same set of arguments
template<typename... args>
class AbstractDelegate
{
public:
virtual void operator()(args...) = 0;
};
There are two concrete Delegate classes. One for non-static member functions and one for free functions:
// Concrete member function delegate that discards the function's return value
template<typename T, typename ReturnType, typename... args>
class ObjDelegate : public AbstractDelegate<args...>
{
public:
typedef ReturnType (T::*ObjMemFn)(args...);
ObjDelegate(T& obj, ObjMemFn memFn)
: obj_(obj),
memFn_(memFn)
{
}
void operator()(args... a)
{
(obj_.*memFn_)(a...);
}
private:
T& obj_;
ObjMemFn memFn_;
};
// Concrete function delegate that discards the function's return value
template<typename ReturnType, typename... args>
class FnDelegate : public AbstractDelegate<args...>
{
public:
FnDelegate(ReturnType (*fn)(args...))
: fn_(fn)
{
}
void operator()(args... a)
{
(*fn_)(a...);
}
private:
ReturnType (*fn_)(args...);
};
Helper functions for Delegate construction:
// create a delegate, returned object can be auto'ed
template<typename T, typename ReturnType, typename... args>
ObjDelegate<T, ReturnType, args...> make_delegate(T& obj, ReturnType (T::*memFn)(args...))
{
return ObjDelegate<T, ReturnType, args...>(obj, memFn);
}
// create a delegate, returned object can be auto'ed
template<typename ReturnType, typename... args>
FnDelegate<ReturnType, args...> make_delegate(ReturnType(*Fn)(args... a))
{
return FnDelegate<ReturnType, args...>(Fn);
}
These two classes provide similar interfaces that I'd like to call when an event occurs:
class A
{
public:
void foo(int i)
{
std::cout << "A::foo(" << i << ")" << std::endl;
}
static int baz(int i) {std::cout << "A::baz(" << i << ")" << std::endl; return i;}
};
class B
{
public:
int bar(int i)
{
std::cout << "B::bar(" << i << "): " << i+1 << std::endl;
return i;
}
};
A free function should also work:
int foo(int i)
{
std::cout << "foo(" << i << ")" << std::endl;
return i;
}
The signal class is used to call a number of delegates. I've only included it here to complete the example, the implementation for the microcontroller application will be different:
template<typename... args> // just the event arguments, nothing more
class Signal
{
public:
typedef AbstractDelegate<args...>* delegate_p;
void operator()(args... a) const
{
typename std::list<delegate_p>::const_iterator i = listeners_.begin();
while(i != listeners_.end())
{
(**i)(a...);
++i;
}
}
void connect(const delegate_p& p)
{
listeners_.push_back(p);
}
private:
std::list<delegate_p> listeners_;
};
main
creates a signal and adds observers to it:
int main()
{
// void(int) member:
A a;
auto d0 = make_delegate(a, &A::foo);
// int(int) member:
B b;
auto d1 = make_delegate(b, &B::bar);
// int(int) free function:
auto d2 = make_delegate(foo);
// int(int) static member function:
auto d3 = make_delegate(&A::baz)
Signal<int> sig;
sig.connect(&d0);
sig.connect(&d1);
sig.connect(&d2);
sig.connect(&d3);
sig(3);
return 0;
}