signal: add return value combiners

This commit is contained in:
Danny Robson 2015-03-10 22:52:38 +11:00
parent d4d5eaa6f8
commit daa492ce07
3 changed files with 172 additions and 50 deletions

View File

@ -21,16 +21,62 @@
#define __UTIL_SIGNAL_HPP #define __UTIL_SIGNAL_HPP
#include "nocopy.hpp" #include "nocopy.hpp"
#include "types/traits.hpp"
#include <functional> #include <functional>
#include <list> #include <list>
#include <type_traits> #include <type_traits>
namespace util { namespace util {
namespace combine {
template <typename F> template <typename F>
struct logical_and {
using R = typename func_traits<F>::return_type;
template <typename T, typename ...Args>
R operator() (T first, T last, Args&&... args)
{
while (first != last)
if (!(*first++)(std::forward<Args> (args)...))
return false;
return true;
}
};
template <typename F>
struct logical_or {
using R = typename func_traits<F>::return_type;
template <typename T, typename ...Args>
R operator() (T first, T last, Args&&... args)
{
while (first != last)
if ((*first++)(std::forward<Args> (args)...))
return true;
return false;
}
};
template <typename F>
struct noop {
using R = void;
template <typename T, typename ...Args>
R operator() (T first, T last, Args&&... args)
{
while (first != last) {
(*first++)(std::forward<Args> (args)...);
}
}
};
}
template <typename F, template <typename> class C = combine::noop>
class signal { class signal {
public: public:
using R = std::result_of<F>; using R = typename C<F>::R;
using callback = std::function<F>; using callback = std::function<F>;
struct cookie; struct cookie;
@ -40,9 +86,10 @@ namespace util {
~signal (); ~signal ();
/// Add a callback to list. /// Add a callback to list.
cookie connect (callback&&);
cookie connect (const callback&); cookie connect (const callback&);
void disconnect (const cookie&); void disconnect (cookie&);
/// Disconnect all callbacks /// Disconnect all callbacks
void clear (void); void clear (void);
@ -51,16 +98,19 @@ namespace util {
unsigned int size (void) const; unsigned int size (void) const;
bool empty (void) const; bool empty (void) const;
/// Execute all callbacks, ignoring the return parameters. Does /// Execute all callbacks
/// not combine results.
template <typename ...Args> template <typename ...Args>
void operator () (Args&&... tail); R
operator() (Args&&... tail);
private: private:
typedef std::list<callback> group; typedef std::list<callback> group;
group m_children; group m_children;
}; };
///////////////////////////////////////////////////////////////////////////
// wrap a value in a signal and trigger on assignment
//template <typename T, template <typename> class C>
template <typename T> template <typename T>
class value_signal : public signal<void(T)> { class value_signal : public signal<void(T)> {
public: public:

View File

@ -27,9 +27,9 @@
namespace util { namespace util {
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
template <typename F> template <typename F, template <typename> class C>
struct signal<F>::cookie : public nocopy { struct signal<F,C>::cookie : public nocopy {
cookie (typename group::iterator, signal<F> &parent); cookie (typename group::iterator, signal<F,C> &parent);
cookie (cookie &&rhs); cookie (cookie &&rhs);
~cookie (); ~cookie ();
@ -37,22 +37,22 @@ namespace util {
void reset (void); void reset (void);
typename group::iterator m_position; typename group::iterator m_position;
signal<F> &m_parent; signal<F,C> &m_parent;
}; };
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
template <typename F> template <typename F, template <typename> class C>
signal<F>::cookie::cookie (typename group::iterator _position, signal<F,C>::cookie::cookie (typename group::iterator _position,
signal<F> &_parent): signal<F,C> &_parent):
m_position (_position), m_position (_position),
m_parent (_parent) m_parent (_parent)
{ ; } { ; }
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
template <typename F> template <typename F, template <typename> class C>
signal<F>::cookie::cookie (cookie &&rhs): signal<F,C>::cookie::cookie (cookie &&rhs):
m_position (rhs.m_position), m_position (rhs.m_position),
m_parent (rhs.m_parent) m_parent (rhs.m_parent)
{ {
@ -61,8 +61,8 @@ namespace util {
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
template <typename F> template <typename F, template <typename> class C>
signal<F>::cookie::~cookie () signal<F,C>::cookie::~cookie ()
{ {
if (m_parent.m_children.end () != m_position) if (m_parent.m_children.end () != m_position)
m_parent.disconnect (*this); m_parent.disconnect (*this);
@ -70,59 +70,81 @@ namespace util {
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
template <typename F> template <typename F, template <typename> class C>
void void
signal<F>::cookie::reset (callback &&cb) signal<F,C>::cookie::reset (callback &&cb)
{ {
*m_position = std::move (cb); *m_position = std::move (cb);
} }
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
template <typename F> template <typename F, template <typename> class C>
void void
signal<F>::cookie::reset (void) signal<F,C>::cookie::reset (void)
{ {
m_position = m_parent.m_children.end (); m_position = m_parent.m_children.end ();
} }
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
template <typename F> template <typename F, template <typename> class C>
signal<F>::signal () signal<F,C>::signal ()
{ ; } { ; }
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
template <typename F> template <typename F, template <typename> class C>
signal<F>::~signal () signal<F,C>::~signal ()
{ {
CHECK (empty ()); CHECK (empty ());
} }
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
template <typename F> template <typename F, template <typename> class C>
typename signal<F>::cookie typename signal<F,C>::cookie
signal<F>::connect (const callback &_cb) signal<F,C>::connect (const callback &_cb)
{ {
return cookie (m_children.insert (m_children.end (), _cb), *this); return cookie (
m_children.insert (
m_children.end (),
std::move (_cb)
),
*this
);
} }
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
template <typename F> template <typename F, template <typename> class C>
typename signal<F,C>::cookie
signal<F,C>::connect (callback &&_cb)
{
return cookie (
m_children.insert (
m_children.end (),
std::move (_cb)
),
*this
);
}
//-------------------------------------------------------------------------
template <typename F, template <typename> class C>
void void
signal<F>::disconnect (const cookie &c) signal<F,C>::disconnect (cookie &c)
{ {
m_children.erase (c.m_position); m_children.erase (c.m_position);
c.m_position = m_children.end ();
} }
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
/// Disconnect all callbacks /// Disconnect all callbacks
template <typename F> template <typename F, template <typename> class C>
void void
signal<F>::clear (void) signal<F,C>::clear (void)
{ {
m_children.clear (); m_children.clear ();
} }
@ -130,42 +152,45 @@ namespace util {
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
/// Returns the number of callbacks connected. /// Returns the number of callbacks connected.
template <typename F> template <typename F, template <typename> class C>
unsigned int unsigned int
signal<F>::size (void) const signal<F,C>::size (void) const
{ {
return m_children.size (); return m_children.size ();
} }
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
template <typename F> template <typename F, template <typename> class C>
bool bool
signal<F>::empty (void) const signal<F,C>::empty (void) const
{ {
return m_children.empty (); return m_children.empty ();
} }
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
template <typename F> template <typename F, template <typename> class C>
template <typename ...Args> template <typename ...Args>
void typename signal<F,C>::R
signal<F>::operator () (Args&&... tail) { signal<F,C>::operator () (Args&&... tail) {
if (m_children.empty ()) if (m_children.empty ())
return; return R();
auto i = m_children.cbegin (); //auto i = m_children.cbegin ();
bool looping; //bool looping;
do { C<F> combiner;
// Increment before we execute so that the caller is able to return combiner (m_children.begin (), m_children.end (), std::forward<Args> (tail)...);
// deregister during execution.
auto current = i++;
looping = m_children.cend () != i;
(*current)(std::forward<Args> (tail)...); //do {
} while (looping); // // Increment before we execute so that the caller is able to
// // deregister themselves during execution.
// auto current = i++;
// looping = m_children.cend () != i;
// (*current)(std::forward<Args> (tail)...);
//} while (looping);
} }

View File

@ -73,6 +73,51 @@ test_value_signal (void)
} }
///////////////////////////////////////////////////////////////////////////////
void
test_combiner (void)
{
{
util::signal<bool(void), util::combine::logical_and> sig;
unsigned count = 0;
auto raii = sig.connect ([&] (void) { ++count; return true; });
auto raii = sig.connect ([&] (void) { ++count; return true; });
auto raii = sig.connect ([&] (void) { ++count; return true; });
CHECK (sig ());
CHECK_EQ (count, 3);
}
{
util::signal<bool(void), util::combine::logical_and> sig;
unsigned count = 0;
auto raii = sig.connect ([&] (void) { ++count; return true; });
auto raii = sig.connect ([&] (void) { ++count; return false; });
auto raii = sig.connect ([&] (void) { ++count; return true; });
CHECK (!sig ());
CHECK_EQ (count, 2);
}
}
///////////////////////////////////////////////////////////////////////////////
void
test_disconnect (void)
{
util::signal<void(void)> sig;
util::signal<void(void)>::cookie a = sig.connect ([&] (void) { sig.disconnect (a); });
util::signal<void(void)>::cookie b = sig.connect ([&] (void) { sig.disconnect (b); });
util::signal<void(void)>::cookie c = sig.connect ([&] (void) { sig.disconnect (c); });
util::signal<void(void)>::cookie d = sig.connect ([&] (void) { sig.disconnect (d); });
sig ();
}
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
int int
main (int, char **) main (int, char **)
@ -81,6 +126,8 @@ main (int, char **)
test_single (); test_single ();
test_double (); test_double ();
test_value_signal (); test_value_signal ();
test_combiner ();
test_disconnect ();
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }