Files
create/src/Base/Observer.h
Zheng, Lei 00312a39f9 Selection: handle exception in observer callback
Selection callback are sometimes called while traversing Coin node tree.
If any unhandled exception is thrown while traversing the tree, FC 3D
rendering will become unstable and non-usable thereafter.
2018-08-26 17:29:14 +02:00

228 lines
7.1 KiB
C++

/***************************************************************************
* (c) Jürgen Riegel (juergen.riegel@web.de) 2002 *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Library General Public License (LGPL) *
* as published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* for detail see the LICENCE text file. *
* *
* FreeCAD 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 Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with FreeCAD; if not, write to the Free Software *
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* USA *
* *
* Juergen Riegel 2002 *
***************************************************************************/
#ifndef BASE_OBSERVER_H
#define BASE_OBSERVER_H
// Std. configurations
#include <assert.h>
#include <set>
#include <cstring>
#include <cstdio>
#include <exception>
#include "Exception.h"
#include "Console.h"
namespace Base
{
template <class MessageType> class Subject;
/** Observer class
* Implementation of the well known Observer Design Pattern.
* The observed object, which inherit FCSubject, will call all
* its observers in case of changes. A observer class has to
* Attach itself to the observed object.
* @see FCSubject
*/
template <class _MessageType>
class Observer
{
public:
/**
* A constructor.
* No special function so far.
*/
Observer(){}
/**
* A destructor.
* No special function so far.
*/
virtual ~Observer(){}
/**
* This method need to be reimplemented from the concrete Observer
* and get called by the observed class
* @param rCaller a reference to the calling object
* @param rcReason
* \todo undocumented parameter 2
*/
virtual void OnChange(Subject<_MessageType>& rCaller,_MessageType rcReason)=0;
/**
* This method need to be reimplemented from the concrete Observer
* and get called by the observed class
* @param rCaller a reference to the calling object
*/
virtual void OnDestroy(Subject<_MessageType> & rCaller) {
(void)rCaller;
}
/**
* This method can be reimplemented from the concrete Observer
* and returns the name of the observer. Needed to use the Get
* Method of the Subject.
*/
virtual const char *Name(void){return 0L;}
};
/** Subject class
* Implementation of the well known Observer Design Pattern.
* The observed object, which inherit FCSubject, will call all
* its observers in case of changes. A observer class has to
* Attach itself to the observed object.
* @see FCObserver
*/
template <class _MessageType>
class Subject
{
public:
typedef Observer<_MessageType> ObserverType;
typedef _MessageType MessageType;
typedef Subject<_MessageType> SubjectType;
/**
* A constructor.
* No special function so far.
*/
Subject(){}
/**
* A destructor.
* No special function so far.
*/
virtual ~Subject()
{
if (_ObserverSet.size() > 0)
{
printf("Not detached all observers yet\n");
assert(0);
}
}
/** Attach an Observer
* Attach an Observer to the list of Observers which get
* called when Notify is called.
* @param ToObserv A pointer to a concrete Observer
* @see Notify
*/
void Attach(Observer<_MessageType> *ToObserv)
{
#ifdef FC_DEBUG
size_t count = _ObserverSet.size();
//printf("Attach observer %p\n", ToObserv);
_ObserverSet.insert(ToObserv);
if ( _ObserverSet.size() == count )
printf("Observer %p already attached\n", ToObserv);
#else
_ObserverSet.insert(ToObserv);
#endif
}
/** Detach an Observer
* Detach an Observer from the list of Observers which get
* called when Notify is called.
* @param ToObserv A pointer to a concrete Observer
* @see Notify
*/
void Detach(Observer<_MessageType> *ToObserv)
{
#ifdef FC_DEBUG
size_t count = _ObserverSet.size();
//printf("Detach observer %p\n", ToObserv);
_ObserverSet.erase(ToObserv);
if ( _ObserverSet.size() == count )
printf("Observer %p already detached\n", ToObserv);
#else
_ObserverSet.erase(ToObserv);
#endif
}
/** Notify all Observers
* Send a message to all Observers attached to this subject.
* The Message depends on the implementation of a concrete
* Oberserver and Subject.
* @see Notify
*/
void Notify(_MessageType rcReason)
{
for(typename std::set<Observer<_MessageType> * >::iterator Iter=_ObserverSet.begin();Iter!=_ObserverSet.end();++Iter)
{
try {
(*Iter)->OnChange(*this,rcReason); // send OnChange-signal
} catch (Base::Exception &e) {
Base::Console().Error("Unhandled Base::Exception caught when notifying observer.\n"
"The error message is: %s\n", e.what());
} catch (std::exception &e) {
Base::Console().Error("Unhandled std::exception caught when notifying observer\n"
"The error message is: %s\n", e.what());
} catch (...) {
Base::Console().Error("Unhandled unknown exception caught in when notifying observer.\n");
}
}
}
/** Get an Observer by name
* Get a observer by name if the observer reimplements the Name() mthode.
* @see Observer
*/
Observer<_MessageType> * Get(const char *Name)
{
const char* OName;
for(typename std::set<Observer<_MessageType> * >::iterator Iter=_ObserverSet.begin();Iter!=_ObserverSet.end();++Iter)
{
OName = (*Iter)->Name(); // get the name
if(OName && strcmp(OName,Name) == 0)
return *Iter;
}
return 0L;
}
/** Clears the list of all registered observers.
* @note Using this function in your code may be an indication of design problems.
*/
void ClearObserver()
{
_ObserverSet.clear();
}
protected:
/// Vector of attached observers
std::set<Observer <_MessageType> *> _ObserverSet;
};
} //namespace Base
#endif // BASE_OBSERVER_H