1 /*!
2  * \file
3  * \brief Variuos send functions for simplification of sending
4  * enveloped messages.
5  *
6  * \since
7  * v.1.2.0
8  */
9 
10 #pragma once
11 
12 #include <so_5_extra/enveloped_msg/errors.hpp>
13 
14 #include <so_5/send_functions.hpp>
15 
16 #include <so_5/optional.hpp>
17 
18 namespace so_5 {
19 
20 namespace extra {
21 
22 namespace enveloped_msg {
23 
24 namespace details {
25 
26 /*!
27  * \brief Internal type that holds a message before it will be enveloped.
28  *
29  * This type provides such methods as:
30  * - envelope() for creation of new envelope;
31  * - send_to for sending of ordinary message to the specified mbox (mchain);
32  * - send_delayed_to for sending of delayed message;
33  * - send_periodic_to for sending of periodic message.
34  *
35  * \note
36  * Object of type payload_holder_t can be in two states: full and empty.
37  * When object is full then envelope() creates an envelope and switches
38  * the object to empty state. If envelope() is called in empty state
39  * an exeception is thrown.
40  *
41  * \since
42  * v.1.2.0
43  */
44 class payload_holder_t final
45 	{
46 		struct data_t final
47 			{
48 				std::type_index m_msg_type;
49 				// Can be null pointer in the case of error.
50 				message_ref_t m_message;
51 			};
52 
53 		optional< data_t > m_data;
54 
55 		void
ensure_not_empty_object(const char * context_name) const56 		ensure_not_empty_object(
57 			const char * context_name ) const
58 			{
59 				if( !m_data )
60 					SO_5_THROW_EXCEPTION(
61 							errors::rc_empty_payload_holder,
62 							std::string( "empty payload_holder can't be used for: " ) +
63 									context_name );
64 			}
65 
66 	public :
payload_holder_t(std::type_index msg_type,message_ref_t message)67 		payload_holder_t(
68 			std::type_index msg_type,
69 			message_ref_t message )
70 			:	m_data{ data_t{ msg_type, message } }
71 			{}
72 
73 		payload_holder_t( const payload_holder_t & ) = delete;
74 
75 		payload_holder_t( payload_holder_t && ) = default;
76 		payload_holder_t & operator=( payload_holder_t && ) = default;
77 
78 		template< typename Envelope, typename... Args >
79 		[[nodiscard]]
80 		payload_holder_t
envelope(Args &&...args)81 		envelope( Args && ...args )
82 			{
83 				ensure_not_empty_object( "envelope()" );
84 
85 				message_ref_t envelope{
86 					std::make_unique< Envelope >(
87 							m_data->m_message,
88 							std::forward<Args>(args)... )
89 				};
90 
91 				payload_holder_t result{
92 						m_data->m_msg_type,
93 						std::move(envelope)
94 				};
95 
96 				// Now data can be dropped. Payload holder becomes empty.
97 				m_data.reset();
98 
99 				return result;
100 			}
101 
102 		template< typename Target >
103 		void
send_to(Target && to)104 		send_to( Target && to )
105 			{
106 				ensure_not_empty_object( "send_to()" );
107 
108 				// NOTE: there is no need to check mutability of a message.
109 				// This check should be performed by the target mbox itself.
110 				so_5::send_functions_details::arg_to_mbox( to )->
111 						do_deliver_message(
112 								m_data->m_msg_type,
113 								m_data->m_message,
114 								1u );
115 			}
116 
117 		void
send_delayed_to(const so_5::mbox_t & to,std::chrono::steady_clock::duration pause)118 		send_delayed_to(
119 			const so_5::mbox_t & to,
120 			std::chrono::steady_clock::duration pause )
121 			{
122 				ensure_not_empty_object( "send_delayed_to()" );
123 
124 				so_5::low_level_api::single_timer(
125 						m_data->m_msg_type,
126 						m_data->m_message,
127 						to,
128 						pause );
129 			}
130 
131 		template< typename Target >
132 		void
send_delayed_to(Target && to,std::chrono::steady_clock::duration pause)133 		send_delayed_to(
134 			Target && to,
135 			std::chrono::steady_clock::duration pause )
136 			{
137 				return this->send_delayed_to(
138 						so_5::send_functions_details::arg_to_mbox( to ),
139 						pause );
140 			}
141 
142 		[[nodiscard]]
143 		auto
send_periodic_to(const so_5::mbox_t & to,std::chrono::steady_clock::duration pause,std::chrono::steady_clock::duration period)144 		send_periodic_to(
145 			const so_5::mbox_t & to,
146 			std::chrono::steady_clock::duration pause,
147 			std::chrono::steady_clock::duration period )
148 			{
149 				ensure_not_empty_object( "send_periodic_to()" );
150 
151 				return so_5::low_level_api::schedule_timer(
152 						m_data->m_msg_type,
153 						m_data->m_message,
154 						to,
155 						pause,
156 						period );
157 			}
158 
159 		template< typename Target >
160 		[[nodiscard]]
161 		auto
send_periodic_to(Target && to,std::chrono::steady_clock::duration pause,std::chrono::steady_clock::duration period)162 		send_periodic_to(
163 			Target && to,
164 			std::chrono::steady_clock::duration pause,
165 			std::chrono::steady_clock::duration period )
166 			{
167 				return this->send_periodic_to(
168 						so_5::send_functions_details::arg_to_mbox( to ),
169 						pause,
170 						period );
171 			}
172 	};
173 
174 } /* namespace details */
175 
176 /*!
177  * \brief A special message builder that allows to wrap a message into an
178  * envelope.
179  *
180  * This function creates an instance of a specified message type and creates
181  * a chain of builders that envelope this instance into an envelope and send
182  * the enveloped message as ordinary or delayed/periodic message.
183  *
184  * Usage examples:
185  * \code
186  * namespace msg_ns = so_5::extra::enveloped_msg;
187  *
188  * // Create message of type my_message, envelop it into my_envelope
189  * // and then send it to the mbox mb1.
190  * so_5::mbox_t mb1 = ...;
191  * msg_ns::make<my_message>(...)
192  * 		.envelope<my_envelope>(...)
193  * 		.send_to(mb1);
194  *
195  * // Create message of type my_message, envelop it into my_envelope
196  * // and then send it to the mchain ch1.
197  * so_5::mchain_t ch1 = ...;
198  * msg_ns::make<my_message>(...)
199  * 		.envelope<my_envelope>(...)
200  * 		.send_to(ch1);
201  *
202  * // Create message of type my_message, envelop it into my_envelope
203  * // and then send it to the direct mbox of the agent a1.
204  * so_5::agent_t & a1 = ...;
205  * msg_ns::make<my_message>(...)
206  * 		.envelope<my_envelope>(...)
207  * 		.send_to(a1);
208  *
209  * // Create message of type my_message, envelop it into my_envelope
210  * // and then send it to the mbox mb1 as delayed message.
211  * so_5::mbox_t mb1 = ...;
212  * msg_ns::make<my_message>(...)
213  * 		.envelope<my_envelope>(...)
214  * 		.send_delayed_to(mb1, 10s);
215  *
216  * // Create message of type my_message, envelop it into my_envelope
217  * // and then send it to the mchain ch1 as delayed message.
218  * so_5::mchain_t ch1 = ...;
219  * msg_ns::make<my_message>(...)
220  * 		.envelope<my_envelope>(...)
221  * 		.send_delayed_to(ch1, 10s);
222  *
223  * // Create message of type my_message, envelop it into my_envelope
224  * // and then send it to the direct mbox of the agent a1 as delayed message.
225  * so_5::agent_t & a1 = ...;
226  * msg_ns::make<my_message>(...)
227  * 		.envelope<my_envelope>(...)
228  * 		.send_delayed_to(a1, 10s);
229  *
230  * // Create message of type my_message, envelop it into my_envelope
231  * // and then send it to the mbox mb1 as periodic message.
232  * so_5::mbox_t mb1 = ...;
233  * auto timer_id = msg_ns::make<my_message>(...)
234  * 		.envelope<my_envelope>(...)
235  * 		.send_periodic_to(mb1, 10s, 30s);
236  *
237  * // Create message of type my_message, envelop it into my_envelope
238  * // and then send it to the mchain ch1 as delayed message.
239  * so_5::mchain_t ch1 = ...;
240  * auto timer_id = msg_ns::make<my_message>(...)
241  * 		.envelope<my_envelope>(...)
242  * 		.send_periodic_to(ch1, 10s, 30s);
243  *
244  * // Create message of type my_message, envelop it into my_envelope
245  * // and then send it to the direct mbox of the agent a1 as delayed message.
246  * so_5::agent_t & a1 = ...;
247  * auto timer_id = msg_ns::make<my_message>(...)
248  * 		.envelope<my_envelope>(...)
249  * 		.send_periodic_to(a1, 10s, 30s);
250  * \endcode
251  *
252  * \since
253  * v.1.2.0
254  */
255 template< typename Message, typename... Args >
256 [[nodiscard]]
257 details::payload_holder_t
make(Args &&...args)258 make( Args && ...args )
259 	{
260 		message_ref_t message{
261 				so_5::details::make_message_instance< Message >(
262 						std::forward<Args>(args)... )
263 		};
264 
265 		so_5::details::mark_as_mutable_if_necessary< Message >( *message );
266 
267 		return {
268 				message_payload_type< Message >::subscription_type_index(),
269 				std::move(message)
270 		};
271 	}
272 
273 } /* namespace enveloped_msg */
274 
275 } /* namespace extra */
276 
277 } /* namespace so_5 */
278 
279