comparison UnitTestsSources/TestMessageBroker2.cpp @ 323:dbfe2e9e5020 am-2

playing with LambdaCallable
author am@osimis.io
date Mon, 15 Oct 2018 23:36:43 +0200
parents 4a79193ffb58
children e7a494bdd956
comparison
equal deleted inserted replaced
322:a902a07769d4 323:dbfe2e9e5020
29 29
30 30
31 int testCounter = 0; 31 int testCounter = 0;
32 namespace { 32 namespace {
33 33
34 // class IObserver;
35 // class IObservable;
36 // class Promise;
37
38 // enum MessageType
39 // {
40 // MessageType_Test1,
41 // MessageType_Test2,
42
43 // MessageType_CustomMessage,
44 // MessageType_LastGenericStoneMessage
45 // };
46
47 // struct IMessage : public boost::noncopyable
48 // {
49 // MessageType messageType_;
50 // public:
51 // IMessage(const MessageType& messageType)
52 // : messageType_(messageType)
53 // {}
54 // virtual ~IMessage() {}
55
56 // virtual int GetType() const {return messageType_;}
57 // };
58
59
60 // struct ICustomMessage : public IMessage
61 // {
62 // int customMessageType_;
63 // public:
64 // ICustomMessage(int customMessageType)
65 // : IMessage(MessageType_CustomMessage),
66 // customMessageType_(customMessageType)
67 // {}
68 // virtual ~ICustomMessage() {}
69
70 // virtual int GetType() const {return customMessageType_;}
71 // };
72
73
74 // // This is referencing an object and member function that can be notified
75 // // by an IObservable. The object must derive from IO
76 // // The member functions must be of type "void Function(const IMessage& message)" or reference a derived class of IMessage
77 // class ICallable : public boost::noncopyable
78 // {
79 // public:
80 // virtual ~ICallable()
81 // {
82 // }
83
84 // virtual void Apply(const IMessage& message) = 0;
85
86 // virtual MessageType GetMessageType() const = 0;
87 // virtual IObserver* GetObserver() const = 0;
88 // };
89
90 // template <typename TObserver,
91 // typename TMessage>
92 // class Callable : public ICallable
93 // {
94 // private:
95 // typedef void (TObserver::* MemberFunction) (const TMessage&);
96
97 // TObserver& observer_;
98 // MemberFunction function_;
99
100 // public:
101 // Callable(TObserver& observer,
102 // MemberFunction function) :
103 // observer_(observer),
104 // function_(function)
105 // {
106 // }
107
108 // void ApplyInternal(const TMessage& message)
109 // {
110 // (observer_.*function_) (message);
111 // }
112
113 // virtual void Apply(const IMessage& message)
114 // {
115 // ApplyInternal(dynamic_cast<const TMessage&>(message));
116 // }
117
118 // virtual MessageType GetMessageType() const
119 // {
120 // return static_cast<MessageType>(TMessage::Type);
121 // }
122
123 // virtual IObserver* GetObserver() const
124 // {
125 // return &observer_;
126 // }
127 // };
128
129
130
131
132 // /*
133 // * This is a central message broker. It keeps track of all observers and knows
134 // * when an observer is deleted.
135 // * This way, it can prevent an observable to send a message to a delete observer.
136 // */
137 // class MessageBroker : public boost::noncopyable
138 // {
139
140 // std::set<IObserver*> activeObservers_; // the list of observers that are currently alive (that have not been deleted)
141
142 // public:
143
144 // void Register(IObserver& observer)
145 // {
146 // activeObservers_.insert(&observer);
147 // }
148
149 // void Unregister(IObserver& observer)
150 // {
151 // activeObservers_.erase(&observer);
152 // }
153
154 // bool IsActive(IObserver* observer)
155 // {
156 // return activeObservers_.find(observer) != activeObservers_.end();
157 // }
158 // };
159
160
161 // class Promise : public boost::noncopyable
162 // {
163 // protected:
164 // MessageBroker& broker_;
165
166 // ICallable* successCallable_;
167 // ICallable* failureCallable_;
168
169 // public:
170 // Promise(MessageBroker& broker)
171 // : broker_(broker),
172 // successCallable_(NULL),
173 // failureCallable_(NULL)
174 // {
175 // }
176
177 // void Success(const IMessage& message)
178 // {
179 // // check the target is still alive in the broker
180 // if (broker_.IsActive(successCallable_->GetObserver()))
181 // {
182 // successCallable_->Apply(message);
183 // }
184 // }
185
186 // void Failure(const IMessage& message)
187 // {
188 // // check the target is still alive in the broker
189 // if (broker_.IsActive(failureCallable_->GetObserver()))
190 // {
191 // failureCallable_->Apply(message);
192 // }
193 // }
194
195 // Promise& Then(ICallable* successCallable)
196 // {
197 // if (successCallable_ != NULL)
198 // {
199 // // TODO: throw throw new "Promise may only have a single success target"
200 // }
201 // successCallable_ = successCallable;
202 // return *this;
203 // }
204
205 // Promise& Else(ICallable* failureCallable)
206 // {
207 // if (failureCallable_ != NULL)
208 // {
209 // // TODO: throw throw new "Promise may only have a single failure target"
210 // }
211 // failureCallable_ = failureCallable;
212 // return *this;
213 // }
214
215 // };
216
217 // class IObserver : public boost::noncopyable
218 // {
219 // protected:
220 // MessageBroker& broker_;
221
222 // public:
223 // IObserver(MessageBroker& broker)
224 // : broker_(broker)
225 // {
226 // broker_.Register(*this);
227 // }
228
229 // virtual ~IObserver()
230 // {
231 // broker_.Unregister(*this);
232 // }
233
234 // };
235
236
237 // class IObservable : public boost::noncopyable
238 // {
239 // protected:
240 // MessageBroker& broker_;
241
242 // typedef std::map<int, std::set<ICallable*> > Callables;
243 // Callables callables_;
244 // public:
245
246 // IObservable(MessageBroker& broker)
247 // : broker_(broker)
248 // {
249 // }
250
251 // virtual ~IObservable()
252 // {
253 // for (Callables::const_iterator it = callables_.begin();
254 // it != callables_.end(); ++it)
255 // {
256 // for (std::set<ICallable*>::const_iterator
257 // it2 = it->second.begin(); it2 != it->second.end(); ++it2)
258 // {
259 // delete *it2;
260 // }
261 // }
262 // }
263
264 // void Register(ICallable* callable)
265 // {
266 // MessageType messageType = callable->GetMessageType();
267
268 // callables_[messageType].insert(callable);
269 // }
270
271 // void EmitMessage(const IMessage& message)
272 // {
273 // Callables::const_iterator found = callables_.find(message.GetType());
274
275 // if (found != callables_.end())
276 // {
277 // for (std::set<ICallable*>::const_iterator
278 // it = found->second.begin(); it != found->second.end(); ++it)
279 // {
280 // if (broker_.IsActive((*it)->GetObserver()))
281 // {
282 // (*it)->Apply(message);
283 // }
284 // }
285 // }
286 // }
287
288 // };
289 using namespace OrthancStone; 34 using namespace OrthancStone;
290 35
291 36
292 enum CustomMessageType 37 enum CustomMessageType
293 { 38 {
422 testCounter = 0; 167 testCounter = 0;
423 observable.EmitMessage(MyObservable::MyCustomMessage(20)); 168 observable.EmitMessage(MyObservable::MyCustomMessage(20));
424 ASSERT_EQ(20, testCounter); 169 ASSERT_EQ(20, testCounter);
425 } 170 }
426 171
172 TEST(MessageBroker2, TestMessageForwarderSimpleUseCase)
173 {
174 MessageBroker broker;
175 MyObservable observable(broker);
176 MyIntermediate intermediate(broker, observable);
177 MyObserver observer(broker);
178
179 // let the observer observers the intermediate that is actually forwarding the messages from the observable
180 intermediate.RegisterObserverCallback(new Callable<MyObserver, MyObservable::MyCustomMessage>(observer, &MyObserver::HandleCompletedMessage));
181
182 testCounter = 0;
183 observable.EmitMessage(MyObservable::MyCustomMessage(12));
184 ASSERT_EQ(12, testCounter);
185
186 // the connection is permanent; if we emit the same message again, the observer will be notified again
187 testCounter = 0;
188 observable.EmitMessage(MyObservable::MyCustomMessage(20));
189 ASSERT_EQ(20, testCounter);
190 }
191
427 TEST(MessageBroker2, TestPermanentConnectionDeleteObserver) 192 TEST(MessageBroker2, TestPermanentConnectionDeleteObserver)
428 { 193 {
429 MessageBroker broker; 194 MessageBroker broker;
430 MyObservable observable(broker); 195 MyObservable observable(broker);
431 MyObserver* observer = new MyObserver(broker); 196 MyObserver* observer = new MyObserver(broker);
443 // the connection is permanent; if we emit the same message again, the observer will be notified again 208 // the connection is permanent; if we emit the same message again, the observer will be notified again
444 testCounter = 0; 209 testCounter = 0;
445 observable.EmitMessage(MyObservable::MyCustomMessage(20)); 210 observable.EmitMessage(MyObservable::MyCustomMessage(20));
446 ASSERT_EQ(0, testCounter); 211 ASSERT_EQ(0, testCounter);
447 } 212 }
448
449 TEST(MessageBroker2, TestMessageForwarderSimpleUseCase)
450 {
451 MessageBroker broker;
452 MyObservable observable(broker);
453 MyIntermediate intermediate(broker, observable);
454 MyObserver observer(broker);
455
456 // let the observer observers the intermediate that is actually forwarding the messages from the observable
457 intermediate.RegisterObserverCallback(new Callable<MyObserver, MyObservable::MyCustomMessage>(observer, &MyObserver::HandleCompletedMessage));
458
459 testCounter = 0;
460 observable.EmitMessage(MyObservable::MyCustomMessage(12));
461 ASSERT_EQ(12, testCounter);
462
463 // the connection is permanent; if we emit the same message again, the observer will be notified again
464 testCounter = 0;
465 observable.EmitMessage(MyObservable::MyCustomMessage(20));
466 ASSERT_EQ(20, testCounter);
467 }
468
469 213
470 TEST(MessageBroker2, TestMessageForwarderDeleteIntermediate) 214 TEST(MessageBroker2, TestMessageForwarderDeleteIntermediate)
471 { 215 {
472 MessageBroker broker; 216 MessageBroker broker;
473 MyObservable observable(broker); 217 MyObservable observable(broker);
560 testCounter = 0; 304 testCounter = 0;
561 source.CompleteSomethingAsyncWithFailure(15); 305 source.CompleteSomethingAsyncWithFailure(15);
562 ASSERT_EQ(0, testCounter); 306 ASSERT_EQ(0, testCounter);
563 } 307 }
564 308
309 #if __cplusplus >= 201103L
310
311 #include <functional>
312
313 namespace OrthancStone {
314
315 template <typename TMessage>
316 class LambdaCallable : public MessageHandler<TMessage>
317 {
318 private:
319
320 IObserver& observer_;
321 std::function<void (const TMessage&)> lambda_;
322
323 public:
324 LambdaCallable(IObserver& observer,
325 std::function<void (const TMessage&)> lambdaFunction) :
326 observer_(observer),
327 lambda_(lambdaFunction)
328 {
329 }
330
331 virtual void Apply(const IMessage& message)
332 {
333 lambda_(dynamic_cast<const TMessage&>(message));
334 }
335
336 virtual MessageType GetMessageType() const
337 {
338 return static_cast<MessageType>(TMessage::Type);
339 }
340
341 virtual IObserver* GetObserver() const
342 {
343 return &observer_;
344 }
345 };
346
347
348 }
349
350 TEST(MessageBroker2, TestLambdaSimpleUseCase)
351 {
352 MessageBroker broker;
353 MyObservable observable(broker);
354 MyObserver* observer = new MyObserver(broker);
355
356 // create a permanent connection between an observable and an observer
357 observable.RegisterObserverCallback(new LambdaCallable<MyObservable::MyCustomMessage>(*observer, [&](const MyObservable::MyCustomMessage& message) {testCounter += 2 * message.payload_;}));
358
359 testCounter = 0;
360 observable.EmitMessage(MyObservable::MyCustomMessage(12));
361 ASSERT_EQ(24, testCounter);
362
363 // delete the observer and check that the callback is not called anymore
364 delete observer;
365
366 // the connection is permanent; if we emit the same message again, the observer will be notified again
367 testCounter = 0;
368 observable.EmitMessage(MyObservable::MyCustomMessage(20));
369 ASSERT_EQ(0, testCounter);
370 }
371
372 namespace {
373 class MyObserverWithLambda : public IObserver {
374 private:
375 int multiplier_; // this is a private variable we want to access in a lambda
376
377 public:
378 MyObserverWithLambda(MessageBroker& broker, int multiplier, MyObservable& observable)
379 : IObserver(broker),
380 multiplier_(multiplier)
381 {
382 // register a callable to a lambda that access private members
383 observable.RegisterObserverCallback(new LambdaCallable<MyObservable::MyCustomMessage>(*this, [this](const MyObservable::MyCustomMessage& message) {
384 testCounter += multiplier_ * message.payload_;
385 }));
386
387 }
388 };
389 }
390
391 TEST(MessageBroker2, TestLambdaCaptureThisAndAccessPrivateMembers)
392 {
393 MessageBroker broker;
394 MyObservable observable(broker);
395 MyObserverWithLambda* observer = new MyObserverWithLambda(broker, 3, observable);
396
397 testCounter = 0;
398 observable.EmitMessage(MyObservable::MyCustomMessage(12));
399 ASSERT_EQ(36, testCounter);
400
401 // delete the observer and check that the callback is not called anymore
402 delete observer;
403
404 // the connection is permanent; if we emit the same message again, the observer will be notified again
405 testCounter = 0;
406 observable.EmitMessage(MyObservable::MyCustomMessage(20));
407 ASSERT_EQ(0, testCounter);
408 }
409
410 #endif // C++ 11