You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

253 lines
7.5 KiB

  1. /*
  2. * This program source code file is part of KICAD, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2016 Kicad Developers, see change_log.txt for contributors.
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, you may find one here:
  18. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  19. * or you may search the http://www.gnu.org website for the version 2 license,
  20. * or you may write to the Free Software Foundation, Inc.,
  21. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  22. */
  23. #ifndef COMMON_OBSERVABLE_H__
  24. #define COMMON_OBSERVABLE_H__
  25. #include <cassert>
  26. #include <memory>
  27. #include <vector>
  28. #include <utility>
  29. /*
  30. model subscriber implementation using links to represent connections.
  31. subscribers can be removed during notification.
  32. if no observers are registered, size is the size of a shared_ptr.
  33. */
  34. namespace UTIL {
  35. class LINK;
  36. namespace DETAIL {
  37. struct OBSERVABLE_BASE {
  38. public:
  39. OBSERVABLE_BASE();
  40. OBSERVABLE_BASE( OBSERVABLE_BASE& other );
  41. ~OBSERVABLE_BASE();
  42. size_t size() const;
  43. private:
  44. friend class UTIL::LINK;
  45. struct IMPL
  46. {
  47. IMPL( OBSERVABLE_BASE* owned_by = nullptr );
  48. bool is_shared() const;
  49. void set_shared();
  50. ~IMPL();
  51. void add_observer( void* observer );
  52. void remove_observer( void* observer );
  53. void collect();
  54. bool is_iterating() const;
  55. void enter_iteration();
  56. void leave_iteration();
  57. std::vector<void*> observers_;
  58. unsigned int iteration_count_;
  59. OBSERVABLE_BASE* owned_by_;
  60. };
  61. void allocate_impl();
  62. void allocate_shared_impl();
  63. void deallocate_impl();
  64. std::shared_ptr<IMPL> get_shared_impl();
  65. protected:
  66. void on_observers_empty();
  67. void enter_iteration();
  68. void leave_iteration();
  69. void add_observer( void* observer );
  70. void remove_observer( void* observer );
  71. std::shared_ptr<IMPL> impl_;
  72. };
  73. }
  74. //
  75. // Simple RAII-handle to a subscription.
  76. //
  77. class LINK {
  78. public:
  79. LINK();
  80. LINK( std::shared_ptr<DETAIL::OBSERVABLE_BASE::IMPL> token, void* observer );
  81. LINK( LINK&& other );
  82. LINK( const LINK& ) = delete;
  83. void operator=( const LINK& ) = delete;
  84. LINK& operator=( LINK&& other );
  85. void reset();
  86. explicit operator bool() const;
  87. ~LINK();
  88. private:
  89. std::shared_ptr<DETAIL::OBSERVABLE_BASE::IMPL> token_;
  90. void* observer_;
  91. };
  92. //
  93. //
  94. //
  95. template<typename ObserverInterface>
  96. class OBSERVABLE :
  97. public DETAIL::OBSERVABLE_BASE
  98. {
  99. public:
  100. /**
  101. * Function Observable()
  102. * Constructor. Constructs an observable with empty non-shared subscription list.
  103. */
  104. OBSERVABLE() {}
  105. /**
  106. * Function Observable(OBSERVABLE&)
  107. * Constructor. Constructs an observable with a shared subscription list.
  108. * @param aInherit Observable to share the subscription list with.
  109. */
  110. OBSERVABLE( OBSERVABLE& aInherit )
  111. : OBSERVABLE_BASE( aInherit )
  112. {}
  113. /**
  114. * Function SubscribeUnmanaged
  115. * adds a subscription without RAII link.
  116. * @param aObserver Observer to subscribe
  117. */
  118. void SubscribeUnmanaged( ObserverInterface* aObserver )
  119. {
  120. OBSERVABLE_BASE::add_observer( static_cast<void*>(aObserver) );
  121. }
  122. /**
  123. * Function Subscribe
  124. * adds a subscription returning an RAII link
  125. * @param aObserver observer to subscribe
  126. * @return RAII link controlling the lifetime of the subscription
  127. */
  128. LINK Subscribe( ObserverInterface* aObserver ) {
  129. OBSERVABLE_BASE::add_observer( static_cast<void*>(aObserver) );
  130. return LINK( impl_, static_cast<void*>(aObserver) );
  131. }
  132. /**
  133. * Function Unsubscribe
  134. * cancels the subscription of a subscriber. Can be called during notification calls.
  135. * @param aObserver observer to remove from the subscription list
  136. */
  137. void Unsubscribe( ObserverInterface* obs )
  138. {
  139. OBSERVABLE_BASE::remove_observer( static_cast<void*>(obs) );
  140. }
  141. /**
  142. * Function Notify
  143. * Notifies event to all subscribed observers.
  144. * @param Ptr pointer to method of the Observer-interface
  145. * @param Args list of arguments to each notification call, will be perfectly forwarded.
  146. */
  147. template< typename... Args1, typename... Args2 >
  148. void Notify( void(ObserverInterface::*Ptr)(Args1...), Args2&&... aArgs )
  149. {
  150. static_assert(sizeof...(Args1) == sizeof...(Args2), "argument counts don't match");
  151. if( impl_ )
  152. {
  153. enter_iteration();
  154. try {
  155. for( auto* void_ptr : impl_->observers_ )
  156. {
  157. if( void_ptr )
  158. {
  159. auto* typed_ptr = static_cast<ObserverInterface*>(void_ptr);
  160. (typed_ptr->*Ptr)(std::forward<Args2>( aArgs )...);
  161. }
  162. }
  163. }
  164. catch(...)
  165. {
  166. leave_iteration();
  167. throw;
  168. }
  169. leave_iteration();
  170. }
  171. }
  172. /**
  173. * Function Notify
  174. * Notifies event to all subscribed observers but one to be ignore.
  175. * @param Ptr pointer to method of the Observer-interface
  176. * @param aIgnore observer to ignore during this notification
  177. * @param Args list of arguments to each notification call, will be perfectly forwarded.
  178. */
  179. template< typename... Args1, typename... Args2 >
  180. void NotifyIgnore( void(ObserverInterface::*Ptr)(Args1...), ObserverInterface* aIgnore,
  181. Args2&&... aArgs )
  182. {
  183. static_assert(sizeof...(Args1) == sizeof...(Args2), "argument counts don't match");
  184. if( impl_ )
  185. {
  186. enter_iteration();
  187. try
  188. {
  189. for(auto* void_ptr : impl_->observers_)
  190. {
  191. if( void_ptr && void_ptr != aIgnore )
  192. {
  193. auto* typed_ptr = static_cast<ObserverInterface*>(void_ptr);
  194. (typed_ptr->*Ptr)(std::forward<Args2>( aArgs )...);
  195. }
  196. }
  197. }
  198. catch(...)
  199. {
  200. leave_iteration();
  201. throw;
  202. }
  203. leave_iteration();
  204. }
  205. }
  206. };
  207. }
  208. #endif