From daa492ce07b62bd07d6120e2fabc3f6f2b6e50c7 Mon Sep 17 00:00:00 2001 From: Danny Robson Date: Tue, 10 Mar 2015 22:52:38 +1100 Subject: [PATCH] signal: add return value combiners --- signal.hpp | 62 +++++++++++++++++++++++--- signal.ipp | 113 +++++++++++++++++++++++++++++------------------- test/signal.cpp | 47 ++++++++++++++++++++ 3 files changed, 172 insertions(+), 50 deletions(-) diff --git a/signal.hpp b/signal.hpp index 7dff0ba3..7c1c0fe4 100644 --- a/signal.hpp +++ b/signal.hpp @@ -21,16 +21,62 @@ #define __UTIL_SIGNAL_HPP #include "nocopy.hpp" +#include "types/traits.hpp" #include #include #include namespace util { - template + namespace combine { + template + struct logical_and { + using R = typename func_traits::return_type; + + template + R operator() (T first, T last, Args&&... args) + { + while (first != last) + if (!(*first++)(std::forward (args)...)) + return false; + + return true; + } + }; + + template + struct logical_or { + using R = typename func_traits::return_type; + + template + R operator() (T first, T last, Args&&... args) + { + while (first != last) + if ((*first++)(std::forward (args)...)) + return true; + + return false; + } + }; + + template + struct noop { + using R = void; + + template + R operator() (T first, T last, Args&&... args) + { + while (first != last) { + (*first++)(std::forward (args)...); + } + } + }; + } + + template class C = combine::noop> class signal { public: - using R = std::result_of; + using R = typename C::R; using callback = std::function; 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 - void operator () (Args&&... tail); + R + operator() (Args&&... tail); private: typedef std::list group; group m_children; }; + /////////////////////////////////////////////////////////////////////////// + // wrap a value in a signal and trigger on assignment + //template class C> template class value_signal : public signal { public: diff --git a/signal.ipp b/signal.ipp index be599c82..76afa5ee 100644 --- a/signal.ipp +++ b/signal.ipp @@ -27,9 +27,9 @@ namespace util { /////////////////////////////////////////////////////////////////////////// - template - struct signal::cookie : public nocopy { - cookie (typename group::iterator, signal &parent); + template class C> + struct signal::cookie : public nocopy { + cookie (typename group::iterator, signal &parent); cookie (cookie &&rhs); ~cookie (); @@ -37,22 +37,22 @@ namespace util { void reset (void); typename group::iterator m_position; - signal &m_parent; + signal &m_parent; }; /////////////////////////////////////////////////////////////////////////// - template - signal::cookie::cookie (typename group::iterator _position, - signal &_parent): + template class C> + signal::cookie::cookie (typename group::iterator _position, + signal &_parent): m_position (_position), m_parent (_parent) { ; } //------------------------------------------------------------------------- - template - signal::cookie::cookie (cookie &&rhs): + template class C> + signal::cookie::cookie (cookie &&rhs): m_position (rhs.m_position), m_parent (rhs.m_parent) { @@ -61,8 +61,8 @@ namespace util { //------------------------------------------------------------------------- - template - signal::cookie::~cookie () + template class C> + signal::cookie::~cookie () { if (m_parent.m_children.end () != m_position) m_parent.disconnect (*this); @@ -70,59 +70,81 @@ namespace util { /////////////////////////////////////////////////////////////////////////// - template + template class C> void - signal::cookie::reset (callback &&cb) + signal::cookie::reset (callback &&cb) { *m_position = std::move (cb); } //------------------------------------------------------------------------- - template + template class C> void - signal::cookie::reset (void) + signal::cookie::reset (void) { m_position = m_parent.m_children.end (); } /////////////////////////////////////////////////////////////////////////// - template - signal::signal () + template class C> + signal::signal () { ; } //------------------------------------------------------------------------- - template - signal::~signal () + template class C> + signal::~signal () { CHECK (empty ()); } /////////////////////////////////////////////////////////////////////////// - template - typename signal::cookie - signal::connect (const callback &_cb) + template class C> + typename signal::cookie + signal::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 + template class C> + typename signal::cookie + signal::connect (callback &&_cb) + { + return cookie ( + m_children.insert ( + m_children.end (), + std::move (_cb) + ), + *this + ); + } + + + //------------------------------------------------------------------------- + template class C> void - signal::disconnect (const cookie &c) + signal::disconnect (cookie &c) { m_children.erase (c.m_position); + c.m_position = m_children.end (); } //------------------------------------------------------------------------- /// Disconnect all callbacks - template + template class C> void - signal::clear (void) + signal::clear (void) { m_children.clear (); } @@ -130,42 +152,45 @@ namespace util { /////////////////////////////////////////////////////////////////////////// /// Returns the number of callbacks connected. - template + template class C> unsigned int - signal::size (void) const + signal::size (void) const { return m_children.size (); } //------------------------------------------------------------------------- - template + template class C> bool - signal::empty (void) const + signal::empty (void) const { return m_children.empty (); } /////////////////////////////////////////////////////////////////////////// - template + template class C> template - void - signal::operator () (Args&&... tail) { + typename signal::R + signal::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 combiner; + return combiner (m_children.begin (), m_children.end (), std::forward (tail)...); - (*current)(std::forward (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 (tail)...); + //} while (looping); } diff --git a/test/signal.cpp b/test/signal.cpp index a4848dc2..260af61f 100644 --- a/test/signal.cpp +++ b/test/signal.cpp @@ -73,6 +73,51 @@ test_value_signal (void) } +/////////////////////////////////////////////////////////////////////////////// +void +test_combiner (void) +{ + { + util::signal 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 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 sig; + + util::signal::cookie a = sig.connect ([&] (void) { sig.disconnect (a); }); + util::signal::cookie b = sig.connect ([&] (void) { sig.disconnect (b); }); + util::signal::cookie c = sig.connect ([&] (void) { sig.disconnect (c); }); + util::signal::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; }