/* * This program source code file is part of KICAD, a free EDA CAD application. * * Copyright (C) 2016 Kicad Developers, see change_log.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #ifndef COMMON_OBSERVABLE_H__ #define COMMON_OBSERVABLE_H__ #include #include #include #include /* model subscriber implementation using links to represent connections. subscribers can be removed during notification. if no observers are registered, size is the size of a shared_ptr. */ namespace UTIL { class LINK; namespace DETAIL { struct OBSERVABLE_BASE { public: OBSERVABLE_BASE(); OBSERVABLE_BASE( OBSERVABLE_BASE& other ); ~OBSERVABLE_BASE(); size_t size() const; private: friend class UTIL::LINK; struct IMPL { IMPL( OBSERVABLE_BASE* owned_by = nullptr ); bool is_shared() const; void set_shared(); ~IMPL(); void add_observer( void* observer ); void remove_observer( void* observer ); void collect(); bool is_iterating() const; void enter_iteration(); void leave_iteration(); std::vector observers_; unsigned int iteration_count_; OBSERVABLE_BASE* owned_by_; }; void allocate_impl(); void allocate_shared_impl(); void deallocate_impl(); std::shared_ptr get_shared_impl(); protected: void on_observers_empty(); void enter_iteration(); void leave_iteration(); void add_observer( void* observer ); void remove_observer( void* observer ); std::shared_ptr impl_; }; } // // Simple RAII-handle to a subscription. // class LINK { public: LINK(); LINK( std::shared_ptr token, void* observer ); LINK( LINK&& other ); LINK( const LINK& ) = delete; void operator=( const LINK& ) = delete; LINK& operator=( LINK&& other ); void reset(); explicit operator bool() const; ~LINK(); private: std::shared_ptr token_; void* observer_; }; // // // template class OBSERVABLE : public DETAIL::OBSERVABLE_BASE { public: /** * Function Observable() * Constructor. Constructs an observable with empty non-shared subscription list. */ OBSERVABLE() {} /** * Function Observable(OBSERVABLE&) * Constructor. Constructs an observable with a shared subscription list. * @param aInherit Observable to share the subscription list with. */ OBSERVABLE( OBSERVABLE& aInherit ) : OBSERVABLE_BASE( aInherit ) {} /** * Function SubscribeUnmanaged * adds a subscription without RAII link. * @param aObserver Observer to subscribe */ void SubscribeUnmanaged( ObserverInterface* aObserver ) { OBSERVABLE_BASE::add_observer( static_cast(aObserver) ); } /** * Function Subscribe * adds a subscription returning an RAII link * @param aObserver observer to subscribe * @return RAII link controlling the lifetime of the subscription */ LINK Subscribe( ObserverInterface* aObserver ) { OBSERVABLE_BASE::add_observer( static_cast(aObserver) ); return LINK( impl_, static_cast(aObserver) ); } /** * Function Unsubscribe * cancels the subscription of a subscriber. Can be called during notification calls. * @param aObserver observer to remove from the subscription list */ void Unsubscribe( ObserverInterface* aObserver ) { OBSERVABLE_BASE::remove_observer( static_cast(aObserver) ); } /** * Function Notify * Notifies event to all subscribed observers. * @param Ptr pointer to method of the Observer-interface * @param aArgs list of arguments to each notification call, will be perfectly forwarded. */ template< typename... Args1, typename... Args2 > void Notify( void(ObserverInterface::*Ptr)(Args1...), Args2&&... aArgs ) { static_assert(sizeof...(Args1) == sizeof...(Args2), "argument counts don't match"); if( impl_ ) { enter_iteration(); try { for( auto* void_ptr : impl_->observers_ ) { if( void_ptr ) { auto* typed_ptr = static_cast(void_ptr); (typed_ptr->*Ptr)(std::forward( aArgs )...); } } } catch(...) { leave_iteration(); throw; } leave_iteration(); } } /** * Function Notify * Notifies event to all subscribed observers but one to be ignore. * @param Ptr pointer to method of the Observer-interface * @param aIgnore observer to ignore during this notification * @param aArgs list of arguments to each notification call, will be perfectly forwarded. */ template< typename... Args1, typename... Args2 > void NotifyIgnore( void(ObserverInterface::*Ptr)(Args1...), ObserverInterface* aIgnore, Args2&&... aArgs ) { static_assert(sizeof...(Args1) == sizeof...(Args2), "argument counts don't match"); if( impl_ ) { enter_iteration(); try { for(auto* void_ptr : impl_->observers_) { if( void_ptr && void_ptr != aIgnore ) { auto* typed_ptr = static_cast(void_ptr); (typed_ptr->*Ptr)(std::forward( aArgs )...); } } } catch(...) { leave_iteration(); throw; } leave_iteration(); } } }; } #endif