patterncppMinor
Delegate and Observer pattern implementation for an embedded system
Viewed 0 times
embeddedobserversystemdelegateforandimplementationpattern
Problem
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
There are two concrete Delegate classes. One for non-static member functions and one for free functions:
Helper functions for Delegate construction:
```
// create a delegate, returned object can be auto'ed
template
ObjDelegate make_deleg
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
#include
#include
// Interface for delegates with the same set of arguments
template
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
class ObjDelegate : public AbstractDelegate
{
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
class FnDelegate : public AbstractDelegate
{
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
ObjDelegate make_deleg
Solution
Some tidbits about using C++11 features efficiently:
-
I find that the old
into this one using a C++11 type alias:
-
You can use curly braces instead of regular parenthesis to initilize the member variables in your constructor initialization lists. That will prevent implicit narrowing type conversions:
Be careful though, it does not work with some types, like reference types for example.
-
you can also use the new
While it is not necessary, it may help you to catch some subtle errors when the signature of the function you want to override in the base class is slightly different from the one you wrote in the derived class.
-
You can use list initialization to reduce the boilerplate in
-
Don't hesitate to use
write:
Note that I used
-
And the usual one, that I almost forgot (because it's not C++11 specific): you do not need to write
-
I find that the old
typedef is rather hard to read for function pointers. You could refactor this line:typedef ReturnType (T::*ObjMemFn)(args...);into this one using a C++11 type alias:
using ObjMemFn = ReturnType (T::*)(args...);-
You can use curly braces instead of regular parenthesis to initilize the member variables in your constructor initialization lists. That will prevent implicit narrowing type conversions:
ObjDelegate(T& obj, ObjMemFn memFn)
: obj_{obj},
memFn_{memFn}
{}Be careful though, it does not work with some types, like reference types for example.
-
you can also use the new
override specifier to explicitly tell the compiler that you do want to override a base class function. For example, ObjDelegate::operator() could be declared as:void operator()(args... a) override
{
// ...
}While it is not necessary, it may help you to catch some subtle errors when the signature of the function you want to override in the base class is slightly different from the one you wrote in the derived class.
-
You can use list initialization to reduce the boilerplate in
make_delegate: using a pair of curly braces in a return statement constructs and returns a value of the same type as the declared return type:template
FnDelegate make_delegate(ReturnType(*Fn)(args... a))
{
return { Fn };
}-
Don't hesitate to use
auto to deduce types instead of writing overly long types. Some same that the idiom should be "Almost Always auto". For example, instead of:typename std::list::const_iterator i = listeners_.begin();write:
auto i = listeners_.cbegin();Note that I used
cbegin instead of begin to make sure that the deduced type would be const. If two overload of a function are available (const and non-const), the one without the const specifier will be choosed by the compiler.-
And the usual one, that I almost forgot (because it's not C++11 specific): you do not need to write
return 0; at the end of your main. If your main ends without having encountered a return statement, the compiler will automagically add return 0; at the end of main for you :)Code Snippets
typedef ReturnType (T::*ObjMemFn)(args...);using ObjMemFn = ReturnType (T::*)(args...);ObjDelegate(T& obj, ObjMemFn memFn)
: obj_{obj},
memFn_{memFn}
{}void operator()(args... a) override
{
// ...
}template<typename ReturnType, typename... args>
FnDelegate<ReturnType, args...> make_delegate(ReturnType(*Fn)(args... a))
{
return { Fn };
}Context
StackExchange Code Review Q#47098, answer score: 6
Revisions (0)
No revisions yet.