SObjectizer-5 Extra
Loading...
Searching...
No Matches
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
26namespace so_5 {
27
28namespace extra {
29
30namespace mboxes {
31
32namespace collecting_mbox {
33
34namespace 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
65namespace 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 */
98template<
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 */
113template< typename Config_Type >
114using collecting_msg_t = typename Config_Type::collecting_msg_type;
115
116template< typename Config_Type >
117using traits_t = typename Config_Type::traits_type;
118
119template< typename Config_Type >
120using 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 */
135template< typename Config_Type >
136void
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 */
158template< typename Config_Type >
159class collected_messages_bunch_t final
160 : public so_5::message_t
161 , traits_t<Config_Type>::messages_collected_mixin_type
162 {
163 template<typename> friend class collected_messages_bunch_builder_t;
164
165 using mixin_base_type =
166 typename traits_t<Config_Type>::messages_collected_mixin_type;
167
168 //! A container for 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() )
253 f(mhood_t< collecting_msg_t<Config_Type> >{ref});
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 ] };
287 f(index, mhood_t< collecting_msg_t<Config_Type> >{ref});
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 */
308inline 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 */
334template< 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
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 */
413template< typename Config_Type >
414class collected_signals_bunch_t final
415 : public so_5::message_t
416 , traits_t<Config_Type>::signals_collected_mixin_type
417 {
418 template<typename> friend class collected_signals_bunch_builder_t;
419
420 using mixin_base_type =
421 typename traits_t<Config_Type>::signals_collected_mixin_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 */
444template< 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
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 */
497template< typename Config_Type >
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 */
529template< typename Config_Type >
530using 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 */
546template<
547 typename Config_Type,
548 typename Tracing_Base >
549class actual_mbox_t final
550 : public ::so_5::abstract_message_box_t
551 , protected traits_t<Config_Type>::size_specific_base_type
552 , protected ::so_5::details::lock_holder_detector< lock_t<Config_Type> >::type
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
558 traits_t<Config_Type>::size_specific_base_type;
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.
566 using messages_collected_t = ::so_5::extra::mboxes::collecting_mbox::details::
567 messages_collected_t<Config_Type>;
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
576 message_payload_type<
577 collecting_msg_t<Config_Type>>::subscription_type;
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<
583 is_mutable_message< collecting_msg_t<Config_Type> >::value,
584 mutable_msg< messages_collected_t >,
585 messages_collected_t >
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,
600 : size_specific_base_type{
601 mbox_id,
602 std::get<Specific_Base_Type_Indexes>(
603 std::forward<Specific_Base_Type_Tuple>(
604 specific_base_type_args))... }
605 , tracing_base_type{
606 std::get<Tracing_Base_Type_Indexes>(
607 std::forward<Tracing_Base_Type_Tuple>(
608 tracing_base_type_args))... }
609 {
610 check_mutability_validity_for_target_mbox<Config_Type>(
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,
640 std::move(size_specific_base_args),
641 std::make_index_sequence<sizeof...(Size_Specific_Base_Args)>{},
642 std::move(tracing_base_args),
643 std::make_index_sequence<sizeof...(Tracing_Base_Args)>{} }
644 {}
645
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 {
657 SO_5_THROW_EXCEPTION(
658 errors::rc_subscribe_event_handler_be_used_on_collecting_mbox,
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 {
672 std::ostringstream s;
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 {
691 ensure_valid_message_type( msg_type );
692
693 typename Tracing_Base::deliver_op_tracer tracer{
694 *this, // as Tracing_Base
695 *this, // as abstract_message_box_t
696 "collect_message",
697 delivery_mode,
698 msg_type, message, overlimit_reaction_deep };
699
700 collect_new_message( tracer, delivery_mode, message );
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 {
709 SO_5_THROW_EXCEPTION(
710 errors::rc_delivery_filter_cannot_be_used_on_collecting_mbox,
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 =
737 typeid(collecting_message_subscription_type);
738
739 if( expected_type_id != msg_type_id )
740 SO_5_THROW_EXCEPTION(
741 errors::rc_different_message_type,
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.
756 m_msg_builder.store( message, this->messages_to_collect() );
757 tracer.make_trace( "collected" );
758
759 // Can we send messages_collected?
760 if( m_msg_builder.is_ready_to_be_sent(
761 this->messages_to_collect() ) )
762 {
763 using namespace ::so_5::impl::msg_tracing_helpers::details;
764
765 auto msg_to_send = m_msg_builder.extract_message();
766
767 tracer.make_trace( "deliver_collected_bunch",
768 text_separator{ "->" },
769 mbox_as_msg_destination{ *(this->m_target) } );
770
771 this->m_target->do_deliver_message(
772 delivery_mode,
773 typeid(messages_collected_subscription_type),
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 */
795template< 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 :
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.
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.
1041class message_collected_t {
1042 ... // Some private stuff.
1043public :
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
1073using my_mbox_type = so_5::extra::mboxes::collecting_mbox::mbox_template_t<
1074 my_msg >;
1075...
1076void 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
1082using my_mbox_type = so_5::extra::mboxes::collecting_mbox::mbox_template_t<
1083 so_5::mutable_msg<my_msg> >;
1084...
1085void 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.
1095class message_collected_t {
1096 ... // Some private stuff.
1097public :
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 */
1131template<
1132 typename Collecting_Msg,
1133 typename Traits = runtime_size_traits_t,
1134 typename Lock_Type = std::mutex >
1135class mbox_template_t final
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
1142 details::messages_collected_t<config_type>;
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 {
1164 ensure_not_mutable_signal< Collecting_Msg >();
1165
1166 return target->environment().make_custom_mbox(
1167 [&]( const mbox_creation_data_t & data ) {
1168 mbox_t result;
1169
1170 if( data.m_tracer.get().is_msg_tracing_enabled() )
1171 {
1172 using T = details::actual_mbox_t<
1173 config_type,
1174 ::so_5::impl::msg_tracing_helpers::tracing_enabled_base >;
1175
1176 result = mbox_t{ new T{
1177 data.m_id,
1178 std::make_tuple( target, std::forward<Args>(args)... ),
1179 std::make_tuple( std::ref(data.m_tracer) )
1180 } };
1181 }
1182 else
1183 {
1184 using T = details::actual_mbox_t<
1185 config_type,
1186 ::so_5::impl::msg_tracing_helpers::tracing_disabled_base >;
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 */
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 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.
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 unsubscribe_event_handler(const std::type_index &, abstract_message_sink_t &) noexcept override
void collect_new_message(typename Tracing_Base::deliver_op_tracer const &tracer, message_delivery_mode_t delivery_mode, const message_ref_t &message)
so_5::environment_t & environment() const noexcept override
void subscribe_event_handler(const std::type_index &, abstract_message_sink_t &) override
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 drop_delivery_filter(const std::type_index &, abstract_message_sink_t &) noexcept override
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 set_delivery_filter(const std::type_index &, const delivery_filter_t &, abstract_message_sink_t &) override
static void ensure_valid_message_type(const std::type_index &msg_type_id)
std::unique_ptr< message_type > m_current_msg
The current instance of messages_collected to store messages to be delivered.
void store(message_ref_t message, std::size_t messages_to_collect)
Store another instance of collecting messages.
traits_t< Config_Type >::container_type m_collected_messages
A container for collected messages.
decltype(auto) with_nth(std::size_t index, F &&f) const
Do some action with Nth collected message.
void for_each_with_index(F &&f) const
Do some action for all collected message.
void for_each(F &&f) const
Do some action for all collected message.
void store_collected_messages(size_t index, message_ref_t msg)
Store another collected message at the specified index.
static mbox_t make(const mbox_t &target, Args &&... args)
Create an instance of 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...
const int collecting_mbox_errors
Starting point for errors of collecting_mbox submodule.
optional< message_ref_t > detect_message_to_store(message_ref_t what)
Detect the actual message to be collected (if it is present).
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.
const int rc_different_message_type
An attempt to send a message or signal of different type.
const int rc_delivery_filter_cannot_be_used_on_collecting_mbox
An attempt to set delivery filter to collecting_mbox.
const int rc_subscribe_event_handler_be_used_on_collecting_mbox
An attempt to make subscription to collecting_mbox.
Ranges for error codes of each submodules.
Definition details.hpp:13
A special mixin which must be used in actual type of collecting mbox.
constexpr std::size_t messages_to_collect() const noexcept
Total count of messages to be collected before messages_collected will be sent.
A trait for mbox_template_t to be used when count of messages to collected is known at the compile ti...
A helper type for selection of actual message type and type of message builder.
A helper type which is a collection of type parameters.
A special mixin which must be used in actual type of collecting mbox.
size_specific_base_type(mbox_id_t mbox_id, mbox_t target, std::size_t size)
Constructor.
std::size_t messages_to_collect() const noexcept
Total count of messages to be collected before messages_collected will be sent.
const std::size_t m_size
Count of messages/signals to be collected.
A trait for mbox_template_t to be used when count of messages to collected is known only at runtime.