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 & /*type_wrapper*/,
655  const so_5::message_limit::control_block_t * /*limit*/,
656  agent_t & /*subscriber*/ ) override
657  {
660  "subscribe_event_handler is called for collecting-mbox" );
661  }
662 
663  void
665  const std::type_index & /*type_wrapper*/,
666  agent_t & /*subscriber*/ ) override
667  {
668  }
669 
670  std::string
671  query_name() const override
672  {
674  s << "<mbox:type=COLLECTINGMBOX:id=" << this->m_id << ">";
675 
676  return s.str();
677  }
678 
680  type() const override
681  {
682  return this->m_target->type();
683  }
684 
685  void
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",
698 
700  }
701 
702  void
704  const std::type_index & /*msg_type*/,
705  const delivery_filter_t & /*filter*/,
706  agent_t & /*subscriber*/ ) override
707  {
710  "set_delivery_filter is called for collecting-mbox" );
711  }
712 
713  void
715  const std::type_index & /*msg_type*/,
716  agent_t & /*subscriber*/ ) noexcept override
717  {
718  // Nothing to do.
719  }
720 
722  environment() const noexcept override
723  {
724  return this->m_target->environment();
725  }
726 
727  private :
728  //! The current instance of messages_collected to store
729  //! messages to be delivered.
730  messages_collected_builder_t m_msg_builder;
731 
732  static void
733  ensure_valid_message_type( const std::type_index & msg_type_id )
734  {
735  static const std::type_index expected_type_id =
737 
741  std::string( "an attempt to send message or signal of "
742  "different type. expected type: " )
743  + expected_type_id.name() + ", actual type: "
744  + msg_type_id.name() );
745  }
746 
747  void
749  typename Tracing_Base::deliver_op_tracer const & tracer,
750  const message_ref_t & message )
751  {
752  this->lock_and_perform( [&] {
753  // A new message must be stored to the current messages_collected.
755  tracer.make_trace( "collected" );
756 
757  // Can we send messages_collected?
759  this->messages_to_collect() ) )
760  {
761  using namespace ::so_5::impl::msg_tracing_helpers::details;
762 
764 
765  tracer.make_trace( "deliver_collected_bunch",
766  text_separator{ "->" },
767  mbox_as_msg_destination{ *(this->m_target) } );
768 
771  std::move(msg_to_send),
772  1u );
773  }
774  } );
775  }
776  };
777 
778 } /* namespace details */
779 
780 /*!
781  * \brief A trait for mbox_template_t to be used when count of
782  * messages to collected is known at the compile time.
783  *
784  * Usage example:
785  * \code
786  * using my_msg_mbox_type = so_5::extra::mboxes::collecting_mbox::mbox_template_t<
787  * my_msg,
788  * so_5::extra::mboxes::collecting_mbox::constexpr_size_traits_t<10> >;
789  * auto my_msg_mbox = my_msg_mbox_type::make( so_environment(), target_mbox );
790  * \endcode
791  */
792 template< std::size_t S >
794  {
795  /*!
796  * \brief Type of container to be used in messages_collected message.
797  *
798  * \note
799  * Because count of collected messages is known at compile time
800  * a very simple and efficient std::array is used.
801  */
803 
804  /*!
805  * \brief A special mixin which must be used in actual type of
806  * messages_collected message for cases when signals are collected.
807  *
808  * \since
809  * v.1.0.2
810  */
812  {
813  public :
815 
816  constexpr std::size_t
817  size() const noexcept { return S; }
818  };
819 
820  /*!
821  * \brief A special mixin which must be used in actual type of
822  * messages_collected message for cases when messages are collected.
823  */
825  {
827  public :
829 
831  storage() noexcept { return m_messages; }
832 
833  const container_type &
834  storage() const noexcept { return m_messages; }
835 
836  constexpr std::size_t
837  size() const noexcept { return S; }
838  };
839 
840  /*!
841  * \brief A special mixin which must be used in actual type of
842  * collecting mbox.
843  */
845  {
846  //! Unique ID of mbox.
848  //! A target for messages_collected.
850 
851  //! Constructor.
853  mbox_id_t mbox_id,
854  mbox_t target )
855  : m_id{ mbox_id }
856  , m_target{ std::move(target) }
857  {}
858 
859  /*!
860  * \brief Total count of messages to be collected before
861  * messages_collected will be sent.
862  */
863  constexpr std::size_t messages_to_collect() const noexcept { return S; }
864  };
865  };
866 
867 /*!
868  * \brief A trait for mbox_template_t to be used when count of
869  * messages to collected is known only at runtime.
870  *
871  * Usage example:
872  * \code
873  * using my_msg_mbox_type = so_5::extra::mboxes::collecting_mbox::mbox_template_t<
874  * my_msg,
875  * so_5::extra::mboxes::collecting_mbox::runtime_size_traits_t >;
876  * auto my_msg_mbox = my_msg_mbox_type::make( so_environment(), target_mbox, collected_msg_count );
877  * \endcode
878  */
880  {
881  /*!
882  * \brief Type of container to be used for collected messages.
883  */
885 
886  /*!
887  * \brief A special mixin which must be used in actual type of
888  * messages_collected message for cases when signals are collected.
889  */
891  {
893  public :
894  signals_collected_mixin_type( std::size_t size ) : m_size{size} {}
895 
896  std::size_t
897  size() const noexcept { return m_size; }
898  };
899 
900  /*!
901  * \brief A special mixin which must be used in actual type of
902  * messages_collected message.
903  */
905  {
907  public :
908  messages_collected_mixin_type( std::size_t size )
910  {}
911 
913  storage() noexcept { return m_messages; }
914 
915  const container_type &
916  storage() const noexcept { return m_messages; }
917 
918  std::size_t
919  size() const noexcept { return m_messages.size(); }
920  };
921 
922  /*!
923  * \brief A special mixin which must be used in actual type of
924  * collecting mbox.
925  */
927  {
928  //! Unique ID of mbox.
930  //! A target for messages_collected.
932  //! Count of messages/signals to be collected.
933  const std::size_t m_size;
934 
935  //! Constructor.
937  mbox_id_t mbox_id,
938  mbox_t target,
939  std::size_t size )
940  : m_id{ mbox_id }
941  , m_target{ std::move(target) }
942  , m_size{ size }
943  {}
944 
945  //! Total count of messages to be collected before
946  //! messages_collected will be sent.
947  std::size_t messages_to_collect() const noexcept { return m_size; }
948  };
949  };
950 
951 //
952 // mbox_template_t
953 //
954 /*!
955  * \brief A template which defines properties for a collecting mbox.
956  *
957  * Usage examples:
958  *
959  * 1. Collecting mbox for immutable messages of type my_msg. Count of
960  * messages to be collected is known only at runtime.
961  * \code
962  * using my_mbox_type = so_5::extra::mboxes::collecting_mbox::mbox_template_t<
963  * my_msg >;
964  * auto my_mbox = my_mbox_type::make(
965  * // A target mbox for messages_collected_t.
966  * target_mbox,
967  * // Count of messages to be collected.
968  * messages_to_collect );
969  *
970  * // To receve messages_collected_t from my_mbox:
971  * void my_agent::on_messages_collected(mhood_t<my_mbox_type::messages_collected_t> cmd) {
972  * ...
973  * }
974  * \endcode
975  *
976  * 2. Collecting mbox for immutable messages of type my_msg. Count of
977  * messages to be collected is known at the compile time.
978  * \code
979  * using my_mbox_type = so_5::extra::mboxes::collecting_mbox::mbox_template_t<
980  * my_msg,
981  * so_5::extra::mboxes::collecting_mbox::constexpr_size_traits_t<10> >;
982  * // Note: there is no need to specify message count because it is already known.
983  * auto my_mbox = my_mbox_type::make(
984  * // A target mbox for messages_collected_t.
985  * target_mbox );
986  *
987  * // To receve messages_collected_t from my_mbox:
988  * void my_agent::on_messages_collected(mhood_t<my_mbox_type::messages_collected_t> cmd) {
989  * ...
990  * }
991  * \endcode
992  *
993  * 3. Collecting mbox for mutable messages of type my_msg. Count of
994  * messages to be collected is known only at runtime.
995  * Please note that message_collected_t is also delivered as mutable message!
996  * \code
997  * using my_mbox_type = so_5::extra::mboxes::collecting_mbox::mbox_template_t<
998  * so_5::mutable_msg<my_msg> >;
999  * auto my_mbox = my_mbox_type::make(
1000  * // A target mbox for messages_collected_t.
1001  * target_mbox,
1002  * // Count of messages to be collected.
1003  * messages_to_collect );
1004  *
1005  * // To receve messages_collected_t from my_mbox:
1006  * void my_agent::on_messages_collected(mutable_mhood_t<my_mbox_type::messages_collected_t> cmd) {
1007  * ...
1008  * }
1009  * \endcode
1010  *
1011  * 4. Collecting mbox for mutable messages of type my_msg. Count of
1012  * messages to be collected is known at the compile time.
1013  * Please note that message_collected_t is also delivered as mutable message!
1014  * \code
1015  * using my_mbox_type = so_5::extra::mboxes::collecting_mbox::mbox_template_t<
1016  * so_5::mutable_msg<my_msg>,
1017  * so_5::extra::mboxes::collecting_mbox::constexpr_size_traits_t<10> >;
1018  * // Note: there is no need to specify message count because it is already known.
1019  * auto my_mbox = my_mbox_type::make(
1020  * // A target mbox for messages_collected_t.
1021  * target_mbox );
1022  *
1023  * // To receve messages_collected_t from my_mbox:
1024  * void my_agent::on_messages_collected(mutable_mhood_t<my_mbox_type::messages_collected_t> cmd) {
1025  * ...
1026  * }
1027  * \endcode
1028  *
1029  * A type of message with collected messages is specified by inner type
1030  * mbox_template_t::messages_collected_t. Please note that actual message type
1031  * for messages_collected_t will depend on \a Collecting_Msg template parameter.
1032  * If \a Collecting_Msg is message type then messages_collected_t will be
1033  * message which holds collected messages inside. Such message type will have
1034  * the following interface:
1035  * \code
1036 // Interface of messages_collected_t for the case
1037 // when Collecting_Msg is a message type.
1038 class message_collected_t {
1039  ... // Some private stuff.
1040 public :
1041  // Count of collected messages.
1042  std::size_t size() const;
1043 
1044  // Perform some action on collected message with the specified index.
1045  template<typename F>
1046  decltype(auto) with_nth(std::size_t index, F && f) const;
1047 
1048  // Perform some action on every collected message.
1049  template<typename F>
1050  void for_each(F && f) const;
1051 
1052  // Perform some action on every collected message.
1053  // Index of message is also passed to functor f.
1054  template<typename F>
1055  void for_each_with_index(F && f) const;
1056 };
1057 \endcode
1058  * A functor for methods `with_nth` and `for_each` must have the following
1059  * format:
1060  * \code
1061  * return_type f(mhood_t<Collecting_Msg> m);
1062  * \endcode
1063  * A functor for method `for_each_with_index` must have the following
1064  * format:
1065  * \code
1066  * return_type f(std::size_t index, mhood_t<Collecting_Msg> m);
1067  * \endcode
1068  * For example, handling of collected immutable messages can looks like:
1069 \code
1070 using my_mbox_type = so_5::extra::mboxes::collecting_mbox::mbox_template_t<
1071  my_msg >;
1072 ...
1073 void my_agent::on_my_messages(mhood_t<my_mbox_type::messages_collected_t> cmd) {
1074  cmd->for_each( [](mhood_t<my_msg> m) { ... } );
1075 }
1076 \endcode
1077  * And handling of collected mutable messages can looks like:
1078 \code
1079 using my_mbox_type = so_5::extra::mboxes::collecting_mbox::mbox_template_t<
1080  so_5::mutable_msg<my_msg> >;
1081 ...
1082 void my_agent::on_my_messages(mutable_mhood_t<my_mbox_type::messages_collected_t> cmd) {
1083  cmd->for_each( [](mutable_mhood_t<my_msg> m) { ... } );
1084 }
1085 \endcode
1086  *
1087  * If \a Collecting_Msg is a type of signal, then
1088  * mbox_template_t::messages_collected_t will have the following format:
1089 \code
1090 // Interface of messages_collected_t for the case
1091 // when Collecting_Msg is a signal type.
1092 class message_collected_t {
1093  ... // Some private stuff.
1094 public :
1095  // Count of collected messages.
1096  std::size_t size() const;
1097 };
1098 \endcode
1099  * It means that if \a Collecting_Msg is a signal type then there is no
1100  * any collected signals instances.
1101  *
1102  * \note
1103  * Collecting mbox can be used for collecting mutable messages. But there are
1104  * some limitations:
1105  * - mutable messages can be collected only if \a target_mbox is
1106  * multi-producer/single-consumer mbox. It is because messages_collected_t
1107  * will be sent also as a mutable message. And sending of mutable messages
1108  * it allowed only to MPSC mboxes;
1109  * - messages_collected_t will be sent as mutable message;
1110  * - it is impossible to collect mutable signals (this is prohibited by
1111  * SObjectizer);
1112  * - it is impossible to collect mutable and immutable messages of the same
1113  * type.
1114  *
1115  * \tparam Collecting_Msg type of message to be collected. It can be simple
1116  * type like `my_msg` (in this case only immutable messages of type `my_msg`
1117  * will be collected). Or it can be `so_5::mutable_msg<my_msg>` (in this
1118  * case only mutable messages of type `my_msg` will be collected).
1119  *
1120  * \tparam Traits type of size-specific traits. It is expected to be
1121  * constexpr_size_traits_t or runtime_size_traits_t (or any other type like
1122  * these two).
1123  *
1124  * \tparam Lock_Type type of lock to be used for thread safety. It can be
1125  * std::mutex or so_5::null_mutex_t (or any other type which can be used
1126  * with std::lock_quard).
1127  */
1128 template<
1129  typename Collecting_Msg,
1130  typename Traits = runtime_size_traits_t,
1131  typename Lock_Type = std::mutex >
1133  {
1134  //! A configuration to be used for that mbox type.
1135  using config_type = details::config_type< Collecting_Msg, Traits, Lock_Type >;
1136  public :
1137  //! Actual type of message_collected instance.
1138  using messages_collected_t = typename
1140 
1141  /*!
1142  * \brief Create an instance of collecting mbox.
1143  *
1144  * Please note that actual list of parameters depends on
1145  * \a Traits type.
1146  * If \a Traits is constexpr_size_traits_t then `make` will have the
1147  * following format:
1148  * \code
1149  * mbox_t make(const mbox_t & target);
1150  * \endcode
1151  * If \a Traits is runtime_size_traits_t then `make` will have the
1152  * following format:
1153  * \code
1154  * mbox_t make(const mbox_t & target, size_t messages_to_collect);
1155  * \endcode
1156  */
1157  template< typename... Args >
1158  static mbox_t
1159  make( const mbox_t & target, Args &&... args )
1160  {
1162 
1164  [&]( const mbox_creation_data_t & data ) {
1165  mbox_t result;
1166 
1168  {
1169  using T = details::actual_mbox_t<
1170  config_type,
1172 
1173  result = mbox_t{ new T{
1174  data.m_id,
1175  std::make_tuple( target, std::forward<Args>(args)... ),
1177  } };
1178  }
1179  else
1180  {
1181  using T = details::actual_mbox_t<
1182  config_type,
1184  result = mbox_t{ new T{
1185  data.m_id,
1186  std::make_tuple( target, std::forward<Args>(args)... ),
1187  std::make_tuple()
1188  } };
1189  }
1190 
1191  return result;
1192  } );
1193  }
1194  };
1195 
1196 } /* namespace collecting_mbox */
1197 
1198 } /* namespace mboxes */
1199 
1200 } /* namespace extra */
1201 
1202 } /* 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 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... >)
void collect_new_message(typename Tracing_Base::deliver_op_tracer const &tracer, const message_ref_t &message)
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 subscribe_event_handler(const std::type_index &, const so_5::message_limit::control_block_t *, agent_t &) override
so_5::environment_t & environment() const noexcept override
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.
void drop_delivery_filter(const std::type_index &, agent_t &) noexcept override
void do_deliver_message(const std::type_index &msg_type, const message_ref_t &message, unsigned int overlimit_reaction_deep) override
void set_delivery_filter(const std::type_index &, const delivery_filter_t &, agent_t &) override
A special mixin which must be used in actual type of messages_collected message for cases when signal...
void unsubscribe_event_handlers(const std::type_index &, agent_t &) override
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.
A special mixin which must be used in actual type of collecting mbox.