text: // observable.hpp v1.3
  /*
   *  Copyright (c) 2007 Leigh Johnston.
   *
   *  All rights reserved.
   *
   *  Redistribution and use in source and binary forms, with or without
   *  modification, are permitted provided that the following conditions are
   *  met:
   *
   *     * Redistributions of source code must retain the above copyright
   *       notice, this list of conditions and the following disclaimer.
   *
   *     * Redistributions in binary form must reproduce the above copyright
   *       notice, this list of conditions and the following disclaimer in the
   *       documentation and/or other materials provided with the distribution.
   *
   *     * Neither the name of Leigh Johnston nor the names of any
   *       other contributors to this software may be used to endorse or
   *       promote products derived from this software without specific prior
   *       written permission.
   *
   *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
   *  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
   *  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   *  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
   *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
   *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
   *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
   *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
  
  #pragma once
  
  #include "neolib.hpp"
  #include <vector>
  #include <list>
  #include <algorithm>
  #include <cassert>
  #include "lifetime.hpp"
  
  namespace neolib
  {
  	template <typename Observer>
  	class observable
  	{
  		// types
  	public:
  		typedef Observer observer_type;
  		typedef observable<observer_type> our_type;
  		typedef typename observer_type::notify_type notify_type;
  	protected:
  		typedef std::vector<observer_type*> observer_list;
  	private:
  		typedef std::list<observer_list> notification_list;
  	public:
  		struct already_an_observer : std::logic_error { already_an_observer() : std::logic_error("neolib::observable::already_an_observer") {} };
  
  		// construction
  	public:
  		observable() 
  		{
  		}
  		observable(const observable&) 
  		{ // no op == no copying of observer lists
  		} 
  		observable& operator=(const observable&) 
  		{ // no op == no copying of observer lists
  			return *this; 
  		} 
  		virtual ~observable()
  		{
  		}
  
  		// operations
  	public:
  		virtual void add_observer(observer_type& aObserver)
  		{
  			if (std::find(iObservers.begin(), iObservers.end(), &aObserver) == iObservers.end())
  				iObservers.push_back(&aObserver);
  			else
  				throw already_an_observer();
  		}
  		virtual void remove_observer(observer_type& aObserver)
  		{
  			iObservers.erase(std::remove(iObservers.begin(), iObservers.end(), &aObserver), iObservers.end());
  			for (typename notification_list::iterator i = iNotifications.begin(); i != iNotifications.end(); ++i)
  				i->erase(std::remove(i->begin(), i->end(), &aObserver), i->end());
  		}
  		template <typename T>
  		void notify_observers(notify_type aType, const T& aParameter) const
  		{
  			notify_observers(aType, static_cast<const void*>(&aParameter));
  		}
  		template <typename T>
  		void notify_observers(notify_type aType, const T& aParameter)
  		{
  			notify_observers(aType, static_cast<const void*>(&aParameter));
  		}
  		template <typename T1, typename T2>
  		void notify_observers(notify_type aType, const T1& aParameter, const T2& aParameter2) const
  		{
  			notify_observers(aType, static_cast<const void*>(&aParameter), static_cast<const void*>(&aParameter2));
  		}
  		template <typename T1, typename T2>
  		void notify_observers(notify_type aType, const T1& aParameter, const T2& aParameter2)
  		{
  			notify_observers(aType, static_cast<const void*>(&aParameter), static_cast<const void*>(&aParameter2));
  		}
  		void notify_observers(notify_type aType, const void* aParameter = 0, const void* aParameter2 = 0) const
  		{
  			do_notify_observers(*this, aType, aParameter, aParameter2);
  		}
  		void notify_observers(notify_type aType, const void* aParameter = 0, const void* aParameter2 = 0)
  		{
  			do_notify_observers(*this, aType, aParameter, aParameter2);
  		}
  		template <typename T>
  		void notify_observer(const observer_type& aObserver, notify_type aType, const T& aParameter) const
  		{
  			notify_observer(aObserver, aType, static_cast<const void*>(&aParameter));
  		}
  		template <typename T>
  		void notify_observer(observer_type& aObserver, notify_type aType, const T& aParameter)
  		{
  			notify_observer(aObserver, aType, static_cast<const void*>(&aParameter));
  		}
  		template <typename T1, typename T2> const
  		void notify_observer(const observer_type& aObserver, notify_type aType, const T1& aParameter, const T2& aParameter2) const
  		{
  			notify_observer(aObserver, aType, static_cast<const void*>(&aParameter), static_cast<const void*>(&aParameter2));
  		}
  		template <typename T1, typename T2>
  		void notify_observer(observer_type& aObserver, notify_type aType, const T1& aParameter, const T2& aParameter2)
  		{
  			notify_observer(aObserver, aType, static_cast<const void*>(&aParameter), static_cast<const void*>(&aParameter2));
  		}
  
  		// implementation
  	protected:
  		virtual void notify_observer(const observer_type& aObserver, notify_type aType, const void* aParameter = 0, const void* aParameter2 = 0) const { /* default: do nothing. */ (void)aObserver; (void)aType; (void)aParameter; (void)aParameter2; };
  		virtual void notify_observer(observer_type& aObserver, notify_type aType, const void* aParameter = 0, const void* aParameter2 = 0) { /* default: do nothing. */ (void)aObserver; (void)aType; (void)aParameter; (void)aParameter2; };
  	private:
  		template <typename OurType>
  		static void do_notify_observers(OurType& aThis, notify_type aType, const void* aParameter = 0, const void* aParameter2 = 0)
  		{
  			lifetime::destroyed_flag destroyed(aThis.iDestroyable);
  			aThis.iNotifications.push_front(aThis.iObservers);
  			typename notification_list::iterator theNotifications = aThis.iNotifications.begin();
  			while (!theNotifications->empty())
  			{
  				observer_type* nextObserver = theNotifications->back();
  				theNotifications->pop_back();
  				aThis.notify_observer(*nextObserver, aType, aParameter, aParameter2);
  				if (destroyed)
  					return;
  			}
  			aThis.iNotifications.erase(theNotifications);
  		}
  
  
  		// attributes
  	protected:
  		observer_list iObservers;
  	private:
  		mutable notification_list iNotifications;
  		mutable lifetime iDestroyable;
  	};
  }
  