// Copyright (C) 2004, 2006 International Business Machines and others. // All Rights Reserved. // This code is published under the Eclipse Public License. // // Authors: Carl Laird, Andreas Waechter IBM 2004-08-13 #ifndef __IPOBSERVER_HPP__ #define __IPOBSERVER_HPP__ #include "IpUtils.hpp" #include #include //#define IP_DEBUG_OBSERVER #if IPOPT_CHECKLEVEL > 2 # define IP_DEBUG_OBSERVER #endif #ifdef IP_DEBUG_OBSERVER # include "IpDebug.hpp" #endif namespace Ipopt { /* Forward declaration */ class Subject; /** Slight Variation of the Observer Design Pattern. * * This class implements the Observer class of the * Observer Design Pattern. An Observer "Attach"es * to a Subject, indicating that it would like to * be notified of changes in the Subject. * Any derived class wishing to receive notifications * from a Subject should inherit off of * Observer and overload the protected method, * ReceiveNotification_(...). */ class IPOPTLIB_EXPORT Observer { public: #ifdef IP_DEBUG_OBSERVER static const Index dbg_verbosity; #endif /**@name Constructors/Destructors */ ///@{ /** Default Constructor */ Observer() { } /** Destructor */ inline virtual ~Observer(); ///@} /** Enumeration specifying the type of notification */ enum NotifyType { NT_All, NT_BeingDestroyed, NT_Changed }; protected: /** Derived classes should call this method * to request an "Attach" to a Subject. * * Do not call "Attach" explicitly on the Subject * since further processing is done here */ inline void RequestAttach( NotifyType notify_type, const Subject* subject ); /** Derived classes should call this method * to request a "Detach" to a Subject. * * Do not call "Detach" explicitly on the Subject * since further processing is done here */ inline void RequestDetach( NotifyType notify_type, const Subject* subject ); /** Derived classes should overload this method to * receive the requested notification from * attached Subjects */ virtual void ReceiveNotification( NotifyType notify_type, const Subject* subject ) = 0; private: /**@name Default Compiler Generated Methods * (Hidden to avoid implicit creation/calling). * * These methods are not implemented and * we do not want the compiler to implement * them for us, so we declare them private * and do not define them. This ensures that * they will not be implicitly created/called. */ ///@{ /** Copy Constructor */ Observer( const Observer& ); /** Default Assignment Operator */ void operator=( const Observer& ); ///@} /** A list of the subjects currently being observed. */ std::vector subjects_; /** Private Method for Receiving Notification * should only be called by the friend class * Subject. * * This method will, in turn, call * the overloaded ReceiveNotification method * for the derived class to process. */ inline void ProcessNotification( NotifyType notify_type, const Subject* subject ); friend class Subject; }; /** Slight Variation of the Observer Design Pattern (Subject part). * * This class implements the Subject class of the Observer Design * Pattern. An Observer "Attach"es to a Subject, indicating that it * would like to be notified of changes in the Subject. Any * derived class that is to be observed has to inherit off the * Subject base class. If the subject needs to notify the * Observer, it calls the Notify method. */ class IPOPTLIB_EXPORT Subject { public: #ifdef IP_DEBUG_OBSERVER static const Index dbg_verbosity; #endif /**@name Constructors/Destructors */ ///@{ /** Default Constructor */ Subject() { } /** Destructor */ inline virtual ~Subject(); ///@} /**@name Methods to Add and Remove Observers. * * Currently, the notify_type flags are not used, * and Observers are attached in general and will * receive all notifications (of the type requested * and possibly of types not requested). It is * up to the observer to ignore the types they * are not interested in. The NotifyType in the * parameter list is so a more efficient mechanism * depending on type could be implemented later if * necessary. */ ///@{ /** Attach the specified observer * (i.e., begin receiving notifications). */ inline void AttachObserver( Observer::NotifyType notify_type, Observer* observer ) const; /** Detach the specified observer * (i.e., no longer receive notifications). */ inline void DetachObserver( Observer::NotifyType notify_type, Observer* observer ) const; ///@} protected: inline void Notify( Observer::NotifyType notify_type ) const; private: /**@name Default Compiler Generated Methods * (Hidden to avoid implicit creation/calling). * * These methods are not implemented and * we do not want the compiler to implement * them for us, so we declare them private * and do not define them. This ensures that * they will not be implicitly created/called. */ ///@{ /** Copy Constructor */ Subject( const Subject& ); /** Default Assignment Operator */ void operator=( const Subject& ); ///@} mutable std::vector observers_; }; /* inline methods */ inline Observer::~Observer() { #ifdef IP_DEBUG_OBSERVER DBG_START_METH("Observer::~Observer", dbg_verbosity); if (DBG_VERBOSITY() >= 1) { for( size_t i = 0; i < subjects_.size(); ++i ) { DBG_PRINT((1, "subjects_[%zd] = %p\n", i, (const void*)subjects_[i])); } } #endif // Detach all subjects for( size_t i = subjects_.size(); i > 0; --i ) { #ifdef IP_DEBUG_OBSERVER DBG_PRINT((1, "About to detach subjects_[%zd] = %p\n", i, (const void*)subjects_[i - 1])); #endif RequestDetach(NT_All, subjects_[i - 1]); } } inline void Observer::RequestAttach( NotifyType notify_type, const Subject* subject ) { #ifdef IP_DEBUG_OBSERVER DBG_START_METH("Observer::RequestAttach", dbg_verbosity); // Add the subject to the list if it does not already exist std::vector::iterator attached_subject; attached_subject = std::find(subjects_.begin(), subjects_.end(), subject); DBG_ASSERT(attached_subject == subjects_.end()); DBG_ASSERT(subject); #endif // add the subject to the list subjects_.push_back(subject); // Attach the observer to the subject subject->AttachObserver(notify_type, this); } inline void Observer::RequestDetach( NotifyType notify_type, const Subject* subject ) { #ifdef IP_DEBUG_OBSERVER DBG_START_METH("Observer::RequestDetach", dbg_verbosity); DBG_PRINT((1, "Requesting detach of subject: %p\n", (const void*)subject)); DBG_ASSERT(subject); #endif if( subject ) { std::vector::iterator attached_subject; attached_subject = std::find(subjects_.begin(), subjects_.end(), subject); #ifdef IP_DEBUG_OBSERVER DBG_ASSERT(attached_subject != subjects_.end()); #endif if( attached_subject != subjects_.end() ) { #ifdef IP_DEBUG_OBSERVER DBG_PRINT((1, "Removing subject: %p from the list\n", (const void*)subject)); #endif subjects_.erase(attached_subject); } // Detach the observer from the subject subject->DetachObserver(notify_type, this); } } inline void Observer::ProcessNotification( NotifyType notify_type, const Subject* subject ) { #ifdef IP_DEBUG_OBSERVER DBG_START_METH("Observer::ProcessNotification", dbg_verbosity); DBG_ASSERT(subject); #endif if( subject ) { std::vector::iterator attached_subject; attached_subject = std::find(subjects_.begin(), subjects_.end(), subject); // We must be processing a notification for a // subject that was previously attached. #ifdef IP_DEBUG_OBSERVER DBG_ASSERT(attached_subject != subjects_.end()); #endif this->ReceiveNotification(notify_type, subject); if( notify_type == NT_BeingDestroyed ) { // the subject is going away, remove it from our list subjects_.erase(attached_subject); } } } inline Subject::~Subject() { #ifdef IP_DEBUG_OBSERVER DBG_START_METH("Subject::~Subject", dbg_verbosity); #endif for( std::vector::iterator iter = observers_.begin(); iter != observers_.end(); ++iter ) { (*iter)->ProcessNotification(Observer::NT_BeingDestroyed, this); } } inline void Subject::AttachObserver( Observer::NotifyType /*notify_type*/, Observer* observer ) const { #ifdef IP_DEBUG_OBSERVER DBG_START_METH("Subject::AttachObserver", dbg_verbosity); // current implementation notifies all observers of everything // they must filter the notifications that they are not interested // in (i.e. a hub, not a router) DBG_ASSERT(observer); std::vector::iterator attached_observer; attached_observer = std::find(observers_.begin(), observers_.end(), observer); DBG_ASSERT(attached_observer == observers_.end()); DBG_ASSERT(observer); #endif observers_.push_back(observer); } inline void Subject::DetachObserver( Observer::NotifyType /*notify_type*/, Observer* observer ) const { #ifdef IP_DEBUG_OBSERVER DBG_START_METH("Subject::DetachObserver", dbg_verbosity); DBG_ASSERT(observer); #endif if( observer ) { std::vector::iterator attached_observer; attached_observer = std::find(observers_.begin(), observers_.end(), observer); #ifdef IP_DEBUG_OBSERVER DBG_ASSERT(attached_observer != observers_.end()); #endif if( attached_observer != observers_.end() ) { observers_.erase(attached_observer); } } } inline void Subject::Notify( Observer::NotifyType notify_type ) const { #ifdef IP_DEBUG_OBSERVER DBG_START_METH("Subject::Notify", dbg_verbosity); #endif for( std::vector::iterator iter = observers_.begin(); iter != observers_.end(); ++iter ) { (*iter)->ProcessNotification(notify_type, this); } } } // namespace Ipopt #endif