|
|
/*
* 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 <cassert>
#include <memory>
#include <vector>
#include <utility>
/*
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<void*> observers_; unsigned int iteration_count_; OBSERVABLE_BASE* owned_by_; };
void allocate_impl(); void allocate_shared_impl();
void deallocate_impl();
std::shared_ptr<IMPL> 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> impl_; };
}
//
// Simple RAII-handle to a subscription.
//
class LINK { public: LINK(); LINK( std::shared_ptr<DETAIL::OBSERVABLE_BASE::IMPL> 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<DETAIL::OBSERVABLE_BASE::IMPL> token_; void* observer_; };
//
//
//
template<typename ObserverInterface> 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<void*>(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<void*>(aObserver) ); return LINK( impl_, static_cast<void*>(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* obs ) { OBSERVABLE_BASE::remove_observer( static_cast<void*>(obs) ); }
/**
* Function Notify * Notifies event to all subscribed observers. * @param Ptr pointer to method of the Observer-interface * @param Args 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<ObserverInterface*>(void_ptr); (typed_ptr->*Ptr)(std::forward<Args2>( 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 Args 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<ObserverInterface*>(void_ptr); (typed_ptr->*Ptr)(std::forward<Args2>( aArgs )...); } } } catch(...) { leave_iteration(); throw; }
leave_iteration(); } }
};
}
#endif
|