BsEvent.h 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  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. private:
  38. void(*mDisconnectCallback) (const std::shared_ptr<BaseConnectionData>&, void*);
  39. std::shared_ptr<BaseConnectionData> mConnection;
  40. void* mEvent;
  41. };
  42. /**
  43. * @brief Events allows you to register method callbacks that get notified
  44. * when the event is triggered.
  45. *
  46. * @note Callback method return value is ignored.
  47. */
  48. template <class RetType, class... Args>
  49. class TEvent
  50. {
  51. struct ConnectionData : BaseConnectionData
  52. {
  53. public:
  54. ConnectionData(std::function<RetType(Args...)> func)
  55. :func(func)
  56. { }
  57. std::function<RetType(Args...)> func;
  58. };
  59. public:
  60. TEvent()
  61. :mHasDisconnectedCallbacks(false)
  62. { }
  63. ~TEvent()
  64. {
  65. clear();
  66. }
  67. /**
  68. * @brief Register a new callback that will get notified once
  69. * the event is triggered.
  70. */
  71. HEvent connect(std::function<RetType(Args...)> func)
  72. {
  73. std::shared_ptr<ConnectionData> connData = bs_shared_ptr<ConnectionData>(func);
  74. connData->isValid = true;
  75. {
  76. BS_LOCK_RECURSIVE_MUTEX(mMutex);
  77. mConnections.push_back(connData);
  78. }
  79. return HEvent(connData, this, &TEvent::disconnectCallback);
  80. }
  81. /**
  82. * @brief Trigger the event, notifying all register callback methods.
  83. */
  84. void operator() (Args... args)
  85. {
  86. BS_LOCK_RECURSIVE_MUTEX(mMutex);
  87. // Here is the only place we remove connections, in order to allow disconnect() and clear() to be called
  88. // recursively from the notify callbacks
  89. if (mHasDisconnectedCallbacks)
  90. {
  91. for (UINT32 i = 0; i < mConnections.size(); i++)
  92. {
  93. if (!mConnections[i]->isValid)
  94. {
  95. mConnections.erase(mConnections.begin() + i);
  96. i--;
  97. }
  98. }
  99. mHasDisconnectedCallbacks = false;
  100. }
  101. // Do not use an iterator here, as new connections might be added during iteration from
  102. // the notify callback
  103. UINT32 numConnections = (UINT32)mConnections.size(); // Remember current num. connections as we don't want to notify new ones
  104. for (UINT32 i = 0; i < numConnections; i++)
  105. mConnections[i]->func(args...);
  106. }
  107. /**
  108. * @brief Clear all callbacks from the event.
  109. */
  110. void clear()
  111. {
  112. BS_LOCK_RECURSIVE_MUTEX(mMutex);
  113. for(auto& connection : mConnections)
  114. connection->isValid = false;
  115. if (mConnections.size() > 0)
  116. mHasDisconnectedCallbacks = true;
  117. }
  118. /**
  119. * @brief Check if event has any callbacks registered.
  120. *
  121. * @note It is safe to trigger an event even if no callbacks are registered.
  122. */
  123. bool empty()
  124. {
  125. BS_LOCK_RECURSIVE_MUTEX(mMutex);
  126. return mConnections.size() == 0;
  127. }
  128. private:
  129. Vector<std::shared_ptr<ConnectionData>> mConnections;
  130. bool mHasDisconnectedCallbacks;
  131. BS_RECURSIVE_MUTEX(mMutex);
  132. /**
  133. * @brief Callback triggered by event handles when they want to disconnect from
  134. * an event.
  135. */
  136. static void disconnectCallback(const std::shared_ptr<BaseConnectionData>& connection, void* event)
  137. {
  138. TEvent<RetType, Args...>* castEvent = reinterpret_cast<TEvent<RetType, Args...>*>(event);
  139. castEvent->disconnect(connection);
  140. }
  141. /**
  142. * @brief Internal method that disconnects the callback described by the provided connection data.
  143. */
  144. void disconnect(const std::shared_ptr<BaseConnectionData>& connData)
  145. {
  146. BS_LOCK_RECURSIVE_MUTEX(mMutex);
  147. for(auto& iter = mConnections.begin(); iter != mConnections.end(); ++iter)
  148. {
  149. if((*iter) == connData)
  150. {
  151. connData->isValid = false;
  152. mHasDisconnectedCallbacks = true;
  153. return;
  154. }
  155. }
  156. }
  157. };
  158. /************************************************************************/
  159. /* SPECIALIZATIONS */
  160. /* SO YOU MAY USE FUNCTION LIKE SYNTAX FOR DECLARING EVENT SIGNATURE */
  161. /************************************************************************/
  162. /**
  163. * @copydoc TEvent
  164. */
  165. template <typename Signature>
  166. class Event;
  167. /**
  168. * @copydoc TEvent
  169. */
  170. template <class RetType, class... Args>
  171. class Event<RetType(Args...) > : public TEvent <RetType, Args...>
  172. { };
  173. }