1 /*
2 * SObjectizer-5
3 */
4
5 /*!
6 * \since
7 * v.5.5.13
8 *
9 * \file
10 * \brief Various stuff for creation of event handlers.
11 */
12
13 #pragma once
14
15 #include <so_5/exception.hpp>
16 #include <so_5/ret_code.hpp>
17
18 #include <so_5/details/lambda_traits.hpp>
19 #include <so_5/details/always_false.hpp>
20
21 #include <so_5/execution_demand.hpp>
22 #include <so_5/message_handler_format_detector.hpp>
23 #include <so_5/mbox.hpp>
24
25 #include <algorithm>
26
27 namespace so_5 {
28
29 namespace details {
30
31 /*!
32 * \brief Various helpers for event subscription.
33 *
34 * \since
35 * v.5.3.0
36 */
37 namespace event_subscription_helpers
38 {
39
40 /*!
41 * \brief Get actual agent pointer.
42 *
43 * \throw exception_t if dynamic_cast fails.
44 */
45 template< class Agent >
46 Agent *
get_actual_agent_pointer(agent_t & agent)47 get_actual_agent_pointer( agent_t & agent )
48 {
49 // Agent must have right type.
50 Agent * cast_result = dynamic_cast< Agent * >( &agent );
51
52 // Was conversion successful?
53 if( nullptr == cast_result )
54 {
55 // No. Actual type of the agent is not convertible to the Agent.
56 SO_5_THROW_EXCEPTION(
57 rc_agent_incompatible_type_conversion,
58 std::string( "Unable convert agent to type: " ) +
59 typeid(Agent).name() );
60 }
61
62 return cast_result;
63 }
64
65 /*!
66 * \brief A helper template for create an argument for event handler
67 * in the case when argument is passed as value or const reference.
68 *
69 * \note Msg can't be a type of a signal.
70 *
71 * \since
72 * v.5.5.14
73 */
74 template< typename Msg >
75 struct event_handler_arg_maker
76 {
77 using traits_type = message_payload_type< Msg >;
78
79 static void
ensure_appropriate_typeso_5::details::event_subscription_helpers::event_handler_arg_maker80 ensure_appropriate_type()
81 {
82 ensure_not_signal< Msg >();
83 }
84
85 static const Msg &
make_argso_5::details::event_subscription_helpers::event_handler_arg_maker86 make_arg( message_ref_t & mf )
87 {
88 auto msg = message_payload_type< Msg >::extract_payload_ptr( mf );
89
90 return *msg;
91 }
92 };
93
94 /*!
95 * \brief A helper template for create an argument for event handler
96 * in the case when argument is passed as message hood.
97 * \since
98 * v.5.5.14
99 */
100 template< typename Msg >
101 struct event_handler_arg_maker< mhood_t< Msg > >
102 {
103 using traits_type = message_payload_type< Msg >;
104
105 static void
ensure_appropriate_typeso_5::details::event_subscription_helpers::event_handler_arg_maker106 ensure_appropriate_type()
107 {
108 }
109
110 static mhood_t< Msg >
make_argso_5::details::event_subscription_helpers::event_handler_arg_maker111 make_arg( message_ref_t & mf )
112 {
113 return mhood_t< Msg >{ mf };
114 }
115 };
116
117 /*!
118 * \brief A helper template for create an argument for event handler
119 * in the case when argument is passed as message hood.
120 *
121 * \since
122 * v.5.5.19
123 */
124 template< typename Msg >
125 struct event_handler_arg_maker< mhood_t< immutable_msg<Msg> > >
126 {
127 using traits_type = message_payload_type< immutable_msg<Msg> >;
128
129 static void
ensure_appropriate_typeso_5::details::event_subscription_helpers::event_handler_arg_maker130 ensure_appropriate_type()
131 {
132 }
133
134 static mhood_t< immutable_msg<Msg> >
make_argso_5::details::event_subscription_helpers::event_handler_arg_maker135 make_arg( message_ref_t & mf )
136 {
137 return { mf };
138 }
139 };
140
141 /*!
142 * \brief A helper template for create an argument for event handler
143 * in the case when argument is passed as message hood.
144 *
145 * \since
146 * v.5.5.19
147 */
148 template< typename Msg >
149 struct event_handler_arg_maker< mhood_t< mutable_msg<Msg> > >
150 {
151 using traits_type = message_payload_type< mutable_msg< Msg > >;
152
153 static void
ensure_appropriate_typeso_5::details::event_subscription_helpers::event_handler_arg_maker154 ensure_appropriate_type()
155 {
156 ensure_not_signal<Msg>();
157 }
158
159 static mhood_t< mutable_msg<Msg> >
make_argso_5::details::event_subscription_helpers::event_handler_arg_maker160 make_arg( message_ref_t & mf )
161 {
162 return { mf };
163 }
164 };
165
166 /*
167 * Disable usage of mutable_msg<Msg> as parameter of an event handler.
168 */
169 template< typename Msg >
170 struct event_handler_arg_maker< mutable_msg<Msg> >
171 {
172 using traits_type = message_payload_type< mutable_msg<Msg> >;
173
174 static void
ensure_appropriate_typeso_5::details::event_subscription_helpers::event_handler_arg_maker175 ensure_appropriate_type()
176 {
177 static_assert(always_false<Msg>::value,
178 "mutable_msg<T> can't be used as type of event handler parameter");
179 }
180
181 static mutable_msg<Msg>
make_argso_5::details::event_subscription_helpers::event_handler_arg_maker182 make_arg( message_ref_t & ) { return {}; }
183 };
184
185 /*
186 * Disable usage of mutable_msg<Msg> as parameter of an event handler.
187 */
188 template< typename Msg >
189 struct event_handler_arg_maker< immutable_msg<Msg> >
190 {
191 using traits_type = message_payload_type< immutable_msg<Msg> >;
192
193 static void
ensure_appropriate_typeso_5::details::event_subscription_helpers::event_handler_arg_maker194 ensure_appropriate_type()
195 {
196 static_assert(always_false<Msg>::value,
197 "immutable_msg<T> can't be used as type of event handler parameter");
198 }
199
200 static immutable_msg<Msg>
make_argso_5::details::event_subscription_helpers::event_handler_arg_maker201 make_arg( message_ref_t & ) { return {}; }
202 };
203
204
205 /*!
206 * \brief A helper for setting a result to a promise.
207 * \since
208 * v.5.5.14
209 */
210 template< typename R, typename L >
211 void
set_promise(std::promise<R> & to,L result_provider)212 set_promise( std::promise< R > & to, L result_provider )
213 {
214 to.set_value( result_provider() );
215 }
216
217 /*!
218 * \since
219 * v.5.5.14
220 *
221 * \brief A helper for setting a result to a promise<void>.
222 */
223 template< typename L >
224 void
set_promise(std::promise<void> & to,L result_provider)225 set_promise( std::promise< void > & to, L result_provider )
226 {
227 result_provider();
228 to.set_value();
229 }
230
231 /*!
232 * \brief Helper template for creation of event handler with actual
233 * argument.
234 *
235 * \since
236 * v.5.5.14
237 */
238 template< typename Handler_Type, typename Result, typename Arg >
239 msg_type_and_handler_pair_t
make_handler_with_arg(Handler_Type lambda)240 make_handler_with_arg( Handler_Type lambda )
241 {
242 using arg_maker = event_handler_arg_maker< Arg >;
243
244 arg_maker::ensure_appropriate_type();
245
246 auto method = [lambda](message_ref_t & message_ref) mutable
247 {
248 lambda( arg_maker::make_arg( message_ref ) );
249 };
250
251 return msg_type_and_handler_pair_t{
252 arg_maker::traits_type::subscription_type_index(),
253 method,
254 arg_maker::traits_type::mutability() };
255 }
256
257 /*!
258 * \brief A function for creation event handler.
259 *
260 * \since
261 * v.5.6.0
262 */
263 template< class Lambda >
264 details::msg_type_and_handler_pair_t
make_handler_from_lambda_of_free_function(Lambda && lambda)265 make_handler_from_lambda_of_free_function( Lambda && lambda )
266 {
267 using namespace so_5::details::lambda_traits;
268 using namespace so_5::details::event_subscription_helpers;
269
270 using Traits = traits< typename std::decay< Lambda >::type >;
271 using Result = typename Traits::result_type;
272 using Message = typename Traits::argument_type;
273 using Transformed_Lambda = typename Traits::pass_by_type;
274
275 return make_handler_with_arg< Transformed_Lambda, Result, Message >(
276 std::forward< Lambda >(lambda) );
277 }
278
279 /*!
280 * \brief Helper template for creation of event handler with actual
281 * argument.
282 *
283 * \since
284 * v.5.5.14
285 */
286 template< typename Agent, typename Method_Pointer >
287 typename std::enable_if<
288 is_agent_method_pointer<
289 method_arity::unary,
290 Method_Pointer>::value,
291 msg_type_and_handler_pair_t >::type
make_handler_with_arg_for_agent(Agent * agent,Method_Pointer pfn)292 make_handler_with_arg_for_agent(
293 Agent * agent,
294 Method_Pointer pfn )
295 {
296 using pfn_traits = is_agent_method_pointer<method_arity::unary, Method_Pointer>;
297
298 static_assert( std::is_same<Agent, typename pfn_traits::agent_type>::value,
299 "Agent type must be the same" );
300
301 using arg_maker = event_handler_arg_maker<
302 typename so_5::details::lambda_traits::plain_argument_type<
303 typename pfn_traits::argument_type>::type >;
304
305 arg_maker::ensure_appropriate_type();
306
307 auto method = [agent, pfn](message_ref_t & message_ref)
308 {
309 (agent->*pfn)( arg_maker::make_arg( message_ref ) );
310 };
311
312 return msg_type_and_handler_pair_t{
313 arg_maker::traits_type::subscription_type_index(),
314 method,
315 arg_maker::traits_type::mutability() };
316 }
317
318 /*!
319 * \brief Ensure that mutability of message is compatible with
320 * mutability of target mbox.
321 *
322 * \since
323 * v.5.5.21
324 */
325 inline void
ensure_handler_can_be_used_with_mbox(const::so_5::details::msg_type_and_handler_pair_t & handler,const::so_5::mbox_t & target_mbox)326 ensure_handler_can_be_used_with_mbox(
327 const ::so_5::details::msg_type_and_handler_pair_t & handler,
328 const ::so_5::mbox_t & target_mbox )
329 {
330 if( message_mutability_t::mutable_message == handler.m_mutability &&
331 mbox_type_t::multi_producer_multi_consumer == target_mbox->type() )
332 SO_5_THROW_EXCEPTION( rc_subscription_to_mutable_msg_from_mpmc_mbox,
333 std::string( "subscription to mutable message from MPMC mbox "
334 "is disabled, msg_type=" )
335 + handler.m_msg_type.name() );
336 }
337
338 } /* namespace event_subscription_helpers */
339
340 } /* namespace details */
341
342 //
343 // preprocess_agent_event_handler
344 //
345 /*!
346 * \brief Do preprocessing and some verification of event handler
347 * and return msg_type_and_handler_pair for it.
348 *
349 * This overload is intended to be used for pointers to members.
350 *
351 * \note
352 * Throws is handler can't be used with this type of mbox
353 * (for example: handler is for mutable message but mbox is
354 * Multi-Consumer one).
355 *
356 * \since
357 * v.5.5.21
358 */
359 template< typename Method_Pointer >
360 typename std::enable_if<
361 details::is_agent_method_pointer<
362 details::method_arity::unary,
363 Method_Pointer>::value,
364 details::msg_type_and_handler_pair_t >::type
preprocess_agent_event_handler(const mbox_t & mbox,agent_t & agent,Method_Pointer pfn)365 preprocess_agent_event_handler(
366 const mbox_t & mbox,
367 agent_t & agent,
368 Method_Pointer pfn )
369 {
370 using namespace details::event_subscription_helpers;
371
372 using agent_type =
373 typename details::is_agent_method_pointer<
374 details::method_arity::unary, Method_Pointer>::agent_type;
375
376 // Agent must have right type.
377 auto cast_result = get_actual_agent_pointer< agent_type >( agent );
378
379 const auto ev = make_handler_with_arg_for_agent( cast_result, pfn );
380
381 ensure_handler_can_be_used_with_mbox( ev, mbox );
382
383 return ev;
384 }
385
386 //
387 // preprocess_agent_event_handler
388 //
389 /*!
390 * \brief Do preprocessing and some verification of event handler
391 * and return msg_type_and_handler_pair for it.
392 *
393 * This overload is intended to be used for lambdas or functional objects.
394 *
395 * \attention
396 * Only lambda functions or functional objects in the following format
397 * are supported:
398 * \code
399 * ret_type (message_type);
400 * ret_type (const message_type &);
401 * ret_type (mhood_t<message_type>);
402 * \endcode
403 *
404 * \since
405 * v.5.5.21
406 */
407 template< typename Lambda >
408 typename std::enable_if<
409 details::lambda_traits::is_lambda<Lambda>::value,
410 details::msg_type_and_handler_pair_t >::type
preprocess_agent_event_handler(const mbox_t & mbox,agent_t &,Lambda && lambda)411 preprocess_agent_event_handler(
412 const mbox_t & mbox,
413 agent_t & /*agent*/,
414 Lambda && lambda )
415 {
416 using namespace details::event_subscription_helpers;
417
418 const auto ev = make_handler_from_lambda_of_free_function(
419 std::forward<Lambda>(lambda) );
420
421 ensure_handler_can_be_used_with_mbox( ev, mbox );
422
423 return ev;
424 }
425
426 namespace details {
427
428 //
429 // handlers_bunch_basics_t
430 //
431 /*!
432 * \brief Basic part of handlers_bunch implementation.
433 *
434 * \note This part is not depends on template parameters.
435 * \since
436 * v.5.5.13
437 */
438 struct handlers_bunch_basics_t
439 {
440 /*!
441 * \brief Preparation of message handlers vector.
442 *
443 * Message handlers vector must be ordered by msg_type.
444 * And must not contain two or more handlers for the same msg_type.
445 *
446 * Vector to be processed is defined by range [left, right).
447 *
448 * \throw so_5::exception_t if there are several handlers for
449 * the same message type.
450 */
451 static inline void
prepare_handlersso_5::details::handlers_bunch_basics_t452 prepare_handlers(
453 msg_type_and_handler_pair_t * left,
454 msg_type_and_handler_pair_t * right )
455 {
456 std::sort( left, right );
457 auto duplicate = std::adjacent_find( left, right );
458 if( duplicate != right )
459 SO_5_THROW_EXCEPTION( rc_several_handlers_for_one_message_type,
460 std::string( "several handlers are defined for message; "
461 "msg_type: " ) + duplicate->m_msg_type.name() );
462 }
463
464 /*!
465 * \brief Find and exec message handler.
466 *
467 * Message handlers vector to be used in handling of message
468 * is defined by range [left, right).
469 *
470 * \note The message handler vector [left, right) must be previously
471 * prepared by prepare_handlers().
472 *
473 * \retval true if handler has been found
474 * \retval false if handler has not been found.
475 */
476 SO_5_FUNC
477 static bool
478 find_and_use_handler(
479 const msg_type_and_handler_pair_t * left,
480 const msg_type_and_handler_pair_t * right,
481 const std::type_index & msg_type,
482 message_ref_t & message );
483 };
484
485 //
486 // handlers_bunch
487 //
488 /*!
489 * \brief Template class for storing bunch of message handlers.
490 * \since
491 * v.5.5.13
492 */
493 template< std::size_t N >
494 class handlers_bunch_t : private handlers_bunch_basics_t
495 {
496 //! Vector of message handlers.
497 /*!
498 * Will be ordered by msg_type after invoking prepare() method.
499 */
500 msg_type_and_handler_pair_t m_handlers[ N ];
501
502 public :
handlers_bunch_t()503 handlers_bunch_t()
504 {}
505
506 //! Add another handler to the specified index.
507 void
add_handler(std::size_t index,msg_type_and_handler_pair_t && handler)508 add_handler(
509 //! Index for new handler.
510 std::size_t index,
511 //! Message handler to be added.
512 msg_type_and_handler_pair_t && handler )
513 {
514 m_handlers[ index ] = std::move(handler);
515 }
516
517 //! Prepare bunch to use with actual messages.
518 /*!
519 * \note This method must be called only after all handlers are
520 * stored in m_handlers vector.
521 */
522 void
prepare()523 prepare()
524 {
525 prepare_handlers( m_handlers, m_handlers + N );
526 }
527
528 //! Find handler for a message and execute it.
529 /*!
530 * \retval true if handler was found.
531 * \retval false if handler was not found.
532 */
533 bool
handle(const std::type_index & msg_type,message_ref_t & message) const534 handle(
535 //! Type of a message or signal.
536 const std::type_index & msg_type,
537 //! Message instance to be processed.
538 message_ref_t & message ) const
539 {
540 return find_and_use_handler(
541 m_handlers, m_handlers + N,
542 msg_type,
543 message );
544 }
545 };
546
547 /*!
548 * \brief A specialization of handlers_bunch_t for the case when there
549 * is no any handlers.
550 * \since
551 * v.5.5.13
552 */
553 template<>
554 class handlers_bunch_t< 0 >
555 {
556 public :
handlers_bunch_t()557 handlers_bunch_t()
558 {}
559
560 void
prepare()561 prepare()
562 {
563 /* NOTHING TO DO! */
564 }
565
566 bool
handle(const std::type_index &,message_ref_t &) const567 handle(
568 const std::type_index & /*msg_type*/,
569 message_ref_t & /*message*/ ) const
570 {
571 return false;
572 }
573 };
574
575 //
576 // fill_handlers_bunch
577 //
578
579 template< typename Bunch >
580 void
fill_handlers_bunch(Bunch & bunch,std::size_t)581 fill_handlers_bunch( Bunch & bunch, std::size_t )
582 {
583 bunch.prepare();
584 }
585
586 template< typename Bunch, typename... Others >
587 void
fill_handlers_bunch(Bunch & bunch,std::size_t index,msg_type_and_handler_pair_t && handler,Others &&...other_handlers)588 fill_handlers_bunch(
589 //! What to fill.
590 Bunch & bunch,
591 //! An index for next handler.
592 std::size_t index,
593 //! Next handler to be inserted.
594 msg_type_and_handler_pair_t && handler,
595 //! All other handlers.
596 Others &&... other_handlers )
597 {
598 bunch.add_handler( index, std::move(handler) );
599 fill_handlers_bunch( bunch, index + 1,
600 std::forward< Others >(other_handlers)... );
601 }
602
603 template< typename Bunch, typename Lambda, typename... Others >
604 void
fill_handlers_bunch(Bunch & bunch,std::size_t index,Lambda && lambda,Others &&...other_handlers)605 fill_handlers_bunch(
606 //! What to fill.
607 Bunch & bunch,
608 //! An index for next handler.
609 std::size_t index,
610 //! Next handler to be inserted.
611 Lambda && lambda,
612 //! All other handlers.
613 Others &&... other_handlers )
614 {
615 using namespace event_subscription_helpers;
616
617 bunch.add_handler( index,
618 make_handler_from_lambda_of_free_function(
619 std::forward<Lambda>( lambda ) ) );
620
621 fill_handlers_bunch( bunch, index + 1,
622 std::forward< Others >(other_handlers)... );
623 }
624
625 } /* namespace details */
626
627 } /* namespace so_5 */
628
629