Explorar el Código

sigh: a more modern approach (as in modern C++) for collectors

Michele Caini hace 6 años
padre
commit
729a071284
Se han modificado 4 ficheros con 82 adiciones y 136 borrados
  1. 6 0
      TODO
  2. 1 1
      src/entt/signal/fwd.hpp
  3. 35 80
      src/entt/signal/sigh.hpp
  4. 40 55
      test/entt/signal/sigh.cpp

+ 6 - 0
TODO

@@ -24,3 +24,9 @@
 * stable component handle that isn't affected by reallocations
 * stable component handle that isn't affected by reallocations
 * multi component registry::remove and some others?
 * multi component registry::remove and some others?
 * built-in support for dual (or N-) buffering
 * built-in support for dual (or N-) buffering
+
+TODO
+* registry::sort and registry::respect also for types that are part of a group (untracked items only)
+* remove collector from sigh and use it as a template parameter of sigh::collect
+* sink<F> receives and stores aside a ref to a sigh<F> (public constructor)
+* introduce connection<F> and scoped_connection<F>

+ 1 - 1
src/entt/signal/fwd.hpp

@@ -17,7 +17,7 @@ template<typename>
 class sink;
 class sink;
 
 
 /*! @class sigh */
 /*! @class sigh */
-template<typename, typename>
+template<typename>
 struct sigh;
 struct sigh;
 
 
 
 

+ 35 - 80
src/entt/signal/sigh.hpp

@@ -16,74 +16,28 @@ namespace entt {
 
 
 
 
 /**
 /**
- * @cond TURN_OFF_DOXYGEN
- * Internal details not to be documented.
- */
-
-
-namespace internal {
-
-
-template<typename Ret>
-struct null_collector {
-    using result_type = Ret;
-    bool operator()(result_type) const ENTT_NOEXCEPT { return true; }
-};
-
-
-template<>
-struct null_collector<void> {
-    using result_type = void;
-    bool operator()() const ENTT_NOEXCEPT { return true; }
-};
-
-
-template<typename>
-struct default_collector;
-
-
-template<typename Ret, typename... Args>
-struct default_collector<Ret(Args...)> {
-    using collector_type = null_collector<Ret>;
-};
-
-
-template<typename Function>
-using default_collector_type = typename default_collector<Function>::collector_type;
-
-
-}
-
-
-/**
- * Internal details not to be documented.
- * @endcond TURN_OFF_DOXYGEN
- */
-
-
-/**
- * @brief Sink implementation.
+ * @brief Unmanaged signal handler declaration.
  *
  *
  * Primary template isn't defined on purpose. All the specializations give a
  * Primary template isn't defined on purpose. All the specializations give a
  * compile-time error unless the template parameter is a function type.
  * compile-time error unless the template parameter is a function type.
  *
  *
  * @tparam Function A valid function type.
  * @tparam Function A valid function type.
+ * @tparam Collector Type of collector to use, if any.
  */
  */
 template<typename Function>
 template<typename Function>
-class sink;
+struct sigh;
 
 
 
 
 /**
 /**
- * @brief Unmanaged signal handler declaration.
+ * @brief Sink implementation.
  *
  *
  * Primary template isn't defined on purpose. All the specializations give a
  * Primary template isn't defined on purpose. All the specializations give a
  * compile-time error unless the template parameter is a function type.
  * compile-time error unless the template parameter is a function type.
  *
  *
  * @tparam Function A valid function type.
  * @tparam Function A valid function type.
- * @tparam Collector Type of collector to use, if any.
  */
  */
-template<typename Function, typename Collector = internal::default_collector_type<Function>>
-struct sigh;
+template<typename Function>
+class sink;
 
 
 
 
 /**
 /**
@@ -103,7 +57,7 @@ struct sigh;
 template<typename Ret, typename... Args>
 template<typename Ret, typename... Args>
 class sink<Ret(Args...)> {
 class sink<Ret(Args...)> {
     /*! @brief A signal is allowed to create sinks. */
     /*! @brief A signal is allowed to create sinks. */
-    template<typename, typename>
+    template<typename>
     friend struct sigh;
     friend struct sigh;
 
 
     sink(std::vector<delegate<Ret(Args...)>> *ref) ENTT_NOEXCEPT
     sink(std::vector<delegate<Ret(Args...)>> *ref) ENTT_NOEXCEPT
@@ -216,26 +170,16 @@ private:
  *
  *
  * This class serves mainly two purposes:
  * This class serves mainly two purposes:
  *
  *
- * * Creating signals used later to notify a bunch of listeners.
+ * * Creating signals to use later to notify a bunch of listeners.
  * * Collecting results from a set of functions like in a voting system.
  * * Collecting results from a set of functions like in a voting system.
  *
  *
- * The default collector does nothing. To properly collect data, define and use
- * a class that has a call operator the signature of which is `bool(Param)` and:
- *
- * * `Param` is a type to which `Ret` can be converted.
- * * The return type is true if the handler must stop collecting data, false
- * otherwise.
- *
  * @tparam Ret Return type of a function type.
  * @tparam Ret Return type of a function type.
  * @tparam Args Types of arguments of a function type.
  * @tparam Args Types of arguments of a function type.
- * @tparam Collector Type of collector to use, if any.
  */
  */
-template<typename Ret, typename... Args, typename Collector>
-struct sigh<Ret(Args...), Collector> {
+template<typename Ret, typename... Args>
+struct sigh<Ret(Args...)> {
     /*! @brief Unsigned integer type. */
     /*! @brief Unsigned integer type. */
     using size_type = typename std::vector<delegate<Ret(Args...)>>::size_type;
     using size_type = typename std::vector<delegate<Ret(Args...)>>::size_type;
-    /*! @brief Collector type. */
-    using collector_type = Collector;
     /*! @brief Sink type. */
     /*! @brief Sink type. */
     using sink_type = entt::sink<Ret(Args...)>;
     using sink_type = entt::sink<Ret(Args...)>;
 
 
@@ -284,34 +228,45 @@ struct sigh<Ret(Args...), Collector> {
      */
      */
     void publish(Args... args) const {
     void publish(Args... args) const {
         for(auto pos = calls.size(); pos; --pos) {
         for(auto pos = calls.size(); pos; --pos) {
-            auto &call = calls[pos-1];
-            call(args...);
+            calls[pos-1](args...);
         }
         }
     }
     }
 
 
     /**
     /**
      * @brief Collects return values from the listeners.
      * @brief Collects return values from the listeners.
+     *
+     * The collector must expose a call operator with the following properties:
+     *
+     * * The return type is either `void` or such that it's convertible to
+     *   `bool`. In the second case, a true value will stop the iteration.
+     * * The list of parameters is empty if `Ret` is `void`, otherwise it
+     *   contains a single element such that `Ret` is convertible to it.
+     *
+     * @tparam Func Type of collector to use, if any.
+     * @param func A valid function object.
      * @param args Arguments to use to invoke listeners.
      * @param args Arguments to use to invoke listeners.
-     * @return An instance of the collector filled with collected data.
      */
      */
-    collector_type collect(Args... args) const {
-        collector_type collector;
+    template<typename Func>
+    void collect(Func func, Args... args) const {
+        bool stop = false;
 
 
-        for(auto &&call: calls) {
+        for(auto pos = calls.size(); pos && !stop; --pos) {
             if constexpr(std::is_void_v<Ret>) {
             if constexpr(std::is_void_v<Ret>) {
-                call(args...);
-
-                if(!collector()) {
-                    break;
+                if constexpr(std::is_invocable_r_v<bool, Func>) {
+                    calls[pos-1](args...);
+                    stop = func();
+                } else {
+                    calls[pos-1](args...);
+                    func();
                 }
                 }
             } else {
             } else {
-                if(!collector(call(args...))) {
-                    break;
+                if constexpr(std::is_invocable_r_v<bool, Func, Ret>) {
+                    stop = func(calls[pos-1](args...));
+                } else {
+                    func(calls[pos-1](args...));
                 }
                 }
             }
             }
         }
         }
-
-        return collector;
     }
     }
 
 
     /**
     /**

+ 40 - 55
test/entt/signal/sigh.cpp

@@ -11,41 +11,11 @@ struct sigh_listener {
 
 
     void i() {}
     void i() {}
     // useless definition just because msvc does weird things if both are empty
     // useless definition just because msvc does weird things if both are empty
-    void l() { k = k ? true : false; }
+    void l() { k = k*k; }
 
 
     bool k{false};
     bool k{false};
 };
 };
 
 
-template<typename Ret>
-struct test_collect_all {
-    std::vector<Ret> vec{};
-    static int f() { return 42; }
-    static int g() { return 3; }
-    bool operator()(Ret r) noexcept {
-        vec.push_back(r);
-        return true;
-    }
-};
-
-template<>
-struct test_collect_all<void> {
-    std::vector<int> vec{};
-    static void h(const void *) {}
-    bool operator()() noexcept {
-        return true;
-    }
-};
-
-template<typename Ret>
-struct test_collect_first {
-    std::vector<Ret> vec{};
-    static int f(const void *) { return 42; }
-    bool operator()(Ret r) noexcept {
-        vec.push_back(r);
-        return false;
-    }
-};
-
 struct const_nonconst_noexcept {
 struct const_nonconst_noexcept {
     void f() { ++cnt; }
     void f() { ++cnt; }
     void g() noexcept { ++cnt; }
     void g() noexcept { ++cnt; }
@@ -163,38 +133,53 @@ TEST(SigH, Members) {
 }
 }
 
 
 TEST(SigH, Collector) {
 TEST(SigH, Collector) {
-    entt::sigh<void(), test_collect_all<void>> sigh_void;
-    const void *fake_instance = nullptr;
+    sigh_listener listener;
+    entt::sigh<bool(int)> sigh;
+    int cnt = 0;
+
+    sigh.sink().connect<&sigh_listener::g>(&listener);
+    sigh.sink().connect<&sigh_listener::h>(&listener);
 
 
-    sigh_void.sink().connect<&test_collect_all<void>::h>(fake_instance);
-    auto collector_void = sigh_void.collect();
+    listener.k = true;
+    sigh.collect([&listener, &cnt](bool value) {
+        ASSERT_TRUE(value);
+        listener.k = true;
+        ++cnt;
+    }, 42);
+
+    ASSERT_FALSE(sigh.empty());
+    ASSERT_EQ(cnt, 2);
 
 
-    ASSERT_FALSE(sigh_void.empty());
-    ASSERT_TRUE(collector_void.vec.empty());
+    cnt = 0;
+    sigh.collect([&cnt](bool value) {
+        // gtest and its macro hell are sometimes really annoying...
+        [](auto v) { ASSERT_TRUE(v); }(value);
+        ++cnt;
+        return true;
+    }, 42);
 
 
-    entt::sigh<int(), test_collect_all<int>> sigh_all;
+    ASSERT_EQ(cnt, 1);
+}
 
 
-    sigh_all.sink().connect<&test_collect_all<int>::f>();
-    sigh_all.sink().connect<&test_collect_all<int>::f>();
-    sigh_all.sink().connect<&test_collect_all<int>::g>();
-    auto collector_all = sigh_all.collect();
+TEST(SigH, CollectorVoid) {
+    sigh_listener listener;
+    entt::sigh<void(int)> sigh;
+    int cnt = 0;
 
 
-    ASSERT_FALSE(sigh_all.empty());
-    ASSERT_FALSE(collector_all.vec.empty());
-    ASSERT_EQ(static_cast<std::vector<int>::size_type>(2), collector_all.vec.size());
-    ASSERT_EQ(42, collector_all.vec[0]);
-    ASSERT_EQ(3, collector_all.vec[1]);
+    sigh.sink().connect<&sigh_listener::g>(&listener);
+    sigh.sink().connect<&sigh_listener::h>(&listener);
+    sigh.collect([&cnt]() { ++cnt; }, 42);
 
 
-    entt::sigh<int(), test_collect_first<int>> sigh_first;
+    ASSERT_FALSE(sigh.empty());
+    ASSERT_EQ(cnt, 2);
 
 
-    sigh_first.sink().connect<&test_collect_first<int>::f>(fake_instance);
-    sigh_first.sink().connect<&test_collect_first<int>::f>(fake_instance);
-    auto collector_first = sigh_first.collect();
+    cnt = 0;
+    sigh.collect([&cnt]() {
+        ++cnt;
+        return true;
+    }, 42);
 
 
-    ASSERT_FALSE(sigh_first.empty());
-    ASSERT_FALSE(collector_first.vec.empty());
-    ASSERT_EQ(static_cast<std::vector<int>::size_type>(1), collector_first.vec.size());
-    ASSERT_EQ(42, collector_first.vec[0]);
+    ASSERT_EQ(cnt, 1);
 }
 }
 
 
 TEST(SigH, ConstNonConstNoExcept) {
 TEST(SigH, ConstNonConstNoExcept) {