SObjectizer-5 Extra
Loading...
Searching...
No Matches
composite.hpp
Go to the documentation of this file.
1/*!
2 * \file
3 * \brief Implementation of composite mbox.
4 *
5 * \since v.1.5.2
6 */
7
8#pragma once
9
10#include <so_5_extra/error_ranges.hpp>
11
12#include <so_5/impl/msg_tracing_helpers.hpp>
13
14#include <so_5/environment.hpp>
15#include <so_5/mbox.hpp>
16
17#include <algorithm>
18#include <map>
19#include <variant>
20#include <vector>
21
22namespace so_5 {
23
24namespace extra {
25
26namespace mboxes {
27
28namespace composite {
29
30namespace errors {
31
32/*!
33 * \brief An attempt to send message of a type for that there is no a target.
34 *
35 * \since v.1.5.2
36 */
39
40/*!
41 * \brief An attempt to add another target for a message type.
42 *
43 * Just one destination mbox can be specified for a message type.
44 * An attempt to add another destination mbox will lead to this error code.
45 *
46 * \since v.1.5.2
47 */
50
51/*!
52 * \brief An attempt to add MPMC target to MPSC mbox.
53 *
54 * If composite mbox is created as MPSC mbox then a MPMC mbox can't be
55 * added as a destination.
56 *
57 * \since v.1.5.2
58 */
61
62/*!
63 * \brief An attempt to use nullptr as the default destination mbox.
64 *
65 * An attempt use null pointer to mbox as the default destination mbox.
66 * For example, an empty mbox_t instance is passed to
67 * redirect_to_if_not_found() function.
68 *
69 * \since v.1.5.2
70 */
73
74} /* namespace errors */
75
76/*!
77 * \brief Description of a case when messages of unknown type have to be
78 * redirected to another mbox.
79 *
80 * \since v.1.5.2
81 */
83 {
84 //! Destination for message of unknown type.
86
87 [[nodiscard]]
88 static mbox_t
90 {
91 if( !dest )
92 SO_5_THROW_EXCEPTION(
94 "nullptr can't be used as the default destination mbox" );
95
96 return dest;
97 }
98
99 public:
100 //! Initializing constructor.
104
105 //! Getter.
106 [[nodiscard]]
107 const mbox_t &
108 dest() const noexcept { return m_dest; }
109 };
110
111/*!
112 * \brief Description of a case when an exception has to be thrown if
113 * the type of a message is unknown.
114 *
115 * An exception will also be thrown on attempts to subscribe to and/or set
116 * delivery filter for unknown message type.
117 *
118 * \since v.1.5.2
119 */
122
123/*!
124 * \brief Description of a case when a message of unknown type has to be dropped.
125 *
126 * Attempts to make subscriptions and/or set delivery filters for unknown
127 * message type will be silently ignored.
128 *
129 * \since v.1.5.2
130 */
133
134/*!
135 * \brief Type that describes the reaction to a message of unknown type.
136 *
137 * \since v.1.5.2
138 */
143 >;
144
145/*!
146 * \brief Helper function to set a reaction to unknown message type.
147 *
148 * Message of unknown type has to be redirected to specified mbox.
149 * Subscriptions and delivery filters for unknown type have also be
150 * handled by \a dest_mbox.
151 *
152 * Usage example:
153 * \code
154 * using namespace so_5::extra::mboxes::composite;
155 *
156 * auto mbox = multi_consumer_builder(redirect_to_if_not_found(default_mbox))
157 * .add<first_message>(first_mbox)
158 * .add<second_message>(second_mbox)
159 * .make(env);
160 * \endcode
161 *
162 * \since v.1.5.2
163 */
164[[nodiscard]]
167 {
168 return { redirect_to_if_not_found_case_t{ dest_mbox } };
169 }
170
171/*!
172 * \brief Helper function to set a reaction to unknown message type.
173 *
174 * Attempt to use unknown message type (e.g. sending of a message,
175 * subscription or settting delivery filter) should lead to raising
176 * an exception (an instance of so_5::exception_t will be thrown).
177 *
178 * Usage example:
179 * \code
180 * using namespace so_5::extra::mboxes::composite;
181 *
182 * auto mbox = multi_consumer_builder(throw_if_not_found(default_mbox))
183 * .add<first_message>(first_mbox)
184 * .add<second_message>(second_mbox)
185 * .make(env);
186 * \endcode
187 *
188 * \since v.1.5.2
189 */
190[[nodiscard]]
193 {
194 return { throw_if_not_found_case_t{} };
195 }
196
197/*!
198 * \brief Helper function to set a reaction to unknown message type.
199 *
200 * Attempt to use unknown message type (e.g. sending of a message,
201 * subscription or settting delivery filter) should be silently ignored.
202 *
203 * Usage example:
204 * \code
205 * using namespace so_5::extra::mboxes::composite;
206 *
207 * auto mbox = multi_consumer_builder(drop_if_not_found(default_mbox))
208 * .add<first_message>(first_mbox)
209 * .add<second_message>(second_mbox)
210 * .make(env);
211 * \endcode
212 *
213 * \since v.1.5.2
214 */
215[[nodiscard]]
218 {
219 return { drop_if_not_found_case_t{} };
220 }
221
222// Forward declaration.
223class mbox_builder_t;
224
225namespace impl {
226
227/*!
228 * \brief Description of one target.
229 *
230 * Contains info about message type and destination mbox for messages
231 * of that type.
232 *
233 * \since v.1.5.2
234 */
236 {
237 //! Type for that the destination has to be used.
239 //! The destination for messages for that type.
241
242 //! Initializing constructor.
243 target_t( std::type_index msg_type, mbox_t dest )
245 , m_dest{ std::move(dest) }
246 {}
247 };
248
249/*!
250 * \brief Type of container for holding targets.
251 *
252 * \since v.1.5.2
253 */
255
256/*!
257 * \brief Comparator function object to be used with std::lower_bound.
258 *
259 * \since v.1.5.2
260 */
261inline const auto target_compare =
262 []( const target_t & target, const std::type_index & msg_type ) -> bool {
263 return target.m_msg_type < msg_type;
264 };
265
267{
268
269/*!
270 * \brief Function object to be used with std::visit.
271 *
272 * Implement logic of so_5::abstract_message_box_t::subscribe_event_handler()
273 * in a case when message type is unknown.
274 */
276 {
279
280 public:
282 const std::type_index & msg_type,
283 abstract_message_sink_t & subscriber )
286 {}
287
288 void
290 {
291 c.dest()->subscribe_event_handler(
292 m_msg_type,
293 m_subscriber );
294 }
295
296 void
298 {
299 SO_5_THROW_EXCEPTION(
300 errors::rc_no_sink_for_message_type,
301 "no destination for this message type, "
302 "msg_type=" + std::string(m_msg_type.name()) );
303 }
304
305 void
307 {
308 // Nothing to do.
309 }
310 };
311
312/*!
313 * \brief Function object to be used with std::visit.
314 *
315 * Implement logic of so_5::abstract_message_box_t::unsubscribe_event_handler()
316 * in a case when message type is unknown.
317 */
319 {
322
323 public:
325 const std::type_index & msg_type,
326 abstract_message_sink_t & subscriber )
329 {}
330
331 void
332 operator()( const redirect_to_if_not_found_case_t & c ) const noexcept
333 {
334 c.dest()->unsubscribe_event_handler(
335 m_msg_type,
336 m_subscriber );
337 }
338
339 void
341 {
342 // Just ignore that case.
343 }
344
345 void
347 {
348 // Nothing to do.
349 }
350 };
351
352/*!
353 * \brief Function object to be used with std::visit.
354 *
355 * Implement logic of so_5::abstract_message_box_t::do_deliver_message()
356 * in a case when message type is unknown.
357 */
358template< typename Tracer >
360 {
361 Tracer & m_tracer;
365 unsigned int m_overlimit_deep;
366
367 public:
369 Tracer & tracer,
370 message_delivery_mode_t delivery_mode,
371 const std::type_index & msg_type,
372 const message_ref_t & msg,
373 unsigned int overlimit_deep )
374 : m_tracer{ tracer }
377 , m_msg{ msg }
379 {}
380
381 void
383 {
384 using namespace ::so_5::impl::msg_tracing_helpers::details;
385
387 "redirect_to_default_destination",
389
393 m_msg,
395 }
396
397 void
399 {
401 "no_destination.throw_exception" );
404 "no destination for this message type, "
405 "msg_type=" + std::string(m_msg_type.name()) );
406 }
407
408 void
410 {
412 "no_destination.drop_message" );
413 }
414 };
415
416/*!
417 * \brief Function object to be used with std::visit.
418 *
419 * Implement logic of so_5::abstract_message_box_t::set_delivery_filter()
420 * in a case when message type is unknown.
421 */
423 {
427
428 public:
430 const std::type_index & msg_type,
431 const delivery_filter_t & filter,
432 abstract_message_sink_t & subscriber )
434 , m_filter{ filter }
436 {}
437
438 void
440 {
441 c.dest()->set_delivery_filter(
442 m_msg_type,
443 m_filter,
444 m_subscriber );
445 }
446
447 void
449 {
450 SO_5_THROW_EXCEPTION(
451 errors::rc_no_sink_for_message_type,
452 "no destination for this message type, "
453 "msg_type=" + std::string(m_msg_type.name()) );
454 }
455
456 void
458 {
459 // Nothing to do.
460 }
461 };
462
463/*!
464 * \brief Function object to be used with std::visit.
465 *
466 * Implement logic of so_5::abstract_message_box_t::drop_delivery_filter()
467 * in a case when message type is unknown.
468 */
470 {
473
474 public:
476 const std::type_index & msg_type,
477 abstract_message_sink_t & subscriber ) noexcept
480 {}
481
482 void
483 operator()( const redirect_to_if_not_found_case_t & c ) const noexcept
484 {
485 c.dest()->drop_delivery_filter(
486 m_msg_type,
487 m_subscriber );
488 }
489
490 void
491 operator()( const throw_if_not_found_case_t & ) const noexcept
492 {
493 // Just ignore that case.
494 }
495
496 void
497 operator()( const drop_if_not_found_case_t & ) const noexcept
498 {
499 // Nothing to do.
500 }
501 };
502
503} /* namespace unknown_msg_type_handlers */
504
505/*!
506 * \brief Mbox data that doesn't depend on template parameters.
507 *
508 * \since v.1.5.2
509 */
511 {
512 //! SObjectizer Environment to work in.
514
515 //! ID of this mbox.
517
518 //! Type of the mbox.
520
521 //! What to do with messages of unknown type.
523
524 //! Registered targets.
526
528 environment_t & env,
529 mbox_id_t id,
530 mbox_type_t mbox_type,
531 type_not_found_reaction_t unknown_type_reaction,
532 target_container_t targets )
533 : m_env_ptr{ &env }
534 , m_id{ id }
538 {}
539 };
540
541/*!
542 * \brief Actual implementation of composite mbox.
543 *
544 * \note
545 * An instance of that class is immutable. It doesn't allow modification of its
546 * state. It makes the internals of actual_mbox_t thread safe.
547 *
548 * \since v.1.5.2
549 */
550template< typename Tracing_Base >
551class actual_mbox_t final
552 : public abstract_message_box_t
553 , private Tracing_Base
554 {
556
557 /*!
558 * \brief Initializing constructor.
559 *
560 * \tparam Tracing_Args parameters for Tracing_Base constructor
561 * (can be empty list if Tracing_Base have only the default constructor).
562 */
563 template< typename... Tracing_Args >
565 //! Data for mbox that doesn't depend on template parameters.
566 mbox_data_t mbox_data,
567 Tracing_Args &&... tracing_args )
568 : Tracing_Base{ std::forward<Tracing_Args>(tracing_args)... }
569 , m_data{ std::move(mbox_data) }
570 {}
571
572 public:
573 ~actual_mbox_t() override = default;
574
576 id() const override
577 {
578 return this->m_data.m_id;
579 }
580
581 void
583 const std::type_index & msg_type,
584 abstract_message_sink_t & subscriber ) override
585 {
586 const auto opt_target = try_find_target_for_msg_type( msg_type );
587 if( opt_target )
588 {
589 (*opt_target)->m_dest->subscribe_event_handler(
590 msg_type,
591 subscriber );
592 }
593 else
594 {
595 std::visit(
596 unknown_msg_type_handlers::subscribe_event_t{
597 msg_type,
598 subscriber },
599 this->m_data.m_unknown_type_reaction );
600 }
601 }
602
603 void
605 const std::type_index & msg_type,
606 abstract_message_sink_t & subscriber ) noexcept override
607 {
608 const auto opt_target = try_find_target_for_msg_type( msg_type );
609 if( opt_target )
610 {
611 (*opt_target)->m_dest->unsubscribe_event_handler(
612 msg_type,
613 subscriber );
614 }
615 else
616 {
617 std::visit(
618 unknown_msg_type_handlers::unsubscribe_event_t{
619 msg_type,
620 subscriber },
621 this->m_data.m_unknown_type_reaction );
622 }
623 }
624
625 std::string
626 query_name() const override
627 {
628 std::ostringstream s;
629 s << "<mbox:type=COMPOSITE";
630
631 switch( this->m_data.m_mbox_type )
632 {
633 case mbox_type_t::multi_producer_multi_consumer:
634 s << "(MPMC)";
635 break;
636
637 case mbox_type_t::multi_producer_single_consumer:
638 s << "(MPSC)";
639 break;
640 }
641
642 s << ":id=" << this->m_data.m_id << ">";
643
644 return s.str();
645 }
646
648 type() const override
649 {
650 return this->m_data.m_mbox_type;
651 }
652
653 void
655 message_delivery_mode_t delivery_mode,
656 const std::type_index & msg_type,
657 const message_ref_t & message,
658 unsigned int redirection_deep ) override
659 {
660 ensure_immutable_message( msg_type, message );
661
662 typename Tracing_Base::deliver_op_tracer tracer{
663 *this, // as Tracing_base
664 *this, // as abstract_message_box_t
665 "deliver_message",
666 delivery_mode,
667 msg_type, message, redirection_deep };
668
669 const auto opt_target = try_find_target_for_msg_type( msg_type );
670 if( opt_target )
671 {
672 using namespace ::so_5::impl::msg_tracing_helpers::details;
673
674 tracer.make_trace(
675 "redirect_to_destination",
676 mbox_as_msg_destination{ *( (*opt_target)->m_dest ) } );
677
678 (*opt_target)->m_dest->do_deliver_message(
679 delivery_mode,
680 msg_type,
681 message,
682 redirection_deep );
683 }
684 else
685 {
686 using handler_t = unknown_msg_type_handlers::deliver_message_t<
687 typename Tracing_Base::deliver_op_tracer >;
688
689 std::visit(
690 handler_t{
691 tracer,
692 delivery_mode,
693 msg_type,
694 message,
695 redirection_deep },
696 this->m_data.m_unknown_type_reaction );
697 }
698 }
699
700 void
702 const std::type_index & msg_type,
703 const delivery_filter_t & filter,
704 abstract_message_sink_t & subscriber ) override
705 {
706 const auto opt_target = try_find_target_for_msg_type( msg_type );
707 if( opt_target )
708 {
709 (*opt_target)->m_dest->set_delivery_filter(
710 msg_type,
711 filter,
712 subscriber );
713 }
714 else
715 {
716 std::visit(
717 unknown_msg_type_handlers::set_delivery_filter_t{
718 msg_type,
719 filter,
720 subscriber },
721 this->m_data.m_unknown_type_reaction );
722 }
723 }
724
725 void
727 const std::type_index & msg_type,
728 abstract_message_sink_t & subscriber ) noexcept override
729 {
730 const auto opt_target = try_find_target_for_msg_type( msg_type );
731 if( opt_target )
732 {
733 (*opt_target)->m_dest->drop_delivery_filter(
734 msg_type,
735 subscriber );
736 }
737 else
738 {
739 std::visit(
740 unknown_msg_type_handlers::drop_delivery_filter_t{
741 msg_type,
742 subscriber },
743 this->m_data.m_unknown_type_reaction );
744 }
745 }
746
748 environment() const noexcept override
749 {
750 return *(this->m_data.m_env_ptr);
751 }
752
753 private:
754 //! Mbox's data.
756
757 /*!
758 * \brief Attempt to find a target for specified message type.
759 *
760 * \note
761 * Since v.1.6.0 this method is marked as noexcept because it is
762 * called in unsubscribe_event_handler().
763 *
764 * \return empty std::optional if \a msg_type is unknown.
765 */
766 [[nodiscard]]
767 std::optional< const target_t * >
769 {
770 const auto last = end( m_data.m_targets );
771 const auto it = std::lower_bound(
772 begin( m_data.m_targets ), last,
773 msg_type,
774 target_compare );
775
776 if( !( it == last ) && msg_type == it->m_msg_type )
777 return { std::addressof( *it ) };
778 else
779 return std::nullopt;
780 }
781
782 /*!
783 * \brief Ensures that message is an immutable message.
784 *
785 * Checks mutability flag and throws an exception if message is
786 * a mutable one.
787 */
788 void
790 const std::type_index & msg_type,
791 const message_ref_t & what ) const
792 {
793 if( (mbox_type_t::multi_producer_multi_consumer ==
794 this->m_data.m_mbox_type) &&
795 (message_mutability_t::immutable_message !=
796 message_mutability( what )) )
797 SO_5_THROW_EXCEPTION(
798 so_5::rc_mutable_msg_cannot_be_delivered_via_mpmc_mbox,
799 "an attempt to deliver mutable message via MPMC mbox"
800 ", msg_type=" + std::string(msg_type.name()) );
801 }
802 };
803
804} /* namespace impl */
805
806/*!
807 * \brief Factory class for building an instance of composite mbox.
808 *
809 * Usage example:
810 * \code
811 * using namespace so_5::extra::mboxes::composite;
812 *
813 * auto mbox = single_consumer_builder(throw_if_not_found())
814 * .add<msg_first>(first_mbox)
815 * .add< so_5::mutable_msg<msg_second> >(second_mbox)
816 * .make(env);
817 * \endcode
818 *
819 * \note
820 * This class is intended to be used in just one chain of add()..make() methods.
821 * It means that code like that:
822 * \code
823 * using namespace so_5::extra::mboxes::composite;
824 *
825 * // Simplest case without storing mbox_builder_t instance.
826 * auto mbox = single_consumer_builder(throw_if_not_found())
827 * .add<msg_first>(first_mbox)
828 * .add< so_5::mutable_msg<msg_second> >(second_mbox)
829 * .make(env);
830 *
831 * // More complex case with holding temporary mbox_builder_t instance.
832 * auto my_builder = multi_consumer_builder(drop_if_not_found());
833 * my_builder.add<msg_first>(first_mbox);
834 * if(some_condition)
835 * my_builder.add<msg_second>(second_mbox);
836 * if(third_mbox_present)
837 * my_builder.add<msg_third>(third_mbox);
838 * auto mbox = my_builder.make(env);
839 * \endcode
840 * Will work in all versions of so5extra. But multiple calls to make() for
841 * the same builder object are not guaranteed to be working. It's depend
842 * on the current implementation and the implementation can change in
843 * future versions of so5extra. It means that you have to avoid code
844 * like that:
845 * \code
846 * using namespace so_5::extra::mboxes::composite;
847 *
848 * // DO NOT DO THIS!
849 * // The behaviour can change in future versions of so5extra without prior notify.
850 * auto my_builder = multi_consumer_builder(redirect_to_if_not_found(default_dest));
851 *
852 * my_builder.add<msg_first>(first_mbox);
853 * auto one = my_builder.make(env); // (1)
854 *
855 * my_builder.add<msg_second>(second_mbox);
856 * auto two = my_builder.make(env);
857 * \endcode
858 * It's not guaranteed thet my_builder will be in a valid state after point (1).
859 *
860 * \attention
861 * An instance of mbox_builder_t isn't thread safe.
862 *
863 * \note
864 * This class has a private constructor and instance of builder can be
865 * obtained only with help from builder(), single_consumer_builder(), and
866 * multi_consumer_builder() functions.
867 *
868 * \since v.1.5.2
869 */
871 {
872 friend mbox_builder_t
873 builder(
874 mbox_type_t mbox_type,
875 type_not_found_reaction_t unknown_type_reaction );
876
877 //! Initializing constructor.
879 //! Type of mbox to be created.
880 mbox_type_t mbox_type,
881 //! Reaction to a unknown message type.
882 type_not_found_reaction_t unknown_type_reaction )
885 {}
886
887 public:
888 ~mbox_builder_t() noexcept = default;
889
890 /*!
891 * \brief Add destination mbox for a message type.
892 *
893 * Usage example:
894 * \code
895 * using namespace so_5::extra::mboxes::composite;
896 *
897 * // A case with holding temporary mbox_builder_t instance.
898 * auto my_builder = multi_consumer_builder(drop_if_not_found());
899 * my_builder.add<msg_first>(first_mbox);
900 * if(some_condition)
901 * my_builder.add<msg_second>(second_mbox);
902 * if(third_mbox_present)
903 * my_builder.add<msg_third>(third_mbox);
904 * auto result_mbox = my_builder.make(env);
905 * \endcode
906 *
907 * If a type for mutable message has to be specified then
908 * so_5::mutable_msg marker should be used:
909 * \code
910 * using namespace so_5::extra::mboxes::composite;
911 *
912 * auto my_builder = single_consumer_builder(drop_if_not_found());
913 * my_builder.add< so_5::mutable_msg<msg_first> >(first_mbox);
914 * \endcode
915 *
916 * Type of mutable message can't be used if:
917 *
918 * - composite mbox is MPMC mbox;
919 * - the destination mbox is MPMC mbox;
920 *
921 * \note
922 * If builder is created to produce a MPSC composite mbox then a MPMC
923 * mbox can be added as the destination mbox, but for immutable message
924 * only. For example:
925 * \code
926 * using namespace so_5::extra::mboxes::composite;
927 *
928 * auto mpmc_dest = env.create_mbox(); // It's MPMC mbox.
929 *
930 * auto result_mbox = single_consumer_builder(throw_if_not_found())
931 * // This call is allowed because my_msg is immutable message.
932 * .add< my_msg >( mpmc_dest )
933 * ...
934 * \endcode
935 *
936 * \attention
937 * An exception will be thrown if destination mbox is already registered
938 * for Msg_Type.
939 *
940 * \tparam Msg_Type type of message to be redirected to specified mbox.
941 */
942 template< typename Msg_Type >
944 add( mbox_t dest_mbox ) &
945 {
946 // Use of mutable message type for MPMC mbox should be prohibited.
947 if constexpr( is_mutable_message< Msg_Type >::value )
948 {
949 switch( m_mbox_type )
950 {
954 "mutable message can't handled with MPMC composite, "
955 "msg_type=" + std::string(typeid(Msg_Type).name()) );
956 break;
957
959 break;
960 }
961
963 dest_mbox->type() )
964 {
967 "MPMC mbox can't be added as a target to MPSC "
968 "composite and mutable message, "
969 "msg_type=" + std::string(typeid(Msg_Type).name()) );
970 }
971 }
972
973 const auto [it, is_inserted] = m_targets.emplace(
975 std::move(dest_mbox) );
976 if( !is_inserted )
979 "message type already has a destination mbox, "
980 "msg_type=" + std::string(typeid(Msg_Type).name()) );
981
982 return *this;
983 }
984
985 /*!
986 * \brief Add destination mbox for a message type.
987 *
988 * Usage example:
989 * \code
990 * using namespace so_5::extra::mboxes::composite;
991 *
992 * // Simplest case without storing mbox_builder_t instance.
993 * auto result_mbox = single_consumer_builder(throw_if_not_found())
994 * .add<msg_first>(first_mbox)
995 * .add< so_5::mutable_msg<msg_second> >(second_mbox)
996 * .make(env);
997 * \endcode
998 *
999 * If a type for mutable message has to be specified then
1000 * so_5::mutable_msg marker should be used:
1001 * \code
1002 * using namespace so_5::extra::mboxes::composite;
1003 *
1004 * auto result_mbox = single_consumer_builder(throw_if_not_found())
1005 * .add< so_5::mutable_msg<message> >(dest_mbox)
1006 * ...
1007 * \endcode
1008 *
1009 * Type of mutable message can't be used if:
1010 *
1011 * - composite mbox is MPMC mbox;
1012 * - the destination mbox is MPMC mbox;
1013 *
1014 * \note
1015 * If builder is created to produce a MPSC composite mbox then a MPMC
1016 * mbox can be added as the destination mbox, but for immutable message
1017 * only. For example:
1018 * \code
1019 * using namespace so_5::extra::mboxes::composite;
1020 *
1021 * auto mpmc_dest = env.create_mbox(); // It's MPMC mbox.
1022 *
1023 * auto result_mbox = single_consumer_builder(throw_if_not_found())
1024 * // This call is allowed because my_msg is immutable message.
1025 * .add< my_msg >( mpmc_dest )
1026 * ...
1027 * \endcode
1028 *
1029 * \attention
1030 * An exception will be thrown if destination mbox is already registered
1031 * for Msg_Type.
1032 *
1033 * \tparam Msg_Type type of message to be redirected to specified mbox.
1034 */
1035 template< typename Msg_Type >
1036 [[nodiscard]]
1038 add( mbox_t dest_mbox ) &&
1039 {
1040 return std::move( add<Msg_Type>( std::move(dest_mbox) ) );
1041 }
1042
1043 /*!
1044 * \brief Make a composite mbox.
1045 *
1046 * The created mbox will be based on information added to builder
1047 * before calling make() method.
1048 *
1049 * Usage example:
1050 * \code
1051 * using namespace so_5::extra::mboxes::composite;
1052 *
1053 * // Simplest case without storing mbox_builder_t instance.
1054 * auto result_mbox = single_consumer_builder(throw_if_not_found())
1055 * .add<msg_first>(first_mbox)
1056 * .add< so_5::mutable_msg<msg_second> >(second_mbox)
1057 * .make(env);
1058 * \endcode
1059 *
1060 * It's guaranteed that the builder object will be in some correct
1061 * state after make() returns. It means that builder can be safely
1062 * deleted or can obtain a new value as the result of assignement.
1063 * But it isn't guaranteed ther the builder will hold values previously
1064 * stored to it by add() methods.
1065 */
1066 [[nodiscard]]
1067 mbox_t
1069 {
1070 return env.make_custom_mbox(
1071 [this]( const mbox_creation_data_t & data )
1072 {
1073 impl::mbox_data_t mbox_data{
1074 data.m_env.get(),
1075 data.m_id,
1076 m_mbox_type,
1077 std::move(m_unknown_type_reaction),
1078 targets_to_vector()
1079 };
1080 mbox_t result;
1081
1082 if( data.m_tracer.get().is_msg_tracing_enabled() )
1083 {
1084 using ::so_5::impl::msg_tracing_helpers::
1085 tracing_enabled_base;
1086 using T = impl::actual_mbox_t< tracing_enabled_base >;
1087
1088 result = mbox_t{ new T{
1089 std::move(mbox_data),
1090 data.m_tracer
1091 } };
1092 }
1093 else
1094 {
1095 using ::so_5::impl::msg_tracing_helpers::
1096 tracing_disabled_base;
1097 using T = impl::actual_mbox_t< tracing_disabled_base >;
1098
1099 result = mbox_t{ new T{ std::move(mbox_data) } };
1100 }
1101
1102 return result;
1103 } );
1104 }
1105
1106 private:
1107 /*!
1108 * \brief Type of container for holding targets.
1109 *
1110 * \note
1111 * std::map is used to simplify the implementation.
1112 */
1114
1115 //! Type of mbox to be created.
1117
1118 //! Reaction to unknown type of a message.
1120
1121 //! Container for registered targets.
1123
1124 /*!
1125 * \return A vector of targets that should be passed to impl::actual_mbox_t
1126 * constructor. That vector is guaranteed to be sorted (it means that
1127 * binary search can be used for searching message types).
1128 */
1129 [[nodiscard]]
1132 {
1133 impl::target_container_t result;
1134 result.reserve( m_targets.size() );
1135
1136 // Use the fact that items in std::map are ordered by keys.
1137 for( const auto & [k, v] : m_targets )
1138 result.emplace_back( k, v );
1139
1140 return result;
1141 }
1142 };
1143
1144/*!
1145 * \brief Factory function for making mbox_builder.
1146 *
1147 * Usage example:
1148 * \code
1149 * using namespace so_5::extra::mboxes::composite;
1150 *
1151 * auto result_mbox = builder(
1152 * so_5::mbox_type_t::multi_producer_multi_consumer,
1153 * redirect_to_if_not_found(default_mbox))
1154 * .add<msg_first>(first_mbox)
1155 * .add<msg_second>(second_mbox)
1156 * .add<msg_third>(third_mbox)
1157 * .make(env);
1158 * \endcode
1159 *
1160 * \since v.1.5.2
1161 */
1162[[nodiscard]]
1163inline mbox_builder_t
1165 //! Type of new mbox: MPMC or MPSC.
1166 mbox_type_t mbox_type,
1167 //! What to do if message type is unknown.
1168 type_not_found_reaction_t unknown_type_reaction )
1169 {
1170 return { mbox_type, std::move(unknown_type_reaction) };
1171 }
1172
1173/*!
1174 * \brief Factory function for making mbox_builder that produces MPMC composite
1175 * mbox.
1176 *
1177 * Usage example:
1178 * \code
1179 * using namespace so_5::extra::mboxes::composite;
1180 *
1181 * auto result_mbox = multi_consumer_builder(
1182 * redirect_to_if_not_found(default_mbox))
1183 * .add<msg_first>(first_mbox)
1184 * .add<msg_second>(second_mbox)
1185 * .add<msg_third>(third_mbox)
1186 * .make(env);
1187 * \endcode
1188 *
1189 * \since v.1.5.2
1190 */
1191[[nodiscard]]
1192inline mbox_builder_t
1194 //! What to do if message type is unknown.
1195 type_not_found_reaction_t unknown_type_reaction )
1196 {
1197 return builder(
1198 mbox_type_t::multi_producer_multi_consumer,
1199 std::move(unknown_type_reaction) );
1200 }
1201
1202/*!
1203 * \brief Factory function for making mbox_builder that produces MPSC composite
1204 * mbox.
1205 *
1206 * Usage example:
1207 * \code
1208 * using namespace so_5::extra::mboxes::composite;
1209 *
1210 * auto result_mbox = single_consumer_builder(
1211 * redirect_to_if_not_found(default_mbox))
1212 * .add<msg_first>(first_mbox)
1213 * .add< so_5::mutable_msg<msg_second> >(second_mbox)
1214 * .add<msg_third>(third_mbox)
1215 * .make(env);
1216 * \endcode
1217 *
1218 * \since v.1.5.2
1219 */
1220[[nodiscard]]
1221inline mbox_builder_t
1223 type_not_found_reaction_t unknown_type_reaction )
1224 {
1225 return builder(
1226 mbox_type_t::multi_producer_single_consumer,
1227 std::move(unknown_type_reaction) );
1228 }
1229
1230} /* namespace composite */
1231
1232} /* namespace mboxes */
1233
1234} /* namespace extra */
1235
1236} /* namespace so_5 */
actual_mbox_t(mbox_data_t mbox_data, Tracing_Args &&... tracing_args)
Initializing constructor.
std::optional< const target_t * > try_find_target_for_msg_type(const std::type_index &msg_type) const noexcept
Attempt to find a target for specified message type.
void subscribe_event_handler(const std::type_index &msg_type, abstract_message_sink_t &subscriber) override
void drop_delivery_filter(const std::type_index &msg_type, abstract_message_sink_t &subscriber) noexcept override
void ensure_immutable_message(const std::type_index &msg_type, const message_ref_t &what) const
Ensures that message is an immutable message.
so_5::environment_t & environment() const noexcept override
void unsubscribe_event_handler(const std::type_index &msg_type, abstract_message_sink_t &subscriber) noexcept override
void do_deliver_message(message_delivery_mode_t delivery_mode, const std::type_index &msg_type, const message_ref_t &message, unsigned int redirection_deep) override
void set_delivery_filter(const std::type_index &msg_type, const delivery_filter_t &filter, abstract_message_sink_t &subscriber) override
void operator()(const redirect_to_if_not_found_case_t &c) const
deliver_message_t(Tracer &tracer, message_delivery_mode_t delivery_mode, const std::type_index &msg_type, const message_ref_t &msg, unsigned int overlimit_deep)
void operator()(const redirect_to_if_not_found_case_t &c) const noexcept
drop_delivery_filter_t(const std::type_index &msg_type, abstract_message_sink_t &subscriber) noexcept
set_delivery_filter_t(const std::type_index &msg_type, const delivery_filter_t &filter, abstract_message_sink_t &subscriber)
void operator()(const redirect_to_if_not_found_case_t &c) const
subscribe_event_t(const std::type_index &msg_type, abstract_message_sink_t &subscriber)
unsubscribe_event_t(const std::type_index &msg_type, abstract_message_sink_t &subscriber)
void operator()(const redirect_to_if_not_found_case_t &c) const noexcept
Factory class for building an instance of composite mbox.
mbox_builder_t & add(mbox_t dest_mbox) &
Add destination mbox for a message type.
impl::target_container_t targets_to_vector() const
friend mbox_builder_t builder(mbox_type_t mbox_type, type_not_found_reaction_t unknown_type_reaction)
Factory function for making mbox_builder.
mbox_t make(environment_t &env)
Make a composite mbox.
mbox_builder_t && add(mbox_t dest_mbox) &&
Add destination mbox for a message type.
mbox_type_t m_mbox_type
Type of mbox to be created.
type_not_found_reaction_t m_unknown_type_reaction
Reaction to unknown type of a message.
mbox_builder_t(mbox_type_t mbox_type, type_not_found_reaction_t unknown_type_reaction)
Initializing constructor.
target_map_t m_targets
Container for registered targets.
Description of a case when messages of unknown type have to be redirected to another mbox.
Definition composite.hpp:83
redirect_to_if_not_found_case_t(mbox_t dest)
Initializing constructor.
mbox_t m_dest
Destination for message of unknown type.
Definition composite.hpp:85
const int mboxes_composite_errors
Starting point for errors of mboxes::composite submodule.
const int rc_null_as_default_destination_mbox
An attempt to use nullptr as the default destination mbox.
Definition composite.hpp:71
const int rc_message_type_already_has_sink
An attempt to add another target for a message type.
Definition composite.hpp:48
const int rc_mpmc_sink_can_be_used_with_mpsc_composite
An attempt to add MPMC target to MPSC mbox.
Definition composite.hpp:59
const int rc_no_sink_for_message_type
An attempt to send message of a type for that there is no a target.
Definition composite.hpp:37
const auto target_compare
Comparator function object to be used with std::lower_bound.
type_not_found_reaction_t throw_if_not_found()
Helper function to set a reaction to unknown message type.
type_not_found_reaction_t drop_if_not_found()
Helper function to set a reaction to unknown message type.
type_not_found_reaction_t redirect_to_if_not_found(const mbox_t &dest_mbox)
Helper function to set a reaction to unknown message type.
mbox_builder_t single_consumer_builder(type_not_found_reaction_t unknown_type_reaction)
Factory function for making mbox_builder that produces MPSC composite mbox.
mbox_builder_t multi_consumer_builder(type_not_found_reaction_t unknown_type_reaction)
Factory function for making mbox_builder that produces MPMC composite mbox.
Ranges for error codes of each submodules.
Definition details.hpp:13
Description of a case when a message of unknown type has to be dropped.
Mbox data that doesn't depend on template parameters.
type_not_found_reaction_t m_unknown_type_reaction
What to do with messages of unknown type.
target_container_t m_targets
Registered targets.
mbox_data_t(environment_t &env, mbox_id_t id, mbox_type_t mbox_type, type_not_found_reaction_t unknown_type_reaction, target_container_t targets)
environment_t * m_env_ptr
SObjectizer Environment to work in.
std::type_index m_msg_type
Type for that the destination has to be used.
mbox_t m_dest
The destination for messages for that type.
target_t(std::type_index msg_type, mbox_t dest)
Initializing constructor.
Description of a case when an exception has to be thrown if the type of a message is unknown.