Commit 064b1a58 authored by Utkarsh Ayachit's avatar Utkarsh Ayachit
Browse files

Added API to add observer to a vtkObject that is a class member function.

Added new API to vtkObject to add class member functions as callbacks to be
called when an event is invoked. Also added a test to test that adding/removing
such observers does not cause any issues.
parent a47aa295
......@@ -18,6 +18,7 @@ CREATE_TEST_SOURCELIST(Tests ${KIT}CxxTests.cxx
TestMath.cxx
TestMatrix3x3.cxx
TestMinimalStandardRandomSequence.cxx
TestObservers.cxx
TestPolynomialSolversUnivariate.cxx
TestSmartPointer.cxx
TestSortDataArray.cxx
......
/*=========================================================================
Program: Visualization Toolkit
Module: TestSmartPointer.cxx
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
// .NAME Test of Observers.
// .SECTION Description
// Tests vtkObject::AddObserver templated API
#include "vtkObjectFactory.h"
#include <vtkstd/map>
class vtkHandler : public vtkObject
{
public:
static vtkstd::map<int, int> EventCounts;
static int VoidEventCounts;
public:
static vtkHandler* New();
vtkTypeMacro(vtkHandler, vtkObject);
void VoidCallback() { this->VoidEventCounts++; }
void CallbackWithArguments(vtkObject*, unsigned long event, void*)
{
this->EventCounts[event]++;
}
};
vtkStandardNewMacro(vtkHandler);
int vtkHandler::VoidEventCounts = 0;
vtkstd::map<int, int> vtkHandler::EventCounts;
int TestObservers(int, char*[])
{
vtkHandler* handler = vtkHandler::New();
vtkObject* volcano = vtkObject::New();
unsigned long event0 = volcano->AddObserver(
1000, handler, &vtkHandler::VoidCallback);
unsigned long event1 = volcano->AddObserver(
1001, handler, &vtkHandler::CallbackWithArguments);
unsigned long event2 = volcano->AddObserver(
1002, handler, &vtkHandler::CallbackWithArguments);
volcano->InvokeEvent(1000);
volcano->InvokeEvent(1001);
volcano->InvokeEvent(1002);
// let's see if removing an observer works
volcano->RemoveObserver(event2);
volcano->InvokeEvent(1000);
volcano->InvokeEvent(1001);
volcano->InvokeEvent(1002);
// now delete the observer, we shouldn't have any dangling pointers.
handler->Delete();
volcano->InvokeEvent(1000);
volcano->InvokeEvent(1001);
volcano->InvokeEvent(1002);
// remove an observer after the handler has been deleted, should work.
volcano->RemoveObserver(event1);
volcano->InvokeEvent(1000);
volcano->InvokeEvent(1001);
volcano->InvokeEvent(1002);
volcano->Delete();
if (vtkHandler::VoidEventCounts == 2 &&
vtkHandler::EventCounts[1000] == 0 &&
vtkHandler::EventCounts[1001] == 2 &&
vtkHandler::EventCounts[1002] == 1)
{
cout << "All callback counts as expected." << endl;
return 0;
}
cerr << "Mismatched callback counts" << endl;
return 1;
}
......@@ -18,6 +18,7 @@
#include "vtkDebugLeaks.h"
#include "vtkGarbageCollector.h"
#include "vtkTimeStamp.h"
#include "vtkWeakPointer.h"
#include <vtkstd/map>
......@@ -883,3 +884,53 @@ void vtkObject::UnRegisterInternal(vtkObjectBase* o, int check)
// Decrement the reference count.
this->Superclass::UnRegisterInternal(o, check);
}
//----------------------------------------------------------------------------
// Internal observer used by vtkObject::AddTemplatedObserver to add a
// vtkClassMemberCallbackBase instance as an observer to an event.
class vtkObjectCommandInternal : public vtkCommand
{
vtkObject::vtkClassMemberCallbackBase* Callable;
public:
static vtkObjectCommandInternal* New()
{ return new vtkObjectCommandInternal(); }
vtkTypeMacro(vtkObjectCommandInternal, vtkCommand);
virtual void Execute(
vtkObject *caller, unsigned long eventId, void *callData)
{
if (this->Callable)
{
(*this->Callable)(caller, eventId, callData);
}
}
// Takes over the ownership of \c callable.
void SetCallable(vtkObject::vtkClassMemberCallbackBase* callable)
{
delete this->Callable;
this->Callable = callable;
}
protected:
vtkObjectCommandInternal()
{
this->Callable = NULL;
}
~vtkObjectCommandInternal()
{
delete this->Callable;
}
};
//----------------------------------------------------------------------------
unsigned long vtkObject::AddTemplatedObserver(
unsigned long event, vtkObject::vtkClassMemberCallbackBase* callable, float priority)
{
vtkObjectCommandInternal* command = vtkObjectCommandInternal::New();
// Takes over the ownership of \c callable.
command->SetCallable(callable);
unsigned long id = this->AddObserver(event, command, priority);
command->Delete();
return id;
}
......@@ -41,6 +41,7 @@
#include "vtkObjectBase.h"
#include "vtkSetGet.h"
#include "vtkTimeStamp.h"
#include "vtkWeakPointerBase.h" // needed for vtkWeakPointer
class vtkSubjectHelper;
class vtkCommand;
......@@ -139,6 +140,42 @@ public:
int HasObserver(unsigned long event);
int HasObserver(const char *event);
//BTX
// Description:
// Overloads to AddObserver that allow developers to add class member
// functions of vtkObjectBase subclasses as callbacks to be called when an
// event is invoked. The callback function can either be a void foo(void) or
// void foo(vtkObject*, unsigned long, void*). Typical usage of these
// functions is as follows:
// \code
// vtkHandlerClass * observer = vtkHandlerClass::New();
// to_observe->AddObserver(event, observer, &vtkHandlerClass::CallbackMethod);
// \endcode
// Note that this does not affect the reference count of the \c observer, so
// if observer is deleted, then the callback won't be called. Interally, it
// uses a vtkWeakPointerBase to avoid dangling pointers.
// Return value is a tag that can be used to remove the observer.
template <class T>
unsigned long AddObserver(unsigned long event,
T* observer, void (T::*callback)(), float priority=0.0f)
{
vtkClassMemberCallback<T> *callable = new vtkClassMemberCallback<T>(observer, callback);
// callable is deleted when the observer is cleaned up (look at
// vtkObjectCommandInternal)
return this->AddTemplatedObserver(event, callable, priority);
}
template <class T>
unsigned long AddObserver(unsigned long event,
T* observer, void (T::*callback)(vtkObject*, unsigned long, void*),
float priority=0.0f)
{
vtkClassMemberCallback<T> *callable = new vtkClassMemberCallback<T>(observer, callback);
// callable is deleted when the observer is cleaned up (look at
// vtkObjectCommandInternal)
return this->AddTemplatedObserver(event, callable, priority);
}
//ETX
// Description:
// Allow people to add/remove/invoke observers (callbacks) to any
// VTK object. This is an implementation of the subject/observer
......@@ -176,7 +213,7 @@ protected:
// Description:
// These methods allow a command to exclusively grab all events. (This
// method is typically used by widgets to grab events once an event
// sequence begins.) These methods are provided in support of the
// sequence begins.) These methods are provided in support of the
// public methods found in the class vtkInteractorObserver. Note that
// these methods are designed to support vtkInteractorObservers since
// they use two separate vtkCommands to watch for mouse and keypress events.
......@@ -184,11 +221,75 @@ protected:
void InternalGrabFocus(vtkCommand *mouseEvents, vtkCommand *keypressEvents=NULL);
void InternalReleaseFocus();
//ETX
//BTX
private:
vtkObject(const vtkObject&); // Not implemented.
void operator=(const vtkObject&); // Not implemented.
// Description:
// Following classes (vtkClassMemberCallbackBase and vtkClassMemberCallback) along with
// vtkObjectCommandInternal are for supporting
// templated AddObserver() overloads that allow developers to add event
// callbacks that are class member functions.
class vtkClassMemberCallbackBase
{
public:
// Description:
// Called when the event is invoked
virtual void operator()(vtkObject*, unsigned long, void*) = 0;
virtual ~vtkClassMemberCallbackBase(){}
protected:
vtkWeakPointerBase Handler;
};
template <class T>
class vtkClassMemberCallback : public vtkClassMemberCallbackBase
{
T* Object;
void (T::*Method1)();
void (T::*Method2)(vtkObject*, unsigned long, void*);
public:
vtkClassMemberCallback(T* handler, void (T::*method)())
{
this->Handler = handler;
this->Method1 = method;
this->Method2 = NULL;
}
vtkClassMemberCallback(T* handler, void (T::*method)(vtkObject*, unsigned long, void*))
{
this->Handler = handler;
this->Method1 = NULL;
this->Method2 = method;
}
virtual ~vtkClassMemberCallback() { }
// Called when the event is invoked
virtual void operator()(vtkObject* caller, unsigned long event, void* calldata)
{
if (this->Handler.GetPointer())
{
T* handler = dynamic_cast<T*>(this->Handler.GetPointer());
if (this->Method1)
{
(handler->*this->Method1)();
}
else if (this->Method2)
{
(handler->*this->Method2)(caller, event, calldata);
}
}
}
};
// Description:
// Called by templated variants of AddObserver.
unsigned long AddTemplatedObserver(
unsigned long event, vtkClassMemberCallbackBase* callable, float priority);
// Friend to access AddTemplatedObserver().
friend class vtkObjectCommandInternal;
//ETX
};
#endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment