BsEvent.h 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. #pragma once
  2. #include "BsPrerequisitesUtil.h"
  3. #include "BsModule.h"
  4. namespace BansheeEngine
  5. {
  6. /**
  7. * @brief Data common to all event connections.
  8. */
  9. class BaseConnectionData
  10. {
  11. public:
  12. bool isValid;
  13. };
  14. /**
  15. * @brief Event handle. Allows you to track to which events you subscribed to and
  16. * disconnect from them when needed.
  17. */
  18. class HEvent
  19. {
  20. public:
  21. HEvent()
  22. :mDisconnectCallback(nullptr), mConnection(nullptr), mEvent(nullptr)
  23. { }
  24. HEvent(std::shared_ptr<BaseConnectionData> connection, void* event, void(*disconnectCallback) (const std::shared_ptr<BaseConnectionData>&, void*))
  25. :mConnection(connection), mEvent(event), mDisconnectCallback(disconnectCallback)
  26. { }
  27. /**
  28. * @brief Disconnect from the event you are subscribed to.
  29. *
  30. * @note Caller must ensure the event is still valid.
  31. */
  32. void disconnect()
  33. {
  34. if (mConnection != nullptr && mConnection->isValid)
  35. mDisconnectCallback(mConnection, mEvent);
  36. }
  37. struct Bool_struct
  38. {
  39. int _Member;
  40. };
  41. /**
  42. * @brief Allows direct conversion of a handle to bool.
  43. *
  44. * @note This is needed because we can't directly convert to bool
  45. * since then we can assign pointer to bool and that's wrong.
  46. */
  47. operator int Bool_struct::*() const
  48. {
  49. return ((mConnection != nullptr && mConnection->isValid) ? &Bool_struct::_Member : 0);
  50. }
  51. private:
  52. void(*mDisconnectCallback) (const std::shared_ptr<BaseConnectionData>&, void*);
  53. std::shared_ptr<BaseConnectionData> mConnection;
  54. void* mEvent;
  55. };
  56. /**
  57. * @brief Events allows you to register method callbacks that get notified
  58. * when the event is triggered.
  59. *
  60. * @note Callback method return value is ignored.
  61. */
  62. template <class RetType, class... Args>
  63. class TEvent
  64. {
  65. struct ConnectionData : BaseConnectionData
  66. {
  67. public:
  68. ConnectionData(std::function<RetType(Args...)> func)
  69. :func(func)
  70. { }
  71. std::function<RetType(Args...)> func;
  72. };
  73. struct InternalData
  74. {
  75. InternalData()
  76. :mHasDisconnectedCallbacks(false)
  77. { }
  78. Vector<std::shared_ptr<ConnectionData>> mConnections;
  79. bool mHasDisconnectedCallbacks;
  80. BS_RECURSIVE_MUTEX(mMutex);
  81. };
  82. public:
  83. TEvent()
  84. :mInternalData(bs_shared_ptr<InternalData>())
  85. { }
  86. ~TEvent()
  87. {
  88. clear();
  89. }
  90. /**
  91. * @brief Register a new callback that will get notified once
  92. * the event is triggered.
  93. */
  94. HEvent connect(std::function<RetType(Args...)> func)
  95. {
  96. std::shared_ptr<ConnectionData> connData = bs_shared_ptr<ConnectionData>(func);
  97. connData->isValid = true;
  98. {
  99. BS_LOCK_RECURSIVE_MUTEX(mInternalData->mMutex);
  100. mInternalData->mConnections.push_back(connData);
  101. }
  102. return HEvent(connData, this, &TEvent::disconnectCallback);
  103. }
  104. /**
  105. * @brief Trigger the event, notifying all register callback methods.
  106. */
  107. void operator() (Args... args)
  108. {
  109. // Increase ref count to ensure this event data isn't destroyed if one of the callbacks
  110. // deletes the event itself.
  111. std::shared_ptr<InternalData> internalData = mInternalData;
  112. BS_LOCK_RECURSIVE_MUTEX(internalData->mMutex);
  113. // Here is the only place we remove connections, in order to allow disconnect() and clear() to be called
  114. // recursively from the notify callbacks
  115. if (internalData->mHasDisconnectedCallbacks)
  116. {
  117. for (UINT32 i = 0; i < internalData->mConnections.size(); i++)
  118. {
  119. if (!internalData->mConnections[i]->isValid)
  120. {
  121. internalData->mConnections.erase(internalData->mConnections.begin() + i);
  122. i--;
  123. }
  124. }
  125. internalData->mHasDisconnectedCallbacks = false;
  126. }
  127. // Do not use an iterator here, as new connections might be added during iteration from
  128. // the notify callback
  129. UINT32 numConnections = (UINT32)internalData->mConnections.size(); // Remember current num. connections as we don't want to notify new ones
  130. for (UINT32 i = 0; i < numConnections; i++)
  131. {
  132. if (internalData->mConnections[i]->func != nullptr)
  133. internalData->mConnections[i]->func(args...);
  134. }
  135. }
  136. /**
  137. * @brief Clear all callbacks from the event.
  138. */
  139. void clear()
  140. {
  141. BS_LOCK_RECURSIVE_MUTEX(mInternalData->mMutex);
  142. for (auto& connection : mInternalData->mConnections)
  143. {
  144. connection->isValid = false;
  145. connection->func = nullptr;
  146. }
  147. if (mInternalData->mConnections.size() > 0)
  148. mInternalData->mHasDisconnectedCallbacks = true;
  149. }
  150. /**
  151. * @brief Check if event has any callbacks registered.
  152. *
  153. * @note It is safe to trigger an event even if no callbacks are registered.
  154. */
  155. bool empty()
  156. {
  157. BS_LOCK_RECURSIVE_MUTEX(mInternalData->mMutex);
  158. return mInternalData->mConnections.size() == 0;
  159. }
  160. private:
  161. std::shared_ptr<InternalData> mInternalData;
  162. /**
  163. * @brief Callback triggered by event handles when they want to disconnect from
  164. * an event.
  165. */
  166. static void disconnectCallback(const std::shared_ptr<BaseConnectionData>& connection, void* event)
  167. {
  168. TEvent<RetType, Args...>* castEvent = reinterpret_cast<TEvent<RetType, Args...>*>(event);
  169. castEvent->disconnect(connection);
  170. }
  171. /**
  172. * @brief Internal method that disconnects the callback described by the provided connection data.
  173. */
  174. void disconnect(const std::shared_ptr<BaseConnectionData>& connData)
  175. {
  176. BS_LOCK_RECURSIVE_MUTEX(mInternalData->mMutex);
  177. std::shared_ptr<ConnectionData> myConnData = std::static_pointer_cast<ConnectionData>(connData);
  178. for (auto& iter = mInternalData->mConnections.begin(); iter != mInternalData->mConnections.end(); ++iter)
  179. {
  180. if ((*iter) == myConnData)
  181. {
  182. myConnData->isValid = false;
  183. myConnData->func = nullptr;
  184. mInternalData->mHasDisconnectedCallbacks = true;
  185. return;
  186. }
  187. }
  188. }
  189. };
  190. /************************************************************************/
  191. /* SPECIALIZATIONS */
  192. /* SO YOU MAY USE FUNCTION LIKE SYNTAX FOR DECLARING EVENT SIGNATURE */
  193. /************************************************************************/
  194. /**
  195. * @copydoc TEvent
  196. */
  197. template <typename Signature>
  198. class Event;
  199. /**
  200. * @copydoc TEvent
  201. */
  202. template <class RetType, class... Args>
  203. class Event<RetType(Args...) > : public TEvent <RetType, Args...>
  204. { };
  205. }