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