1 /*
2 	SObjectizer 5.
3 */
4 
5 /*!
6 	\file
7 	\brief Definition of the template class message_holder.
8 
9 	\since
10 	v.5.6.0
11 */
12 
13 #pragma once
14 
15 #include <so_5/message.hpp>
16 
17 #include <so_5/compiler_features.hpp>
18 
19 #include <type_traits>
20 
21 namespace so_5
22 {
23 
24 /*!
25  * \brief Type of ownership of a message instance inside message_holder.
26  *
27  * This type is intended to be used as parameter for message_holder_t
28  * template.
29  *
30  * \since
31  * v.5.6.0
32  */
33 enum class message_ownership_t
34 	{
35 		//! Type of ownership will be automatically detected in dependency
36 		//! of message mutability.
37 		autodetected,
38 		//! An instance of message_holder should be the unique holder of
39 		//! the message instance.
40 		//! In that case message_holder will be similar to unique_ptr.
41 		unique,
42 		//! Several instances of message_holder can own the message instance.
43 		//! In that case message_holder will be similar to shared_ptr.
44 		shared
45 	};
46 
47 namespace details
48 {
49 
50 namespace message_holder_details
51 {
52 
53 //! A helper function to get a const raw pointer from smart pointer.
54 template< typename M >
55 M *
get_ptr(const intrusive_ptr_t<M> & msg)56 get_ptr( const intrusive_ptr_t<M> & msg ) noexcept
57 	{
58 		return msg.get();
59 	}
60 
61 //! A helper function to get a const raw pointer from smart pointer.
62 /*!
63  * This overload is for case when M is a user type message.
64  */
65 template< typename M >
66 M *
get_ptr(const intrusive_ptr_t<user_type_message_t<M>> & msg)67 get_ptr( const intrusive_ptr_t< user_type_message_t<M> > & msg ) noexcept
68 	{
69 		return std::addressof(msg->m_payload);
70 	}
71 
72 /*!
73  * \brief Basic part of message_holder implementations.
74  *
75  * Contains method which should be present in all implementations.
76  *
77  * \since
78  * v.5.6.0
79  */
80 template< typename Payload, typename Envelope >
81 class basic_message_holder_impl_t
82 	{
83 	protected :
84 		//! Message instance.
85 		/*!
86 		 * Can be empty if the message_holder doens't hold anything
87 		 * (in that case empty message_holder is an analogue of nullptr).
88 		 */
89 		intrusive_ptr_t< Envelope > m_msg;
90 
91 	public :
92 		using payload_type = Payload;
93 		using envelope_type = Envelope;
94 
95 		basic_message_holder_impl_t() noexcept = default;
96 
basic_message_holder_impl_t(intrusive_ptr_t<Envelope> msg)97 		basic_message_holder_impl_t(
98 			intrusive_ptr_t< Envelope > msg ) noexcept
99 			:	m_msg{ std::move(msg) }
100 			{}
101 
102 		//! Drops to pointer to the message instance.
103 		/*!
104 		 * The message_holder becomes empty as the result.
105 		 */
106 		void
reset()107 		reset() noexcept
108 			{
109 				m_msg.reset();
110 			}
111 
112 		//! Check for the emptiness of message_holder.
113 		[[nodiscard]]
114 		bool
empty() const115 		empty() const noexcept
116 			{
117 				return !static_cast<bool>( m_msg );
118 			}
119 
120 		//! Check for the non-emptiness of message_holder.
121 		[[nodiscard]]
operator bool() const122 		operator bool() const noexcept
123 			{
124 				return !this->empty();
125 			}
126 
127 		//! Check for the emptiness of message_holder.
128 		[[nodiscard]]
operator !() const129 		bool operator!() const noexcept
130 			{
131 				return this->empty();
132 			}
133 	};
134 
135 /*!
136  * \brief A part of implementation of message_holder to be used for
137  * shared ownership of message instances.
138  *
139  * This implementation allows copy and move.
140  *
141  * This implementation provides const make_reference() method that
142  * returns a copy of underlying smart pointer.
143  *
144  * \since
145  * v.5.6.0
146  */
147 template< typename Payload, typename Envelope >
148 class shared_message_holder_impl_t
149 	:	public basic_message_holder_impl_t<Payload, Envelope>
150 	{
151 		using direct_base_type = basic_message_holder_impl_t<Payload, Envelope>;
152 
153 	public :
154 		using direct_base_type::direct_base_type;
155 
156 		//! Make an another reference to the message.
157 		/*!
158 		 * Returns empty smart pointer if message_holder is empty.
159 		 */
160 		[[nodiscard]]
161 		intrusive_ptr_t< Envelope >
make_reference() const162 		make_reference() const noexcept
163 			{
164 				return this->m_msg;
165 			}
166 	};
167 
168 /*!
169  * \brief A part of implementation of message_holder to be used for
170  * unique ownership of message instances.
171  *
172  * This implementation allows only move operations and disables
173  * copy constructor/operator.
174  *
175  * This implementation provides non-const make_reference() method that
176  * extracts underlying smart pointer and leaves message_holder empty.
177  *
178  * \since
179  * v.5.6.0
180  */
181 template< typename Payload, typename Envelope >
182 class unique_message_holder_impl_t
183 	:	public basic_message_holder_impl_t<Payload, Envelope>
184 	{
185 		using direct_base_type = basic_message_holder_impl_t<Payload, Envelope>;
186 
187 	public :
188 		using direct_base_type::direct_base_type;
189 
190 		unique_message_holder_impl_t(
191 			const unique_message_holder_impl_t & ) = delete;
192 
193 		unique_message_holder_impl_t(
194 			unique_message_holder_impl_t && ) = default;
195 
196 		unique_message_holder_impl_t &
197 		operator=(
198 			const unique_message_holder_impl_t & ) = delete;
199 
200 		unique_message_holder_impl_t &
201 		operator=(
202 			unique_message_holder_impl_t && ) = default;
203 
204 		//! Extracts the smart pointer to the message.
205 		/*!
206 		 * Returns empty smart pointer if message_holder is empty.
207 		 *
208 		 * Leaves the message_holder instance empty.
209 		 */
210 		[[nodiscard]]
211 		intrusive_ptr_t< Envelope >
make_reference()212 		make_reference() noexcept
213 			{
214 				return { std::move(this->m_msg) };
215 			}
216 	};
217 
218 /*!
219  * \brief A meta-function for selection a base of message_holder implementation
220  * in compile-time.
221  *
222  * If Ownership is message_ownership_t::autodetect then message's mutability
223  * is examined. If message immutable then shared_message_holder_impl_t
224  * will be selected as the base class. For mutable messages
225  * unique_message_holder_impl_t will be used.
226  *
227  * \since
228  * v.5.6.0
229  */
230 template<
231 	typename Msg,
232 	message_ownership_t Ownership >
233 struct impl_selector
234 	{
235 		static_assert( !is_signal<Msg>::value,
236 				"Signals can't be used with message_holder" );
237 
238 		using P = typename message_payload_type< Msg >::payload_type;
239 		using E = typename message_payload_type< Msg >::envelope_type;
240 
241 		using type = std::conditional_t<
242 				message_ownership_t::autodetected == Ownership,
243 					std::conditional_t<
244 							message_mutability_t::immutable_message ==
245 									message_mutability_traits<Msg>::mutability,
246 							shared_message_holder_impl_t<P, E>,
247 							unique_message_holder_impl_t<P, E> >,
248 					std::conditional_t<
249 							message_ownership_t::shared == Ownership,
250 							shared_message_holder_impl_t<P, E>,
251 							unique_message_holder_impl_t<P, E> >
252 			>;
253 	};
254 
255 /*!
256  * \brief Just a shortcut for impl_selector meta-function.
257  *
258  * \since
259  * v.5.6.0
260  */
261 template<
262 	typename Msg,
263 	message_ownership_t Ownership >
264 using impl_selector_t = typename impl_selector<Msg, Ownership>::type;
265 
266 /*!
267  * \brief An of mixin with getters for message_holder.
268  *
269  * It is assumed that shared_message_holder_impl_t or
270  * unique_message_holder_impl_t will be used as Base template parameter.
271  *
272  * \since
273  * v.5.6.0
274  */
275 template< typename Base, typename Return_Type >
276 class msg_accessors_t : public Base
277 	{
278 	public :
279 		using Base::Base;
280 
281 		//! Get a pointer to the message inside message_holder.
282 		/*!
283 		 * \attention
284 		 * Returns nullptr is message_holder is empty.
285 		 */
286 		[[nodiscard]]
287 		Return_Type *
get() const288 		get() const noexcept
289 			{
290 				return get_ptr( this->m_msg );
291 			}
292 
293 		//! Get a reference to the message inside message_holder.
294 		/*!
295 		 * \attention
296 		 * An attempt to use this method on empty message_holder is UB.
297 		 */
298 		[[nodiscard]]
299 		Return_Type &
operator *() const300 		operator * () const noexcept { return *get(); }
301 
302 		//! Get a pointer to the message inside message_holder.
303 		/*!
304 		 * \attention
305 		 * An attempt to use this method on empty message_holder is UB.
306 		 */
307 		[[nodiscard]]
308 		Return_Type *
operator ->() const309 		operator->() const noexcept { return get(); }
310 	};
311 
312 /*!
313  * \brief A meta-function for selection of type of accessors mixin.
314  *
315  * \since
316  * v.5.6.0
317  */
318 template<
319 	message_mutability_t Mutability,
320 	typename Base >
321 struct accessor_selector
322 	{
323 		using type = std::conditional_t<
324 				message_mutability_t::immutable_message == Mutability,
325 				msg_accessors_t<Base, typename Base::payload_type const>,
326 				msg_accessors_t<Base, typename Base::payload_type> >;
327 	};
328 
329 /*!
330  * \brief Just a shortcut for accessor_selector meta-function.
331  *
332  * \since
333  * v.5.6.0
334  */
335 template<
336 	message_mutability_t Mutability,
337 	typename Base >
338 using accessor_selector_t =
339 		typename accessor_selector<Mutability, Base>::type;
340 
341 } /* namespace message_holder_details */
342 
343 } /* namespace details */
344 
345 /*!
346  * \brief A class for holding an instance of a message.
347  *
348  * \attention
349  * This class should be used with messages only. Signals are not supported
350  * by that class.
351  *
352  * This class is intended for simplification of holding message instances
353  * for some time and resending them later. For example:
354  * \code
355  * class my_actor final : public so_5::agent_t {
356  * 	// A stored message.
357  * 	so_5::message_holder_t<my_message> stored_;
358  * 	...
359  * 	void on_message(mhood_t<my_message> cmd) {
360  * 		// Store message inside the agent.
361  * 		stored_ = cmd.make_holder();
362  * 		...
363  * 		// Initiate a delayed message to resend the stored message later.
364  * 		so_5::send_delayed<resend_message>(*this, 10s);
365  * 	}
366  * 	...
367  * 	void on_resend_message(mhood_t<resend_message>) {
368  * 		// Resend the stored message.
369  * 		so_5::send(some_target, stored_);
370  * 		// The stored message is no more needed.
371  * 		stored_.reset();
372  *
373  * 		// Or we can write:
374  * 		// so_5::send(some_target, std::move(stored_));
375  * 	}
376  * };
377  * \endcode
378  * This class is also intended to be used with preallocated messages:
379  * \code
380  * class prealloc_msg_demo final : public so_5::agent_t {
381  * 	so_5::message_holder_t<request> request_;
382  * 	...
383  * 	prealloc_msg_demo(
384  * 		context_t ctx,
385  * 		... // Some other params.
386  * 		) : request_{std::piecewise_construct, ...} // Preallocation of message.
387  * 		{}
388  *
389  * 	void on_some_event(...) {
390  * 		...
391  * 		// It is time to send preallocated message.
392  * 		so_5::send(some_target, request_);
393  * 		...
394  * 	}
395  * };
396  * \endcode
397  *
398  * The main benefit of that class is the ability to correctly handle
399  * messages of arbitrary user types (e.g. messages not derived from
400  * so_5::message_t class) and mutability flags. For example, the following
401  * cases are correctly handled by message_holder_t:
402  * \code
403  * struct status_data { // This is message that is not derived from so_5::message_t.
404  * 	... // Some fields.
405  * };
406  *
407  * so_5::message_holder_t<status_data> msg1;
408  * so_5::message_holder_t<so_5::immutable_msg<status_data>> msg2;
409  * so_5::message_holder_t<so_5::mutable_msg<status_data>> msg3;
410  * \endcode
411  *
412  * \par Methods of message_holder_t class
413  *
414  * Class message_holder_t provides the following methods:
415  * \code
416  * // Default constructor. Creates an empty holder.
417  * message_holder_t();
418  *
419  * // Constructs holder for holding the specified message instance.
420  * message_holder_t(so_5::intrusive_ptr<envelope_type> msg);
421  *
422  * // Creates a new instance of message from 'args' and constructs holder for it.
423  * template<typename... Args>
424  * message_holder_t(std::piecewise_construct_t, Args && ...args);
425  *
426  * // Creates a new instance of message from 'args' and constructs holder for it.
427  * template<typename... Args>
428  * static message_holder_t make(Args && ...args);
429  *
430  * // Returns true if message_holder is empty.
431  * bool empty() const noexcept;
432  * bool operator!() const noexcept;
433  *
434  * // Returns true if message_holder is not empty.
435  * operator bool() const noexcept;
436  *
437  * // Drops the content of message_holder.
438  * void reset() noexcept;
439  * \endcode
440  *
441  * There are also some more methods which are depend on mutability of
442  * message and the type of ownership. They are described below.
443  *
444  * \par Getters are depend on mutability of message
445  *
446  * If a message_holder holds an immutable message then there are the following
447  * getter methods:
448  * \code
449  * const payload_type * get() const noexcept;
450  * const payload_type & operator*() const noexcept;
451  * const payload_type * operator->() const noexcept;
452  * \endcode
453  * But if message_holder holds a mutable message those getters are still here
454  * but they have non-const return type:
455  * \code
456  * payload_type * get() const noexcept;
457  * payload_type & operator*() const noexcept;
458  * payload_type * operator->() const noexcept;
459  * \endcode
460  *
461  * \par Shared and unique ownership
462  *
463  * A message_holder works like a smart pointer. But what kind of smart pointer?
464  *
465  * It depends on Ownership template parameters.
466  *
467  * But default Ownership is so_5::message_ownership_t::autodetected.
468  * In this case the behavior of a message_holder depends of the mutability
469  * of message. If message is immutable then message_holders is like
470  * std::shared_ptr: several message_holders can hold pointers to the
471  * same message instances.
472  *
473  * If message is mutable then message_holder is like
474  * std::unique_ptr: only one message_holder can hold a pointer to a message
475  * instance.
476  *
477  * For example:
478  * \code
479  * // Immutable message.
480  * so_5::message_holder_t<my_msg> msg1{std::piecewise_construct, ...};
481  * so_5::message_holder_t<my_msg> msg2{ msg1 };
482  * assert(msg1.get() == msg2.get()); // Now msg1 and msg2 refer to the same msg.
483  *
484  * // Mutable message.
485  * so_5::message_holder_t<so_5::mutable_msg<my_msg>> msg3{...};
486  * so_5::message_holder_t<so_5::mutable_msg<my_msg>> msg4{ msg3 }; // WON'T COMPILE!
487  * so_5::message_holder_t<so_5::mutable_msg<my_msg>> msg5{ std::move(msg3) };
488  * assert(msg3.empty()); // Now msg3 is empty.
489  * assert(!msg5.empty()); // And only msg5 holds the message.
490  * \endcode
491  *
492  * The value of Ownership parameter can be specified manually.
493  * In that case we can have an unique-holder for an immutable message:
494  * \code
495  * so_5::message_holder_t<my_msg, so_5::message_ownership_t::unique> msg1{...};
496  * // WON'T COMPILE!
497  * so_5::message_holder_t<my_msg, so_5::message_ownership_t::unique> msg2{ msg1 };
498  * // Will compile but ownership will be moved:
499  * so_5::message_holder_t<my_msg, so_5::message_ownership_t::unique> msg3{ std::move(msg) };
500  * \endcode
501  * There can also be a shared-holder for a mutable message:
502  * \code
503  * so_5::message_holder_t<my_msg, so_5::message_ownership_t::shared> msg1{...};
504  * // No problems.
505  * so_5::message_holder_t<my_msg, so_5::message_ownership_t::shared> msg2{ msg1 };
506  * \endcode
507  * But this approach should be taken with an additional care because it allows
508  * to make several sends of the same mutable message instances at the same time.
509  *
510  * If a message_holder works as std::shared_ptr then there is the following
511  * methods:
512  * \code
513  * // Copy constructor and operator.
514  * message_holder_t(const message_holder_t &) noexcept;
515  * message_holder_t & operator=(const message_holder_t &) noexcept;
516  * // Move constructor and operator.
517  * message_holder_t(message_holder_t &&) noexcept;
518  * message_holder_t & operator=(message_holder_t &&) noexcept;
519  *
520  * // Getter for the underlying smart pointer.
521  * intrusive_ptr_t<envelope_type> make_reference() const noexcept;
522  * \endcode
523  *
524  * If a message_holder works as std::unique_ptr then copy operator/constructors
525  * are disabled and make_reference() leaves the message_holder object empty:
526  * \code
527  * // Move constructor and operator.
528  * message_holder_t(message_holder_t &&) noexcept;
529  * message_holder_t & operator=(message_holder_t &&) noexcept;
530  *
531  * // Extracts the underlying smart pointer.
532  * // Leaves the message_holder object empty.
533  * intrusive_ptr_t<envelope_type> make_reference() noexcept;
534  * \endcode
535  *
536  * \par Creation of an instance of message to be stored inside a message_holder
537  *
538  * There are several ways of creation of a message to be stored inside
539  * a message_holder object.
540  *
541  * The recommended way is to use the constructor of message_holder with
542  * std::piecewise_construct_t argument. This constructor automatically
543  * creates an underlying message instance:
544  * \code
545  * struct my_msg {
546  * 	int a_;
547  * 	std::string b_;
548  * };
549  * so_5::message_holder_t<my_msg> msg{std::piecewise_construct,
550  * 	0, // value for my_msg's a_ field.
551  * 	"hello" // value for my_msg's b_ field.
552  * };
553  * \endcode
554  * Sometimes a static method make() can be used for similar purpose:
555  * \code
556  * auto make_message() {
557  * 	return so_5::message_holder_t<my_msg>::make(0, "hello");
558  * }
559  * \endcode
560  *
561  * But sometimes an instance of message is present as raw pointer,
562  * std::unique_ptr or so_5::intrusive_ptr_t objects. In that case the
563  * constructor that accepts intrusive_ptr_t can be used:
564  * \code
565  * // Somewhere in 3rd-party library.
566  * std::unique_ptr<some_message> make_message() {
567  * 	return std::make_unique<some_message>(...);
568  * }
569  *
570  * // Somewhere in your code.
571  * so_5::message_holder_t<some_message> msg{make_message()};
572  * \endcode
573  *
574  * \since
575  * v.5.6.0
576  */
577 template<
578 	typename Msg,
579 	message_ownership_t Ownership = message_ownership_t::autodetected >
580 class message_holder_t
581 	:	public details::message_holder_details::accessor_selector_t<
582 				details::message_mutability_traits<Msg>::mutability,
583 				details::message_holder_details::impl_selector_t<Msg, Ownership> >
584 	{
585 		using base_type = details::message_holder_details::accessor_selector_t<
586 				details::message_mutability_traits<Msg>::mutability,
587 				details::message_holder_details::impl_selector_t<Msg, Ownership> >;
588 
589 	public :
590 		using payload_type = typename base_type::payload_type;
591 		using envelope_type = typename base_type::envelope_type;
592 
593 		using base_type::base_type;
594 
595 		//! Special constructor for constructing message_holder with
596 		//! a new message instance inside.
597 		/*!
598 		 * Usage example:
599 		 * \code
600 		 * struct my_message {
601 		 * 	int a_;
602 		 * 	std::string b_;
603 		 * 	std::chrono::millisecons c_;
604 		 * };
605 		 *
606 		 * so_5::message_holder_t<my_message> msg{ std::piecewise_construct,
607 		 * 		0, // value for my_message's a_ field.
608 		 * 		"hello", // value for my_message's b_ field.
609 		 * 		15s // value for my_message's c_ field.
610 		 * };
611 		 * \endcode
612 		 */
613 		template< typename... Args >
message_holder_t(std::piecewise_construct_t,Args &&...args)614 		message_holder_t(
615 			std::piecewise_construct_t,
616 			Args && ...args )
617 			:	base_type{ make_msg_instance( std::forward<Args>(args)... ) }
618 			{}
619 
620 		friend void
swap(message_holder_t & a,message_holder_t & b)621 		swap( message_holder_t & a, message_holder_t & b ) noexcept
622 			{
623 				using std::swap;
624 				swap( a.m_msg, b.m_msg );
625 			}
626 
627 		//! Create a new instance of message_holder with a new message inside.
628 		/*!
629 		 * Usage example:
630 		 * \code
631 		 * struct my_message {
632 		 * 	int a_;
633 		 * 	std::string b_;
634 		 * 	std::chrono::millisecons c_;
635 		 * };
636 		 *
637 		 * auto make_message() {
638 		 * 	return so_5::message_holder_t<my_message>(
639 		 * 		0, // value for my_message's a_ field.
640 		 * 		"hello", // value for my_message's b_ field.
641 		 * 		15s ); // value for my_message's c_ field.
642 		 * }
643 		 * \endcode
644 		 */
645 		template< typename... Args >
646 		[[nodiscard]]
647 		static message_holder_t
make(Args &&...args)648 		make( Args && ...args )
649 			{
650 				return { make_msg_instance( std::forward<Args>(args)... ) };
651 			}
652 
653 	private :
654 		//! Create a new instance of message.
655 		template< typename... Args >
656 		[[nodiscard]]
657 		static intrusive_ptr_t< envelope_type >
make_msg_instance(Args &&...args)658 		make_msg_instance( Args && ...args )
659 			{
660 				using namespace details;
661 
662 				intrusive_ptr_t< envelope_type > msg{
663 					make_message_instance< Msg >( std::forward<Args>(args)... )
664 				};
665 				mark_as_mutable_if_necessary< Msg >( *msg );
666 
667 				return msg;
668 			}
669 	};
670 
671 } /* namespace so_5 */
672 
673