[Insight-developers] Problem with SmartPointer<const Self>

Luis Ibanez ibanez@cs.unc.edu
Fri, 19 Jan 2001 17:45:02 -0500


Brad King wrote:

> Hello, all:
>
> I've looked into implementing the support for smart pointers to const
> objects, and ran into a problem with making Register/UnRegister methods
> const.  The UnRegister method looks like this:
>
> void LightObject::UnRegister()
> {
>   if(--m_ReferenceCount <= 0)
>     {
>     /**
>      * If there is a delete method, invoke it.
>      */
>     this->InvokeEvent(Command::DeleteEvent);
>     delete this;
>     }
> }
>
> Although the "delete this" statement is considered const, the InvokeEvent
> is not.  I traced through the execution of InvokeEvent, and there is no
> way to make the whole chain const because it makes Execute calls that are
> allowed to modify the object.


Well, in fact, this is one of the first needs for
ConstSmartPointer. It is basically related with
the implementation of the Observer Pattern.

This is the fundamental mechanism of comunication
that is expected to be used between itk and GUI's,
in both senses (GUI-->itk) (itk-->GUI), so it is 
worth take a closer look at how it works.

As far as I know this was implemented by Bill, so 
surely he could gives us better light on this point.

---

In the observer pattern, the main actors are Subjects
and Observers. Subjects are the objects that we want 
to track and Observers are the ones interested in to 
know when a particular event happen to the Subject.

One tipical use of this mecanism is to refresh the 
Window that displays an itkImage which is the output 
of one filter. In this case the itkImage is on the 
Subject side while the Window is on the Observer side.

Subjects don't want to know specific information
about the real nature of Observers. They just agree
in keeping observers informed. So the Observer is
supposed to be an abstract class that will be derived
to create specific implementations.  One subject is
able to keep references to many observers. One Observer
may be interested into monitor several different Subjects, 
and for some of those Subjects it could be interested in 
several different kinds of events.



   +---------+ *         +----------+
   | Subject |---------->| Observer |
   +---------+           +----------+
                              |(derivation)
                              V
                 +------------------------+
                 | ObserverImplementation |
                 +------------------------+


The limitation of this setup is that Subjects have
to call one specific method in the Observer to tell
them that the event happend. Given that all observers
look the same, the methods should be only one. And
given that one observer may be interested in several
subject and in several events, the method to be called
will receives as parameters the Event that happend and
a pointer to the Subject that originated the call.

If the observer is monitoring several different subjects
he should now start comparing the received pointer to 
find the real identity of the subject that trigger the 
call among all the subjects that it is currently watching.

-----

The current itk implementation of the Observer pattern
is much more flexible because it has been combined with
the Command pattern. 

In the original Observer Pattern, Subjects have to know 
about the Observers. With the addition of the Command, 
the real final observers are notified through a Command 
object. That isolates real observers from Subjects. 
Allowing the freedom that not all real observers has to 
derive from itk::Observer.


 +---------------------+ (points to many) +--------+
 |SubjectImplementation|----------------->|Observer|
 +---------------------+                  +--------+
                                               | (has one)
                                               V
                                          +---------+
                                          | Command |
                                          +---------+


In itk, Subjects are called SubjectImplemtation. Each
SubjectImplementation object has a list of pointers 
to its Observers. When the InvokeEvent() method of the
Subject is called, it parses the list of observers and
notify one by one that the event did happened. 

itk Observers are interested in only one event at a time,
and have a pointer to only one particular Command. 

The Observer has as member data:

1- an enum with the code of the Event it is interested on
2- a pointer to an itk::Command object, to be called when 
   the Event happens.

The complete frame is implemented in itk by having on every 
itk::LightObject a member of type SubjectImplementation.


   +-------------+
   | LightObject |
   +-------------+
          | (has one)
          V
 +---------------------+ (points to many) +--------+
 |SubjectImplementation|----------------->|Observer|
 +---------------------+                  +--------+
                                               | (has one)
                                               V
                                          +---------+
                                          | Command |
                                          +---------+

 

A call to LightObject::InvokeEvent( Event e ) method will 
just delegate to SubjectImplementation::InvokeEvent( Event e ). 

Then SubjectImplementation::InvokeEvent() goes through
all its registered Observers testing if the are interested 
in the recevied event. Each time it finds an Observer that 
is watching for this Event, it gets the itk::Command object
of this Observer and calls the Execute method of this
itk::Command, which is declared as:

   itk::Command::Execute( Event, LightObject * );

The execute method receive as parameters the Event and
a normal pointer to a itk::LightObject (the one that originated
the call).   

       
          [[[[[ This pointer is not const. ]]]]]]


The non-const pointer to LightObject is the one that 
prevents InvokeEvent from being a const method.



It is conceivable that some Observers want to act on the 
LightObject when they are notified of the event. But is 
also conceivable that some Observer just want to be informed 
and are not intended to change the subject when the event 
appears. (e.g. the Window GUI object just want to know that 
the image has changed, so it is time to refresh itself).

This underlines one important point.  If an Image changes,
and it notifies its Observers. It is possible that one 
observer on the head of the list modify the image before 
the other observers on the list are notified of the first 
change. That could even create infinite loops of Events.



It is interesting to consider if the Commands shouldn't be 
passive with respect to the LightObject that originates
the call.

One possible solution is to create two kinds of Commands, 
(for example PassiveCommands and ActiveCommands), the first 
with a Execute() method that receives a const pointer to 
LightObject like:

  void itk::PassiveCommand::Execute( Event, const LightObject *);

and the second with the non-const pointer, like

  void itk::ActiveCommand::Execute( Event, LightObject *);

The SubjectImplementation class will need to have now 
two separate lists, one for PassiveCommands and the other 
por ActiveCommands.  

It will also have two InvokeEvent() methods one const that will 
notify all the PassiveCommands, and another non-const that will 
notify all the ActiveCommands. 


The non-const version of InvokeEvent() should internally call 
the const version, basically by casting "this" to (const Self *) 
like: (which is a legal cast)

   SubjectImplementation::InvokeEvent()   // the non-const version
    {
     ((const Self *)this )->InvokeEvent( e );  // const version
     ....// now notify all the active commands.
    }

In that way, a non-const object will notify both its Passive
and Active commands, while a const object will only notify
its Passive commands.

Note that the PassiveCommand::Execute() and ActiveCommand::Execute()
methods don't need to be const. 

This is because when the SubjectImplementation is declared const, 
that will prevent the pointers on the list from changing, but 
still allows those pointer to call their non-const methods(). 
So the Execute() method can be overloaded to do any other task 
(e.g. refreshing a Window in a GUI) even if that changes its 
internal state.



--------



Luis







--
______________________________________________________________________

Luis Ibanez
Research Assistant Professor - Division of Neurosurgery
University of North Carolina at Chapel Hill
CB# 7060, Chapel Hill, NC 27599
email : ibanez@cs.unc.edu       home  : http://www.cs.unc.edu/~ibanez
phone : (919)-843-9961          fax   : (919)-966-6627
______________________________________________________________________