SObjectizer-5 Extra
collecting_mbox.hpp
Go to the documentation of this file.
1 /*!
2  * \file
3  * \brief Implementation of collecting mbox.
4  *
5  * \since
6  * v.1.0.1
7  */
8 
9 #pragma once
10 
11 #include <so_5_extra/error_ranges.hpp>
12 
13 #include <so_5/impl/msg_tracing_helpers.hpp>
14 
15 #include <so_5/details/sync_helpers.hpp>
16 
17 #include <so_5/mbox.hpp>
18 #include <so_5/enveloped_msg.hpp>
19 
20 #include <so_5/optional.hpp>
21 
22 #include <memory>
23 #include <tuple>
24 #include <utility>
25 
26 namespace so_5 {
27 
28 namespace extra {
29 
30 namespace mboxes {
31 
32 namespace collecting_mbox {
33 
34 namespace errors {
35 
36 /*!
37  * \brief An attempt to make subscription to collecting_mbox.
38  *
39  * \since
40  * v.1.0.1
41  */
44 
45 /*!
46  * \brief An attempt to set delivery filter to collecting_mbox.
47  *
48  * \since
49  * v.1.0.1
50  */
53 
54 /*!
55  * \brief An attempt to send a message or signal of different type.
56  *
57  * \since
58  * v.1.0.1
59  */
62 
63 } /* namespace errors */
64 
65 namespace details {
66 
67 /*!
68  * \brief A helper type which is a collection of type parameters.
69  *
70  * This type is used to simplify code of collecting_mbox internals.
71  * Instead of writting something like:
72  * \code
73  * template< typename Collecting_Msg, typename Traits >
74  * class ... {...};
75  *
76  * template< typename Collecting_Msg, typename Traits, typename Lock_Type >
77  * class ... {...};
78  * \endcode
79  * this config_type allows to write like that:
80  * \code
81  * template< typename Config_Type >
82  * class ... {...};
83  *
84  * template< typename Config_Type >
85  * class ... {...};
86  * \endcode
87  *
88  * \tparam Collecting_Msg type of collecting messages or signals. Note: if
89  * mutable messages is collecting then it should be so_5::mutable_msg<M>.
90  *
91  * \tparam Traits type of size-dependent traits (like
92  * so_5::extra::mboxes::collecting_mbox::constexpr_size_traits_t or
93  * so_5::extra::mboxes::collecting_mbox::runtime_size_traits_t).
94  *
95  * \tparam Lock_Type type of object to be used for thread-safety (like
96  * std::mutex or so_5::null_mutex_t).
97  */
98 template<
99  typename Collecting_Msg,
100  typename Traits,
101  typename Lock_Type >
103  {
104  using collecting_msg_type = Collecting_Msg;
105  using traits_type = Traits;
106  using lock_type = Lock_Type;
107  };
108 
109 /*!
110  * \name Type extractors for config_type
111  * \{
112  */
113 template< typename Config_Type >
114 using collecting_msg_t = typename Config_Type::collecting_msg_type;
115 
116 template< typename Config_Type >
117 using traits_t = typename Config_Type::traits_type;
118 
119 template< typename Config_Type >
120 using lock_t = typename Config_Type::lock_type;
121 /*!
122  * \}
123  */
124 
125 /*!
126  * \brief Helper method for checking message mutability and type of
127  * the target mbox.
128  *
129  * \throw so_5::exception_t if message is mutable but \a target is not
130  * MPSC-mbox.
131  *
132  * \tparam Config_Type a type with enumeration of all necessary type traits.
133  * It is expected to be config_type with appropriate type parameters.
134  */
135 template< typename Config_Type >
136 void
138  //! A target mbox for messages_collected message.
139  const so_5::mbox_t & target )
140  {
145  "a target for collecting_mbox must be MPSC mbox in case "
146  "of a mutable messge" );
147  }
148 
149 //
150 // collected_messages_bunch_t
151 //
152 /*!
153  * \brief Type of message to be sent when all collecting messages are received.
154  *
155  * \tparam Config_Type a type with enumeration of all necessary type traits.
156  * It is expected to be config_type with appropriate type parameters.
157  */
158 template< typename Config_Type >
160  : public so_5::message_t
162  {
163  template<typename> friend class collected_messages_bunch_builder_t;
164 
165  using mixin_base_type =
167 
168  //! A container for collected messages.
169  typename traits_t<Config_Type>::container_type m_collected_messages;
170 
171  //! Store another collected message at the specified index.
172  void
174  //! Index at which message should be stored.
175  size_t index,
176  //! Message to be stored.
177  message_ref_t msg )
178  {
179  this->storage()[ index ] = std::move(msg);
180  }
181 
182  //! Initializing constructor.
183  collected_messages_bunch_t( std::size_t size )
184  : mixin_base_type( size )
185  {}
186 
187  public :
188  using mixin_base_type::size;
189 
190  //! Do some action with Nth collected message.
191  /*!
192  * \note This method can be used for immutable and for mutable messages.
193  *
194  * \attention
195  * \a index should be less than size(). Value of \a index is not
196  * checked at the run-time.
197  *
198  * \return value of f(mhood_t<Config_Type::collecting_msg_t>(...)).
199  *
200  * \tparam F type of functor/lambda which accepts mhood_t.
201  *
202  * Usage example:
203  * \code
204  * struct my_msg final : public so_5::message_t {
205  * std::string value_;
206  * ...
207  * };
208  * using my_msg_collector = so_5::extra::mboxes::collecting_mbox::mbox_template_t<
209  * my_msg, so_5::extra::mboxes::collecting_msg::runtime_size_traits_t >;
210  * ...
211  * void my_actor::on_my_msg_collected(mhood_t<typename my_msg_collector::messages_collected_t> cmd) {
212  * std::string v = cmd->with_nth( 0, [](auto m) { return m->value_; } );
213  * }
214  * \endcode
215  */
216  template< typename F >
217  decltype(auto)
219  std::size_t index,
220  F && f ) const
221  {
222  message_ref_t ref{ this->storage()[ index ] };
223  return f(mhood_t< collecting_msg_t<Config_Type> >{ref});
224  }
225 
226  //! Do some action for all collected message.
227  /*!
228  * \note This method can be used for immutable and for mutable messages.
229  *
230  * \return value of f(mhood_t<Config_Type::collecting_msg_t>(...)).
231  *
232  * \tparam F type of functor/lambda which accepts mhood_t.
233  *
234  * Usage example:
235  * \code
236  * struct my_msg final : public so_5::message_t {
237  * std::string value_;
238  * ...
239  * };
240  * using my_msg_collector = so_5::extra::mboxes::collecting_mbox::mbox_template_t<
241  * my_msg, so_5::extra::mboxes::collecting_msg::runtime_size_traits_t >;
242  * ...
243  * void my_actor::on_my_msg_collected(mhood_t<typename my_msg_collector::messages_collected_t> cmd) {
244  * cmd->for_all( [](auto m) { std::cout << m->value_; } );
245  * }
246  * \endcode
247  */
248  template< typename F >
249  void
250  for_each( F && f ) const
251  {
252  for( message_ref_t ref : this->storage() )
254  }
255 
256  //! Do some action for all collected message.
257  /*!
258  * \note This method can be used for immutable and for mutable messages.
259  *
260  * \return value of f(index, mhood_t<Config_Type::collecting_msg_t>(...)).
261  *
262  * \tparam F type of functor/lambda which accepts two parameters:
263  * \a index of std::size_t and \a cmd of mhood_t.
264  *
265  * Usage example:
266  * \code
267  * struct my_msg final : public so_5::message_t {
268  * std::string value_;
269  * ...
270  * };
271  * using my_msg_collector = so_5::extra::mboxes::collecting_mbox::mbox_template_t<
272  * my_msg, so_5::extra::mboxes::collecting_msg::runtime_size_traits_t >;
273  * ...
274  * void my_actor::on_my_msg_collected(mhood_t<typename my_msg_collector::messages_collected_t> cmd) {
275  * cmd->for_all_with_index( [](auto i, auto m) { std::cout << i << ":" m->value_; } );
276  * }
277  * \endcode
278  */
279  template< typename F >
280  void
281  for_each_with_index( F && f ) const
282  {
283  const auto total = this->size();
284  for( std::size_t index = 0; index < total; ++index )
285  {
286  message_ref_t ref{ this->storage()[ index ] };
288  }
289  }
290  };
291 
292 //
293 // detect_message_to_store
294 //
295 /*!
296  * \brief Detect the actual message to be collected (if it is present).
297  *
298  * SO-5.5.23 introduced enveloped messages. In the case of enveloped message
299  * the payload must be extrected and stored inside collected_mbox.
300  * This function checks the kind of a message and extract payload if
301  * message is an enveloped.
302  *
303  * Original value of \a what is returned in \a what is not an envelope.
304  *
305  * \since
306  * v.1.2.0
307  */
308 inline optional< message_ref_t >
310  {
311  if( message_t::kind_t::enveloped_msg == message_kind(what) )
312  {
313  // Envelope's payload must be extracted.
314  auto opt_payload_info = ::so_5::enveloped_msg::
315  extract_payload_for_message_transformation( what );
316  if( opt_payload_info )
317  return { opt_payload_info->message() };
318  else
319  return {};
320  }
321  else
322  return { std::move(what) };
323  }
324 
325 //
326 // collected_messages_bunch_builder_t
327 //
328 /*!
329  * \brief A builder for case when collecting_mbox collects messages.
330  *
331  * \tparam Config_Type a type with enumeration of all necessary type traits.
332  * It is expected to be config_type with appropriate type parameters.
333  */
334 template< typename Config_Type >
336  {
337  public :
338  //! Actual message type to be used.
339  using message_type = collected_messages_bunch_t< Config_Type >;
340 
341  private :
342  //! The current instance of messages_collected to store
343  //! messages to be delivered.
344  /*!
345  * Can be nullptr if there is no new messages.
346  */
348 
349  //! Count of collected messages.
350  /*!
351  * If m_collected_messages != 0 then m_current_msg must not be nullptr.
352  */
354 
355  public :
356  //! Store another instance of collecting messages.
357  void
359  //! Message to be stored.
360  message_ref_t message,
361  //! Total count of message to be collected.
362  //! This parameter is necessary because a new instance of
363  //! messages_collected can be created inside this method.
364  std::size_t messages_to_collect )
365  {
366  // Since SO-5.5.23 it is necessary to check a type of message.
367  // If it is an envelope then the content of the envelope should
368  // be extracted.
370  std::move(message) );
371  // There can be a case when payload is missing.
372  // In that case nothing will be stored.
373  if( opt_msg_to_store )
374  {
376  if( !storage )
377  {
381  }
382 
385  std::move( *opt_msg_to_store ) );
387  }
388  }
389 
390  bool
391  is_ready_to_be_sent( std::size_t messages_to_collect ) const noexcept
392  {
394  }
395 
398  {
400  return std::move( m_current_msg );
401  }
402  };
403 
404 //
405 // collected_signals_bunch_t
406 //
407 /*!
408  * \brief A type of message to be sent when all collected signals are received.
409  *
410  * \tparam Config_Type a type with enumeration of all necessary type traits.
411  * It is expected to be config_type with appropriate type parameters.
412  */
413 template< typename Config_Type >
415  : public so_5::message_t
417  {
418  template<typename> friend class collected_signals_bunch_builder_t;
419 
420  using mixin_base_type =
422 
423  collected_signals_bunch_t( std::size_t size )
424  : mixin_base_type( size )
425  {}
426 
427  public :
428  using mixin_base_type::size;
429  };
430 
431 //
432 // collected_signals_bunch_builder_t
433 //
434 /*!
435  * \brief A builder for case when collecting_mbox collects signals.
436  *
437  * In this case only count of collected signals must be maintained.
438  *
439  * A message to be sent can be created directly in extract_message().
440  *
441  * \tparam Config_Type a type with enumeration of all necessary type traits.
442  * It is expected to be config_type with appropriate type parameters.
443  */
444 template< typename Config_Type >
446  {
447  public :
448  // Actual message type to be used.
449  using message_type = collected_signals_bunch_t<Config_Type>;
450 
451  private :
452  //! Count of collected signals.
454 
455  public :
456  void
458  message_ref_t /*message*/,
459  std::size_t /*messages_to_collect*/ )
460  {
462  }
463 
464  bool
465  is_ready_to_be_sent( std::size_t messages_to_collect ) const
466  {
468  }
469 
472  {
475  return std::unique_ptr< message_type >{
476  new message_type{ constructor_arg } };
477  }
478  };
479 
480 //
481 // collected_bunch_type_selector
482 //
483 /*!
484  * \brief A helper type for selection of actual message type and
485  * type of message builder.
486  *
487  * It defines two typedefs:
488  *
489  * * message_type. This will be a type for message to be sent when
490  * all collecting messages/signal are received;
491  * * builder_type. This will be a type of object to collect received
492  * messages or signals and to build a new message to be sent.
493  *
494  * \tparam Config_Type a type with enumeration of all necessary type traits.
495  * It is expected to be config_type with appropriate type parameters.
496  */
497 template< typename Config_Type >
499  {
500  static constexpr bool is_signal =
502 
503  using message_type = typename std::conditional<
504  is_signal,
507  ::type;
508 
509  using builder_type = typename std::conditional<
510  is_signal,
513  ::type;
514  };
515 
516 //
517 // messages_collected_t
518 //
519 /*!
520  * \brief Type of message to be sent as messages_collected instance.
521  *
522  * It will be collected_messages_bunch_t if Config_Type::collecting_msg_type
523  * is a type of a message. Or it will be collected_signals_bunch_t if
524  * Config_Type::collecting_msg_type is a type of a signal.
525  *
526  * \tparam Config_Type a type with enumeration of all necessary type traits.
527  * It is expected to be config_type with appropriate type parameters.
528  */
529 template< typename Config_Type >
530 using messages_collected_t = typename
531  collected_bunch_type_selector<Config_Type>::message_type;
532 
533 //
534 // actual_mbox_t
535 //
536 /*!
537  * \brief Actual implementation of collecting mbox.
538  *
539  * \tparam Config_Type a type with enumeration of all necessary type traits.
540  * It is expected to be config_type with appropriate type parameters.
541  *
542  * \tparam Tracing_Base base class with implementation of message
543  * delivery tracing methods. Expected to be tracing_enabled_base or
544  * tracing_disabled_base from so_5::impl::msg_tracing_helpers namespace.
545  */
546 template<
547  typename Config_Type,
548  typename Tracing_Base >
550  : public ::so_5::abstract_message_box_t
553  , protected Tracing_Base
554  {
555  //! Short alias for base type which is depended on consexpr or runtime
556  //! size.
557  using size_specific_base_type = typename
559 
560  //! Short alias for base type which is depended on msg_tracing
561  //! facilities.
562  using tracing_base_type = Tracing_Base;
563 
564  //! Alias for actual message which will be sent when all messages
565  //! or signals are collected.
568 
569  //! Alias for builder of message_collected.
570  using messages_collected_builder_t = typename
571  collected_bunch_type_selector<Config_Type>::builder_type;
572 
573  //! Alias for type which should be used for subscription to
574  //! collecting messages.
575  using collecting_message_subscription_type = typename
578 
579  //! Alias for type which should be used for subscription to
580  //! message_collected message.
581  using messages_collected_subscription_type = typename
582  std::conditional<
586  ::type;
587 
588  // Actual constructor which does calls of constructors of base classes.
589  template<
590  typename Specific_Base_Type_Tuple,
591  std::size_t... Specific_Base_Type_Indexes,
592  typename Tracing_Base_Type_Tuple,
593  std::size_t... Tracing_Base_Type_Indexes >
595  mbox_id_t mbox_id,
596  Specific_Base_Type_Tuple && specific_base_type_args,
598  Tracing_Base_Type_Tuple && tracing_base_type_args,
601  mbox_id,
609  {
611  this->m_target );
612  }
613 
614  public :
615  //! A public constructor.
616  /*!
617  * Receives two tuples: one for parameters for size_specific_base_type's
618  * constructor and another for parameters for tracing_base_type's
619  * constructor.
620  *
621  * \tparam Size_Specific_Base_Args list of types for parameters for
622  * size_specific_base_type's constructor.
623  *
624  * \tparam Tracing_Base_Args list of types for parameters for
625  * tracing_base_type's constructor. Note: this can be an empty list.
626  */
627  template<
628  typename... Size_Specific_Base_Args,
629  typename... Tracing_Base_Args >
631  //! Unique ID for that mbox.
632  mbox_id_t mbox_id,
633  //! Parameters related to constexpr or runtime size.
634  std::tuple<Size_Specific_Base_Args...> && size_specific_base_args,
635  //! Parameters related to msg_tracing facilities.
636  //! Note: this can be an empty tuple.
637  std::tuple<Tracing_Base_Args...> && tracing_base_args )
638  : actual_mbox_t{
639  mbox_id,
643  std::make_index_sequence<sizeof...(Tracing_Base_Args)>{} }
644  {}
645 
646  mbox_id_t
647  id() const override
648  {
649  return this->m_id;
650  }
651 
652  void
654  const std::type_index & /*msg_type*/,
655  abstract_message_sink_t & /*subscriber*/ ) override
656  {
659  "subscribe_event_handler is called for collecting-mbox" );
660  }
661 
662  void
664  const std::type_index & /*msg_type*/,
665  abstract_message_sink_t & /*subscriber*/ ) noexcept override
666  {
667  }
668 
669  std::string
670  query_name() const override
671  {
673  s << "<mbox:type=COLLECTINGMBOX:id=" << this->m_id << ">";
674 
675  return s.str();
676  }
677 
679  type() const override
680  {
681  return this->m_target->type();
682  }
683 
684  void
686  message_delivery_mode_t delivery_mode,
687  const std::type_index & msg_type,
688  const message_ref_t & message,
689  unsigned int overlimit_reaction_deep ) override
690  {
692 
694  *this, // as Tracing_Base
695  *this, // as abstract_message_box_t
696  "collect_message",
699 
701  }
702 
703  void
705  const std::type_index & /*msg_type*/,
706  const delivery_filter_t & /*filter*/,
707  abstract_message_sink_t & /*subscriber*/ ) override
708  {
711  "set_delivery_filter is called for collecting-mbox" );
712  }
713 
714  void
716  const std::type_index & /*msg_type*/,
717  abstract_message_sink_t & /*subscriber*/ ) noexcept override
718  {
719  // Nothing to do.
720  }
721 
723  environment() const noexcept override
724  {
725  return this->m_target->environment();
726  }
727 
728  private :
729  //! The current instance of messages_collected to store
730  //! messages to be delivered.
731  messages_collected_builder_t m_msg_builder;
732 
733  static void
734  ensure_valid_message_type( const std::type_index & msg_type_id )
735  {
736  static const std::type_index expected_type_id =
738 
742  std::string( "an attempt to send message or signal of "
743  "different type. expected type: " )
744  + expected_type_id.name() + ", actual type: "
745  + msg_type_id.name() );
746  }
747 
748  void
750  typename Tracing_Base::deliver_op_tracer const & tracer,
751  message_delivery_mode_t delivery_mode,
752  const message_ref_t & message )
753  {
754  this->lock_and_perform( [&] {
755  // A new message must be stored to the current messages_collected.
757  tracer.make_trace( "collected" );
758 
759  // Can we send messages_collected?
761  this->messages_to_collect() ) )
762  {
763  using namespace ::so_5::impl::msg_tracing_helpers::details;
764 
766 
767  tracer.make_trace( "deliver_collected_bunch",
768  text_separator{ "->" },
769  mbox_as_msg_destination{ *(this->m_target) } );
770 
774  std::move(msg_to_send),
775  1u );
776  }
777  } );
778  }
779  };
780 
781 } /* namespace details */
782 
783 /*!
784  * \brief A trait for mbox_template_t to be used when count of
785  * messages to collected is known at the compile time.
786  *
787  * Usage example:
788  * \code
789  * using my_msg_mbox_type = so_5::extra::mboxes::collecting_mbox::mbox_template_t<
790  * my_msg,
791  * so_5::extra::mboxes::collecting_mbox::constexpr_size_traits_t<10> >;
792  * auto my_msg_mbox = my_msg_mbox_type::make( so_environment(), target_mbox );
793  * \endcode
794  */
795 template< std::size_t S >
797  {
798  /*!
799  * \brief Type of container to be used in messages_collected message.
800  *
801  * \note
802  * Because count of collected messages is known at compile time
803  * a very simple and efficient std::array is used.
804  */
806 
807  /*!
808  * \brief A special mixin which must be used in actual type of
809  * messages_collected message for cases when signals are collected.
810  *
811  * \since
812  * v.1.0.2
813  */
815  {
816  public :
818 
819  constexpr std::size_t
820  size() const noexcept { return S; }
821  };
822 
823  /*!
824  * \brief A special mixin which must be used in actual type of
825  * messages_collected message for cases when messages are collected.
826  */
828  {
830  public :
832 
834  storage() noexcept { return m_messages; }
835 
836  const container_type &
837  storage() const noexcept { return m_messages; }
838 
839  constexpr std::size_t
840  size() const noexcept { return S; }
841  };
842 
843  /*!
844  * \brief A special mixin which must be used in actual type of
845  * collecting mbox.
846  */
848  {
849  //! Unique ID of mbox.
851  //! A target for messages_collected.
853 
854  //! Constructor.
856  mbox_id_t mbox_id,
857  mbox_t target )
858  : m_id{ mbox_id }
859  , m_target{ std::move(target) }
860  {}
861 
862  /*!
863  * \brief Total count of messages to be collected before
864  * messages_collected will be sent.
865  */
866  constexpr std::size_t messages_to_collect() const noexcept { return S; }
867  };
868  };
869 
870 /*!
871  * \brief A trait for mbox_template_t to be used when count of
872  * messages to collected is known only at runtime.
873  *
874  * Usage example:
875  * \code
876  * using my_msg_mbox_type = so_5::extra::mboxes::collecting_mbox::mbox_template_t<
877  * my_msg,
878  * so_5::extra::mboxes::collecting_mbox::runtime_size_traits_t >;
879  * auto my_msg_mbox = my_msg_mbox_type::make( so_environment(), target_mbox, collected_msg_count );
880  * \endcode
881  */
883  {
884  /*!
885  * \brief Type of container to be used for collected messages.
886  */
888 
889  /*!
890  * \brief A special mixin which must be used in actual type of
891  * messages_collected message for cases when signals are collected.
892  */
894  {
896  public :
897  signals_collected_mixin_type( std::size_t size ) : m_size{size} {}
898 
899  std::size_t
900  size() const noexcept { return m_size; }
901  };
902 
903  /*!
904  * \brief A special mixin which must be used in actual type of
905  * messages_collected message.
906  */
908  {
910  public :
911  messages_collected_mixin_type( std::size_t size )
913  {}
914 
916  storage() noexcept { return m_messages; }
917 
918  const container_type &
919  storage() const noexcept { return m_messages; }
920 
921  std::size_t
922  size() const noexcept { return m_messages.size(); }
923  };
924 
925  /*!
926  * \brief A special mixin which must be used in actual type of
927  * collecting mbox.
928  */
930  {
931  //! Unique ID of mbox.
933  //! A target for messages_collected.
935  //! Count of messages/signals to be collected.
936  const std::size_t m_size;
937 
938  //! Constructor.
940  mbox_id_t mbox_id,
941  mbox_t target,
942  std::size_t size )
943  : m_id{ mbox_id }
944  , m_target{ std::move(target) }
945  , m_size{ size }
946  {}
947 
948  //! Total count of messages to be collected before
949  //! messages_collected will be sent.
950  std::size_t messages_to_collect() const noexcept { return m_size; }
951  };
952  };
953 
954 //
955 // mbox_template_t
956 //
957 /*!
958  * \brief A template which defines properties for a collecting mbox.
959  *
960  * Usage examples:
961  *
962  * 1. Collecting mbox for immutable messages of type my_msg. Count of
963  * messages to be collected is known only at runtime.
964  * \code
965  * using my_mbox_type = so_5::extra::mboxes::collecting_mbox::mbox_template_t<
966  * my_msg >;
967  * auto my_mbox = my_mbox_type::make(
968  * // A target mbox for messages_collected_t.
969  * target_mbox,
970  * // Count of messages to be collected.
971  * messages_to_collect );
972  *
973  * // To receve messages_collected_t from my_mbox:
974  * void my_agent::on_messages_collected(mhood_t<my_mbox_type::messages_collected_t> cmd) {
975  * ...
976  * }
977  * \endcode
978  *
979  * 2. Collecting mbox for immutable messages of type my_msg. Count of
980  * messages to be collected is known at the compile time.
981  * \code
982  * using my_mbox_type = so_5::extra::mboxes::collecting_mbox::mbox_template_t<
983  * my_msg,
984  * so_5::extra::mboxes::collecting_mbox::constexpr_size_traits_t<10> >;
985  * // Note: there is no need to specify message count because it is already known.
986  * auto my_mbox = my_mbox_type::make(
987  * // A target mbox for messages_collected_t.
988  * target_mbox );
989  *
990  * // To receve messages_collected_t from my_mbox:
991  * void my_agent::on_messages_collected(mhood_t<my_mbox_type::messages_collected_t> cmd) {
992  * ...
993  * }
994  * \endcode
995  *
996  * 3. Collecting mbox for mutable messages of type my_msg. Count of
997  * messages to be collected is known only at runtime.
998  * Please note that message_collected_t is also delivered as mutable message!
999  * \code
1000  * using my_mbox_type = so_5::extra::mboxes::collecting_mbox::mbox_template_t<
1001  * so_5::mutable_msg<my_msg> >;
1002  * auto my_mbox = my_mbox_type::make(
1003  * // A target mbox for messages_collected_t.
1004  * target_mbox,
1005  * // Count of messages to be collected.
1006  * messages_to_collect );
1007  *
1008  * // To receve messages_collected_t from my_mbox:
1009  * void my_agent::on_messages_collected(mutable_mhood_t<my_mbox_type::messages_collected_t> cmd) {
1010  * ...
1011  * }
1012  * \endcode
1013  *
1014  * 4. Collecting mbox for mutable messages of type my_msg. Count of
1015  * messages to be collected is known at the compile time.
1016  * Please note that message_collected_t is also delivered as mutable message!
1017  * \code
1018  * using my_mbox_type = so_5::extra::mboxes::collecting_mbox::mbox_template_t<
1019  * so_5::mutable_msg<my_msg>,
1020  * so_5::extra::mboxes::collecting_mbox::constexpr_size_traits_t<10> >;
1021  * // Note: there is no need to specify message count because it is already known.
1022  * auto my_mbox = my_mbox_type::make(
1023  * // A target mbox for messages_collected_t.
1024  * target_mbox );
1025  *
1026  * // To receve messages_collected_t from my_mbox:
1027  * void my_agent::on_messages_collected(mutable_mhood_t<my_mbox_type::messages_collected_t> cmd) {
1028  * ...
1029  * }
1030  * \endcode
1031  *
1032  * A type of message with collected messages is specified by inner type
1033  * mbox_template_t::messages_collected_t. Please note that actual message type
1034  * for messages_collected_t will depend on \a Collecting_Msg template parameter.
1035  * If \a Collecting_Msg is message type then messages_collected_t will be
1036  * message which holds collected messages inside. Such message type will have
1037  * the following interface:
1038  * \code
1039 // Interface of messages_collected_t for the case
1040 // when Collecting_Msg is a message type.
1041 class message_collected_t {
1042  ... // Some private stuff.
1043 public :
1044  // Count of collected messages.
1045  std::size_t size() const;
1046 
1047  // Perform some action on collected message with the specified index.
1048  template<typename F>
1049  decltype(auto) with_nth(std::size_t index, F && f) const;
1050 
1051  // Perform some action on every collected message.
1052  template<typename F>
1053  void for_each(F && f) const;
1054 
1055  // Perform some action on every collected message.
1056  // Index of message is also passed to functor f.
1057  template<typename F>
1058  void for_each_with_index(F && f) const;
1059 };
1060 \endcode
1061  * A functor for methods `with_nth` and `for_each` must have the following
1062  * format:
1063  * \code
1064  * return_type f(mhood_t<Collecting_Msg> m);
1065  * \endcode
1066  * A functor for method `for_each_with_index` must have the following
1067  * format:
1068  * \code
1069  * return_type f(std::size_t index, mhood_t<Collecting_Msg> m);
1070  * \endcode
1071  * For example, handling of collected immutable messages can looks like:
1072 \code
1073 using my_mbox_type = so_5::extra::mboxes::collecting_mbox::mbox_template_t<
1074  my_msg >;
1075 ...
1076 void my_agent::on_my_messages(mhood_t<my_mbox_type::messages_collected_t> cmd) {
1077  cmd->for_each( [](mhood_t<my_msg> m) { ... } );
1078 }
1079 \endcode
1080  * And handling of collected mutable messages can looks like:
1081 \code
1082 using my_mbox_type = so_5::extra::mboxes::collecting_mbox::mbox_template_t<
1083  so_5::mutable_msg<my_msg> >;
1084 ...
1085 void my_agent::on_my_messages(mutable_mhood_t<my_mbox_type::messages_collected_t> cmd) {
1086  cmd->for_each( [](mutable_mhood_t<my_msg> m) { ... } );
1087 }
1088 \endcode
1089  *
1090  * If \a Collecting_Msg is a type of signal, then
1091  * mbox_template_t::messages_collected_t will have the following format:
1092 \code
1093 // Interface of messages_collected_t for the case
1094 // when Collecting_Msg is a signal type.
1095 class message_collected_t {
1096  ... // Some private stuff.
1097 public :
1098  // Count of collected messages.
1099  std::size_t size() const;
1100 };
1101 \endcode
1102  * It means that if \a Collecting_Msg is a signal type then there is no
1103  * any collected signals instances.
1104  *
1105  * \note
1106  * Collecting mbox can be used for collecting mutable messages. But there are
1107  * some limitations:
1108  * - mutable messages can be collected only if \a target_mbox is
1109  * multi-producer/single-consumer mbox. It is because messages_collected_t
1110  * will be sent also as a mutable message. And sending of mutable messages
1111  * it allowed only to MPSC mboxes;
1112  * - messages_collected_t will be sent as mutable message;
1113  * - it is impossible to collect mutable signals (this is prohibited by
1114  * SObjectizer);
1115  * - it is impossible to collect mutable and immutable messages of the same
1116  * type.
1117  *
1118  * \tparam Collecting_Msg type of message to be collected. It can be simple
1119  * type like `my_msg` (in this case only immutable messages of type `my_msg`
1120  * will be collected). Or it can be `so_5::mutable_msg<my_msg>` (in this
1121  * case only mutable messages of type `my_msg` will be collected).
1122  *
1123  * \tparam Traits type of size-specific traits. It is expected to be
1124  * constexpr_size_traits_t or runtime_size_traits_t (or any other type like
1125  * these two).
1126  *
1127  * \tparam Lock_Type type of lock to be used for thread safety. It can be
1128  * std::mutex or so_5::null_mutex_t (or any other type which can be used
1129  * with std::lock_quard).
1130  */
1131 template<
1132  typename Collecting_Msg,
1133  typename Traits = runtime_size_traits_t,
1134  typename Lock_Type = std::mutex >
1136  {
1137  //! A configuration to be used for that mbox type.
1138  using config_type = details::config_type< Collecting_Msg, Traits, Lock_Type >;
1139  public :
1140  //! Actual type of message_collected instance.
1141  using messages_collected_t = typename
1143 
1144  /*!
1145  * \brief Create an instance of collecting mbox.
1146  *
1147  * Please note that actual list of parameters depends on
1148  * \a Traits type.
1149  * If \a Traits is constexpr_size_traits_t then `make` will have the
1150  * following format:
1151  * \code
1152  * mbox_t make(const mbox_t & target);
1153  * \endcode
1154  * If \a Traits is runtime_size_traits_t then `make` will have the
1155  * following format:
1156  * \code
1157  * mbox_t make(const mbox_t & target, size_t messages_to_collect);
1158  * \endcode
1159  */
1160  template< typename... Args >
1161  static mbox_t
1162  make( const mbox_t & target, Args &&... args )
1163  {
1165 
1167  [&]( const mbox_creation_data_t & data ) {
1168  mbox_t result;
1169 
1171  {
1172  using T = details::actual_mbox_t<
1173  config_type,
1175 
1176  result = mbox_t{ new T{
1177  data.m_id,
1178  std::make_tuple( target, std::forward<Args>(args)... ),
1180  } };
1181  }
1182  else
1183  {
1184  using T = details::actual_mbox_t<
1185  config_type,
1187  result = mbox_t{ new T{
1188  data.m_id,
1189  std::make_tuple( target, std::forward<Args>(args)... ),
1190  std::make_tuple()
1191  } };
1192  }
1193 
1194  return result;
1195  } );
1196  }
1197  };
1198 
1199 } /* namespace collecting_mbox */
1200 
1201 } /* namespace mboxes */
1202 
1203 } /* namespace extra */
1204 
1205 } /* namespace so_5 */
static mbox_t make(const mbox_t &target, Args &&... args)
Create an instance of collecting mbox.
actual_mbox_t(mbox_id_t mbox_id, std::tuple< Size_Specific_Base_Args... > &&size_specific_base_args, std::tuple< Tracing_Base_Args... > &&tracing_base_args)
A public constructor.
void do_deliver_message(message_delivery_mode_t delivery_mode, const std::type_index &msg_type, const message_ref_t &message, unsigned int overlimit_reaction_deep) override
void for_each_with_index(F &&f) const
Do some action for all collected message.
A type of message to be sent when all collected signals are received.
const std::size_t m_size
Count of messages/signals to be collected.
static void ensure_valid_message_type(const std::type_index &msg_type_id)
bool is_ready_to_be_sent(std::size_t messages_to_collect) const noexcept
void check_mutability_validity_for_target_mbox(const so_5::mbox_t &target)
Helper method for checking message mutability and type of the target mbox.
std::unique_ptr< message_type > m_current_msg
The current instance of messages_collected to store messages to be delivered.
constexpr std::size_t messages_to_collect() const noexcept
Total count of messages to be collected before messages_collected will be sent.
actual_mbox_t(mbox_id_t mbox_id, Specific_Base_Type_Tuple &&specific_base_type_args, std::index_sequence< Specific_Base_Type_Indexes... >, Tracing_Base_Type_Tuple &&tracing_base_type_args, std::index_sequence< Tracing_Base_Type_Indexes... >)
Ranges for error codes of each submodules.
Definition: details.hpp:13
traits_t< Config_Type >::container_type m_collected_messages
A container for collected messages.
A trait for mbox_template_t to be used when count of messages to collected is known only at runtime...
void drop_delivery_filter(const std::type_index &, abstract_message_sink_t &) noexcept override
void unsubscribe_event_handler(const std::type_index &, abstract_message_sink_t &) noexcept override
so_5::environment_t & environment() const noexcept override
A special mixin which must be used in actual type of messages_collected message.
void collect_new_message(typename Tracing_Base::deliver_op_tracer const &tracer, message_delivery_mode_t delivery_mode, const message_ref_t &message)
A special mixin which must be used in actual type of messages_collected message for cases when signal...
messages_collected_builder_t m_msg_builder
The current instance of messages_collected to store messages to be delivered.
A helper type which is a collection of type parameters.
decltype(auto) with_nth(std::size_t index, F &&f) const
Do some action with Nth collected message.
void subscribe_event_handler(const std::type_index &, abstract_message_sink_t &) override
A special mixin which must be used in actual type of messages_collected message for cases when signal...
A special mixin which must be used in actual type of messages_collected message for cases when messag...
A special mixin which must be used in actual type of collecting mbox.
const int rc_subscribe_event_handler_be_used_on_collecting_mbox
An attempt to make subscription to collecting_mbox.
size_specific_base_type(mbox_id_t mbox_id, mbox_t target, std::size_t size)
Constructor.
void store_collected_messages(size_t index, message_ref_t msg)
Store another collected message at the specified index.
A template which defines properties for a collecting mbox.
A trait for mbox_template_t to be used when count of messages to collected is known at the compile ti...
void for_each(F &&f) const
Do some action for all collected message.
A helper type for selection of actual message type and type of message builder.
const int rc_different_message_type
An attempt to send a message or signal of different type.
void store(message_ref_t message, std::size_t messages_to_collect)
Store another instance of collecting messages.
optional< message_ref_t > detect_message_to_store(message_ref_t what)
Detect the actual message to be collected (if it is present).
std::size_t messages_to_collect() const noexcept
Total count of messages to be collected before messages_collected will be sent.
const int rc_delivery_filter_cannot_be_used_on_collecting_mbox
An attempt to set delivery filter to collecting_mbox.
void set_delivery_filter(const std::type_index &, const delivery_filter_t &, abstract_message_sink_t &) override
A special mixin which must be used in actual type of collecting mbox.