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
#include "nocopy.hpp"
#include "types/traits.hpp"
#include <functional>
#include <list>
#include <type_traits>
namespace util {
namespace combine {
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 {
public:
using R = std::result_of<F>;
using R = typename C<F>::R;
using callback = std::function<F>;
struct cookie;
@ -40,9 +86,10 @@ namespace util {
~signal ();
/// Add a callback to list.
cookie connect (callback&&);
cookie connect (const callback&);
void disconnect (const cookie&);
void disconnect (cookie&);
/// Disconnect all callbacks
void clear (void);
@ -51,16 +98,19 @@ namespace util {
unsigned int size (void) const;
bool empty (void) const;
/// Execute all callbacks, ignoring the return parameters. Does
/// not combine results.
/// Execute all callbacks
template <typename ...Args>
void operator () (Args&&... tail);
R
operator() (Args&&... tail);
private:
typedef std::list<callback> group;
group m_children;
};
///////////////////////////////////////////////////////////////////////////
// wrap a value in a signal and trigger on assignment
//template <typename T, template <typename> class C>
template <typename T>
class value_signal : public signal<void(T)> {
public:

View File

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