delegate.hpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. #ifndef ENTT_SIGNAL_DELEGATE_HPP
  2. #define ENTT_SIGNAL_DELEGATE_HPP
  3. #include <cstddef>
  4. #include <functional>
  5. #include <tuple>
  6. #include <type_traits>
  7. #include <utility>
  8. #include "../config/config.h"
  9. #include "../core/type_traits.hpp"
  10. #include "fwd.hpp"
  11. namespace entt {
  12. /**
  13. * @cond TURN_OFF_DOXYGEN
  14. * Internal details not to be documented.
  15. */
  16. namespace internal {
  17. template<typename Ret, typename... Args>
  18. constexpr auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...);
  19. template<typename Ret, typename Type, typename... Args, typename Other>
  20. constexpr auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...);
  21. template<typename Class, typename Ret, typename... Args, typename... Other>
  22. constexpr auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...);
  23. template<typename Class, typename Ret, typename... Args, typename... Other>
  24. constexpr auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...);
  25. template<typename Class, typename Type, typename... Other>
  26. constexpr auto function_pointer(Type Class::*, Other &&...) -> Type (*)();
  27. template<typename... Type>
  28. using function_pointer_t = decltype(function_pointer(std::declval<Type>()...));
  29. template<typename... Class, typename Ret, typename... Args>
  30. [[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) {
  31. return std::index_sequence_for<Class..., Args...>{};
  32. }
  33. } // namespace internal
  34. /**
  35. * Internal details not to be documented.
  36. * @endcond
  37. */
  38. /**
  39. * @brief Basic delegate implementation.
  40. *
  41. * Primary template isn't defined on purpose. All the specializations give a
  42. * compile-time error unless the template parameter is a function type.
  43. */
  44. template<typename>
  45. class delegate;
  46. /**
  47. * @brief Utility class to use to send around functions and members.
  48. *
  49. * Unmanaged delegate for function pointers and members. Users of this class are
  50. * in charge of disconnecting instances before deleting them.
  51. *
  52. * A delegate can be used as a general purpose invoker without memory overhead
  53. * for free functions possibly with payloads and bound or unbound members.
  54. *
  55. * @tparam Ret Return type of a function type.
  56. * @tparam Args Types of arguments of a function type.
  57. */
  58. template<typename Ret, typename... Args>
  59. class delegate<Ret(Args...)> {
  60. template<auto Candidate, std::size_t... Index>
  61. [[nodiscard]] auto wrap(std::index_sequence<Index...>) noexcept {
  62. return [](const void *, Args... args) -> Ret {
  63. [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
  64. if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), type_list_element_t<Index, type_list<Args...>>...>) {
  65. return static_cast<Ret>(std::invoke(Candidate, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
  66. } else {
  67. constexpr auto offset = sizeof...(Args) - sizeof...(Index);
  68. return static_cast<Ret>(std::invoke(Candidate, std::forward<type_list_element_t<Index + offset, type_list<Args...>>>(std::get<Index + offset>(arguments))...));
  69. }
  70. };
  71. }
  72. template<auto Candidate, typename Type, std::size_t... Index>
  73. [[nodiscard]] auto wrap(Type &, std::index_sequence<Index...>) noexcept {
  74. return [](const void *payload, Args... args) -> Ret {
  75. [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
  76. Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
  77. if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type &, type_list_element_t<Index, type_list<Args...>>...>) {
  78. return static_cast<Ret>(std::invoke(Candidate, *curr, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
  79. } else {
  80. constexpr auto offset = sizeof...(Args) - sizeof...(Index);
  81. return static_cast<Ret>(std::invoke(Candidate, *curr, std::forward<type_list_element_t<Index + offset, type_list<Args...>>>(std::get<Index + offset>(arguments))...));
  82. }
  83. };
  84. }
  85. template<auto Candidate, typename Type, std::size_t... Index>
  86. [[nodiscard]] auto wrap(Type *, std::index_sequence<Index...>) noexcept {
  87. return [](const void *payload, Args... args) -> Ret {
  88. [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
  89. Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
  90. if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type *, type_list_element_t<Index, type_list<Args...>>...>) {
  91. return static_cast<Ret>(std::invoke(Candidate, curr, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
  92. } else {
  93. constexpr auto offset = sizeof...(Args) - sizeof...(Index);
  94. return static_cast<Ret>(std::invoke(Candidate, curr, std::forward<type_list_element_t<Index + offset, type_list<Args...>>>(std::get<Index + offset>(arguments))...));
  95. }
  96. };
  97. }
  98. public:
  99. /*! @brief Function type of the contained target. */
  100. using function_type = Ret(const void *, Args...);
  101. /*! @brief Function type of the delegate. */
  102. using type = Ret(Args...);
  103. /*! @brief Return type of the delegate. */
  104. using result_type = Ret;
  105. /*! @brief Default constructor. */
  106. delegate() noexcept
  107. : instance{nullptr},
  108. fn{nullptr} {}
  109. /**
  110. * @brief Constructs a delegate with a given object or payload, if any.
  111. * @tparam Candidate Function or member to connect to the delegate.
  112. * @tparam Type Type of class or type of payload, if any.
  113. * @param value_or_instance Optional valid object that fits the purpose.
  114. */
  115. template<auto Candidate, typename... Type>
  116. delegate(connect_arg_t<Candidate>, Type &&...value_or_instance) noexcept {
  117. connect<Candidate>(std::forward<Type>(value_or_instance)...);
  118. }
  119. /**
  120. * @brief Constructs a delegate and connects an user defined function with
  121. * optional payload.
  122. * @param function Function to connect to the delegate.
  123. * @param payload User defined arbitrary data.
  124. */
  125. delegate(function_type *function, const void *payload = nullptr) noexcept {
  126. connect(function, payload);
  127. }
  128. /**
  129. * @brief Connects a free function or an unbound member to a delegate.
  130. * @tparam Candidate Function or member to connect to the delegate.
  131. */
  132. template<auto Candidate>
  133. void connect() noexcept {
  134. instance = nullptr;
  135. if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Args...>) {
  136. fn = [](const void *, Args... args) -> Ret {
  137. return Ret(std::invoke(Candidate, std::forward<Args>(args)...));
  138. };
  139. } else if constexpr(std::is_member_pointer_v<decltype(Candidate)>) {
  140. fn = wrap<Candidate>(internal::index_sequence_for<type_list_element_t<0, type_list<Args...>>>(internal::function_pointer_t<decltype(Candidate)>{}));
  141. } else {
  142. fn = wrap<Candidate>(internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate)>{}));
  143. }
  144. }
  145. /**
  146. * @brief Connects a free function with payload or a bound member to a
  147. * delegate.
  148. *
  149. * The delegate isn't responsible for the connected object or the payload.
  150. * Users must always guarantee that the lifetime of the instance overcomes
  151. * the one of the delegate.<br/>
  152. * When used to connect a free function with payload, its signature must be
  153. * such that the instance is the first argument before the ones used to
  154. * define the delegate itself.
  155. *
  156. * @tparam Candidate Function or member to connect to the delegate.
  157. * @tparam Type Type of class or type of payload.
  158. * @param value_or_instance A valid reference that fits the purpose.
  159. */
  160. template<auto Candidate, typename Type>
  161. void connect(Type &value_or_instance) noexcept {
  162. instance = &value_or_instance;
  163. if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type &, Args...>) {
  164. fn = [](const void *payload, Args... args) -> Ret {
  165. Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
  166. return Ret(std::invoke(Candidate, *curr, std::forward<Args>(args)...));
  167. };
  168. } else {
  169. fn = wrap<Candidate>(value_or_instance, internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate), Type>{}));
  170. }
  171. }
  172. /**
  173. * @brief Connects a free function with payload or a bound member to a
  174. * delegate.
  175. *
  176. * @sa connect(Type &)
  177. *
  178. * @tparam Candidate Function or member to connect to the delegate.
  179. * @tparam Type Type of class or type of payload.
  180. * @param value_or_instance A valid pointer that fits the purpose.
  181. */
  182. template<auto Candidate, typename Type>
  183. void connect(Type *value_or_instance) noexcept {
  184. instance = value_or_instance;
  185. if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type *, Args...>) {
  186. fn = [](const void *payload, Args... args) -> Ret {
  187. Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
  188. return Ret(std::invoke(Candidate, curr, std::forward<Args>(args)...));
  189. };
  190. } else {
  191. fn = wrap<Candidate>(value_or_instance, internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate), Type>{}));
  192. }
  193. }
  194. /**
  195. * @brief Connects an user defined function with optional payload to a
  196. * delegate.
  197. *
  198. * The delegate isn't responsible for the connected object or the payload.
  199. * Users must always guarantee that the lifetime of an instance overcomes
  200. * the one of the delegate.<br/>
  201. * The payload is returned as the first argument to the target function in
  202. * all cases.
  203. *
  204. * @param function Function to connect to the delegate.
  205. * @param payload User defined arbitrary data.
  206. */
  207. void connect(function_type *function, const void *payload = nullptr) noexcept {
  208. ENTT_ASSERT(function != nullptr, "Uninitialized function pointer");
  209. instance = payload;
  210. fn = function;
  211. }
  212. /**
  213. * @brief Resets a delegate.
  214. *
  215. * After a reset, a delegate cannot be invoked anymore.
  216. */
  217. void reset() noexcept {
  218. instance = nullptr;
  219. fn = nullptr;
  220. }
  221. /**
  222. * @brief Returns a pointer to the stored callable function target, if any.
  223. * @return An opaque pointer to the stored callable function target.
  224. */
  225. [[nodiscard]] function_type *target() const noexcept {
  226. return fn;
  227. }
  228. /**
  229. * @brief Returns the instance or the payload linked to a delegate, if any.
  230. * @return An opaque pointer to the underlying data.
  231. */
  232. [[nodiscard]] const void *data() const noexcept {
  233. return instance;
  234. }
  235. /**
  236. * @brief Triggers a delegate.
  237. *
  238. * The delegate invokes the underlying function and returns the result.
  239. *
  240. * @warning
  241. * Attempting to trigger an invalid delegate results in undefined
  242. * behavior.
  243. *
  244. * @param args Arguments to use to invoke the underlying function.
  245. * @return The value returned by the underlying function.
  246. */
  247. Ret operator()(Args... args) const {
  248. ENTT_ASSERT(static_cast<bool>(*this), "Uninitialized delegate");
  249. return fn(instance, std::forward<Args>(args)...);
  250. }
  251. /**
  252. * @brief Checks whether a delegate actually stores a listener.
  253. * @return False if the delegate is empty, true otherwise.
  254. */
  255. [[nodiscard]] explicit operator bool() const noexcept {
  256. // no need to also test instance
  257. return !(fn == nullptr);
  258. }
  259. /**
  260. * @brief Compares the contents of two delegates.
  261. * @param other Delegate with which to compare.
  262. * @return False if the two contents differ, true otherwise.
  263. */
  264. [[nodiscard]] bool operator==(const delegate<Ret(Args...)> &other) const noexcept {
  265. return fn == other.fn && instance == other.instance;
  266. }
  267. private:
  268. const void *instance;
  269. function_type *fn;
  270. };
  271. /**
  272. * @brief Compares the contents of two delegates.
  273. * @tparam Ret Return type of a function type.
  274. * @tparam Args Types of arguments of a function type.
  275. * @param lhs A valid delegate object.
  276. * @param rhs A valid delegate object.
  277. * @return True if the two contents differ, false otherwise.
  278. */
  279. template<typename Ret, typename... Args>
  280. [[nodiscard]] bool operator!=(const delegate<Ret(Args...)> &lhs, const delegate<Ret(Args...)> &rhs) noexcept {
  281. return !(lhs == rhs);
  282. }
  283. /**
  284. * @brief Deduction guide.
  285. * @tparam Candidate Function or member to connect to the delegate.
  286. */
  287. template<auto Candidate>
  288. delegate(connect_arg_t<Candidate>) -> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate)>>>;
  289. /**
  290. * @brief Deduction guide.
  291. * @tparam Candidate Function or member to connect to the delegate.
  292. * @tparam Type Type of class or type of payload.
  293. */
  294. template<auto Candidate, typename Type>
  295. delegate(connect_arg_t<Candidate>, Type &&) -> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate), Type>>>;
  296. /**
  297. * @brief Deduction guide.
  298. * @tparam Ret Return type of a function type.
  299. * @tparam Args Types of arguments of a function type.
  300. */
  301. template<typename Ret, typename... Args>
  302. delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate<Ret(Args...)>;
  303. } // namespace entt
  304. #endif