Tulip  5.4.0
Large graphs analysis and drawing
tlp::Observable Class Reference

#include <Observable.h>

+ Inheritance diagram for tlp::Observable:

Public Member Functions

void addListener (Observable *const listener) const
 
void addListener (Observable &listener) const
 
void addObserver (Observable *const observer) const
 
void addObserver (Observable &observer) const
 
unsigned int countListeners () const
 
unsigned int countObservers () const
 
unsigned int getReceived () const
 
unsigned int getSent () const
 
void removeListener (Observable *const listener) const
 
void removeListener (Observable &listener) const
 
void removeObserver (Observable *const observer) const
 
void removeObserver (Observable &observer) const
 

Static Public Member Functions

static void disableEventNotification ()
 
static void enableEventNotification ()
 
static tlp::node getNode (const tlp::Observable *obs)
 
static ObservablegetObject (tlp::node n)
 
static const tlp::VectorGraph & getObservableGraph ()
 
static unsigned int getScheduled (tlp::node n)
 
static void holdObservers ()
 
static bool isAlive (tlp::node n)
 
static unsigned int observersHoldCounter ()
 
static void unholdObservers ()
 

Protected Member Functions

 Observable (const Observable &)
 
bool hasOnlookers () const
 
void observableDeleted ()
 
Observableoperator= (const Observable &)
 
void sendEvent (const Event &message)
 
virtual void treatEvent (const Event &message)
 
virtual void treatEvents (const std::vector< Event > &events)
 

Friends

class Event
 

Detailed Description

The Observable class is the base of Tulip's observation system.

Each class that wishes to send or receive notifications needs to inherit from Observable.

Tulip has two separate mechanisms of observation, Observers and Listeners. These two mechanisms work through the same class, the difference lies in the way an Observer or a Listener is attached to the object whose events it will receive.

The Listener is closer to the original pattern, where events are sent directly to the recipient. The Observer is a twist for performance purposes, it can receive the events in a delayed fashion, and cannot know if the event was just sent or was delayed.

The purpose of this twist is to allow algorithms that perform a high number of modifications (e.g. creating a grid to route edges, creating multiple subgraphs with metrics or layouts) to run smoothly without overcrowding the event queue. As events are sent for every graph modification (e.g. adding a node, deleting a node, setting a value on a node), the sheer quantity of events sent by these algorithms would cause a big performance hit.

This can be avoided by using Observable::holdObservers(). This holds all events in a queue and only sends them when Observable::unholdObservers() is called.

The only exception to this mechanism is the Event::DELETE kind of event, that is never held back. Think of it as an unmaskable POSIX signal; whatever the connection to the object and the state of holdObserver, this event will get through. This is used to prevent crashes in the case where and object is listened or observed and is deleted, as it is likely the recipient keeps a pointer to the now deleted object.

The Listener however is not affected by the use of Observable::holdObservers() and Observable::unholdObservers().

The observable Observers and Listeners are internally store in a Graph structure, allowing to easily visualize their connections. This eases debugging of Observation-related bugs.

Events are always sent to Listeners first, and then to Observers, even when there is no hold.

If you wish to receive events, you must inherit from Observable, and implement at least one of the two virtual functions below.

If you need to receive events without delay, you will be a Listener, and need to implement treatEvent(const Event &message). You will attach to the object you wish to receive events from using addListener().

If you can receive events after a delay, you will be an Observer, and need to implement treatEvents(const std::vector<Event> &events). You will attach to the object you wish to receive events from using addObserver().

Definition at line 127 of file Observable.h.

Member Function Documentation

◆ addListener() [1/2]

void tlp::Observable::addListener ( Observable *const  listener) const

Adds a Listener to this object.

The Listener will receive events regardless of the state of holdObservers() and unholdObservers().

Parameters
listenerThe object that will receive events.
+ Here is the caller graph for this function:

◆ addListener() [2/2]

void tlp::Observable::addListener ( Observable listener) const
inline

Adds a Listener to this object.

The Listener will receive events regardless of the state of holdObservers() and unholdObservers().

Parameters
listenerThe object that will receive events.

Definition at line 232 of file Observable.h.

◆ addObserver() [1/2]

void tlp::Observable::addObserver ( Observable *const  observer) const

Adds an Observer to this object.

The observer will receive events sent by this object, if there is no hold applied.

Parameters
observerThe object that will receive events.

◆ addObserver() [2/2]

void tlp::Observable::addObserver ( Observable observer) const
inline

Adds an Observer to this object.

The observer will receive events sent by this object, if there is no hold applied.

Parameters
observerThe object that will receive events.

Definition at line 212 of file Observable.h.

◆ countListeners()

unsigned int tlp::Observable::countListeners ( ) const

gets the number of listeners attached to this object.

Returns
the number of listeners attached to this object.

◆ countObservers()

unsigned int tlp::Observable::countObservers ( ) const

gets the number of observers attached to this object.

Returns
the number of observers attached to this object.

◆ disableEventNotification()

static void tlp::Observable::disableEventNotification ( )
inlinestatic

disable the whole event notification mechanism Until a call to enableEventNotification(), all sent events will be lost, except TLP_DELETE events which are synchronously processed, in order to void dangling pointers.

Warning
this function is a first step to allow parallel computation (of Tulip properties, for example). Use it at your own risk and avoid the sent of TLP_DELETE events whose management is not thread safe.

Definition at line 185 of file Observable.h.

◆ enableEventNotification()

static void tlp::Observable::enableEventNotification ( )
inlinestatic

re-enable the whole event notification mechanism All events sent since a previous call to enableEventNotification(), are definitively lost.

Definition at line 194 of file Observable.h.

◆ getNode()

static tlp::node tlp::Observable::getNode ( const tlp::Observable obs)
static

internal function for debugging purpose Get the node representing this object in the Observable Graph.

Parameters
obsthe Observable object
Returns
the node of the Observable object in the Observable Graph.

◆ getObject()

static Observable* tlp::Observable::getObject ( tlp::node  n)
static

internal function for debugging purpose If called during an update, it is possible the pointed object has been deleted. use isAlive() to check for this and avoid a crash.

Parameters
nThe node representing the object to retrieve.
Returns
The object represented by the node.

◆ getObservableGraph()

static const tlp::VectorGraph& tlp::Observable::getObservableGraph ( )
static

Gets the observation graph.

Returns
The graph containing a node for each Observable/Observer/Listener, and an edge between connected objects.

◆ getReceived()

unsigned int tlp::Observable::getReceived ( ) const

get the number of received events.

Returns
the number of received events (0 when compiling with -DNDEBUG).

◆ getScheduled()

static unsigned int tlp::Observable::getScheduled ( tlp::node  n)
static

internal function for debugging purpose getScheduled get the number of scheduled events for this Observable.

Parameters
nThe node of an Observable object
Returns
the number of scheduled events involving this Observable as sender or receiver.

◆ getSent()

unsigned int tlp::Observable::getSent ( ) const

gets the number of sent events.

Returns
the number of sent events (0 when compiling with -DNDEBUG).

◆ hasOnlookers()

bool tlp::Observable::hasOnlookers ( ) const
protected

Checks whether there are Observers/Listeners attached to this object.

Using this function avoids the creation of events that no-one will see :

if (hasOnlookers()) {
sendEvent(GraphEvent(*this, GraphEvent::TLP_ADD_NODE, n));
}
Returns

◆ holdObservers()

static void tlp::Observable::holdObservers ( )
static

Holds back all events until Observable::unholdObservers() is called.

Listeners are not affected by this function. Once this function is called, all events heading to an Observer will be held, except TLP_DELETE events. The events are stored in a queue, and will be sent once Observable::unholdObservers() is called.

It is possible to nest calls to Observable::holdObservers() and Observable::unholdObservers(), and in this case the events will only be sent when there have been as many calls to Observable::unholdObservers() as to Observable::holdObservers().

It is possible to check whether the events are being help by checking the Observable::observersHoldCounter() function.

See also
unholdObservers
observersHoldCounter

◆ isAlive()

static bool tlp::Observable::isAlive ( tlp::node  n)
static

internal function for debugging purpose If there is no hold currently applied, or no update ongoing, always returns true. Checks if the object represented by the node has been deleted.

Parameters
nThe node to check for life signs.
Returns
Whether the node is dead.

◆ observableDeleted()

void tlp::Observable::observableDeleted ( )
protected

Sends the Event::DELETE before the deletion of the subclass and its internal objects.

The observation system automatically sends the DELETE event when the Observable is deleted, but in the case you need to access members of the class inheriting from Observable, you need the event sent before the outermost objects are destroyed.

To achieve this, you can call this function in your destructor, and the DELETE event will be sent. This will allow your Listeners/Observers to access your members one last time before deletion.

Warning
This function must be called only once per object. Make sure no other class in the inheritance tree calls this function before adding this call to your destructor.

◆ observersHoldCounter()

static unsigned int tlp::Observable::observersHoldCounter ( )
inlinestatic

observersHoldCounter gives the number of times holdObservers() has been called without a matching unholdObservers() call.

Returns
the number of times holdObservers() has been called without a matching unholdObservers() call.

Definition at line 170 of file Observable.h.

◆ removeListener() [1/2]

void tlp::Observable::removeListener ( Observable *const  listener) const

Removes a listener from this object.

The listener will no longer receive events from this object.

Parameters
listenerThe listener to remove from this object.
+ Here is the caller graph for this function:

◆ removeListener() [2/2]

void tlp::Observable::removeListener ( Observable listener) const
inline

Removes a listener from this object.

The listener will no longer receive events from this object.

Parameters
listenerThe listener to remove from this object.

Definition at line 268 of file Observable.h.

+ Here is the call graph for this function:

◆ removeObserver() [1/2]

void tlp::Observable::removeObserver ( Observable *const  observer) const

Removes an observer from this object.

The observer will no longer receive events from this object.

Parameters
observerThe observer to remove from this object.

◆ removeObserver() [2/2]

void tlp::Observable::removeObserver ( Observable observer) const
inline

Removes an observer from this object.

The observer will no longer receive events from this object.

Parameters
observerThe observer to remove from this object.

Definition at line 250 of file Observable.h.

◆ sendEvent()

void tlp::Observable::sendEvent ( const Event message)
protected

Sends an event to all the Observers/Listeners. It is higly recommended to check if there are observers/listeners to send the event to before actually sending it to avoid the overhead of creating too many objects.

This can be achieved by using the hasOnLookers() function :

if (hasOnlookers()) {
sendEvent(GraphEvent(*this, GraphEvent::TLP_ADD_NODE, n));
}
Parameters
messageThe message to send to the listeners/observers.

◆ treatEvent()

virtual void tlp::Observable::treatEvent ( const Event message)
protectedvirtual

This function is called when events are sent to the Listeners, and Listeners only.

Is it passed a reference to the event that just happened.

Parameters
messageThe event that was sent.

◆ treatEvents()

virtual void tlp::Observable::treatEvents ( const std::vector< Event > &  events)
protectedvirtual

This function is called when events are sent to Observers, and Observers only.

It is passed a vector of all the events that happened since the last call. If events were held, this vector can be pretty large, and if events were not held it is likely it only contains a single element.

Parameters
eventsThe events that happened since the last unHoldObservers().

Reimplemented in tlp::View.

◆ unholdObservers()

static void tlp::Observable::unholdObservers ( )
static

Sends all held events to the Observers.

Listeners are not affected by this function.

In debug mode, if the hold counter is less than one when calling this function, an ObservableException will be raised.

See also
holdObservers
observersHoldCounter