# HG changeset patch # User am@osimis.io # Date 1543398268 -3600 # Node ID 751fb354149e98ce4c569e822191659998071a37 # Parent 660fe6f6bf4ac23aee691e253bab062a4499d97e ability to change the scene of the RadiographyWidget diff -r 660fe6f6bf4a -r 751fb354149e Applications/Samples/CMakeLists.txt --- a/Applications/Samples/CMakeLists.txt Thu Nov 22 23:15:24 2018 +0100 +++ b/Applications/Samples/CMakeLists.txt Wed Nov 28 10:44:28 2018 +0100 @@ -193,7 +193,6 @@ ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestCommands.cpp ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestExceptions.cpp ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestMessageBroker.cpp - ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestMessageBroker2.cpp ${ORTHANC_STONE_ROOT}/UnitTestsSources/UnitTestsMain.cpp ) diff -r 660fe6f6bf4a -r 751fb354149e Applications/Samples/SingleFrameEditorApplication.h --- a/Applications/Samples/SingleFrameEditorApplication.h Thu Nov 22 23:15:24 2018 +0100 +++ b/Applications/Samples/SingleFrameEditorApplication.h Wed Nov 28 10:44:28 2018 +0100 @@ -303,7 +303,7 @@ if (context_ != NULL) { widget.GetScene().ExportDicom(context_->GetOrthancApiClient(), - tags, 0.1, 0.1, widget.IsInverted(), + tags, std::string(), 0.1, 0.1, widget.IsInverted(), widget.GetInterpolation(), EXPORT_USING_PAM); } @@ -380,7 +380,7 @@ public IObserver { private: - std::auto_ptr scene_; + boost::shared_ptr scene_; RadiographyEditorInteractor interactor_; public: @@ -465,7 +465,7 @@ } - mainWidget_ = new RadiographyWidget(GetBroker(), *scene_, "main-widget"); + mainWidget_ = new RadiographyWidget(GetBroker(), scene_, "main-widget"); mainWidget_->SetTransmitMouseOver(true); mainWidget_->SetInteractor(interactor_); diff -r 660fe6f6bf4a -r 751fb354149e Framework/Messages/IObservable.cpp --- a/Framework/Messages/IObservable.cpp Thu Nov 22 23:15:24 2018 +0100 +++ b/Framework/Messages/IObservable.cpp Wed Nov 28 10:44:28 2018 +0100 @@ -64,6 +64,25 @@ callables_[messageType].insert(callable); } + void IObservable::Unregister(IObserver *observer) + { + // delete all callables from this observer + for (Callables::iterator itCallableSet = callables_.begin(); + itCallableSet != callables_.end(); ++itCallableSet) + { + for (std::set::const_iterator + itCallable = itCallableSet->second.begin(); itCallable != itCallableSet->second.end(); ) + { + if ((*itCallable)->GetObserver() == observer) + { + delete *itCallable; + itCallableSet->second.erase(itCallable++); + } + else + ++itCallable; + } + } + } void IObservable::EmitMessage(const IMessage& message) { diff -r 660fe6f6bf4a -r 751fb354149e Framework/Messages/IObservable.h --- a/Framework/Messages/IObservable.h Thu Nov 22 23:15:24 2018 +0100 +++ b/Framework/Messages/IObservable.h Wed Nov 28 10:44:28 2018 +0100 @@ -58,6 +58,8 @@ // Takes ownsership void RegisterObserverCallback(ICallable* callable); + void Unregister(IObserver* observer); + void EmitMessage(const IMessage& message); // Takes ownsership diff -r 660fe6f6bf4a -r 751fb354149e Framework/Messages/MessageForwarder.h --- a/Framework/Messages/MessageForwarder.h Thu Nov 22 23:15:24 2018 +0100 +++ b/Framework/Messages/MessageForwarder.h Wed Nov 28 10:44:28 2018 +0100 @@ -59,10 +59,10 @@ * C is an observer of B and knows that B is re-emitting many messages from A * * instead of implementing a callback, B will create a MessageForwarder that will emit the messages in his name: - * A.RegisterObserverCallback(new MessageForwarder(broker, *this) // where this is B + * A.RegisterObserverCallback(new MessageForwarder(broker, *this) // where "this" is B * * in C: - * B.RegisterObserverCallback(new Callable(*this, &B::MyCallback)) // where this is C + * B.RegisterObserverCallback(new Callable(*this, &B::MyCallback)) // where "this" is C */ template class MessageForwarder : public IMessageForwarder, public Callable, TMessage> diff -r 660fe6f6bf4a -r 751fb354149e Framework/Radiography/RadiographyScene.cpp --- a/Framework/Radiography/RadiographyScene.cpp Thu Nov 22 23:15:24 2018 +0100 +++ b/Framework/Radiography/RadiographyScene.cpp Wed Nov 28 10:44:28 2018 +0100 @@ -412,6 +412,8 @@ std::auto_ptr raii(layer); + LOG(INFO) << "Registering layer: " << countLayers_; + size_t index = countLayers_++; raii->SetIndex(index); layers_[index] = raii.release(); @@ -445,6 +447,8 @@ void RadiographyScene::RemoveLayer(size_t layerIndex) { + LOG(INFO) << "Removing layer: " << layerIndex; + if (layerIndex > countLayers_) { throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); @@ -452,6 +456,7 @@ delete layers_[layerIndex]; layers_.erase(layerIndex); countLayers_--; + LOG(INFO) << "Removing layer, there are now : " << countLayers_ << " layers"; } RadiographyLayer& RadiographyScene::GetLayer(size_t layerIndex) diff -r 660fe6f6bf4a -r 751fb354149e Framework/Radiography/RadiographyWidget.cpp --- a/Framework/Radiography/RadiographyWidget.cpp Thu Nov 22 23:15:24 2018 +0100 +++ b/Framework/Radiography/RadiographyWidget.cpp Wed Nov 28 10:44:28 2018 +0100 @@ -31,7 +31,7 @@ ImageInterpolation interpolation) { float windowCenter, windowWidth; - scene_.GetWindowingWithDefault(windowCenter, windowWidth); + scene_->GetWindowingWithDefault(windowCenter, windowWidth); float x0 = windowCenter - windowWidth / 2.0f; float x1 = windowCenter + windowWidth / 2.0f; @@ -56,7 +56,7 @@ cairoBuffer_.reset(new CairoSurface(width, height)); } - scene_.Render(*floatBuffer_, GetView().GetMatrix(), interpolation); + scene_->Render(*floatBuffer_, GetView().GetMatrix(), interpolation); // Conversion from Float32 to BGRA32 (cairo). Very similar to // GrayscaleFrameRenderer => TODO MERGE? @@ -128,7 +128,7 @@ if (hasSelection_) { - scene_.DrawBorder(context, selectedLayer_, view.GetZoom()); + scene_->DrawBorder(context, selectedLayer_, view.GetZoom()); } return true; @@ -136,23 +136,16 @@ RadiographyWidget::RadiographyWidget(MessageBroker& broker, - RadiographyScene& scene, + boost::shared_ptr scene, const std::string& name) : WorldSceneWidget(name), IObserver(broker), - scene_(scene), invert_(false), interpolation_(ImageInterpolation_Nearest), hasSelection_(false), selectedLayer_(0) // Dummy initialization { - scene.RegisterObserverCallback( - new Callable - (*this, &RadiographyWidget::OnGeometryChanged)); - - scene.RegisterObserverCallback( - new Callable - (*this, &RadiographyWidget::OnContentChanged)); + SetScene(scene); } @@ -216,4 +209,25 @@ NotifyContentChanged(); } } + + void RadiographyWidget::SetScene(boost::shared_ptr scene) + { + if (scene_ != NULL) + { + scene_->Unregister(this); + } + + scene_ = scene; + + scene_->RegisterObserverCallback( + new Callable + (*this, &RadiographyWidget::OnGeometryChanged)); + + scene_->RegisterObserverCallback( + new Callable + (*this, &RadiographyWidget::OnContentChanged)); + + // force redraw + FitContent(); + } } diff -r 660fe6f6bf4a -r 751fb354149e Framework/Radiography/RadiographyWidget.h --- a/Framework/Radiography/RadiographyWidget.h Thu Nov 22 23:15:24 2018 +0100 +++ b/Framework/Radiography/RadiographyWidget.h Wed Nov 28 10:44:28 2018 +0100 @@ -32,7 +32,7 @@ public IObserver { private: - RadiographyScene& scene_; + boost::shared_ptr scene_; std::auto_ptr floatBuffer_; std::auto_ptr cairoBuffer_; bool invert_; @@ -47,7 +47,7 @@ protected: virtual Extent2D GetSceneExtent() { - return scene_.GetSceneExtent(); + return scene_->GetSceneExtent(); } virtual bool RenderScene(CairoContext& context, @@ -55,14 +55,16 @@ public: RadiographyWidget(MessageBroker& broker, - RadiographyScene& scene, + boost::shared_ptr scene, // TODO: check how we can avoid boost::shared_ptr here since we don't want them in the public API (app is keeping a boost::shared_ptr to this right now) const std::string& name); RadiographyScene& GetScene() const { - return scene_; + return *scene_; } + void SetScene(boost::shared_ptr scene); + void Unselect() { hasSelection_ = false; diff -r 660fe6f6bf4a -r 751fb354149e UnitTestsSources/TestMessageBroker.cpp --- a/UnitTestsSources/TestMessageBroker.cpp Thu Nov 22 23:15:24 2018 +0100 +++ b/UnitTestsSources/TestMessageBroker.cpp Wed Nov 28 10:44:28 2018 +0100 @@ -1,158 +1,416 @@ -///** -// * Stone of Orthanc -// * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics -// * Department, University Hospital of Liege, Belgium -// * Copyright (C) 2017-2018 Osimis S.A., Belgium -// * -// * This program is free software: you can redistribute it and/or -// * modify it under the terms of the GNU Affero General Public License -// * as published by the Free Software Foundation, either version 3 of -// * the License, or (at your option) any later version. -// * -// * This program 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 -// * Affero General Public License for more details. -// * -// * You should have received a copy of the GNU Affero General Public License -// * along with this program. If not, see . -// **/ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ -//#include "gtest/gtest.h" +#include "gtest/gtest.h" + +#include "Framework/Messages/MessageBroker.h" +#include "Framework/Messages/Promise.h" +#include "Framework/Messages/IObservable.h" +#include "Framework/Messages/IObserver.h" +#include "Framework/Messages/MessageForwarder.h" + -//#include "../Framework/Messages/MessageBroker.h" -//#include "../Framework/Messages/IMessage.h" -//#include "../Framework/Messages/IObservable.h" -//#include "../Framework/Messages/IObserver.h" -//#include "../Framework/StoneEnumerations.h" +int testCounter = 0; +namespace { + + using namespace OrthancStone; + + + enum CustomMessageType + { + CustomMessageType_First = MessageType_CustomMessage + 1, + + CustomMessageType_Completed, + CustomMessageType_Increment + }; -//static int test1Counter = 0; -//static int test2Counter = 0; -//class MyFullObserver : public OrthancStone::IObserver -//{ + class MyObservable : public IObservable + { + public: + struct MyCustomMessage: public BaseMessage + { + int payload_; + + MyCustomMessage(int payload) + : BaseMessage(), + payload_(payload) + {} + }; + + MyObservable(MessageBroker& broker) + : IObservable(broker) + {} + + }; -//public: -// MyFullObserver(OrthancStone::MessageBroker& broker) -// : OrthancStone::IObserver(broker) -// { -//// DeclareHandledMessage(OrthancStone::MessageType_Test1); -//// DeclareIgnoredMessage(OrthancStone::MessageType_Test2); -// } + class MyObserver : public IObserver + { + public: + MyObserver(MessageBroker& broker) + : IObserver(broker) + {} + + void HandleCompletedMessage(const MyObservable::MyCustomMessage& message) + { + testCounter += message.payload_; + } + + }; + + + class MyIntermediate : public IObserver, public IObservable + { + IObservable& observedObject_; + public: + MyIntermediate(MessageBroker& broker, IObservable& observedObject) + : IObserver(broker), + IObservable(broker), + observedObject_(observedObject) + { + observedObject_.RegisterObserverCallback(new MessageForwarder(broker, *this)); + } + }; -// void HandleMessage(OrthancStone::IObservable& from, const OrthancStone::IMessage& message) { -// switch (message.GetType()) -// { -// case OrthancStone::MessageType_Test1: -// test1Counter++; -// break; -// case OrthancStone::MessageType_Test2: -// test2Counter++; -// break; -// default: -// throw OrthancStone::MessageNotDeclaredException(message.GetType()); -// } -// } + class MyPromiseSource : public IObservable + { + Promise* currentPromise_; + public: + struct MyPromiseMessage: public BaseMessage + { + int increment; + + MyPromiseMessage(int increment) + : BaseMessage(), + increment(increment) + {} + }; + + MyPromiseSource(MessageBroker& broker) + : IObservable(broker), + currentPromise_(NULL) + {} + + Promise& StartSomethingAsync() + { + currentPromise_ = new Promise(GetBroker()); + return *currentPromise_; + } -//}; + void CompleteSomethingAsyncWithSuccess(int payload) + { + currentPromise_->Success(MyPromiseMessage(payload)); + delete currentPromise_; + } -//class MyPartialObserver : public OrthancStone::IObserver -//{ + void CompleteSomethingAsyncWithFailure(int payload) + { + currentPromise_->Failure(MyPromiseMessage(payload)); + delete currentPromise_; + } + }; + -//public: -// MyPartialObserver(OrthancStone::MessageBroker& broker) -// : OrthancStone::IObserver(broker) -// { -//// DeclareHandledMessage(OrthancStone::MessageType_Test1); -// // don't declare Test2 on purpose -// } + class MyPromiseTarget : public IObserver + { + public: + MyPromiseTarget(MessageBroker& broker) + : IObserver(broker) + {} + + void IncrementCounter(const MyPromiseSource::MyPromiseMessage& args) + { + testCounter += args.increment; + } + + void DecrementCounter(const MyPromiseSource::MyPromiseMessage& args) + { + testCounter -= args.increment; + } + }; +} -// void HandleMessage(OrthancStone::IObservable& from, const OrthancStone::IMessage& message) { -// switch (message.GetType()) -// { -// case OrthancStone::MessageType_Test1: -// test1Counter++; -// break; -// case OrthancStone::MessageType_Test2: -// test2Counter++; -// break; -// default: -// throw OrthancStone::MessageNotDeclaredException(message.GetType()); -// } -// } +TEST(MessageBroker, TestPermanentConnectionSimpleUseCase) +{ + MessageBroker broker; + MyObservable observable(broker); + MyObserver observer(broker); + + // create a permanent connection between an observable and an observer + observable.RegisterObserverCallback(new Callable(observer, &MyObserver::HandleCompletedMessage)); + + testCounter = 0; + observable.EmitMessage(MyObservable::MyCustomMessage(12)); + ASSERT_EQ(12, testCounter); + + // the connection is permanent; if we emit the same message again, the observer will be notified again + testCounter = 0; + observable.EmitMessage(MyObservable::MyCustomMessage(20)); + ASSERT_EQ(20, testCounter); + + // Unregister the observer; make sure it's not called anymore + observable.Unregister(&observer); + testCounter = 0; + observable.EmitMessage(MyObservable::MyCustomMessage(20)); + ASSERT_EQ(0, testCounter); +} + +TEST(MessageBroker, TestMessageForwarderSimpleUseCase) +{ + MessageBroker broker; + MyObservable observable(broker); + MyIntermediate intermediate(broker, observable); + MyObserver observer(broker); + + // let the observer observers the intermediate that is actually forwarding the messages from the observable + intermediate.RegisterObserverCallback(new Callable(observer, &MyObserver::HandleCompletedMessage)); + + testCounter = 0; + observable.EmitMessage(MyObservable::MyCustomMessage(12)); + ASSERT_EQ(12, testCounter); + + // the connection is permanent; if we emit the same message again, the observer will be notified again + testCounter = 0; + observable.EmitMessage(MyObservable::MyCustomMessage(20)); + ASSERT_EQ(20, testCounter); +} + +TEST(MessageBroker, TestPermanentConnectionDeleteObserver) +{ + MessageBroker broker; + MyObservable observable(broker); + MyObserver* observer = new MyObserver(broker); -//}; + // create a permanent connection between an observable and an observer + observable.RegisterObserverCallback(new Callable(*observer, &MyObserver::HandleCompletedMessage)); + testCounter = 0; + observable.EmitMessage(MyObservable::MyCustomMessage(12)); + ASSERT_EQ(12, testCounter); + + // delete the observer and check that the callback is not called anymore + delete observer; -//class MyObservable : public OrthancStone::IObservable -//{ + // the connection is permanent; if we emit the same message again, the observer will be notified again + testCounter = 0; + observable.EmitMessage(MyObservable::MyCustomMessage(20)); + ASSERT_EQ(0, testCounter); +} + +TEST(MessageBroker, TestMessageForwarderDeleteIntermediate) +{ + MessageBroker broker; + MyObservable observable(broker); + MyIntermediate* intermediate = new MyIntermediate(broker, observable); + MyObserver observer(broker); + + // let the observer observers the intermediate that is actually forwarding the messages from the observable + intermediate->RegisterObserverCallback(new Callable(observer, &MyObserver::HandleCompletedMessage)); -//public: -// MyObservable(OrthancStone::MessageBroker& broker) -// : OrthancStone::IObservable(broker) -// { -// DeclareEmittableMessage(OrthancStone::MessageType_Test1); -// DeclareEmittableMessage(OrthancStone::MessageType_Test2); -// } + testCounter = 0; + observable.EmitMessage(MyObservable::MyCustomMessage(12)); + ASSERT_EQ(12, testCounter); + + delete intermediate; + + observable.EmitMessage(MyObservable::MyCustomMessage(20)); + ASSERT_EQ(12, testCounter); +} -//}; +TEST(MessageBroker, TestCustomMessage) +{ + MessageBroker broker; + MyObservable observable(broker); + MyIntermediate intermediate(broker, observable); + MyObserver observer(broker); + + // let the observer observers the intermediate that is actually forwarding the messages from the observable + intermediate.RegisterObserverCallback(new Callable(observer, &MyObserver::HandleCompletedMessage)); + + testCounter = 0; + observable.EmitMessage(MyObservable::MyCustomMessage(12)); + ASSERT_EQ(12, testCounter); + + // the connection is permanent; if we emit the same message again, the observer will be notified again + testCounter = 0; + observable.EmitMessage(MyObservable::MyCustomMessage(20)); + ASSERT_EQ(20, testCounter); +} -//TEST(MessageBroker, NormalUsage) -//{ -// OrthancStone::MessageBroker broker; -// MyObservable observable(broker); +TEST(MessageBroker, TestPromiseSuccessFailure) +{ + MessageBroker broker; + MyPromiseSource source(broker); + MyPromiseTarget target(broker); + + // test a successful promise + source.StartSomethingAsync() + .Then(new Callable(target, &MyPromiseTarget::IncrementCounter)) + .Else(new Callable(target, &MyPromiseTarget::DecrementCounter)); + + testCounter = 0; + source.CompleteSomethingAsyncWithSuccess(10); + ASSERT_EQ(10, testCounter); -// test1Counter = 0; + // test a failing promise + source.StartSomethingAsync() + .Then(new Callable(target, &MyPromiseTarget::IncrementCounter)) + .Else(new Callable(target, &MyPromiseTarget::DecrementCounter)); + + testCounter = 0; + source.CompleteSomethingAsyncWithFailure(15); + ASSERT_EQ(-15, testCounter); +} -// // no observers have been registered -> nothing shall happen -// observable.EmitMessage(OrthancStone::IMessage(OrthancStone::MessageType_Test1)); +TEST(MessageBroker, TestPromiseDeleteTarget) +{ + MessageBroker broker; + MyPromiseSource source(broker); + MyPromiseTarget* target = new MyPromiseTarget(broker); -// ASSERT_EQ(0, test1Counter); + // create the promise + source.StartSomethingAsync() + .Then(new Callable(*target, &MyPromiseTarget::IncrementCounter)) + .Else(new Callable(*target, &MyPromiseTarget::DecrementCounter)); -// // register an observer, check it is called -// MyFullObserver fullObserver(broker); -// ASSERT_NO_THROW(observable.RegisterObserver(fullObserver)); + // delete the promise target + delete target; + + // trigger the promise, make sure it does not throw and does not call the callback + testCounter = 0; + source.CompleteSomethingAsyncWithSuccess(10); + ASSERT_EQ(0, testCounter); -// observable.EmitMessage(OrthancStone::IMessage(OrthancStone::MessageType_Test1)); + // test a failing promise + source.StartSomethingAsync() + .Then(new Callable(*target, &MyPromiseTarget::IncrementCounter)) + .Else(new Callable(*target, &MyPromiseTarget::DecrementCounter)); + + testCounter = 0; + source.CompleteSomethingAsyncWithFailure(15); + ASSERT_EQ(0, testCounter); +} + +#if __cplusplus >= 201103L -// ASSERT_EQ(1, test1Counter); +#include + +namespace OrthancStone { + + template + class LambdaCallable : public MessageHandler + { + private: -// // register an invalid observer, check it raises an exception -// MyPartialObserver partialObserver(broker); -// ASSERT_THROW(observable.RegisterObserver(partialObserver), OrthancStone::MessageNotDeclaredException); + IObserver& observer_; + std::function lambda_; -// // check an exception is thrown when the observable emits an undeclared message -// ASSERT_THROW(observable.EmitMessage(OrthancStone::IMessage(OrthancStone::MessageType_VolumeSlicer_GeometryReady)), OrthancStone::MessageNotDeclaredException); + public: + LambdaCallable(IObserver& observer, + std::function lambdaFunction) : + observer_(observer), + lambda_(lambdaFunction) + { + } -// // unregister the observer, make sure nothing happens afterwards -// observable.UnregisterObserver(fullObserver); -// observable.EmitMessage(OrthancStone::IMessage(OrthancStone::MessageType_Test1)); -// ASSERT_EQ(1, test1Counter); -//} + virtual void Apply(const IMessage& message) + { + lambda_(dynamic_cast(message)); + } + + virtual MessageType GetMessageType() const + { + return static_cast(TMessage::Type); + } + + virtual IObserver* GetObserver() const + { + return &observer_; + } + }; + -//TEST(MessageBroker, DeleteObserverWhileRegistered) -//{ -// OrthancStone::MessageBroker broker; -// MyObservable observable(broker); +} + +TEST(MessageBroker, TestLambdaSimpleUseCase) +{ + MessageBroker broker; + MyObservable observable(broker); + MyObserver* observer = new MyObserver(broker); -// test1Counter = 0; + // create a permanent connection between an observable and an observer + observable.RegisterObserverCallback(new LambdaCallable(*observer, [&](const MyObservable::MyCustomMessage& message) {testCounter += 2 * message.payload_;})); + + testCounter = 0; + observable.EmitMessage(MyObservable::MyCustomMessage(12)); + ASSERT_EQ(24, testCounter); + + // delete the observer and check that the callback is not called anymore + delete observer; -// { -// // register an observer, check it is called -// MyFullObserver observer(broker); -// observable.RegisterObserver(observer); + // the connection is permanent; if we emit the same message again, the observer will be notified again + testCounter = 0; + observable.EmitMessage(MyObservable::MyCustomMessage(20)); + ASSERT_EQ(0, testCounter); +} -// observable.EmitMessage(OrthancStone::IMessage(OrthancStone::MessageType_Test1)); +namespace { + class MyObserverWithLambda : public IObserver { + private: + int multiplier_; // this is a private variable we want to access in a lambda + + public: + MyObserverWithLambda(MessageBroker& broker, int multiplier, MyObservable& observable) + : IObserver(broker), + multiplier_(multiplier) + { + // register a callable to a lambda that access private members + observable.RegisterObserverCallback(new LambdaCallable(*this, [this](const MyObservable::MyCustomMessage& message) { + testCounter += multiplier_ * message.payload_; + })); -// ASSERT_EQ(1, test1Counter); -// } + } + }; +} + +TEST(MessageBroker, TestLambdaCaptureThisAndAccessPrivateMembers) +{ + MessageBroker broker; + MyObservable observable(broker); + MyObserverWithLambda* observer = new MyObserverWithLambda(broker, 3, observable); -// // at this point, the observer has been deleted, the handle shall not be called again (and it shall not crash !) -// observable.EmitMessage(OrthancStone::IMessage(OrthancStone::MessageType_Test1)); + testCounter = 0; + observable.EmitMessage(MyObservable::MyCustomMessage(12)); + ASSERT_EQ(36, testCounter); + + // delete the observer and check that the callback is not called anymore + delete observer; -// ASSERT_EQ(1, test1Counter); -//} + // the connection is permanent; if we emit the same message again, the observer will be notified again + testCounter = 0; + observable.EmitMessage(MyObservable::MyCustomMessage(20)); + ASSERT_EQ(0, testCounter); +} + +#endif // C++ 11