Skip to content

Commit

Permalink
ENH: Add direct support for lambda command to Object class
Browse files Browse the repository at this point in the history
Overload AddObserver with std::function argument to allow direct usage
of lambdas without the explicit addition of a new Command class.
  • Loading branch information
blowekamp authored and hjmjohnson committed Jul 9, 2020
1 parent b07b3cd commit b895526
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 1 deletion.
1 change: 1 addition & 0 deletions Modules/Core/Common/include/itkCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include "itkObject.h"
#include "itkObjectFactory.h"
#include <functional>

namespace itk
{
Expand Down
23 changes: 22 additions & 1 deletion Modules/Core/Common/include/itkObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
#include "itkMetaDataDictionary.h"
#include "itkSingletonMacro.h"

#include <functional>

namespace itk
{
// Forward reference because of private implementation
Expand Down Expand Up @@ -153,10 +155,29 @@ class ITKCommon_EXPORT Object : public LightObject
* different objects */
unsigned long
AddObserver(const EventObject & event, Command *);

unsigned long
AddObserver(const EventObject & event, Command *) const;

/** \brief A convenient method to add an C++ lambda function as an observer.
*
* A FunctionCommand object in implicitly create to hold function, and
* passed to this object as a standard observer. The command will be invoked
* for the specified event.
*
* The function or lambda \b cannot captured this object as SmartPoint because
* a circular dependency is generated which causing memory leaks.
* Sample usage:
* \code
* auto &objRef = *o.GetPointer();
* o->AddObserver(itk::AnyEvent(), [&objRef](const itk::EventObject &event)
* { std::cout << "Object: " << objRef.GetNameOfClass() << " Event: " << event << std::endl; });
* \endcode
*
* \see FunctionCommand
*/
unsigned long
AddObserver(const EventObject & event, std::function<void(const EventObject &)> function) const;

/** Get the command associated with the given tag. NOTE: This returns
* a pointer to a Command, but it is safe to assign this to a
* Command::Pointer. Since Command inherits from LightObject, at this
Expand Down
9 changes: 9 additions & 0 deletions Modules/Core/Common/src/itkObject.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,15 @@ Object ::AddObserver(const EventObject & event, Command * cmd) const
return this->m_SubjectImplementation->AddObserver(event, cmd);
}

unsigned long
Object::AddObserver(const EventObject & event, std::function<void(const EventObject &)> function) const
{
auto cmd = FunctionCommand::New();
cmd->SetCallback(std::move(function));
return this->AddObserver(event, cmd);
}


Command *
Object ::GetCommand(unsigned long tag)
{
Expand Down
23 changes: 23 additions & 0 deletions Modules/Core/Common/test/itkCommandObserverObjectTest.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,28 @@ testDeleteEventThrow()
return EXIT_SUCCESS;
}

int
testLambdaCommand()
{
// check the case where an exception in thrown in the DeleteEvent
itk::Object::Pointer o = itk::Object::New();

int cnt = 0;
o->AddObserver(itk::AnyEvent(), [&cnt](const itk::EventObject &) { ++cnt; });

auto & objRef = *o.GetPointer();
o->AddObserver(itk::AnyEvent(), [&objRef](const itk::EventObject & event) {
std::cout << "Object: " << objRef.GetNameOfClass() << " Event: " << event << std::endl;
});

o->InvokeEvent(itk::AnyEvent());

ITK_TEST_EXPECT_EQUAL(1, cnt);

return EXIT_SUCCESS;
}


} // end namespace


Expand All @@ -295,6 +317,7 @@ itkCommandObserverObjectTest(int, char *[])
ret &= (testCommandConstObject() == EXIT_SUCCESS);
ret &= (testCommandRecursiveObject() == EXIT_SUCCESS);
ret &= (testDeleteEventThrow() == EXIT_SUCCESS);
ret &= (testLambdaCommand() == EXIT_SUCCESS);

return ret ? EXIT_SUCCESS : EXIT_FAILURE;
}

0 comments on commit b895526

Please sign in to comment.