kicad/include/observable.h

254 lines
7.5 KiB
C++

/*
* 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