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 sink.
34  *
35  * \since v.1.5.2
36  */
39 
40 /*!
41  * \brief An attempt to add another sink 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 sink 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 sink.
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 sink_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  sink_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 sinks.
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 sink_compare =
262  []( const sink_t & sink, const std::type_index & msg_type ) -> bool {
263  return sink.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  {
280 
281  public:
283  const std::type_index & msg_type,
284  const so_5::message_limit::control_block_t * limit,
285  agent_t & subscriber )
286  : m_msg_type{ msg_type }
287  , m_limit{ limit }
289  {}
290 
291  void
293  {
294  c.dest()->subscribe_event_handler(
295  m_msg_type,
296  m_limit,
297  m_subscriber );
298  }
299 
300  void
302  {
303  SO_5_THROW_EXCEPTION(
304  errors::rc_no_sink_for_message_type,
305  "no destination for this message type, "
306  "msg_type=" + std::string(m_msg_type.name()) );
307  }
308 
309  void
311  {
312  // Nothing to do.
313  }
314  };
315 
316 /*!
317  * \brief Function object to be used with std::visit.
318  *
319  * Implement logic of so_5::abstract_message_box_t::unsubscribe_event_handlers()
320  * in a case when message type is unknown.
321  */
323  {
326 
327  public:
329  const std::type_index & msg_type,
330  agent_t & subscriber )
331  : m_msg_type{ msg_type }
333  {}
334 
335  void
337  {
338  c.dest()->unsubscribe_event_handlers(
339  m_msg_type,
340  m_subscriber );
341  }
342 
343  void
345  {
346  // Just ignore that case.
347  }
348 
349  void
351  {
352  // Nothing to do.
353  }
354  };
355 
356 /*!
357  * \brief Function object to be used with std::visit.
358  *
359  * Implement logic of so_5::abstract_message_box_t::do_deliver_message()
360  * in a case when message type is unknown.
361  */
362 template< typename Tracer >
364  {
365  Tracer & m_tracer;
368  unsigned int m_overlimit_deep;
369 
370  public:
372  Tracer & tracer,
373  const std::type_index & msg_type,
374  const message_ref_t & msg,
375  unsigned int overlimit_deep )
376  : m_tracer{ tracer }
377  , m_msg_type{ msg_type }
378  , m_msg{ msg }
380  {}
381 
382  void
384  {
385  using namespace ::so_5::impl::msg_tracing_helpers::details;
386 
388  "redirect_to_default_destination",
389  mbox_as_msg_destination{ *(c.dest()) } );
390 
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  agent_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  agent_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 sinks.
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  sink_container_t sinks )
533  : m_env_ptr{ &env }
534  , m_id{ id }
537  , m_sinks{ std::move(sinks) }
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  const so_5::message_limit::control_block_t * limit,
585  agent_t & subscriber ) override
586  {
588  if( opt_sink )
589  {
591  msg_type,
592  limit,
593  subscriber );
594  }
595  else
596  {
597  std::visit(
599  msg_type,
600  limit,
601  subscriber },
603  }
604  }
605 
606  void
608  const std::type_index & msg_type,
609  agent_t & subscriber ) override
610  {
612  if( opt_sink )
613  {
615  msg_type,
616  subscriber );
617  }
618  else
619  {
620  std::visit(
622  msg_type,
623  subscriber },
625  }
626  }
627 
628  std::string
629  query_name() const override
630  {
632  s << "<mbox:type=COMPOSITE";
633 
634  switch( this->m_data.m_mbox_type )
635  {
637  s << "(MPMC)";
638  break;
639 
641  s << "(MPSC)";
642  break;
643  }
644 
645  s << ":id=" << this->m_data.m_id << ">";
646 
647  return s.str();
648  }
649 
651  type() const override
652  {
653  return this->m_data.m_mbox_type;
654  }
655 
656  void
658  const std::type_index & msg_type,
659  const message_ref_t & message,
660  unsigned int overlimit_reaction_deep ) override
661  {
663 
665  *this, // as Tracing_base
666  *this, // as abstract_message_box_t
667  "deliver_message",
669 
671  if( opt_sink )
672  {
673  using namespace ::so_5::impl::msg_tracing_helpers::details;
674 
676  "redirect_to_destination",
678 
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,
692  msg_type,
693  message,
696  }
697  }
698 
699  void
701  const std::type_index & msg_type,
702  const delivery_filter_t & filter,
703  agent_t & subscriber ) override
704  {
706  if( opt_sink )
707  {
709  msg_type,
710  filter,
711  subscriber );
712  }
713  else
714  {
715  std::visit(
717  msg_type,
718  filter,
719  subscriber },
721  }
722  }
723 
724  void
726  const std::type_index & msg_type,
727  agent_t & subscriber ) noexcept override
728  {
730  if( opt_sink )
731  {
733  msg_type,
734  subscriber );
735  }
736  else
737  {
738  std::visit(
740  msg_type,
741  subscriber },
743  }
744  }
745 
747  environment() const noexcept override
748  {
749  return *(this->m_data.m_env_ptr);
750  }
751 
752  private:
753  //! Mbox's data.
755 
756  /*!
757  * \brief Attempt to find a sink for specified message type.
758  *
759  * \return empty std::optional if \a msg_type is unknown.
760  */
761  [[nodiscard]]
762  std::optional< const sink_t * >
764  {
765  const auto last = end( m_data.m_sinks );
766  const auto it = std::lower_bound(
767  begin( m_data.m_sinks ), last,
768  msg_type,
769  sink_compare );
770 
771  if( !( it == last ) && msg_type == it->m_msg_type )
772  return { std::addressof( *it ) };
773  else
774  return std::nullopt;
775  }
776 
777  /*!
778  * \brief Ensures that message is an immutable message.
779  *
780  * Checks mutability flag and throws an exception if message is
781  * a mutable one.
782  */
783  void
785  const std::type_index & msg_type,
786  const message_ref_t & what ) const
787  {
789  this->m_data.m_mbox_type) &&
794  "an attempt to deliver mutable message via MPMC mbox"
795  ", msg_type=" + std::string(msg_type.name()) );
796  }
797  };
798 
799 } /* namespace impl */
800 
801 /*!
802  * \brief Factory class for building an instance of composite mbox.
803  *
804  * Usage example:
805  * \code
806  * using namespace so_5::extra::mboxes::composite;
807  *
808  * auto mbox = single_consumer_builder(throw_if_not_found())
809  * .add<msg_first>(first_mbox)
810  * .add< so_5::mutable_msg<msg_second> >(second_mbox)
811  * .make(env);
812  * \endcode
813  *
814  * \note
815  * This class is intended to be used in just one chain of add()..make() methods.
816  * It means that code like that:
817  * \code
818  * using namespace so_5::extra::mboxes::composite;
819  *
820  * // Simplest case without storing mbox_builder_t instance.
821  * auto mbox = single_consumer_builder(throw_if_not_found())
822  * .add<msg_first>(first_mbox)
823  * .add< so_5::mutable_msg<msg_second> >(second_mbox)
824  * .make(env);
825  *
826  * // More complex case with holding temporary mbox_builder_t instance.
827  * auto my_builder = multi_consumer_builder(drop_if_not_found());
828  * my_builder.add<msg_first>(first_mbox);
829  * if(some_condition)
830  * my_builder.add<msg_second>(second_mbox);
831  * if(third_mbox_present)
832  * my_builder.add<msg_third>(third_mbox);
833  * auto mbox = my_builder.make(env);
834  * \endcode
835  * Will work in all versions of so5extra. But multiple calls to make() for
836  * the same builder object are not guaranteed to be working. It's depend
837  * on the current implementation and the implementation can change in
838  * future versions of so5extra. It means that you have to avoid code
839  * like that:
840  * \code
841  * using namespace so_5::extra::mboxes::composite;
842  *
843  * // DO NOT DO THIS!
844  * // The behaviour can change in future versions of so5extra without prior notify.
845  * auto my_builder = multi_consumer_builder(redirect_to_if_not_found(default_dest));
846  *
847  * my_builder.add<msg_first>(first_mbox);
848  * auto one = my_builder.make(env); // (1)
849  *
850  * my_builder.add<msg_second>(second_mbox);
851  * auto two = my_builder.make(env);
852  * \endcode
853  * It's not guaranteed thet my_builder will be in a valid state after point (1).
854  *
855  * \attention
856  * An instance of mbox_builder_t isn't thread safe.
857  *
858  * \note
859  * This class has a private constructor and instance of builder can be
860  * obtained only with help from builder(), single_consumer_builder(), and
861  * multi_consumer_builder() functions.
862  *
863  * \since v.1.5.2
864  */
866  {
867  friend mbox_builder_t
868  builder(
869  mbox_type_t mbox_type,
870  type_not_found_reaction_t unknown_type_reaction );
871 
872  //! Initializing constructor.
874  //! Type of mbox to be created.
875  mbox_type_t mbox_type,
876  //! Reaction to a unknown message type.
877  type_not_found_reaction_t unknown_type_reaction )
880  {}
881 
882  public:
883  ~mbox_builder_t() noexcept = default;
884 
885  /*!
886  * \brief Add destination mbox for a message type.
887  *
888  * Usage example:
889  * \code
890  * using namespace so_5::extra::mboxes::composite;
891  *
892  * // A case with holding temporary mbox_builder_t instance.
893  * auto my_builder = multi_consumer_builder(drop_if_not_found());
894  * my_builder.add<msg_first>(first_mbox);
895  * if(some_condition)
896  * my_builder.add<msg_second>(second_mbox);
897  * if(third_mbox_present)
898  * my_builder.add<msg_third>(third_mbox);
899  * auto result_mbox = my_builder.make(env);
900  * \endcode
901  *
902  * If a type for mutable message has to be specified then
903  * so_5::mutable_msg marker should be used:
904  * \code
905  * using namespace so_5::extra::mboxes::composite;
906  *
907  * auto my_builder = single_consumer_builder(drop_if_not_found());
908  * my_builder.add< so_5::mutable_msg<msg_first> >(first_mbox);
909  * \endcode
910  *
911  * Type of mutable message can't be used if:
912  *
913  * - composite mbox is MPMC mbox;
914  * - the destination mbox is MPMC mbox;
915  *
916  * \note
917  * If builder is created to produce a MPSC composite mbox then a MPMC
918  * mbox can be added as the destination mbox, but for immutable message
919  * only. For example:
920  * \code
921  * using namespace so_5::extra::mboxes::composite;
922  *
923  * auto mpmc_dest = env.create_mbox(); // It's MPMC mbox.
924  *
925  * auto result_mbox = single_consumer_builder(throw_if_not_found())
926  * // This call is allowed because my_msg is immutable message.
927  * .add< my_msg >( mpmc_dest )
928  * ...
929  * \endcode
930  *
931  * \attention
932  * An exception will be thrown if destination mbox is already registered
933  * for Msg_Type.
934  *
935  * \tparam Msg_Type type of message to be redirected to specified mbox.
936  */
937  template< typename Msg_Type >
939  add( mbox_t dest_mbox ) &
940  {
941  // Use of mutable message type for MPMC mbox should be prohibited.
942  if constexpr( is_mutable_message< Msg_Type >::value )
943  {
944  switch( m_mbox_type )
945  {
949  "mutable message can't handled with MPMC composite, "
950  "msg_type=" + std::string(typeid(Msg_Type).name()) );
951  break;
952 
954  break;
955  }
956 
958  dest_mbox->type() )
959  {
962  "MPMC mbox can't be added as a sink to MPSC "
963  "composite and mutable message, "
964  "msg_type=" + std::string(typeid(Msg_Type).name()) );
965  }
966  }
967 
968  const auto [it, is_inserted] = m_sinks.emplace(
970  std::move(dest_mbox) );
971  if( !is_inserted )
974  "message type already has a destination mbox, "
975  "msg_type=" + std::string(typeid(Msg_Type).name()) );
976 
977  return *this;
978  }
979 
980  /*!
981  * \brief Add destination mbox for a message type.
982  *
983  * Usage example:
984  * \code
985  * using namespace so_5::extra::mboxes::composite;
986  *
987  * // Simplest case without storing mbox_builder_t instance.
988  * auto result_mbox = single_consumer_builder(throw_if_not_found())
989  * .add<msg_first>(first_mbox)
990  * .add< so_5::mutable_msg<msg_second> >(second_mbox)
991  * .make(env);
992  * \endcode
993  *
994  * If a type for mutable message has to be specified then
995  * so_5::mutable_msg marker should be used:
996  * \code
997  * using namespace so_5::extra::mboxes::composite;
998  *
999  * auto result_mbox = single_consumer_builder(throw_if_not_found())
1000  * .add< so_5::mutable_msg<message> >(dest_mbox)
1001  * ...
1002  * \endcode
1003  *
1004  * Type of mutable message can't be used if:
1005  *
1006  * - composite mbox is MPMC mbox;
1007  * - the destination mbox is MPMC mbox;
1008  *
1009  * \note
1010  * If builder is created to produce a MPSC composite mbox then a MPMC
1011  * mbox can be added as the destination mbox, but for immutable message
1012  * only. For example:
1013  * \code
1014  * using namespace so_5::extra::mboxes::composite;
1015  *
1016  * auto mpmc_dest = env.create_mbox(); // It's MPMC mbox.
1017  *
1018  * auto result_mbox = single_consumer_builder(throw_if_not_found())
1019  * // This call is allowed because my_msg is immutable message.
1020  * .add< my_msg >( mpmc_dest )
1021  * ...
1022  * \endcode
1023  *
1024  * \attention
1025  * An exception will be thrown if destination mbox is already registered
1026  * for Msg_Type.
1027  *
1028  * \tparam Msg_Type type of message to be redirected to specified mbox.
1029  */
1030  template< typename Msg_Type >
1031  [[nodiscard]]
1032  mbox_builder_t &&
1033  add( mbox_t dest_mbox ) &&
1034  {
1035  return std::move( add<Msg_Type>( std::move(dest_mbox) ) );
1036  }
1037 
1038  /*!
1039  * \brief Make a composite mbox.
1040  *
1041  * The created mbox will be based on information added to builder
1042  * before calling make() method.
1043  *
1044  * Usage example:
1045  * \code
1046  * using namespace so_5::extra::mboxes::composite;
1047  *
1048  * // Simplest case without storing mbox_builder_t instance.
1049  * auto result_mbox = single_consumer_builder(throw_if_not_found())
1050  * .add<msg_first>(first_mbox)
1051  * .add< so_5::mutable_msg<msg_second> >(second_mbox)
1052  * .make(env);
1053  * \endcode
1054  *
1055  * It's guaranteed that the builder object will be in some correct
1056  * state after make() returns. It means that builder can be safely
1057  * deleted or can obtain a new value as the result of assignement.
1058  * But it isn't guaranteed ther the builder will hold values previously
1059  * stored to it by add() methods.
1060  */
1061  [[nodiscard]]
1062  mbox_t
1064  {
1065  return env.make_custom_mbox(
1066  [this]( const mbox_creation_data_t & data )
1067  {
1068  impl::mbox_data_t mbox_data{
1069  data.m_env.get(),
1070  data.m_id,
1071  m_mbox_type,
1072  std::move(m_unknown_type_reaction),
1073  sinks_to_vector()
1074  };
1075  mbox_t result;
1076 
1077  if( data.m_tracer.get().is_msg_tracing_enabled() )
1078  {
1079  using ::so_5::impl::msg_tracing_helpers::
1080  tracing_enabled_base;
1081  using T = impl::actual_mbox_t< tracing_enabled_base >;
1082 
1083  result = mbox_t{ new T{
1084  std::move(mbox_data),
1085  data.m_tracer.get()
1086  } };
1087  }
1088  else
1089  {
1090  using ::so_5::impl::msg_tracing_helpers::
1091  tracing_disabled_base;
1092  using T = impl::actual_mbox_t< tracing_disabled_base >;
1093 
1094  result = mbox_t{ new T{ std::move(mbox_data) } };
1095  }
1096 
1097  return result;
1098  } );
1099  }
1100 
1101  private:
1102  /*!
1103  * \brief Type of container for holding sinks.
1104  *
1105  * \note
1106  * std::map is used to simplify the implementation.
1107  */
1109 
1110  //! Type of mbox to be created.
1112 
1113  //! Reaction to unknown type of a message.
1115 
1116  //! Container for registered sinks.
1118 
1119  /*!
1120  * \return A vector of sinks that should be passed to impl::actual_mbox_t
1121  * constructor. That vector is guaranteed to be sorted (it means that
1122  * binary search can be used for searching message types).
1123  */
1124  [[nodiscard]]
1127  {
1128  impl::sink_container_t result;
1129  result.reserve( m_sinks.size() );
1130 
1131  // Use the fact that items in std::map are ordered by keys.
1132  for( const auto & [k, v] : m_sinks )
1133  result.emplace_back( k, v );
1134 
1135  return result;
1136  }
1137  };
1138 
1139 /*!
1140  * \brief Factory function for making mbox_builder.
1141  *
1142  * Usage example:
1143  * \code
1144  * using namespace so_5::extra::mboxes::composite;
1145  *
1146  * auto result_mbox = builder(
1147  * so_5::mbox_type_t::multi_producer_multi_consumer,
1148  * redirect_to_if_not_found(default_mbox))
1149  * .add<msg_first>(first_mbox)
1150  * .add<msg_second>(second_mbox)
1151  * .add<msg_third>(third_mbox)
1152  * .make(env);
1153  * \endcode
1154  *
1155  * \since v.1.5.2
1156  */
1157 [[nodiscard]]
1158 inline mbox_builder_t
1160  //! Type of new mbox: MPMC or MPSC.
1161  mbox_type_t mbox_type,
1162  //! What to do if message type is unknown.
1163  type_not_found_reaction_t unknown_type_reaction )
1164  {
1165  return { mbox_type, std::move(unknown_type_reaction) };
1166  }
1167 
1168 /*!
1169  * \brief Factory function for making mbox_builder that produces MPMC composite
1170  * mbox.
1171  *
1172  * Usage example:
1173  * \code
1174  * using namespace so_5::extra::mboxes::composite;
1175  *
1176  * auto result_mbox = multi_consumer_builder(
1177  * redirect_to_if_not_found(default_mbox))
1178  * .add<msg_first>(first_mbox)
1179  * .add<msg_second>(second_mbox)
1180  * .add<msg_third>(third_mbox)
1181  * .make(env);
1182  * \endcode
1183  *
1184  * \since v.1.5.2
1185  */
1186 [[nodiscard]]
1187 inline mbox_builder_t
1189  //! What to do if message type is unknown.
1190  type_not_found_reaction_t unknown_type_reaction )
1191  {
1192  return builder(
1193  mbox_type_t::multi_producer_multi_consumer,
1194  std::move(unknown_type_reaction) );
1195  }
1196 
1197 /*!
1198  * \brief Factory function for making mbox_builder that produces MPSC composite
1199  * mbox.
1200  *
1201  * Usage example:
1202  * \code
1203  * using namespace so_5::extra::mboxes::composite;
1204  *
1205  * auto result_mbox = single_consumer_builder(
1206  * redirect_to_if_not_found(default_mbox))
1207  * .add<msg_first>(first_mbox)
1208  * .add< so_5::mutable_msg<msg_second> >(second_mbox)
1209  * .add<msg_third>(third_mbox)
1210  * .make(env);
1211  * \endcode
1212  *
1213  * \since v.1.5.2
1214  */
1215 [[nodiscard]]
1216 inline mbox_builder_t
1218  type_not_found_reaction_t unknown_type_reaction )
1219  {
1220  return builder(
1221  mbox_type_t::multi_producer_single_consumer,
1222  std::move(unknown_type_reaction) );
1223  }
1224 
1225 } /* namespace composite */
1226 
1227 } /* namespace mboxes */
1228 
1229 } /* namespace extra */
1230 
1231 } /* namespace so_5 */
Description of a case when messages of unknown type have to be redirected to another mbox...
Definition: composite.hpp:82
mbox_t make(environment_t &env)
Make a composite mbox.
Definition: composite.hpp:1063
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 sink to MPSC mbox.
Definition: composite.hpp:59
std::optional< const sink_t *> try_find_sink_for_msg_type(const std::type_index &msg_type) const
Attempt to find a sink for specified message type.
Definition: composite.hpp:763
subscribe_event_t(const std::type_index &msg_type, const so_5::message_limit::control_block_t *limit, agent_t &subscriber)
Definition: composite.hpp:282
Actual implementation of composite mbox.
Definition: composite.hpp:551
void set_delivery_filter(const std::type_index &msg_type, const delivery_filter_t &filter, agent_t &subscriber) override
Definition: composite.hpp:700
actual_mbox_t(mbox_data_t mbox_data, Tracing_Args &&... tracing_args)
Initializing constructor.
Definition: composite.hpp:564
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
void drop_delivery_filter(const std::type_index &msg_type, agent_t &subscriber) noexcept override
Definition: composite.hpp:725
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:1159
Ranges for error codes of each submodules.
Definition: details.hpp:13
const int rc_no_sink_for_message_type
An attempt to send message of a type for that there is no a sink.
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:784
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:1217
mbox_t m_dest
The destination for messages for that type.
Definition: composite.hpp:240
set_delivery_filter_t(const std::type_index &msg_type, const delivery_filter_t &filter, agent_t &subscriber)
Definition: composite.hpp:429
sink_t(std::type_index msg_type, mbox_t dest)
Initializing constructor.
Definition: composite.hpp:243
void operator()(const redirect_to_if_not_found_case_t &c) const
Definition: composite.hpp:292
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
const int rc_null_as_default_destination_mbox
An attempt to use nullptr as the default destination mbox.
Definition: composite.hpp:71
mbox_data_t(environment_t &env, mbox_id_t id, mbox_type_t mbox_type, type_not_found_reaction_t unknown_type_reaction, sink_container_t sinks)
Definition: composite.hpp:527
void do_deliver_message(const std::type_index &msg_type, const message_ref_t &message, unsigned int overlimit_reaction_deep) override
Definition: composite.hpp:657
std::type_index m_msg_type
Type for that the destination has to be used.
Definition: composite.hpp:238
drop_delivery_filter_t(const std::type_index &msg_type, agent_t &subscriber) noexcept
Definition: composite.hpp:475
mbox_builder_t & add(mbox_t dest_mbox) &
Add destination mbox for a message type.
Definition: composite.hpp:939
const mbox_data_t m_data
Mbox&#39;s data.
Definition: composite.hpp:754
void subscribe_event_handler(const std::type_index &msg_type, const so_5::message_limit::control_block_t *limit, agent_t &subscriber) override
Definition: composite.hpp:582
so_5::environment_t & environment() const noexcept override
Definition: composite.hpp:747
mbox_type_t m_mbox_type
Type of mbox to be created.
Definition: composite.hpp:1111
sink_map_t m_sinks
Container for registered sinks.
Definition: composite.hpp:1117
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:873
const int rc_message_type_already_has_sink
An attempt to add another sink for a message type.
Definition: composite.hpp:48
const auto sink_compare
Comparator function object to be used with std::lower_bound.
Definition: composite.hpp:261
void operator()(const redirect_to_if_not_found_case_t &c) const
Definition: composite.hpp:383
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
type_not_found_reaction_t m_unknown_type_reaction
Reaction to unknown type of a message.
Definition: composite.hpp:1114
impl::sink_container_t sinks_to_vector() const
Definition: composite.hpp:1126
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:1188
mbox_builder_t && add(mbox_t dest_mbox) &&
Add destination mbox for a message type.
Definition: composite.hpp:1033
sink_container_t m_sinks
Registered sinks.
Definition: composite.hpp:525
void unsubscribe_event_handlers(const std::type_index &msg_type, agent_t &subscriber) override
Definition: composite.hpp:607
deliver_message_t(Tracer &tracer, const std::type_index &msg_type, const message_ref_t &msg, unsigned int overlimit_deep)
Definition: composite.hpp:371
unsubscribe_event_t(const std::type_index &msg_type, agent_t &subscriber)
Definition: composite.hpp:328
Factory class for building an instance of composite mbox.
Definition: composite.hpp:865