The one thing that didn't really work as suggested by Morween was using a brace-enclosed initializer list for reference members (see ObjDelegate(T& obj, ObjMemFn memFn)
). I think the same issue is discussed in this question this question.
The one thing that didn't really work as suggested by Morween was using a brace-enclosed initializer list for reference members (see ObjDelegate(T& obj, ObjMemFn memFn)
). I think the same issue is discussed in this question.
The one thing that didn't really work as suggested by Morween was using a brace-enclosed initializer list for reference members (see ObjDelegate(T& obj, ObjMemFn memFn)
). I think the same issue is discussed in this question.
Below is my signals and connections header file. I'm only beginning to use it in my actual application, but it seems to provide what I want.
The one thing that didn't really work as suggested by Morween was using a brace-enclosed initializer list for reference members (see ObjDelegate(T& obj, ObjMemFn memFn)
). I think the same issue is discussed in enter link description herethis question.
gcc -v
:
compiler: gcc version 4.8.3 20140228 (release) [ARM/embedded-4_8-branch revision 208322]).
Below is my signals and connections header file. I'm only beginning to use it in my actual application, but it seems to provide what I want. The one thing that didn't really work as suggested by Morween was using a brace-enclosed initializer list for reference members (see ObjDelegate(T& obj, ObjMemFn memFn)
). I think the same issue is discussed in enter link description here.
Below is my signals and connections header file. I'm only beginning to use it in my actual application, but it seems to provide what I want.
The one thing that didn't really work as suggested by Morween was using a brace-enclosed initializer list for reference members (see ObjDelegate(T& obj, ObjMemFn memFn)
). I think the same issue is discussed in this question.
gcc -v
:
compiler: gcc version 4.8.3 20140228 (release) [ARM/embedded-4_8-branch revision 208322]).
Below is my signals and connections header file. I'm only beginning to use it in my actual application, but it seems to provide what I want. The one thing that didn't really work as suggested by Morween was using a brace-enclosed initializer list for reference members (see ObjDelegate(T& obj, ObjMemFn memFn)
). I think the same issue is discussed in enter link description here.
The make_delegate(...)
functions had to go in favour of a Connection
class and free connect
functions:
#ifndef _SIGNALS_H_
#define _SIGNALS_H_
#include <utility>
/** Interface for delegates with a specific set of arguments **/
template<typename... args>
class AbstractDelegate
{
public:
virtual void operator()(args&&...) const = 0;
virtual ~AbstractDelegate() {}
};
/** Concrete member function delegate that discards the function's return value **/
template<typename T, typename ReturnType, typename... args>
class ObjDelegate : public AbstractDelegate<args...>
{
public:
/** member function typedef **/
using ObjMemFn = ReturnType (T::*)(args...);
/** constructor **/
ObjDelegate(T& obj, ObjMemFn memFn)
// : obj_{obj}, //error: invalid initialization of non-const reference of type 'Watch&' from an rvalue of type '<brace-enclosed initializer list>'
: obj_(obj),
memFn_{memFn} // here the brace-enclosed list works, probably because memFn is _not_ a reference
{
}
/** call operator that calls the stored function on the stored object **/
void operator()(args&&... a) const override
{
(obj_.*memFn_)(std::forward<args>(a)...);
}
private:
/** reference to the object **/
T& obj_;
/** member function pointer **/
const ObjMemFn memFn_;
};
/** Concrete function delegate that discards the function's return value **/
template<typename ReturnType, typename... args>
class FnDelegate : public AbstractDelegate<args...>
{
public:
/** member function typedef **/
using Fn = ReturnType(*)(args...);
/** constructor **/
FnDelegate(Fn fn)
: fn_{fn}
{
}
/** call operator that calls the stored function **/
void operator()(args&&... a) const override
{
(*fn_)(std::forward<args>(a)...);
}
private:
/** function pointer **/
const Fn fn_;
};
/** forward declaration **/
template<typename... args>
class Connection;
/** Signal class that can be connected to**/
template<typename... args>
class Signal
{
public:
/** connection pointer typedef **/
typedef Connection<args...>* connection_p;
/** constructor **/
Signal()
: connections_(NULL)
{
}
/** call operator that calls all connected delegates.
The most recently connected delegate will be called first **/
void operator()(args&&... a) const
{
auto c = connections_;
while(c != NULL)
{
(c->delegate())(std::forward<args>(a)...);
c = c->next();
}
}
/** connect to this signal **/
void connect(connection_p p)
{
p->next_ = connections_;
connections_ = p;
p->signal_ = this;
}
/** disconnect from this signal.
Invalidates the connection's signal pointer
and removes the connection from the list **/
void disconnect(connection_p conn)
{
// find connection and remove it from the list
connection_p c = connections_;
if (c == conn)
{
connections_ = connections_->next();
conn->next_ = NULL;
conn->signal_ = NULL;
return;
}
while(c != NULL)
{
if (c->next() == conn)
{
c->next_ = conn->next();
conn->next_ = NULL;
conn->signal_ = NULL;
return;
}
c = c->next();
}
}
/** destructor. disconnects all connections **/
~Signal()
{
connection_p p = connections_;
while(p != NULL)
{
connection_p n = p->next();
disconnect(p);
p = n;
}
}
private:
connection_p connections_;
};
/** connection class that can be connected to a signal **/
template<typename... args>
class Connection
{
public:
/** template constructor for non-static member functions.
allocates a new delegate on the heap **/
template<typename T, typename ReturnType>
Connection(Signal<args...>* signal, T& obj, ReturnType (T::*memFn)(args...))
: delegate_(new ObjDelegate<T, ReturnType, args...>(obj, memFn)),
signal_(NULL),
next_(NULL)
{
signal->connect(this);
}
/** template constructor for static member functions and free functions.
allocates a new delegate on the heap **/
template<typename ReturnType>
Connection(Signal<args...>* signal, ReturnType (*Fn)(args...))
: delegate_(new FnDelegate<ReturnType, args...>(Fn)),
signal_(NULL),
next_(NULL)
{
signal->connect(this);
}
/** get reference to this connection's delegate **/
AbstractDelegate<args...>& delegate() const
{
return *delegate_;
}
/** get pointer to next connection in the signal's list **/
Connection* next() const
{
return next_;
}
/** is this connection connected to a valid signal? **/
bool connected() const
{
return (signal_ != NULL);
}
/** desctructor. If the signal is still alive, disconnects from it **/
~Connection()
{
if (signal_ != NULL)
{
signal_->disconnect(this);
}
delete delegate_;
}
friend class Signal<args...>;
private:
AbstractDelegate<args...>* delegate_;
Signal<args...>* signal_;
Connection* next_;
};
/** free connect function: creates a connection (non-static member function) on the heap
that can be used anonymously **/
template<typename T, typename ReturnType, typename... args>
Connection<args...>* connect(Signal<args...>* signal, T& obj, ReturnType (T::*memFn)(args...))
{
return new Connection<args...>(signal, obj, memFn);
}
/** free connect function: creates a connection (static member or free function) on the heap
that can be used anonymously **/
template<typename ReturnType, typename... args>
Connection<args...>* connect(Signal<args...>* signal, ReturnType (*fn)(args...))
{
return new Connection<args...>(signal, fn);
}
#endif // _SIGNALS_H_
I am still not totally satisfied with this, because the function pointers stored in the delegates don't necessarily need to end up on the heap. I want to use as little heap space as possible.