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