Mercurial > hg > orthanc-stone
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 |