SObjectizer-5 Extra
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 
22 namespace so_5 {
23 
24 namespace extra {
25 
26 namespace mboxes {
27 
28 namespace composite {
29 
30 namespace 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(
93  errors::rc_null_as_default_destination_mbox,
94  "nullptr can't be used as the default destination mbox" );
95 
96  return dest;
97  }
98 
99  public:
100  //! Initializing constructor.
103  {}
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  */
121  {};
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  */
132  {};
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.
223 class mbox_builder_t;
224 
225 namespace 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  */
235 struct target_t
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 )
244  : m_msg_type{ std::move(msg_type) }
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  */
261 inline 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 )
284  : m_msg_type{ msg_type }
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 )
327  : m_msg_type{ msg_type }
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  */
358 template< 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 }
376  , m_msg_type{ msg_type }
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",
388  mbox_as_msg_destination{ *(c.dest()) } );
389 
392  m_msg_type,
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 )
433  : m_msg_type{ msg_type }
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
478  : m_msg_type{ msg_type }
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 }
537  , m_targets{ std::move(targets) }
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  */
550 template< typename Tracing_Base >
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 )
569  , m_data{ std::move(mbox_data) }
570  {}
571 
572  public:
573  ~actual_mbox_t() override = default;
574 
575  mbox_id_t
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  {
587  if( opt_target )
588  {
590  msg_type,
591  subscriber );
592  }
593  else
594  {
595  std::visit(
597  msg_type,
598  subscriber },
600  }
601  }
602 
603  void
605  const std::type_index & msg_type,
606  abstract_message_sink_t & subscriber ) noexcept override
607  {
609  if( opt_target )
610  {
612  msg_type,
613  subscriber );
614  }
615  else
616  {
617  std::visit(
619  msg_type,
620  subscriber },
622  }
623  }
624 
625  std::string
626  query_name() const override
627  {
629  s << "<mbox:type=COMPOSITE";
630 
631  switch( this->m_data.m_mbox_type )
632  {
634  s << "(MPMC)";
635  break;
636 
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  {
661 
663  *this, // as Tracing_base
664  *this, // as abstract_message_box_t
665  "deliver_message",
668 
670  if( opt_target )
671  {
672  using namespace ::so_5::impl::msg_tracing_helpers::details;
673 
675  "redirect_to_destination",
677 
680  msg_type,
681  message,
683  }
684  else
685  {
687  typename Tracing_Base::deliver_op_tracer >;
688 
689  std::visit(
690  handler_t{
691  tracer,
693  msg_type,
694  message,
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  {
707  if( opt_target )
708  {
710  msg_type,
711  filter,
712  subscriber );
713  }
714  else
715  {
716  std::visit(
718  msg_type,
719  filter,
720  subscriber },
722  }
723  }
724 
725  void
727  const std::type_index & msg_type,
728  abstract_message_sink_t & subscriber ) noexcept override
729  {
731  if( opt_target )
732  {
734  msg_type,
735  subscriber );
736  }
737  else
738  {
739  std::visit(
741  msg_type,
742  subscriber },
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(
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  {
794  this->m_data.m_mbox_type) &&
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]]
1037  mbox_builder_t &&
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]]
1163 inline 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]]
1192 inline 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]]
1221 inline 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 */
Description of a case when messages of unknown type have to be redirected to another mbox...
Definition: composite.hpp:82
void drop_delivery_filter(const std::type_index &msg_type, abstract_message_sink_t &subscriber) noexcept override
Definition: composite.hpp:726
mbox_t make(environment_t &env)
Make a composite mbox.
Definition: composite.hpp:1068
Description of a case when a message of unknown type has to be dropped.
Definition: composite.hpp:131
mbox_type_t m_mbox_type
Type of the mbox.
Definition: composite.hpp:519
mbox_t m_dest
Destination for message of unknown type.
Definition: composite.hpp:85
redirect_to_if_not_found_case_t(mbox_t dest)
Initializing constructor.
Definition: composite.hpp:101
const int rc_mpmc_sink_can_be_used_with_mpsc_composite
An attempt to add MPMC target to MPSC mbox.
Definition: composite.hpp:59
Actual implementation of composite mbox.
Definition: composite.hpp:551
set_delivery_filter_t(const std::type_index &msg_type, const delivery_filter_t &filter, abstract_message_sink_t &subscriber)
Definition: composite.hpp:429
actual_mbox_t(mbox_data_t mbox_data, Tracing_Args &&... tracing_args)
Initializing constructor.
Definition: composite.hpp:564
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)
Definition: composite.hpp:527
std::type_index m_msg_type
Type for that the destination has to be used.
Definition: composite.hpp:238
environment_t * m_env_ptr
SObjectizer Environment to work in.
Definition: composite.hpp:513
type_not_found_reaction_t m_unknown_type_reaction
What to do with messages of unknown type.
Definition: composite.hpp:522
mbox_builder_t builder(mbox_type_t mbox_type, type_not_found_reaction_t unknown_type_reaction)
Factory function for making mbox_builder.
Definition: composite.hpp:1164
Ranges for error codes of each submodules.
Definition: details.hpp:13
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.
Definition: composite.hpp:768
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
void ensure_immutable_message(const std::type_index &msg_type, const message_ref_t &what) const
Ensures that message is an immutable message.
Definition: composite.hpp:789
mbox_t m_dest
The destination for messages for that type.
Definition: composite.hpp:240
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.
Definition: composite.hpp:1222
void operator()(const redirect_to_if_not_found_case_t &c) const noexcept
Definition: composite.hpp:332
impl::target_container_t targets_to_vector() const
Definition: composite.hpp:1131
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
Definition: composite.hpp:654
void operator()(const redirect_to_if_not_found_case_t &c) const
Definition: composite.hpp:289
type_not_found_reaction_t drop_if_not_found()
Helper function to set a reaction to unknown message type.
Definition: composite.hpp:217
type_not_found_reaction_t throw_if_not_found()
Helper function to set a reaction to unknown message type.
Definition: composite.hpp:192
target_t(std::type_index msg_type, mbox_t dest)
Initializing constructor.
Definition: composite.hpp:243
subscribe_event_t(const std::type_index &msg_type, abstract_message_sink_t &subscriber)
Definition: composite.hpp:281
const int rc_null_as_default_destination_mbox
An attempt to use nullptr as the default destination mbox.
Definition: composite.hpp:71
target_container_t m_targets
Registered targets.
Definition: composite.hpp:525
mbox_builder_t & add(mbox_t dest_mbox) &
Add destination mbox for a message type.
Definition: composite.hpp:944
const mbox_data_t m_data
Mbox&#39;s data.
Definition: composite.hpp:755
const auto target_compare
Comparator function object to be used with std::lower_bound.
Definition: composite.hpp:261
drop_delivery_filter_t(const std::type_index &msg_type, abstract_message_sink_t &subscriber) noexcept
Definition: composite.hpp:475
so_5::environment_t & environment() const noexcept override
Definition: composite.hpp:748
mbox_type_t m_mbox_type
Type of mbox to be created.
Definition: composite.hpp:1116
void set_delivery_filter(const std::type_index &msg_type, const delivery_filter_t &filter, abstract_message_sink_t &subscriber) override
Definition: composite.hpp:701
void operator()(const redirect_to_if_not_found_case_t &c) const noexcept
Definition: composite.hpp:483
Description of a case when an exception has to be thrown if the type of a message is unknown...
Definition: composite.hpp:120
Mbox data that doesn&#39;t depend on template parameters.
Definition: composite.hpp:510
mbox_builder_t(mbox_type_t mbox_type, type_not_found_reaction_t unknown_type_reaction)
Initializing constructor.
Definition: composite.hpp:878
const int rc_message_type_already_has_sink
An attempt to add another target for a message type.
Definition: composite.hpp:48
void operator()(const redirect_to_if_not_found_case_t &c) const
Definition: composite.hpp:382
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.
Definition: composite.hpp:166
unsubscribe_event_t(const std::type_index &msg_type, abstract_message_sink_t &subscriber)
Definition: composite.hpp:324
type_not_found_reaction_t m_unknown_type_reaction
Reaction to unknown type of a message.
Definition: composite.hpp:1119
void subscribe_event_handler(const std::type_index &msg_type, abstract_message_sink_t &subscriber) override
Definition: composite.hpp:582
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)
Definition: composite.hpp:368
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.
Definition: composite.hpp:1193
mbox_builder_t && add(mbox_t dest_mbox) &&
Add destination mbox for a message type.
Definition: composite.hpp:1038
target_map_t m_targets
Container for registered targets.
Definition: composite.hpp:1122
Factory class for building an instance of composite mbox.
Definition: composite.hpp:870
void unsubscribe_event_handler(const std::type_index &msg_type, abstract_message_sink_t &subscriber) noexcept override
Definition: composite.hpp:604