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