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
|
||||
|
||||
#include "nocopy.hpp"
|
||||
#include "types/traits.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <type_traits>
|
||||
|
||||
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 {
|
||||
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:
|
||||
|
113
signal.ipp
113
signal.ipp
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user