I am working on code to implement a lightweight version of something similar to Qt's signals/slots mechanism (i.e. a glorified Observer pattern) where I can connect "signals" to "slots." A "connection" can only be made if the two signatures are identical. Any time a "signal" is emitted, all of the calls to any attached "slots" will be queued to be executed in their appropriate threads at a later time.
I have been doing a large amount of research on this topic and understand that what I want can be achieved through some combination of Functors and templates. However, I am unable to figure out how to make everything work the way I would like. Also, this is being used on an embedded processor so I don't want to use std::function which, as I have read, has a large amount of overhead associated with it.
So far, I have written a successful signature for a "connect" function as follows:
template<typename OBJECT, typename FUNC>
static void connect(OBJECT *sender, FUNC signal, OBJECT *receiver, FUNC slot) {
}
//...
Test1 t;
Test1::connect(&t, &Test1::signal1, &t, &Test1::slot1);
Now, I need some way to store a function call associated with the object/slot to be stored and called by the signal when it is emitted. I understand that this should be done with a Functor. However, I can't figure out how to write a functor that is agnostic of the object but requires a specific signature. I am looking for something along the lines of:
GenericFunctor<int, int> slotsConnectedToSignal1[4];
GenericFunctor<int, char, int> slotsConnectedToSignal2[4];
In this way, the signal (which has the same signature as the array that contains its connected slots) can loop through the array and call all the functor.
Is there any way to achieve what I am trying to accomplish and am I on the right track?
Thank You!
EDIT
I am getting closer to what I want using the following definitions for connect().
template <typename ObjSender, typename Ret, typename ObjReceiver>
static void connect(ObjSender *sender, Ret(ObjSender::*signal)(), ObjReceiver *receiver, Ret(ObjReceiver::*slot)()) {
std::function<Ret()> fSender = std::bind(signal, sender);
std::function<Ret()> fReceiver = std::bind(slot, receiver);
}
template <typename ObjSender, typename Ret, typename ARG0, typename ObjReceiver>
static void connect(ObjSender *sender, Ret(ObjSender::*signal)(ARG0), ObjReceiver *receiver, Ret(ObjReceiver::*slot)(ARG0)) {
std::function<Ret(ARG0)> fSender = std::bind(signal, sender, std::placeholders::_1);
std::function<Ret(ARG0)> fReceiver = std::bind(slot, receiver, std::placeholders::_1);
}
Now, my next question is how to store and recall these std::function objects in their correct signals. For example, when the user calls signal1(1, 2), this function should be able to look up all the "connected" std::function objects associated with it and call each one in turn with arguments.
Also, I need to mention that this code is targeted for an embedded system which is why I am attempting to develop this from scratch so as to minimize the overhead from external libraries.
EDIT 2
Based upon some of the feedback I have received, the following is my most recent attempt to achieve my desired results.
template<typename ... ARGS>
class Signal {
public:
void operator()(ARGS... args) {
_connection(args...);
}
void connect(std::function<void(ARGS...)> slot) {
_connection = slot;
}
private:
std::function<void(ARGS...)> _connection;
};
class Test2 {
public:
Signal<int, int> signal1;
Signal<int, int> signal2;
void slot1(int a, int b) {
signal1(a, b);
}
void slot2(int c, int d) {
int i = c + d;
(void)i;
}
};
int main(void) {
Test2 t2;
t2.signal1.connect(t2.signal2);
t2.signal2.connect(std::bind(&Test2::slot2, &t2, std::placeholders::_1, std::placeholders::_2));
t2.slot1(1, 2);
}
However, I still have the problem in this case that, when I want to connect to a "slot" function (instead of another signal), I need to used std::bind with the right number of placeholders. I know there must be a way to do this but am not familiar enough with out std::function and lambdas work.
What you are doing in your main() in "EDIT 2" is a little convoluted, but the basic issue is that your first connect() call is making a copy of the signal1 object, which is not the same as a reference to t2's member variable. One way to address this is to capture it with a lambda:
#include <iostream>
#include <functional>
#include <vector>
template<typename ... ARGS>
class Signal {
public:
void operator()(ARGS... args) const {
for( const auto& slot : _connection ) {
slot(args...);
}
}
// Return the index as a handle to unregister later
auto connect(std::function<void(ARGS...)> slot) {
_connection.emplace_back( std::move( slot ) );
return _connection.size() - 1;
}
// ... unregister function, etc.
private:
std::vector<std::function<void(ARGS...)>> _connection;
};
class Test2 {
public:
Signal<int, int> signal1;
Signal<int, int> signal2;
void slot1(int a, int b) {
std::cout << "slot1 " << a << ' ' << b << '\n';
signal1(a,b);
}
void slot2(int c, int d) {
std::cout << "slot2 " << c << ' ' << d << '\n';
}
};
int main() {
Test2 t2;
//t2.signal1.connect( t2.signal2 ); // Makes a copy of t2.signal2
t2.signal1.connect( [&]( auto x, auto y ) { t2.signal2(x,y); } ); // Keeps a reference to t2.signal2
t2.signal2.connect( [&]( auto x, auto y ) { t2.slot2( x, y ); } );
t2.signal1(1, 2);
}
See it live on Wandbox, where it calls only slot2() as expected:
slot2 1 2
FWIW, here is the same code (but shorter!) using Boost.Signals2:
#include <iostream>
#include <boost/signals2.hpp>
class Test2 {
public:
boost::signals2::signal<void(int, int)> signal1;
boost::signals2::signal<void(int, int)> signal2;
void slot1(int a, int b) {
std::cout << "slot1 " << a << ' ' << b << '\n';
signal1(a,b);
}
void slot2(int c, int d) {
std::cout << "slot2 " << c << ' ' << d << '\n';
}
};
int main() {
Test2 t2;
//t2.signal1.connect( t2.signal2 );
t2.signal1.connect( [&]( auto x, auto y ) { t2.signal2(x,y); } );
t2.signal2.connect( [&]( auto x, auto y ) { t2.slot2( x, y ); } );
t2.signal1(1, 2);
}
The only differences are the #includes, the declarations of signal1 and signal2, and the deletion of the home-spun Signal class. See it live on Wandbox.
Note that when Qt does this, it relies on macros and the MOC to generate some extra glue code to make it all work.
Update: Replying to your comment, yes. To support that syntax, you could add an overload that does the binding work for the user. You may get into some complications with having to supply multiple overloads for different const-nesses, but this should give you an idea:
#include <iostream>
#include <functional>
#include <utility>
#include <vector>
template<typename ... ARGS>
class Signal {
public:
void operator()(ARGS... args) const {
for( const auto& slot : _connection ) {
slot(args...);
}
}
template<class T>
auto connect(T& t, void(T::* fn)(ARGS...)) {
const auto lambda = [&t, fn](ARGS... args) {
(t.*fn)( std::forward<ARGS>( args )... );
};
return connect( lambda );
}
auto connect(std::function<void(ARGS...)> slot) {
_connection.emplace_back( std::move( slot ) );
return _connection.size() - 1;
}
private:
std::vector<std::function<void(ARGS...)>> _connection;
};
class Sender {
public:
Signal<int, int> signal1;
};
class Receiver {
public:
void slot1(int a, int b) {
std::cout << "slot1 " << a << ' ' << b << '\n';
}
void slot2(int a, int b) {
std::cout << "slot2 " << a << ' ' << b << '\n';
}
};
// Stand-alone slot
void slot3(int a, int b) {
std::cout << "slot3 " << a << ' ' << b << '\n';
}
int main() {
auto sender = Sender{};
auto recv = Receiver{};
// Register three different slots
sender.signal1.connect( [&]( auto x, auto y ) { recv.slot1( x, y ); } );
sender.signal1.connect( recv, &Receiver::slot2 );
sender.signal1.connect( &slot3 );
// Fire the signal
sender.signal1(1, 2);
}
I refactored a little and hopefully made it a little easier to grok. See it live on Wandbox, where it outputs the expected:
slot1 1 2
slot2 1 2
slot3 1 2
I think I have finally come up with a method that works for me based upon all the comments I have received. Here is a simplified version for anyone who is interested:
template<typename ... ARGS>
class Signal {
#define MAX_CONNECTIONS 4
public:
#define CONNECT_FAILED (ConnectionHandle)(-1);
Signal() : _connections{nullptr} {};
/**
* #brief Implementation of the "function" operator
*
* #param args Arguments passed to all connected slots (or signals)
*/
void operator()(ARGS... args) {
//Loop through the connections array
for (int i = 0; i < MAX_CONNECTIONS; i++) {
if (_connections[i]) {
/*
* Call the connected function
* This will either
* a) Call a lambda which will invoke a slot
* b) Call operator() on another signal (i.e. recursive signals)
*/
_connections[i](args...);
}
}
}
/**
* #brief Make a connection to a slot
*
* #param t A pointer to the object instance
* #param fn A member function pointer to the slot
* #return A handle (ID) used to disconnect the connection if desired
*
* #note This function assumes that T is a subclass of ActiveObject (i.e. has the invoke() method)
*/
template<class T>
inline ConnectionHandle connect(T* t, void(T::* fn)(ARGS...)) {
//This lambda will use ActiveObject::invoke to queue the connected slot for later execution
const auto lambda = [=](ARGS... args) { T::invoke(t, fn, args...); };
return connect(lambda);
}
/**
* #brief Make a connection to another signal
*
* #param t A pointer to the object instance
* #param s The signal
* #return A handle (ID) used to disconnect the connection if desired
*/
template<class T>
inline ConnectionHandle connect(T *t, Signal<ARGS...> T::*s) {
return connect(t->*s);
}
/**
* #brief Make a generic connection to a slot which takes an Event smart pointer as its argument
*
* #param t The object to connect to
* #param fn The member function of t to connect to
* #param eventIndex The user-defined index to assign to the event
* #return A handle (ID) used to disconnect the connection if desired
*
* #note This version of connect is useful to connect any signal to the same slot function which may dispatch the eent directly into its state machine (if derived from StateMachine)
*/
template<class T>
inline ConnectionHandle connect(T *t, void(T::* fn)(std::shared_ptr<Event> e), int eventIndex) {
const auto lambda = [=](ARGS... args){
std::shared_ptr<Event> ptr = std::make_shared<Event>(eventIndex);
T::invoke(t, fn, ptr);
};
return connect(lambda);
}
/**
* #brief Make a connection to an abstract function
*
* #param slot The function to connect to
* #return A handle (ID) used to disconnect the connection if desired
*/
ConnectionHandle connect(std::function<void(ARGS...)> slot) {
ConnectionHandle i;
//2nd: Make the connection in an empty slot
for (i = 0; i < MAX_CONNECTIONS; i++) {
if (!_connections[i]) {
//Make the connection
_connections[i] = slot;
return i;
}
}
return CONNECT_FAILED;
}
/**
* #brief Remove the given connection by its handle (i.e. ID)
* #param h The handle previously returned by a call to connect()
*/
void disconnect(ConnectionHandle h) {
if ((h < 0) || (h >= MAX_CONNECTIONS)) return;
_connections[h] = nullptr;
}
private:
std::function<void(ARGS...)> _connections[MAX_CONNECTIONS];
};
Where any object that wants to take advantage of signals and slots need to subclass the following:
class ActiveObject {
public:
#define DECLARE_SIGNAL(name,...) Signal<__VA_ARGS__> name
#define EMIT
#define DECLARE_SLOT(name, ...) void name(__VA_ARGS__)
#define DEFINE_SLOT(className, name, ...) void className::name(__VA_ARGS__)
ActiveObject(ActiveObjectThread *parentThread);
virtual ~ActiveObject();
/**
* #brief Called by the parent thread during initialization once the thread has started and is running
* #note This function may be overridden by sub-classes to provide initialization code
*/
virtual void initialize() {};
/**
* #brief Return the parent thread of this active object
*/
inline ActiveObjectThread *thread() const { return _parentThread; }
/**
* #brief Queue a slot to be called later by the parent thread
*
* #param t A pointer to the active object
* #param fn A member function pointer within the active object to execute
* #param args The arguments to pass to the slot function when called
* #note Invoke should ALWAYS be used when calling a slot function to ensure that it is executed within the same thread as the active object (i.e. the parent thread)
*/
template<class T, typename ... ARGS>
inline static void invoke(T *t, void(T::* fn)(ARGS...), ARGS... args) {
std::function<void()> *f = new std::function<void()>([=]() { (t->*fn)( args... ); });
//Queue in the parent thread
t->_parentThread->queueInvokable(f);
}
inline static void invoke(ActiveObject *ao, std::function<void()> f) {
std::function<void()> *newF = new std::function<void()>(f);
ao->_parentThread->queueInvokable(newF);
}
private:
ActiveObjectThread *_parentThread;
};
In my application, I am not calling any of the slots directly, but rather am queuing them for later execution by a thread.
Here is an example of how to use these classes:
class MyActiveObject : public ActiveObjecte {
public:
MyActiveObject() :
//Just create a thread as part of the object's constructor
ActiveObject(new ActiveObjectThread("MyActiveObject", 512, 1))
{
thread()->Start();
}
~MyActiveObject() {
//Make sure to delete the thread we created
delete thread();
}
DECLARE_SIGNAL(signal1, int, int);
DECLARE_SLOT(slot1) {
GenericEvent *e = new GenericEvent(EVENT_1);
e->args()[0] = 100;
//Dispatch the event into the state machine
dispatch(e);
EMIT signal1(5, 6);
}
DECLARE_SLOT(slot2, int a, int b) {
GenericEvent *e = new GenericEvent(EVENT_2);
e->args()[0] = a;
e->args()[1] = b;
//Dispatch the event into the state machine
dispatch(e);
}
DECLARE_SLOT(slotEventHander, std::shared_ptr<Event> e) {
dispatch(e.get());
}
};
MyActiveObject myActiveObject;
myActiveObject.signal1.connect(&myActiveObject, &MyActiveObject::slot2);
myActiveObject.signal1.connect(&myActiveObject, &MyActiveObject::slotEventHander, MyActiveObject::EVENT_2);
ActiveObject::invoke(&myActiveObject, &MyActiveObject::slot1);
I have taken out the code that implements the state machine because that is not relevant to this topic.
I hope this helps someone out!
Related
I have a question on callbacks. Previously, I am associating my callbacks to a class Q
class Q{
using Callback = std::function<void(char*, int)>;
Q:Q();
Q:~Q();
void Q::RegisterCB(Callback callbackfunc)
{
callback_func = callbackfunc;
}
void Q:someEvent()
{
callback_func();
}
};
void handleCallback( char*, int)
{
// perform some routine
}
// from my main file
int main()
{
Q q;
q.RegisterCB(&handleCallback);
}
It works well for me. However, when I need to transfer the handleCallback function to another class for cleaner code. I have problem with using same code
class R{
void R::handleCallback( char*, int)
{
// perform some routine
}
void R::someOp()
{
// q is some member variables of R
q.RegisterCB(&R::handleCallback, this);
}
};
However, i run into some problems of saying there is a "no matching function for call to .....". I thought it was just simply assigning from function name to class function name
May I have a hint to where I might go wrong?
Regards
&R::handleCallback has the type void (R::*)(char*, int), which is not convertible to std::function<void(char*, int)>.
Also, RegisterCB takes one argument, not two.
The most straightforward fix is to wrap the call in a lambda function,
q.RegisterCB([this](char* p, int x) { handleCallback(p, x); });
Example on how to use a lambda function to register a member function of an instance of R as event handler. (I replaced char* with string_view out of habit, it's not essential for this example). The use of "const" wherever you can is a recommendation.
#include <functional>
#include <string_view>
#include <iostream>
class Q
{
public:
// use const arguments, the callback is not supposed to change them
// just passing information on to callback
using callback_t = std::function<void(const std::string_view&, const int)>;
// initialize callback with a (lambda) function that does nothing
// this prevents the need for a check if callback has been set or not
// (Pattern : Null Strategy)
Q() :
m_callback_func( [](const std::string_view&,const int) {} )
{
}
~Q() = default;
void RegisterCallback(callback_t fn)
{
m_callback_func = fn;
}
void Event(const std::string_view& string, const int value)
{
m_callback_func(string,value);
}
private:
callback_t m_callback_func;
};
void handleCallback(const std::string_view& string, const int value)
{
std::cout << string << ", " << value << "\n";
}
class R
{
public:
void handleCallback(const std::string_view& string, const int value)
{
std::cout << string << ", " << value << "\n";
}
};
// from my main file
int main()
{
Q q1;
q1.RegisterCallback(handleCallback);
q1.Event("Hello", 42);
// to pass a callback to an instance of a class
// you can use a lambda function https://en.cppreference.com/w/cpp/language/lambda
R r;
Q q2;
q2.RegisterCallback([&r](const std::string_view& string, const int value)
{
r.handleCallback(string,value);
});
q2.Event("World",21);
return 0;
}
I have recently returned to Visual C++ after a while programming in C where callbacks are much easier.
I have a singleton class which controls 0..* connected devices.
My idea is to create a function in this class which will iterate over the set of
connected devices and publish it via a callback to whatever might require it.
e.g.
Singleton class
typedef void (CALLBACK * PortListCallback_t)(ptrConstCComPortInfo_t);
.
.
.
void CCommsMgr::listPorts(PortListCallback_t cb)
{
PortInfoSetConstIter_t i;
for (i = m_setPorts.begin(); i != m_setPorts.end(); i++)
{
cb(*i);
}
}
In the first instance the consumer is an MFC dialog class which works fine if it's callback is static. However in order to access member data/functions of the dialog class I would need to pass 'this' to the singleton class and have it reflected back.
e.g.
Singleton class
typedef void (CALLBACK * PortListCallback_t)(void *, ptrConstCComPortInfo_t);
.
.
.
void CCommsMgr::listPorts(void *pObj, PortListCallback_t cb)
{
PortInfoSetConstIter_t i;
for (i = m_setPorts.begin(); i != m_setPorts.end(); i++)
{
cb(pObj, *i);
}
}
Dialog Class
static void CALLBACK getPorts(void *obj, ptrConstCComPortInfo_t port);
.
.
.
void CALLBACK CMFC_iTFTPDlg::getPorts(void *obj, ptrConstCComPortInfo_t port)
{
CMFC_iTFTPDlg *pThis = (CMFC_iTFTPDlg*)obj;
// do something with it
}
My question - Is there a better way of doing this? Static functions feel like a kludge and I do not want the Singleton class to be constrained by how it might be used.
If I remove the static on getPorts it will not compile. To repeat myself the Singleton class should have no knowledge of it's consumer.
With help from the excellent hints from WhozCraig, this is what I came up with:
#include <functional> // std::function, std::bind, std::placeholders
#include <iostream>
#include <vector>
class ConstCComPortInfo {};
using ptrConstCComPortInfo_t = ConstCComPortInfo*;
using callback_t = void(void*, ptrConstCComPortInfo_t);
using function_t = std::function<callback_t>;
// an example class with a member function to call
class foo {
public:
foo(const std::string& name) : instance_name(name) {}
void bar(void* something, ptrConstCComPortInfo_t c) {
std::cout << "foo::bar(" << instance_name << ") called\n"
"void* = " << something << "\n"
"ptrConstCComPortInfo_t = " << c << "\n";
}
private:
std::string instance_name;
};
// and a free function to call
void free_func(void* something, ptrConstCComPortInfo_t c) {
std::cout << "free_func_called\n"
"void* = " << something << "\n"
"ptrConstCComPortInfo_t = " << c << "\n";
}
int main() {
// some instances of the class
foo via_bind("called_via_bind");
foo via_lambda("called_via_lambda");
ptrConstCComPortInfo_t bork = nullptr; // dummy value
// a vector of callback subscribers
std::vector<function_t> subscribers{
&free_func,
std::bind(&foo::bar, &via_bind, std::placeholders::_1, std::placeholders::_2),
[&via_lambda](void* p, ptrConstCComPortInfo_t c) { via_lambda.bar(p, c); }
};
// perform callbacks
for(auto& cb : subscribers) {
cb(nullptr, bork);
}
}
Output:
free_func_called
void* = 0
ptrConstCComPortInfo_t = 0
foo::bar(called_via_bind) called
void* = 0
ptrConstCComPortInfo_t = 0
foo::bar(called_via_lambda) called
void* = 0
ptrConstCComPortInfo_t = 0
I want to do a member function that will call every X seconds. I did a little prototype that can handle non member function, but I don't know if I did it well, and I can't handle both member function and non member function.
I have an Event object, which handle the function and the delay, with a basic timer, to detect when we need to run the function:
typedef void (*ScheduleFunction)(float dt);
class Event
{
private:
ScheduleFunction m_Func;
double m_Timer;
double m_Delay;
public:
Event(ScheduleFunction function, double delay)
{
m_Func = function;
m_Delay = delay;
}
void Call(float dt)
{
m_Timer += dt;
if (m_Timer >= m_Delay)
{
m_Func(dt);
m_Timer = 0.0;
}
}
};
Then, I have another object that call every frames each function into a vector<Event>:
class Handler
{
private:
void m_MemberFunction(float dt)
{
std::cout << "A member function." << std::endl;
}
std::vector<Event> m_ScheduleItems;
public:
Handler()
{
// This will not compile, because the function expect a non member function
Schedule(&Handler::m_MemberFunction, 1.0);
}
void CallScheduledFunctions(float dt)
{
for (std::vector<Event>::iterator it = m_ScheduleItems.begin(); it != m_ScheduleItems.end(); ++it)
{
it->Call(dt);
}
}
void Schedule(ScheduleFunction func, double delay)
{
Event event(func, delay);
m_ScheduleItems.push_back(event);
}
void Unschedule()
{
// TODO
}
};
As you can see, I have a function Schedule that register new Event. But right now, it only handle non member function. Is there a way that I can handle non member function and member function, not only from the Handler but also on all others objects?
If there is no way, how can I achieve this?
Using std::function is the way to go. Anything that can be called can be transformed/wrapped into an std::function.
In your case, you could write your Event constructor like this:
Event(std::function<void(float)>, double delay);
And you can call this with a standalone function, a functor or a lambda.
Some examples:
// declaration
auto myDummyFunction (float) -> void;
// Calling the constructor
auto event = Event(myDummyFunction,1.0);
If we want to pass a member function, just use a lambda:
// declaration of the class with the member function
class SomeOtherClass
{
public:
auto someMethod(float) -> void;
};
// Calling the constructor
auto someOtherClass = SomeOtherClass{};
auto event = Event([&someOtherClass](float f){someOtherClass.someMethod(v)},1.0);
In general I find lambda's more readable and flexible than the std::bind approach. As far as I can remember, it's advised (was it Herb or Scott?) not to use std::bind anymore, but to use lambda's instead.
UPDATE 1
Added "call any object's members" below.
BRIEF
I recommend using std::function and std::bind. But remind that std::function has some overhead due to the internal mechanisms!
std::function is very powerful as there are many things you can store in it.
Important:
Using a function-pointer only approach is possible, but would cause some code and complexity if you must retain the simple unified interface.
EXAMPLE
#include <functional>
using ScheduleFunction_t = std::function<void(float)>;
class Event {
private:
ScheduleFunction_t
m_Func;
double
m_Timer,
m_Delay;
public:
Event(
ScheduleFunction_t const&function,
double delay)
: m_Func(function)
, m_Delay(delay)
{ }
void Call(float dt) {
m_Timer += dt;
if (m_Timer >= m_Delay)
{
// Important, if you do not assert in the constructor, check if the fn is valid...
// The ctr shouldn't throw on runtime assert fail... memory leak and incpomplete construction...
if(m_Func)
m_Func(dt);
m_Timer = 0.0;
}
}
};
As you can see, including the <functional> header will give you the template std::function<R(Args...)>, where R is the return type and Args... a comma separated list of fully qualified argument types.
void g_freeFunction(float f) {
std::cout << "Globally floating for " << f << "ms" << std::endl;
}
class Handler {
private:
void m_MemberFunction(float dt) {
std::cout << "Floating around with " << dt << " m/s" << std::endl;
}
std::vector<Event> m_ScheduleItems;
public:
Handler() {
// Bind member function
Schedule<Handler, &Handler::m_MemberFunction>(this);
// Or free
Schedule(&g_freeFunction);
// Or lambda
Schedule([](float f) -> void { std::cout << "Weeeeeeeh...." << std::endl; });
}
void CallScheduledFunctions(float dt)
{
for(Event& e : m_ScheduleItems)
e.Call(dt);
}
template <typename TClass, void(TClass::*TFunc)(float)>
void Schedule(
TClass *const pInstance,
double delay = 0.0)
{
m_ScheduleItems.emplace_back(std::bind(TFunc, pInstance, std::placeholders::_1), delay); // Create in place at the end of vector.
}
void Schedule(
ScheduleFunction_t fn,
double delay = 0.0)
{
m_ScheduleItems.emplace_back(fn, delay); // Create in place at the end of vector.
}
void Unschedule() { /* TODO */ }
};
This way you can now bind almost whatever you want. :D
Update:
The Schedule-function can not be called for any other type that has a matching public method, e.g.:
struct Test {
void foo(float f) {
std::cout << "TEST ME!" << std::endl;
}
};
int main()
{
Test t={};
Handler h = Handler();
h.Schedule<Test, &Test::foo>(&t);
for(uint32_t k=0; k < 32; ++k)
h.CallScheduledFunctions(k);
}
RESOURCES
http://en.cppreference.com/w/cpp/utility/functional
http://en.cppreference.com/w/cpp/utility/functional/function
http://en.cppreference.com/w/cpp/utility/functional/bind
WORKING EXAMPLE
http://cpp.sh/7uluut
My problem is the following. I start several operations asynchronously, and I want to continue until all of them are finished. Using Boost Asio, the most straightforward way to do this is the following. Suppose tasks is some kind of container of objects that support some asynchronous operation.
tasksToGo = tasks.size();
for (auto task: tasks) {
task.async_do_something([](const boost::system::error_code& ec)
{
if (ec) {
// handle error
} else {
if (--taslsToGo == 0) {
tasksFinished();
}
}
});
}
The problem with this solution is that it feels like a workaround. In Boost 1.54 I can do it with futures but I can only wait synchronously, which is only possible from a thread separate from where run() is called.
for (auto task: tasks) {
futures.push_back(task.async_do_something(boost::asio::use_future));
}
for (auto future: futures) {
future.wait();
}
This code is much clearer than the previous one, but I need a separate thread which I don't want. I want something that can be used like this:
for (auto task: tasks) {
futures.push_back(task.async_do_something(boost::asio::use_future));
}
boost::asio::spawn(ioService, [](boost::asio::yield_context yield)
{
for (auto future: futures) {
future.async_wait(yield);
}
tasksFinished();
}
Is there anything that can be used similarly?
As far as I know, there is currently no first-class support for this. However, given the direction of the library, I would be surprised if this functionality was not available in the future.
A few papers have been proposed to add support for this type of functionality:
N3558 - A Standardized Representation of Asynchronous Operations is particularly interesting. It proposes when_all(futures) and future.next(). If it is implemented, then it would be possible to represent the asynchronous chain as:
for (auto task: tasks) {
futures.push_back(task.async_do_something(boost::asio::use_future));
}
when_all(futures).then(&tasksFinished);
N3562 - Executors and schedulers introduces executors. Which can be used to provided finer control as to the context in which an async can execute. For Boost.Asio, this would likely require providing some type of executor that defers to the io_service.
While these papers are still ongoing, it may be worthwhile to periodically check Boost.Thread's Conformance and Extension page and Boost.Asio's github for early adaptations of these proposals.
I had the need for this functionality a year ago with a much earlier version of Boost so worked on my own solution. There are still some rough areas with regards to the semantics, but it may be helpful as a reference material until something official is adopted. Before I provide the code, here is an example application based on your question:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include "async_ops.hpp"
void handle_timer(const boost::system::error_code& error, int x)
{
std::cout << "in handle timer: " << x << " : "
<< error.message() << std::endl;
}
void a() { std::cout << "a" << std::endl; }
void b() { std::cout << "b" << std::endl; }
void c() { std::cout << "c" << std::endl; }
int main()
{
boost::asio::io_service io_service;
boost::asio::deadline_timer timer1(io_service);
boost::asio::deadline_timer timer2(io_service);
// Create a chain that will continue once 2 handlers have been executed.
chain all_expired = when_all(io_service, 2);
all_expired.then(&a) // Once 2 handlers finish, run a within io_service.
.then(&b) // Once a has finished, run b within io_service.
.then(&c); // Once b has finished, run c within io_service.
// Set expiration times for timers.
timer1.expires_from_now(boost::posix_time::seconds(2));
timer2.expires_from_now(boost::posix_time::seconds(5));
// Asynchrnously wait for the timers, wrapping the handlers with the chain.
timer1.async_wait(all_expired.wrap(
boost::bind(&handle_timer, boost::asio::placeholders::error, 1)));
timer2.async_wait(all_expired.wrap(
boost::bind(&handle_timer, boost::asio::placeholders::error, 2)));
// Run the io_service.
io_service.run();
}
Which produces the following output:
in handle timer: 1 : Success
in handle timer: 2 : Success
a
b
c
And here is async_ops.hpp:
#include <vector>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/bind/protect.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/foreach.hpp>
#include <boost/function.hpp>
#include <boost/make_shared.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread/locks.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/type_traits/is_integral.hpp>
#include <boost/type_traits/remove_reference.hpp>
#include <boost/utility/enable_if.hpp>
class chain;
namespace detail {
/// #brief Chained handler connects two handlers together that will
/// be called sequentially.
///
/// #note Type erasure is not performed on Handler1 to allow resolving
/// to the correct asio_handler_invoke via ADL.
template <typename Handler1>
class chained_handler
{
public:
template <typename Handler2>
chained_handler(Handler1 handler1, Handler2 handler2)
: handler1_(handler1),
handler2_(handler2)
{}
void operator()()
{
handler1_();
handler2_();
}
template <typename Arg1>
void operator()(const Arg1& a1)
{
handler1_(a1);
handler2_();
}
template <typename Arg1, typename Arg2>
void operator()(const Arg1& a1, const Arg2& a2)
{
handler1_(a1, a2);
handler2_();
}
//private:
Handler1 handler1_;
boost::function<void()> handler2_;
};
/// #brief Hook that allows the sequential_handler to be invoked
/// within specific context based on the hander's type.
template <typename Function,
typename Handler>
void asio_handler_invoke(
Function function,
chained_handler<Handler>* handler)
{
boost_asio_handler_invoke_helpers::invoke(
function, handler->handler1_);
}
/// #brief No operation.
void noop() {}
/// #brief io_service_executor is used to wrap handlers, providing a
/// deferred posting to an io_service. This allows for chains
/// to inherit io_services from other chains.
class io_service_executor
: public boost::enable_shared_from_this<io_service_executor>
{
public:
/// #brief Constructor.
explicit
io_service_executor(boost::asio::io_service* io_service)
: io_service_(io_service)
{}
/// #brief Wrap a handler, returning a functor that will post the
/// provided handler into the io_service.
///
/// #param handler Handler to be wrapped for deferred posting.
/// #return Functor that will post handler into io_service.
template <typename Handler>
boost::function<void()> wrap(Handler handler)
{
// By binding to the io_service_exectuer's post, the io_service
// into which the handler can be posted can be specified at a later
// point in time.
return boost::bind(&io_service_executor::post<Handler>,
shared_from_this(), handler);
}
/// #brief Set the io_service.
void io_service(boost::asio::io_service* io_service)
{
io_service_ = io_service;
}
/// #brief Get the io_service.
boost::asio::io_service* io_service()
{
return io_service_;
}
private:
/// #brief Post handler into the io_service.
///
/// #param handler The handler to post.
template <typename Handler>
void post(Handler handler)
{
io_service_->post(handler);
}
private:
boost::asio::io_service* io_service_;
};
/// #brief chain_impl is an implementation for a chain. It is responsible
/// for lifetime management, tracking posting and wrapped functions,
/// as well as determining when run criteria has been satisfied.
class chain_impl
: public boost::enable_shared_from_this<chain_impl>
{
public:
/// #brief Constructor.
chain_impl(boost::shared_ptr<io_service_executor> executor,
std::size_t required)
: executor_(executor),
required_(required)
{}
/// #brief Destructor will invoke all posted handlers.
~chain_impl()
{
run();
}
/// #brief Post a handler that will be posted into the executor
/// after run criteria has been satisfied.
template <typename Handler>
void post(const Handler& handler)
{
deferred_handlers_.push_back(executor_->wrap(handler));
}
/// #brief Wrap a handler, returning a chained_handler. The returned
/// handler will notify the impl when it has been invoked.
template <typename Handler>
chained_handler<Handler> wrap(const Handler& handler)
{
return chained_handler<Handler>(
handler, // handler1
boost::bind(&chain_impl::complete, shared_from_this())); // handler2
}
/// #brief Force run of posted handlers.
void run()
{
boost::unique_lock<boost::mutex> guard(mutex_);
run(guard);
}
/// #brief Get the executor.
boost::shared_ptr<io_service_executor> executor() { return executor_; }
private:
/// #brief Completion handler invoked when a wrapped handler has been
/// invoked.
void complete()
{
boost::unique_lock<boost::mutex> guard(mutex_);
// Update tracking.
if (required_)
--required_;
// If criteria has not been met, then return early.
if (required_) return;
// Otherwise, run the handlers.
run(guard);
}
/// #brief Run handlers.
void run(boost::unique_lock<boost::mutex>& guard)
{
// While locked, swap handlers into a temporary.
std::vector<boost::function<void()> > handlers;
using std::swap;
swap(handlers, deferred_handlers_);
// Run handlers without mutex.
guard.unlock();
BOOST_FOREACH(boost::function<void()>& handler, handlers)
handler();
guard.lock();
}
private:
boost::shared_ptr<io_service_executor> executor_;
boost::mutex mutex_;
std::size_t required_;
std::vector<boost::function<void()> > deferred_handlers_;
};
/// #brief Functor used to wrap and post handlers or chains between two
/// implementations.
struct wrap_and_post
{
wrap_and_post(
boost::shared_ptr<detail::chain_impl> current,
boost::shared_ptr<detail::chain_impl> next
)
: current_(current),
next_(next)
{}
/// #brief Wrap a handler with next, then post into current.
template <typename Handler>
void operator()(Handler handler)
{
// Wrap the handler with the next implementation, then post into the
// current. The wrapped handler will keep next alive, and posting into
// current will cause next::complete to be invoked when current is ran.
current_->post(next_->wrap(handler));
}
/// #brief Wrap an entire chain, posting into the current.
void operator()(chain chain);
private:
boost::shared_ptr<detail::chain_impl> current_;
boost::shared_ptr<detail::chain_impl> next_;
};
} // namespace detail
/// #brief Used to indicate that the a chain will inherit its service from an
/// outer chain.
class inherit_service_type {};
inherit_service_type inherit_service;
/// #brief Chain represents an asynchronous call chain, allowing the overall
/// chain to be constructed in a verbose and explicit manner.
class chain
{
public:
/// #brief Constructor.
///
/// #param io_service The io_service in which the chain will run.
explicit
chain(boost::asio::io_service& io_service)
: impl_(boost::make_shared<detail::chain_impl>(
boost::make_shared<detail::io_service_executor>(&io_service),
0)),
root_impl_(impl_)
{}
/// #brief Constructor. The chain will inherit its io_service from an
/// outer chain.
explicit
chain(inherit_service_type)
: impl_(boost::make_shared<detail::chain_impl>(
boost::make_shared<detail::io_service_executor>(
static_cast<boost::asio::io_service*>(NULL)),
0)),
root_impl_(impl_)
{}
/// #brief Force run posted handlers.
void run()
{
root_impl_->run();
}
/// #brief Chain link that will complete when the amount of wrapped
/// handlers is equal to required.
///
/// #param required The amount of handlers required to be complete.
template <typename T>
typename boost::enable_if<boost::is_integral<
typename boost::remove_reference<T>::type>, chain>::type
any(std::size_t required = 1)
{
return chain(root_impl_, required);
}
/// #brief Chain link that wraps all handlers in container, and will
/// be complete when the amount of wrapped handlers is equal to
/// required.
///
/// #param Container of handlers to wrap.
/// #param required The amount of handlers required to be complete.
template <typename Container>
typename boost::disable_if<boost::is_integral<
typename boost::remove_reference<Container>::type>, chain>::type
any(const Container& container,
std::size_t required = 1)
{
return post(container, required);
}
/// #brief Chain link that wraps all handlers in iterator range, and will
/// be complete when the amount of wrapped handlers is equal to
/// required.
///
/// #param Container of handlers to wrap.
/// #param required The amount of handlers required to be complete.
template <typename Iterator>
chain any(Iterator begin, Iterator end,
std::size_t required = 1)
{
return any(boost::make_iterator_range(begin, end), required);
}
/// #brief Chain link that will complete when the amount of wrapped
/// handlers is equal to required.
///
/// #param required The amount of handlers required to be complete.
template <typename T>
typename boost::enable_if<boost::is_integral<
typename boost::remove_reference<T>::type>, chain>::type
all(T required)
{
return any<T>(required);
}
/// #brief Chain link that wraps all handlers in container, and will
/// be complete when all wrapped handlers from the container
/// have been executed.
///
/// #param Container of handlers to wrap.
template <typename Container>
typename boost::disable_if<boost::is_integral<
typename boost::remove_reference<Container>::type>, chain>::type
all(const Container& container)
{
return any(container, container.size());
}
/// #brief Chain link that wraps all handlers in iterator range, and will
/// be complete when all wrapped handlers from the iterator range
/// have been executed.
///
/// #param Container of handlers to wrap.
template <typename Iterator>
chain all(Iterator begin, Iterator end)
{
return all(boost::make_iterator_range(begin, end));
}
/// #brief Chain link that represents a single sequential link.
template <typename Handler>
chain then(const Handler& handler)
{
boost::array<Handler, 1> handlers = {{handler}};
return all(handlers);
}
/// #brief Wrap a handler, returning a chained_handler.
template <typename Handler>
detail::chained_handler<Handler> wrap(const Handler& handler)
{
return impl_->wrap(handler);
}
/// #brief Set the executor.
void executor(boost::asio::io_service& io_service)
{
impl_->executor()->io_service(&io_service);
}
/// #brief Check if this chain should inherit its executor.
bool inherits_executor()
{
return !impl_->executor()->io_service();
}
private:
/// #brief Private constructor used to create links in the chain.
///
/// #note All links maintain a handle to the root impl. When constructing a
/// chain, this allows for links later in the chain to be stored as
/// non-temporaries.
chain(boost::shared_ptr<detail::chain_impl> root_impl,
std::size_t required)
: impl_(boost::make_shared<detail::chain_impl>(
root_impl->executor(), required)),
root_impl_(root_impl)
{}
/// #brief Create a new chain link, wrapping handlers and posting into
/// the current chain.
template <typename Container>
chain post(const Container& container,
std::size_t required)
{
// Create next chain.
chain next(root_impl_, required);
// Wrap handlers from the next chain, and post into the current chain.
std::for_each(container.begin(), container.end(),
detail::wrap_and_post(impl_, next.impl_));
return next;
}
private:
boost::shared_ptr<detail::chain_impl> impl_;
boost::shared_ptr<detail::chain_impl> root_impl_;
};
void detail::wrap_and_post::operator()(chain c)
{
// If next does not have an executor, then inherit from current.
if (c.inherits_executor())
c.executor(*current_->executor()->io_service());
// When current completes, start the chain.
current_->post(boost::protect(boost::bind(&chain::run, c)));
// The next impl needs to be aware of when the chain stops, so
// wrap a noop and append it to the end of the chain.
c.then(next_->wrap(&detail::noop));
}
// Convenience functions.
template <typename T, typename Handler>
chain async(T& t, const Handler& handler)
{
return chain(t).then(handler);
}
template <typename T,
typename Container>
chain when_all(T& t, const Container& container)
{
return chain(t).all(container);
}
template <typename T,
typename Iterator>
chain when_all(T& t, Iterator begin, Iterator end)
{
return chain(t).all(begin, end);
}
template <typename T,
typename Container>
chain when_any(T& t, const Container& container)
{
return chain(t).any(container);
}
template <typename T,
typename Iterator>
chain when_any(T& t, Iterator begin, Iterator end)
{
return chain(t).any(begin, end);
}
Here are some basic to advance examples using the above code with two threads. My notation:
a -> b expresses a then b
(a | b) expresses a or b. Thus (a | b) -> c implies when either a or b finish, then run c.
(a & b) expresses a and b. Thus (a & b) -> c implies when both a and b finish, then run c.
Before each case, I print the chain's notation. Additionally, each function will print a capital letter when entering, and a lower letter when exiting.
#include <iostream>
#include <boost/asio.hpp>
#include <boost/assign.hpp>
#include <boost/thread.hpp>
#include "async_ops.hpp"
/// #brief Print identifiers when entering and exiting scope,
/// sleeping between.
void print_and_sleep(char id, unsigned int sleep_time)
{
std::cout << char(toupper(id));
boost::this_thread::sleep_for(boost::chrono::milliseconds(sleep_time));
std::cout << char(tolower(id));
std::cout.flush();
}
/// #brief Convenience function to create functors.
boost::function<void()> make_fn(char id, unsigned int sleep_time)
{
return boost::bind(&print_and_sleep, id, sleep_time);
}
/// #brief Run an io_service with multiple threads.
void run_service(boost::asio::io_service& io_service)
{
boost::thread_group threads;
threads.create_thread(boost::bind(
&boost::asio::io_service::run, &io_service));
io_service.run();
threads.join_all();
}
int main()
{
boost::function<void()> a = make_fn('a', 500);
boost::function<void()> b = make_fn('b', 1000);
boost::function<void()> c = make_fn('c', 500);
boost::function<void()> d = make_fn('d', 1000);
boost::function<void()> e = make_fn('e', 500);
{
std::cout << "a -> b -> c\n"
" ";
boost::asio::io_service io_service;
async(io_service, a)
.then(b)
.then(c);
run_service(io_service);
std::cout << std::endl;
}
{
std::cout << "(a & b) -> c\n"
" ";
boost::asio::io_service io_service;
when_all(io_service, boost::assign::list_of(a)(b))
.then(c);
run_service(io_service);
std::cout << std::endl;
}
{
std::cout << "(a | b) -> c\n"
" ";
boost::asio::io_service io_service;
when_any(io_service, boost::assign::list_of(a)(b))
.then(c);
run_service(io_service);
std::cout << std::endl;
}
{
std::cout << "(a & b) -> (c & d)\n"
" ";
boost::asio::io_service io_service;
when_all(io_service, boost::assign::list_of(a)(b))
.all(boost::assign::list_of(c)(d));
run_service(io_service);
std::cout << std::endl;
}
{
std::cout << "(a & b) -> c -> (d & e)\n"
" ";
boost::asio::io_service io_service;
when_all(io_service, boost::assign::list_of(a)(b))
.then(c)
.all(boost::assign::list_of(d)(e));
run_service(io_service);
std::cout << std::endl;
}
std::cout << "(a & b) -> (c & d) -> e\n"
" ";
{
boost::asio::io_service io_service;
when_all(io_service, boost::assign::list_of(a)(b))
.all(boost::assign::list_of(c)(d))
.then(e);
run_service(io_service);
std::cout << std::endl;
}
std::cout << "(a | b) -> (c | d) -> e\n"
" ";
{
boost::asio::io_service io_service;
when_any(io_service, boost::assign::list_of(a)(b))
.any(boost::assign::list_of(c)(d))
.then(e);
run_service(io_service);
std::cout << std::endl;
}
std::cout << "(a | b) -> (c & d) -> e\n"
" ";
{
boost::asio::io_service io_service;
when_any(io_service, boost::assign::list_of(a)(b))
.all(boost::assign::list_of(c)(d))
.then(e);
run_service(io_service);
std::cout << std::endl;
}
{
std::cout << "a -> ((b -> d) | c) -> e\n"
" ";
boost::asio::io_service io_service;
async(io_service, a)
.any(boost::assign::list_of
(async(io_service, b).then(d))
(async(inherit_service, c)))
.then(e);
run_service(io_service);
std::cout << std::endl;
}
}
Produces the following output:
a -> b -> c
AaBbCc
(a & b) -> c
ABabCc
(a | b) -> c
ABaCbc
(a & b) -> (c & d)
ABabCDcd
(a & b) -> c -> (d & e)
ABabCcDEed
(a & b) -> (c & d) -> e
ABabCDcdEe
(a | b) -> (c | d) -> e
ABaCbDcEed
(a | b) -> (c & d) -> e
ABaCbDcdEe
a -> ((b -> d) | c) -> e
AaBCcEbDed
How about using the work concept? io_service::run will run as long as the work is available and will terminate when work is deleted as soon as there is no unfinished task.
Before calling run you create a work instance:
boost::shared_ptr<boost::asio::io_service::work> work( new boost::asio::io_service::work( io_service) );
And in your other thread you call as soon as you want to allow io_servce::run to terminate.
work.reset();
see also http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/reference/io_service.html#boost_asio.reference.io_service.stopping_the_io_service_from_running_out_of_work
Below I have attempted to write a sudo code for the Observer pattern when observers wish to observe different items.
Ignore the syntax errors. I wish to know if this is the correct way to implement this. If not, please suggest better ways.
// Used by the subject for keeping a track of what items the observer wants to observe
typedef struct observerListStruct
{
bool getTemperatureUpdate;
bool getHumidityUpdate;
bool getPressureUpdate;
observer's-function pointer's address;
};
// Subject's class
class weatherData
{
public:
// Observers will call this function to register themselves. The function pointer will point to the function which will get called when updates are available.
void registerObservers (observer obj, observer's-FunctionPointer)
{
// This observer's function returns which items to observe.
char* f = obj.returnItemsToObserve ();
if f[0] = `1`
observerListStruct.getTemperatureUpdate = true;
}
void unregisterObservers (observer obj) {}
private:
vector <observerListStruct> observerList;
float temperature;
float humidity;
float pressure;
void notifyObservers () {}
float getTemperature () {}
float getHumidity () {}
float getPressure () {}
} weatherDataObject;
// Base class for observers containing common functions
class observers
{
char ItemsToObserve [3] = {1, 2, 3};
// This observer's function returns which items to observe. Default - return all items
virtual char* returnItemsToObserve ()
{
return ItemsToObserve;
}
};
class observerDisplayElementCurrentConditions : public observers
{
char ItemsToObserve [3] = {1, 2};
char* returnItemsToObserve ()
{
return ItemsToObserve;
}
// this function will be used as a function pointer for getting updates
void getUpdatesAndDisplayWeatherData (float, float) {}
};
A more pattern oriented solution (but without function pointers) could be the following. You could parametrize the WeatherObserver-Class to get only the values, you want.
#include <list>
#include <iostream>
class Observable; //forward declaration
//Base class for all observers
class Observer {
friend class Observable; //allow access to observedSubject
protected:
Observable *observedSubject;
public:
virtual void update(){};
};
//Base class for all observables
class Observable {
private:
std::list<Observer * const> m_registeredObservers;
public:
~Observable()
{
//delete the observers
std::list<Observer * const>::iterator it = m_registeredObservers.begin();
while (it != m_registeredObservers.end())
{
delete *it;
it = m_registeredObservers.erase(it);
}
}
void addObserver(Observer * const _pObserver)
{
_pObserver->observedSubject = this;
m_registeredObservers.push_back(_pObserver);
}
void removeObserver(Observer * const _pObserver)
{
m_registeredObservers.remove(_pObserver);
delete _pObserver;
}
void notifyObservers()
{
std::list<Observer * const>::iterator it = m_registeredObservers.begin();
while (it != m_registeredObservers.end())
{
(*it)->update();
it++;
}
}
};
//Concrete Observable
class WeatherData : public Observable {
private:
float temperature;
float humidity;
float pressure;
public:
WeatherData(): temperature(0), humidity(0), pressure(0)
{};
float getTemperature () const
{
return temperature;
}
float getHumidity () const
{
return humidity;
}
float getPressure () const
{
return pressure;
}
void setTemperature(float _temperature)
{
if (temperature != _temperature)
{
temperature = _temperature;
notifyObservers();
}
}
void setHumidity(float _humidity)
{
if (humidity != _humidity)
{
humidity = _humidity;
notifyObservers();
}
}
void setPressure(float _pressure)
{
if (pressure != _pressure)
{
pressure = _pressure;
notifyObservers();
}
}
};
//Concrete implementation of an weather observer
class WeatherObserver : public Observer
{
public:
WeatherObserver():Observer(){};
void update()
{
WeatherData* pWeatherPtr = static_cast<WeatherData*>(observedSubject);
if (pWeatherPtr != 0)
{
float actHumidity = pWeatherPtr->getHumidity();
float actPressure = pWeatherPtr->getPressure();
float actTemperature = pWeatherPtr->getTemperature();
//do something with the data
std::cout << "WeatherObserver update" << std::endl;
std::cout << "Temperature : " << actTemperature << std::endl;
std::cout << "Humidity : " << actHumidity << std::endl;
std::cout << "Pressure : " << actPressure << std::endl;
}
}
};
int main()
{
WeatherData weatherData;
Observer * pObserver = new WeatherObserver();
weatherData.addObserver(pObserver);
weatherData.setHumidity(100);
weatherData.setTemperature(100);
}
#include <algorithm>
#include <vector>
class WeatherFlags
{
public:
WeatherFlags()
: mask_(0)
{}
union {
struct {
unsigned int temperature_ : 1;
unsigned int humidity_ : 1;
unsigned int pressure_ : 1;
};
unsigned int mask_;
};
};
class WeatherData;
class WeatherEvent
{
public:
WeatherEvent(WeatherData* data, WeatherFlags const& flags)
: data_(data)
, flags_(flags)
{}
double getTemperature() const;
WeatherData* data_;
WeatherFlags flags_;
};
class WeatherListener
{
public:
virtual ~WeatherListener() = 0;
virtual void onWeatherUpdate(WeatherEvent& e) = 0;
};
inline WeatherListener::~WeatherListener() {}
class WeatherListenerEntry
{
public:
WeatherListenerEntry()
: listener_(0)
{}
WeatherListenerEntry(WeatherListener* listener, WeatherFlags const& flags)
: listener_(listener)
, flags_(flags)
{}
WeatherListener* listener_;
WeatherFlags flags_;
};
class WeatherData
{
public:
WeatherData();
void addListener(WeatherListener* listener, WeatherFlags const& flags);
void removeListener(WeatherListener* listener);
void notify(WeatherFlags const& flags);
double getTemperature() const { return temperature_; }
private:
typedef std::vector<WeatherListenerEntry> Listeners;
Listeners listeners_;
double temperature_;
};
WeatherData::WeatherData()
: temperature_(0)
{}
void WeatherData::addListener(WeatherListener* listener, WeatherFlags const& flags)
{
// TODO Could maybe check for the addition of duplicates here...
listeners_.push_back(WeatherListenerEntry(listener, flags));
}
void WeatherData::removeListener(WeatherListener* listener)
{
struct ListenerEquals {
WeatherListener* listener_;
ListenerEquals(WeatherListener* listener)
: listener_(listener)
{}
bool operator()(WeatherListenerEntry const& e) const {
return (e.listener_ == listener_);
}
};
listeners_.erase(
std::remove_if(listeners_.begin(), listeners_.end(), ListenerEquals(listener)),
listeners_.end());
}
void WeatherData::notify(WeatherFlags const& flags)
{
WeatherEvent evt(this, flags);
for (Listeners::iterator i = listeners_.begin(); i != listeners_.end(); ++i)
{
if (0 != (i->flags_.mask_ & flags.mask_)) {
i->listener_->onWeatherUpdate(evt);
}
}
}
double
WeatherEvent::getTemperature() const
{
return data_->getTemperature();
}
#include <iostream>
class WeatherObserverStdout : public WeatherListener
{
public:
void observe(WeatherData& data) {
WeatherFlags flags;
flags.temperature_ = true; // interested in temperature only.
data.addListener(this, flags);
}
virtual void onWeatherUpdate(WeatherEvent& e);
};
void
WeatherObserverStdout::onWeatherUpdate(WeatherEvent& e)
{
double temp = e.getTemperature();
std::cout << "Temperatrure: " << temp << std::endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
WeatherData wdata;
WeatherObserverStdout obs;
obs.observe(wdata);
WeatherFlags flags;
wdata.notify(flags);
flags.temperature_ = true;
wdata.notify(flags);
return 0;
}
I think it is easier, and more scalable, to define a set of event types that each observer can listen to. Then you register the observer to listen to that particular event type. The observed then keeps a list of observers registered for each event, and notifies them if and when the event occurs. Using a combination of std::function, std::bind (or boost equivalents), it is easy to register callbacks for a given event type. You could put the callbacks in a map of event type to callback.
For example, something along these lines (almost pseudo-code, has not been tested)
class Publisher {
public :
void subscribe(const std::string& event,
std::function<void(double)> callback) {
m_subscribers[s].push_back(callback);
}
void publish(const std::string& event) const {
for (auto& f : m_subscribers[event]) f( some double );}
void event(const std::string& event) const { publish(event);}
private:
// map of event types (here simply strings) to list of callbacks
std::map<std::string&,
std::list<std::function<void(const std::string&)>>> m_subscribers;
};
struct Foo {
void foo(double x) {
std::cout << "Foo received message: " << x << "\n";
}
};
struct Bar {
void bar(double x) {
std::cout << "Bar received message: " << x << "\n";
}
};
int main() {
Publisher pub;
Foo f0;
Foo f1;
Bar bar0;
pub.subscribe("RED", std::bind(&Foo::foo, &foo0, _1));
pub.subscribe("GREEN", std::bind(&Foo::foo, &foo1, _1));
pub.subscribe("WHITE", std::bind(&Foo::foo, &foo1, _1));
pub.subscribe("RED", std::bind(&Bar::bar, &bar0, _1));
pub.subscribe("BLUE", std::bind(&Bar::bar, &bar0, _1));
pub.subscribe("MAGENTA", std::bind(&Bar::bar, &bar0, _1));
// trigger a "GREEN" event
pub.event("GREEN");
}
Here, the observers (or subscribers) register to some events, represented by strings here, and their registered callbacks get called when this event happens. In the example above I manually trigger an event to illustrate the mechanism.
This event-callback mechanism allows to decouple the actual items from the callback action. The Observed (or publisher) knows what parameter to pass the callback for a given event, and which callbacks to call, so the observers are not dependent on the internal data of the observed object.
I write a lot of C++ code and needed to create an Observer for some game components I was working on. I needed something to distribute "start of frame", "user input", etc., as events in the game to interested parties.
I also wanted more granularity in the events that could be handled. I have a lot of little things that go off...I don't need to have the parts that are interested in resetting for the next frame worried about a change in the user input.
I also wanted it to be straight C++, not dependent on the platform or a specific technology (such as boost, Qt, etc.) because I often build and re-use components (and the ideas behind them) across different projects.
Here is the rough sketch of what I came up with as a solution:
The Observer is a singleton with keys (enumerated values, not strings; this is a speed tradeoff since the keys are not searched hashed, but it means no easy "string" names and you have to define them ahead of time) for Subjects to register interest in. Because it is a singleton, it always exists.
Each subject is derived from a common base class. The base class has an abstract virtual function Notify(...) which must be implemented in derived classes, and a destructor that removes it from the Observer (which it can always reach) when it is deleted.
Inside the Observer itself, if Detach(...) is called while a Notify(...) is in progress, any detached Subjects end up on a list.
When Notify(...) is called on the Observer, it creates a temporary copy of the Subject list. As it iterates over it, it compare it to the recently detached. If the target is not on it, Notify(...) is called on the target. Otherwise, it is skipped.
Notify(...) in the Observer also keeps track of the depth to handle cascading calls (A notifies B, C, D, and the D.Notify(...) triggers a Notify(...) call to E, etc.)
This is what the interface ended up looking like:
/*
The Notifier is a singleton implementation of the Subject/Observer design
pattern. Any class/instance which wishes to participate as an observer
of an event can derive from the Notified base class and register itself
with the Notiifer for enumerated events.
Notifier derived classes MUST implement the notify function, which has
a prototype of:
void Notify(const NOTIFIED_EVENT_TYPE_T& event)
This is a data object passed from the Notifier class. The structure
passed has a void* in it. There is no illusion of type safety here
and it is the responsibility of the user to ensure it is cast properly.
In most cases, it will be "NULL".
Classes derived from Notified do not need to deregister (though it may
be a good idea to do so) as the base class destructor will attempt to
remove itself from the Notifier system automatically.
The event type is an enumeration and not a string as it is in many
"generic" notification systems. In practical use, this is for a closed
application where the messages will be known at compile time. This allows
us to increase the speed of the delivery by NOT having a
dictionary keyed lookup mechanism. Some loss of generality is implied
by this.
This class/system is NOT thread safe, but could be made so with some
mutex wrappers. It is safe to call Attach/Detach as a consequence
of calling Notify(...).
*/
class Notified;
class Notifier : public SingletonDynamic<Notifier>
{
public:
typedef enum
{
NE_MIN = 0,
NE_DEBUG_BUTTON_PRESSED = NE_MIN,
NE_DEBUG_LINE_DRAW_ADD_LINE_PIXELS,
NE_DEBUG_TOGGLE_VISIBILITY,
NE_DEBUG_MESSAGE,
NE_RESET_DRAW_CYCLE,
NE_VIEWPORT_CHANGED,
NE_MAX,
} NOTIFIED_EVENT_TYPE_T;
private:
typedef vector<NOTIFIED_EVENT_TYPE_T> NOTIFIED_EVENT_TYPE_VECTOR_T;
typedef map<Notified*,NOTIFIED_EVENT_TYPE_VECTOR_T> NOTIFIED_MAP_T;
typedef map<Notified*,NOTIFIED_EVENT_TYPE_VECTOR_T>::iterator NOTIFIED_MAP_ITER_T;
typedef vector<Notified*> NOTIFIED_VECTOR_T;
typedef vector<NOTIFIED_VECTOR_T> NOTIFIED_VECTOR_VECTOR_T;
NOTIFIED_MAP_T _notifiedMap;
NOTIFIED_VECTOR_VECTOR_T _notifiedVector;
NOTIFIED_MAP_ITER_T _mapIter;
// This vector keeps a temporary list of observers that have completely
// detached since the current "Notify(...)" operation began. This is
// to handle the problem where a Notified instance has called Detach(...)
// because of a Notify(...) call. The removed instance could be a dead
// pointer, so don't try to talk to it.
vector<Notified*> _detached;
int32 _notifyDepth;
void RemoveEvent(NOTIFIED_EVENT_TYPE_VECTOR_T& orgEventTypes, NOTIFIED_EVENT_TYPE_T eventType);
void RemoveNotified(NOTIFIED_VECTOR_T& orgNotified, Notified* observer);
public:
virtual void Reset();
virtual bool Init() { Reset(); return true; }
virtual void Shutdown() { Reset(); }
void Attach(Notified* observer, NOTIFIED_EVENT_TYPE_T eventType);
// Detach for a specific event
void Detach(Notified* observer, NOTIFIED_EVENT_TYPE_T eventType);
// Detach for ALL events
void Detach(Notified* observer);
/* The design of this interface is very specific. I could
* create a class to hold all the event data and then the
* method would just have take that object. But then I would
* have to search for every place in the code that created an
* object to be used and make sure it updated the passed in
* object when a member is added to it. This way, a break
* occurs at compile time that must be addressed.
*/
void Notify(NOTIFIED_EVENT_TYPE_T, const void* eventData = NULL);
/* Used for CPPUnit. Could create a Mock...maybe...but this seems
* like it will get the job done with minimal fuss. For now.
*/
// Return all events that this object is registered for.
vector<NOTIFIED_EVENT_TYPE_T> GetEvents(Notified* observer);
// Return all objects registered for this event.
vector<Notified*> GetNotified(NOTIFIED_EVENT_TYPE_T event);
};
/* This is the base class for anything that can receive notifications.
*/
class Notified
{
public:
virtual void Notify(Notifier::NOTIFIED_EVENT_TYPE_T eventType, const void* eventData) = 0;
virtual ~Notified();
};
typedef Notifier::NOTIFIED_EVENT_TYPE_T NOTIFIED_EVENT_TYPE_T;
NOTE: The Notified class has a single function, Notify(...) here. Because the void* is not type safe, I created other versions where notify looks like:
virtual void Notify(Notifier::NOTIFIED_EVENT_TYPE_T eventType, int value);
virtual void Notify(Notifier::NOTIFIED_EVENT_TYPE_T eventType, const string& str);
Corresponding Notify(...) methods were added to the Notifier itself. All these used a single function to get the "target list" then called the appropriate function on the targets. This works well and keeps the receiver from having to do ugly casts.
This seems to work well. The solution is posted on the web here along with the source code. This is a relatively new design, so any feedback is greatly appreciated.
My two cents...
Classic (Gang of Four) implementation of Observer pattern notifies observer on changes in any property of the subject. In your question you want to register observer to particular properties, not to a subject as a whole. You can move Observer pattern one level down and take properties as concrete subjects and define their observers (per property) but there is one nicer way to solve this problem.
In C# Observer pattern is implemented through events and delegates. Delegates represent event handlers - functions that should be executed when an event is fired. Delegates can be added (registered) or removed(unregistered) from events.
In C++, functors act as delegates - they can store all necessary information to call some global function or class method in a different context. Events are collections of (registered) functors and when event is raised (called) it basically goes through that list and calls all functors (see Publisher::publish method in juanchopanza's solution).
I tried to implement C++ version of events and delegates and use them in modified Observer pattern which could be applied in your case. This is what I came up with:
#include <list>
#include <iostream>
#include <algorithm>
// use base class to resolve the problem of how to put into collection objects of different types
template <typename TPropertyType>
struct PropertyChangedDelegateBase
{
virtual ~PropertyChangedDelegateBase(){};
virtual void operator()(const TPropertyType& t) = 0;
};
template <typename THandlerOwner, typename TPropertyType>
struct PropertyChangedDelegate : public PropertyChangedDelegateBase<TPropertyType>
{
THandlerOwner* pHandlerOwner_;
typedef void (THandlerOwner::*TPropertyChangeHandler)(const TPropertyType&);
TPropertyChangeHandler handler_;
public:
PropertyChangedDelegate(THandlerOwner* pHandlerOwner, TPropertyChangeHandler handler) :
pHandlerOwner_(pHandlerOwner), handler_(handler){}
void operator()(const TPropertyType& t)
{
(pHandlerOwner_->*handler_)(t);
}
};
template<typename TPropertyType>
class PropertyChangedEvent
{
public:
virtual ~PropertyChangedEvent(){};
void add(PropertyChangedDelegateBase<TPropertyType>* const d)
{
std::list<PropertyChangedDelegateBase<TPropertyType>* const>::const_iterator it = std::find(observers_.begin(), observers_.end(), d);
if(it != observers_.end())
throw std::runtime_error("Observer already registered");
observers_.push_back(d);
}
void remove(PropertyChangedDelegateBase<TPropertyType>* const d)
{
std::list<PropertyChangedDelegateBase<TPropertyType>* const>::const_iterator it = std::find(observers_.begin(), observers_.end(), d);
if(it != observers_.end())
observers_.remove(d);
}
// notify
void operator()(const TPropertyType& newValue)
{
std::list<PropertyChangedDelegateBase<TPropertyType>* const>::const_iterator it = observers_.begin();
for(; it != observers_.end(); ++it)
{
(*it)->operator()(newValue);
}
}
protected:
std::list<PropertyChangedDelegateBase<TPropertyType>* const> observers_;
};
// class that owns concrete subjects
class PropertyOwner1
{
int property1_;
float property2_;
public:
PropertyChangedEvent<int> property1ChangedEvent;
PropertyChangedEvent<float> property2ChangedEvent;
PropertyOwner1() :
property1_(0),
property2_(0.0f)
{}
int property1() const {return property1_;}
void property1(int n)
{
if(property1_ != n)
{
property1_ = n;
std::cout << "PropertyOwner1::property1(): property1_ set to " << property1_ << std::endl;
property1ChangedEvent(property1_);
}
}
float property2() const {return property2_;}
void property2(float n)
{
if(property2_ != n)
{
property2_ = n;
std::cout << "PropertyOwner1::property2(): property2_ set to " << property2_ << std::endl;
property2ChangedEvent(property2_);
}
}
};
// class that owns concrete subjects
class PropertyOwner2
{
bool property1_;
double property2_;
public:
PropertyChangedEvent<bool> property1ChangedEvent;
PropertyChangedEvent<double> property2ChangedEvent;
PropertyOwner2() :
property1_(false),
property2_(0.0)
{}
bool property1() const {return property1_;}
void property1(bool n)
{
if(property1_ != n)
{
property1_ = n;
std::cout << "PropertyOwner2::property1(): property1_ set to " << property1_ << std::endl;
property1ChangedEvent(property1_);
}
}
double property2() const {return property2_;}
void property2(double n)
{
if(property2_ != n)
{
property2_ = n;
std::cout << "PropertyOwner2::property2(): property2_ set to " << property2_ << std::endl;
property2ChangedEvent(property2_);
}
}
};
// class that observes changes in property1 of PropertyOwner1 and property1 of PropertyOwner2
struct PropertyObserver1
{
void OnPropertyOwner1Property1Changed(const int& newValue)
{
std::cout << "\tPropertyObserver1::OnPropertyOwner1Property1Changed(): \n\tnew value is: " << newValue << std::endl;
}
void OnPropertyOwner2Property1Changed(const bool& newValue)
{
std::cout << "\tPropertyObserver1::OnPropertyOwner2Property1Changed(): \n\tnew value is: " << newValue << std::endl;
}
};
// class that observes changes in property2 of PropertyOwner1 and property2 of PropertyOwner2
struct PropertyObserver2
{
void OnPropertyOwner1Property2Changed(const float& newValue)
{
std::cout << "\tPropertyObserver2::OnPropertyOwner1Property2Changed(): \n\tnew value is: " << newValue << std::endl;
}
void OnPropertyOwner2Property2Changed(const double& newValue)
{
std::cout << "\tPropertyObserver2::OnPropertyOwner2Property2Changed(): \n\tnew value is: " << newValue << std::endl;
}
};
int main(int argc, char** argv)
{
PropertyOwner1 propertyOwner1;
PropertyOwner2 propertyOwner2;
PropertyObserver1 propertyObserver1;
PropertyObserver2 propertyObserver2;
// register observers
PropertyChangedDelegate<PropertyObserver1, int> delegate1(&propertyObserver1, &PropertyObserver1::OnPropertyOwner1Property1Changed);
propertyOwner1.property1ChangedEvent.add(&delegate1);
PropertyChangedDelegate<PropertyObserver2, float> delegate2(&propertyObserver2, &PropertyObserver2::OnPropertyOwner1Property2Changed);
propertyOwner1.property2ChangedEvent.add(&delegate2);
PropertyChangedDelegate<PropertyObserver1, bool> delegate3(&propertyObserver1, &PropertyObserver1::OnPropertyOwner2Property1Changed);
propertyOwner2.property1ChangedEvent.add(&delegate3);
PropertyChangedDelegate<PropertyObserver2, double> delegate4(&propertyObserver2, &PropertyObserver2::OnPropertyOwner2Property2Changed);
propertyOwner2.property2ChangedEvent.add(&delegate4);
propertyOwner1.property1(1);
propertyOwner1.property2(1.2f);
propertyOwner2.property1(true);
propertyOwner2.property2(3.4);
// unregister PropertyObserver1
propertyOwner1.property1ChangedEvent.remove(&delegate1);
propertyOwner2.property1ChangedEvent.remove(&delegate3);
propertyOwner1.property1(2);
propertyOwner1.property2(4.5f);
}
Output:
PropertyOwner1::property1(): property1_ set to 1
PropertyObserver1::OnPropertyOwner1Property1Changed():
new value is: 1
PropertyOwner1::property2(): property2_ set to 1.2
PropertyObserver2::OnPropertyOwner1Property2Changed():
new value is: 1.2
PropertyOwner2::property1(): property1_ set to 1
PropertyObserver1::OnPropertyOwner2Property1Changed():
new value is: 1
PropertyOwner2::property2(): property2_ set to 3.4
PropertyObserver2::OnPropertyOwner2Property2Changed():
new value is: 3.4
PropertyOwner1::property1(): property1_ set to 2
PropertyOwner1::property2(): property2_ set to 4.5
PropertyObserver2::OnPropertyOwner1Property2Changed():
new value is: 4.5
Each observer is registered with a particular property and when notified, each observer knows exactly who is the owner of the property and what's property's new value.