signal: add return value combiners
This commit is contained in:
parent
d4d5eaa6f8
commit
daa492ce07
62
signal.hpp
62
signal.hpp
@ -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 {
|
||||||
template <typename F>
|
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 {
|
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:
|
||||||
|
113
signal.ipp
113
signal.ipp
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user