|
|
@@ -16,7 +16,7 @@ namespace entt {
|
|
|
|
|
|
|
|
|
/**
|
|
|
- * @brief Unmanaged signal handler declaration.
|
|
|
+ * @brief Sink implementation.
|
|
|
*
|
|
|
* Primary template isn't defined on purpose. All the specializations give a
|
|
|
* compile-time error unless the template parameter is a function type.
|
|
|
@@ -24,11 +24,11 @@ namespace entt {
|
|
|
* @tparam Function A valid function type.
|
|
|
*/
|
|
|
template<typename Function>
|
|
|
-struct sigh;
|
|
|
+struct sink;
|
|
|
|
|
|
|
|
|
/**
|
|
|
- * @brief Sink implementation.
|
|
|
+ * @brief Unmanaged signal handler declaration.
|
|
|
*
|
|
|
* Primary template isn't defined on purpose. All the specializations give a
|
|
|
* compile-time error unless the template parameter is a function type.
|
|
|
@@ -36,7 +36,121 @@ struct sigh;
|
|
|
* @tparam Function A valid function type.
|
|
|
*/
|
|
|
template<typename Function>
|
|
|
-class sink;
|
|
|
+class sigh;
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Unmanaged signal handler definition.
|
|
|
+ *
|
|
|
+ * 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.
|
|
|
+ *
|
|
|
+ * This class serves mainly two purposes:
|
|
|
+ *
|
|
|
+ * * Creating signals to use later to notify a bunch of listeners.
|
|
|
+ * * Collecting results from a set of functions like in a voting system.
|
|
|
+ *
|
|
|
+ * @tparam Ret Return type of a function type.
|
|
|
+ * @tparam Args Types of arguments of a function type.
|
|
|
+ */
|
|
|
+template<typename Ret, typename... Args>
|
|
|
+class sigh<Ret(Args...)> {
|
|
|
+ /*! @brief A sink is allowed to modify a signal. */
|
|
|
+ friend struct sink<Ret(Args...)>;
|
|
|
+
|
|
|
+public:
|
|
|
+ /*! @brief Unsigned integer type. */
|
|
|
+ using size_type = typename std::vector<delegate<Ret(Args...)>>::size_type;
|
|
|
+ /*! @brief Sink type. */
|
|
|
+ using sink_type = entt::sink<Ret(Args...)>;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @brief Instance type when it comes to connecting member functions.
|
|
|
+ * @tparam Class Type of class to which the member function belongs.
|
|
|
+ */
|
|
|
+ template<typename Class>
|
|
|
+ using instance_type = Class *;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @brief Number of listeners connected to the signal.
|
|
|
+ * @return Number of listeners currently connected.
|
|
|
+ */
|
|
|
+ size_type size() const ENTT_NOEXCEPT {
|
|
|
+ return calls.size();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @brief Returns false if at least a listener is connected to the signal.
|
|
|
+ * @return True if the signal has no listeners connected, false otherwise.
|
|
|
+ */
|
|
|
+ bool empty() const ENTT_NOEXCEPT {
|
|
|
+ return calls.empty();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @brief Triggers a signal.
|
|
|
+ *
|
|
|
+ * All the listeners are notified. Order isn't guaranteed.
|
|
|
+ *
|
|
|
+ * @param args Arguments to use to invoke listeners.
|
|
|
+ */
|
|
|
+ void publish(Args... args) const {
|
|
|
+ for(auto pos = calls.size(); pos; --pos) {
|
|
|
+ calls[pos-1](args...);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @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.
|
|
|
+ */
|
|
|
+ template<typename Func>
|
|
|
+ void collect(Func func, Args... args) const {
|
|
|
+ bool stop = false;
|
|
|
+
|
|
|
+ for(auto pos = calls.size(); pos && !stop; --pos) {
|
|
|
+ if constexpr(std::is_void_v<Ret>) {
|
|
|
+ if constexpr(std::is_invocable_r_v<bool, Func>) {
|
|
|
+ calls[pos-1](args...);
|
|
|
+ stop = func();
|
|
|
+ } else {
|
|
|
+ calls[pos-1](args...);
|
|
|
+ func();
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if constexpr(std::is_invocable_r_v<bool, Func, Ret>) {
|
|
|
+ stop = func(calls[pos-1](args...));
|
|
|
+ } else {
|
|
|
+ func(calls[pos-1](args...));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @brief Swaps listeners between the two signals.
|
|
|
+ * @param lhs A valid signal object.
|
|
|
+ * @param rhs A valid signal object.
|
|
|
+ */
|
|
|
+ friend void swap(sigh &lhs, sigh &rhs) {
|
|
|
+ using std::swap;
|
|
|
+ swap(lhs.calls, rhs.calls);
|
|
|
+ }
|
|
|
+
|
|
|
+private:
|
|
|
+ std::vector<delegate<Ret(Args...)>> calls;
|
|
|
+};
|
|
|
|
|
|
|
|
|
/**
|
|
|
@@ -54,22 +168,21 @@ class sink;
|
|
|
* @tparam Args Types of arguments of a function type.
|
|
|
*/
|
|
|
template<typename Ret, typename... Args>
|
|
|
-class sink<Ret(Args...)> {
|
|
|
- /*! @brief A signal is allowed to create sinks. */
|
|
|
- template<typename>
|
|
|
- friend struct sigh;
|
|
|
-
|
|
|
- sink(std::vector<delegate<Ret(Args...)>> *ref) ENTT_NOEXCEPT
|
|
|
- : calls{ref}
|
|
|
+struct sink<Ret(Args...)> {
|
|
|
+ /**
|
|
|
+ * @brief Constructs a sink that is allowed to modify a given signal.
|
|
|
+ * @param ref A valid reference to a signal object.
|
|
|
+ */
|
|
|
+ sink(sigh<Ret(Args...)> &ref) ENTT_NOEXCEPT
|
|
|
+ : parent{&ref}
|
|
|
{}
|
|
|
|
|
|
-public:
|
|
|
/**
|
|
|
* @brief Returns false if at least a listener is connected to the sink.
|
|
|
* @return True if the sink has no listeners connected, false otherwise.
|
|
|
*/
|
|
|
bool empty() const ENTT_NOEXCEPT {
|
|
|
- return calls->empty();
|
|
|
+ return parent->calls.empty();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -85,7 +198,7 @@ public:
|
|
|
disconnect<Function>();
|
|
|
delegate<Ret(Args...)> delegate{};
|
|
|
delegate.template connect<Function>();
|
|
|
- calls->emplace_back(std::move(delegate));
|
|
|
+ parent->calls.emplace_back(std::move(delegate));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -109,7 +222,7 @@ public:
|
|
|
disconnect<Candidate>(value_or_instance);
|
|
|
delegate<Ret(Args...)> delegate{};
|
|
|
delegate.template connect<Candidate>(value_or_instance);
|
|
|
- calls->emplace_back(std::move(delegate));
|
|
|
+ parent->calls.emplace_back(std::move(delegate));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -118,9 +231,10 @@ public:
|
|
|
*/
|
|
|
template<auto Function>
|
|
|
void disconnect() {
|
|
|
+ auto &calls = parent->calls;
|
|
|
delegate<Ret(Args...)> delegate{};
|
|
|
delegate.template connect<Function>();
|
|
|
- calls->erase(std::remove(calls->begin(), calls->end(), std::move(delegate)), calls->end());
|
|
|
+ calls.erase(std::remove(calls.begin(), calls.end(), std::move(delegate)), calls.end());
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -132,11 +246,12 @@ public:
|
|
|
*/
|
|
|
template<auto Candidate, typename Type>
|
|
|
void disconnect(Type *value_or_instance) {
|
|
|
+ auto &calls = parent->calls;
|
|
|
delegate<Ret(Args...)> delegate{};
|
|
|
delegate.template connect<Candidate>(value_or_instance);
|
|
|
- calls->erase(std::remove_if(calls->begin(), calls->end(), [delegate](const auto &other) {
|
|
|
+ calls.erase(std::remove_if(calls.begin(), calls.end(), [delegate](const auto &other) {
|
|
|
return other == delegate && other.instance() == delegate.instance();
|
|
|
- }), calls->end());
|
|
|
+ }), calls.end());
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -145,142 +260,33 @@ public:
|
|
|
* @param value_or_instance A valid pointer that fits the purpose.
|
|
|
*/
|
|
|
void disconnect(const void *value_or_instance) {
|
|
|
- calls->erase(std::remove_if(calls->begin(), calls->end(), [value_or_instance](const auto &delegate) {
|
|
|
+ auto &calls = parent->calls;
|
|
|
+ calls.erase(std::remove_if(calls.begin(), calls.end(), [value_or_instance](const auto &delegate) {
|
|
|
return value_or_instance == delegate.instance();
|
|
|
- }), calls->end());
|
|
|
+ }), calls.end());
|
|
|
}
|
|
|
|
|
|
/*! @brief Disconnects all the listeners from a signal. */
|
|
|
void disconnect() {
|
|
|
- calls->clear();
|
|
|
+ parent->calls.clear();
|
|
|
}
|
|
|
|
|
|
private:
|
|
|
- std::vector<delegate<Ret(Args...)>> *calls;
|
|
|
+ sigh<Ret(Args...)> *parent;
|
|
|
};
|
|
|
|
|
|
|
|
|
/**
|
|
|
- * @brief Unmanaged signal handler definition.
|
|
|
+ * @brief Deduction guide.
|
|
|
*
|
|
|
- * 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.
|
|
|
- *
|
|
|
- * This class serves mainly two purposes:
|
|
|
- *
|
|
|
- * * Creating signals to use later to notify a bunch of listeners.
|
|
|
- * * Collecting results from a set of functions like in a voting system.
|
|
|
+ * It allows to deduce the function type of a sink directly from the signal it
|
|
|
+ * refers to.
|
|
|
*
|
|
|
* @tparam Ret Return type of a function type.
|
|
|
* @tparam Args Types of arguments of a function type.
|
|
|
*/
|
|
|
template<typename Ret, typename... Args>
|
|
|
-struct sigh<Ret(Args...)> {
|
|
|
- /*! @brief Unsigned integer type. */
|
|
|
- using size_type = typename std::vector<delegate<Ret(Args...)>>::size_type;
|
|
|
- /*! @brief Sink type. */
|
|
|
- using sink_type = entt::sink<Ret(Args...)>;
|
|
|
-
|
|
|
- /**
|
|
|
- * @brief Instance type when it comes to connecting member functions.
|
|
|
- * @tparam Class Type of class to which the member function belongs.
|
|
|
- */
|
|
|
- template<typename Class>
|
|
|
- using instance_type = Class *;
|
|
|
-
|
|
|
- /**
|
|
|
- * @brief Number of listeners connected to the signal.
|
|
|
- * @return Number of listeners currently connected.
|
|
|
- */
|
|
|
- size_type size() const ENTT_NOEXCEPT {
|
|
|
- return calls.size();
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * @brief Returns false if at least a listener is connected to the signal.
|
|
|
- * @return True if the signal has no listeners connected, false otherwise.
|
|
|
- */
|
|
|
- bool empty() const ENTT_NOEXCEPT {
|
|
|
- return calls.empty();
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * @brief Returns a sink object for the given signal.
|
|
|
- *
|
|
|
- * A sink is an opaque object used to connect listeners to signals.<br/>
|
|
|
- * The function type for a listener is the one of the signal to which it
|
|
|
- * belongs. The order of invocation of the listeners isn't guaranteed.
|
|
|
- *
|
|
|
- * @return A temporary sink object.
|
|
|
- */
|
|
|
- sink_type sink() ENTT_NOEXCEPT {
|
|
|
- return { &calls };
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * @brief Triggers a signal.
|
|
|
- *
|
|
|
- * All the listeners are notified. Order isn't guaranteed.
|
|
|
- *
|
|
|
- * @param args Arguments to use to invoke listeners.
|
|
|
- */
|
|
|
- void publish(Args... args) const {
|
|
|
- for(auto pos = calls.size(); pos; --pos) {
|
|
|
- calls[pos-1](args...);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * @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.
|
|
|
- */
|
|
|
- template<typename Func>
|
|
|
- void collect(Func func, Args... args) const {
|
|
|
- bool stop = false;
|
|
|
-
|
|
|
- for(auto pos = calls.size(); pos && !stop; --pos) {
|
|
|
- if constexpr(std::is_void_v<Ret>) {
|
|
|
- if constexpr(std::is_invocable_r_v<bool, Func>) {
|
|
|
- calls[pos-1](args...);
|
|
|
- stop = func();
|
|
|
- } else {
|
|
|
- calls[pos-1](args...);
|
|
|
- func();
|
|
|
- }
|
|
|
- } else {
|
|
|
- if constexpr(std::is_invocable_r_v<bool, Func, Ret>) {
|
|
|
- stop = func(calls[pos-1](args...));
|
|
|
- } else {
|
|
|
- func(calls[pos-1](args...));
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * @brief Swaps listeners between the two signals.
|
|
|
- * @param lhs A valid signal object.
|
|
|
- * @param rhs A valid signal object.
|
|
|
- */
|
|
|
- friend void swap(sigh &lhs, sigh &rhs) {
|
|
|
- using std::swap;
|
|
|
- swap(lhs.calls, rhs.calls);
|
|
|
- }
|
|
|
-
|
|
|
-private:
|
|
|
- std::vector<delegate<Ret(Args...)>> calls;
|
|
|
-};
|
|
|
+sink(sigh<Ret(Args...)> &) ENTT_NOEXCEPT -> sink<Ret(Args...)>;
|
|
|
|
|
|
|
|
|
}
|