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.

251 lines
6.8 KiB

  1. /*
  2. * This program source code file is part of KICAD, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2016-20 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. * A model subscriber implementation using links to represent connections. Subscribers
  31. * can be removed during notification. If no observers are registered, size is the size
  32. * of a shared_ptr.
  33. */
  34. namespace UTIL
  35. {
  36. class LINK;
  37. namespace DETAIL
  38. {
  39. struct OBSERVABLE_BASE
  40. {
  41. public:
  42. OBSERVABLE_BASE();
  43. OBSERVABLE_BASE( OBSERVABLE_BASE& other );
  44. ~OBSERVABLE_BASE();
  45. size_t size() const;
  46. private:
  47. friend class UTIL::LINK;
  48. struct IMPL
  49. {
  50. IMPL( OBSERVABLE_BASE* owned_by = nullptr );
  51. bool is_shared() const;
  52. void set_shared();
  53. ~IMPL();
  54. void add_observer( void* observer );
  55. void remove_observer( void* observer );
  56. void collect();
  57. bool is_iterating() const;
  58. void enter_iteration();
  59. void leave_iteration();
  60. std::vector<void*> observers_;
  61. unsigned int iteration_count_;
  62. OBSERVABLE_BASE* owned_by_;
  63. };
  64. void allocate_impl();
  65. void allocate_shared_impl();
  66. void deallocate_impl();
  67. std::shared_ptr<IMPL> get_shared_impl();
  68. protected:
  69. void on_observers_empty();
  70. void enter_iteration();
  71. void leave_iteration();
  72. void add_observer( void* observer );
  73. void remove_observer( void* observer );
  74. std::shared_ptr<IMPL> impl_;
  75. };
  76. } // namespace DETAIL
  77. /**
  78. * Simple RAII-handle to a subscription.
  79. */
  80. class LINK
  81. {
  82. public:
  83. LINK();
  84. LINK( std::shared_ptr<DETAIL::OBSERVABLE_BASE::IMPL> token, void* observer );
  85. LINK( LINK&& other );
  86. LINK( const LINK& ) = delete;
  87. void operator=( const LINK& ) = delete;
  88. LINK& operator=( LINK&& other );
  89. void reset();
  90. explicit operator bool() const;
  91. ~LINK();
  92. private:
  93. std::shared_ptr<DETAIL::OBSERVABLE_BASE::IMPL> token_;
  94. void* observer_;
  95. };
  96. template <typename ObserverInterface>
  97. class OBSERVABLE : public DETAIL::OBSERVABLE_BASE
  98. {
  99. public:
  100. /**
  101. * Construct an observable with empty non-shared subscription list.
  102. */
  103. OBSERVABLE() {}
  104. /**
  105. * Construct an observable with a shared subscription list.
  106. *
  107. * @param aInherit Observable to share the subscription list with.
  108. */
  109. OBSERVABLE( OBSERVABLE& aInherit ) : OBSERVABLE_BASE( aInherit ) {}
  110. /**
  111. * Add a subscription without RAII link.
  112. *
  113. * @param aObserver Observer to subscribe.
  114. */
  115. void SubscribeUnmanaged( ObserverInterface* aObserver )
  116. {
  117. OBSERVABLE_BASE::add_observer( static_cast<void*>( aObserver ) );
  118. }
  119. /**
  120. * Add a subscription returning an RAII link.
  121. *
  122. * @param aObserver observer to subscribe
  123. * @return RAII link controlling the lifetime of the subscription
  124. */
  125. LINK Subscribe( ObserverInterface* aObserver )
  126. {
  127. OBSERVABLE_BASE::add_observer( static_cast<void*>( aObserver ) );
  128. return LINK( impl_, static_cast<void*>( aObserver ) );
  129. }
  130. /**
  131. * Cancel the subscription of a subscriber.
  132. *
  133. * This can be called during notification calls.
  134. *
  135. * @param aObserver observer to remove from the subscription list.
  136. */
  137. void Unsubscribe( ObserverInterface* aObserver )
  138. {
  139. OBSERVABLE_BASE::remove_observer( static_cast<void*>( aObserver ) );
  140. }
  141. /**
  142. * Notify event to all subscribed observers.
  143. *
  144. * @param Ptr is a pointer to method of the observer interface.
  145. * @param aArgs is a 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. {
  156. for( auto* void_ptr : impl_->observers_ )
  157. {
  158. if( void_ptr )
  159. {
  160. auto* typed_ptr = static_cast<ObserverInterface*>( void_ptr );
  161. ( typed_ptr->*Ptr )( std::forward<Args2>( aArgs )... );
  162. }
  163. }
  164. }
  165. catch( ... )
  166. {
  167. leave_iteration();
  168. throw;
  169. }
  170. leave_iteration();
  171. }
  172. }
  173. /**
  174. * Notify event to all subscribed observers but one to be ignore.
  175. *
  176. * @param Ptr is a pointer to method of the observer interface.
  177. * @param aIgnore is an observer to ignore during this notification.
  178. * @param aArgs is a list of arguments to each notification call, will be perfectly forwarded.
  179. */
  180. template <typename... Args1, typename... Args2>
  181. void NotifyIgnore( void ( ObserverInterface::*Ptr )( Args1... ), ObserverInterface* aIgnore,
  182. Args2&&... aArgs )
  183. {
  184. static_assert( sizeof...( Args1 ) == sizeof...( Args2 ), "argument counts don't match" );
  185. if( impl_ )
  186. {
  187. enter_iteration();
  188. try
  189. {
  190. for( auto* void_ptr : impl_->observers_ )
  191. {
  192. if( void_ptr && void_ptr != aIgnore )
  193. {
  194. auto* typed_ptr = static_cast<ObserverInterface*>( void_ptr );
  195. ( typed_ptr->*Ptr )( std::forward<Args2>( aArgs )... );
  196. }
  197. }
  198. }
  199. catch( ... )
  200. {
  201. leave_iteration();
  202. throw;
  203. }
  204. leave_iteration();
  205. }
  206. }
  207. };
  208. } // namespace UTIL
  209. #endif