Explorar o código

added connection/scoped_connection (#267 - test required)

Michele Caini %!s(int64=6) %!d(string=hai) anos
pai
achega
573750a43d
Modificáronse 1 ficheiros con 95 adicións e 12 borrados
  1. 95 12
      src/entt/signal/sigh.hpp

+ 95 - 12
src/entt/signal/sigh.hpp

@@ -16,7 +16,7 @@ namespace entt {
 
 
 /**
- * @brief Sink implementation.
+ * @brief Sink class.
  *
  * Primary template isn't defined on purpose. All the specializations give a
  * compile-time error unless the template parameter is a function type.
@@ -28,7 +28,7 @@ struct sink;
 
 
 /**
- * @brief Unmanaged signal handler declaration.
+ * @brief Unmanaged signal handler.
  *
  * Primary template isn't defined on purpose. All the specializations give a
  * compile-time error unless the template parameter is a function type.
@@ -40,11 +40,11 @@ class sigh;
 
 
 /**
- * @brief Unmanaged signal handler definition.
+ * @brief Unmanaged signal handler.
  *
- * Unmanaged signal handler. It works directly with naked pointers to classes
- * and pointers to member functions as well as pointers to free functions. Users
- * of this class are in charge of disconnecting instances before deleting them.
+ * It works directly with naked pointers to classes and pointers to member
+ * functions as well as pointers to free functions. Users of this class are in
+ * charge of disconnecting instances before deleting them.
  *
  * This class serves mainly two purposes:
  *
@@ -154,21 +154,74 @@ private:
 
 
 /**
- * @brief Sink implementation.
+ * @brief Connection class.
  *
- * A sink is an opaque object used to connect listeners to signals.<br/>
+ * Opaque object the aim of which is to allow users to release an already
+ * estabilished connection without having to keep a reference to the signal or
+ * the sink that generated it.
+ */
+class connection {
+    /*! @brief A sink is allowed to create connection objects. */
+    template<typename>
+    friend struct sink;
+
+public:
+    /*! @brief Breaks the connection. */
+    void release() {
+        disconnect(parent, value_or_instance);
+    }
+
+private:
+    void(* disconnect)(void *, const void *){};
+    const void *value_or_instance{};
+    void *parent{};
+};
+
+
+/**
+ * @brief Scoped connection class.
+ *
+ * Opaque object the aim of which is to allow users to release an already
+ * estabilished connection without having to keep a reference to the signal or
+ * the sink that generated it.<br/>
+ * A scoped connection automatically breaks the link between the two objects
+ * when it goes out of scope.
+ */
+struct scoped_connection: private connection {
+    /**
+     * @brief Constructs a scoped connection from a basic connection.
+     * @param conn A valid connection object.
+     */
+    scoped_connection(const connection &conn)
+        : connection{conn}
+    {}
+
+    /*! @brief Automatically breaks the link on destruction. */
+    ~scoped_connection() {
+        connection::release();
+    }
+};
+
+
+/**
+ * @brief Sink class.
+ *
+ * A sink is used to connect listeners to signals and to disconnect them.<br/>
  * The function type for a listener is the one of the signal to which it
  * belongs.
  *
  * The clear separation between a signal and a sink permits to store the former
  * as private data member without exposing the publish functionality to the
- * users of a class.
+ * users of the class.
  *
  * @tparam Ret Return type of a function type.
  * @tparam Args Types of arguments of a function type.
  */
 template<typename Ret, typename... Args>
 struct sink<Ret(Args...)> {
+    /*! @brief Signal type. */
+    using signal_type = sigh<Ret(Args...)>;
+
     /**
      * @brief Constructs a sink that is allowed to modify a given signal.
      * @param ref A valid reference to a signal object.
@@ -192,13 +245,24 @@ struct sink<Ret(Args...)> {
      * functions.
      *
      * @tparam Function A valid free function pointer.
+     * @return A properly initialized connection object.
      */
     template<auto Function>
-    void connect() {
+    connection connect() {
         disconnect<Function>();
+
         delegate<Ret(Args...)> delegate{};
         delegate.template connect<Function>();
         parent->calls.emplace_back(std::move(delegate));
+
+        connection conn;
+        conn.parent = parent;
+        conn.value_or_instance = nullptr;
+        conn.disconnect = [](void *parent, const void *) {
+            sink{*static_cast<signal_type *>(parent)}.disconnect<Function>();
+        };
+
+        return conn;
     }
 
     /**
@@ -216,13 +280,32 @@ struct sink<Ret(Args...)> {
      * @tparam Candidate Member or free function to connect to the signal.
      * @tparam Type Type of class or type of payload.
      * @param value_or_instance A valid pointer that fits the purpose.
+     * @return A properly initialized connection object.
      */
     template<auto Candidate, typename Type>
-    void connect(Type *value_or_instance) {
+    connection connect(Type *value_or_instance) {
         disconnect<Candidate>(value_or_instance);
+
         delegate<Ret(Args...)> delegate{};
         delegate.template connect<Candidate>(value_or_instance);
         parent->calls.emplace_back(std::move(delegate));
+
+        connection conn;
+        conn.parent = parent;
+        conn.value_or_instance = value_or_instance;
+        conn.disconnect = [](void *parent, const void *value_or_instance) {
+            Type *curr = nullptr;
+
+            if constexpr(std::is_const_v<Type>) {
+                curr = static_cast<Type *>(value_or_instance);
+            } else {
+                curr = static_cast<Type *>(const_cast<void *>(value_or_instance));
+            }
+
+            sink{*static_cast<signal_type *>(parent)}.disconnect<Candidate>(curr);
+        };
+
+        return conn;
     }
 
     /**
@@ -272,7 +355,7 @@ struct sink<Ret(Args...)> {
     }
 
 private:
-    sigh<Ret(Args...)> *parent;
+    signal_type *parent;
 };