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