SObjectizer  5.5
Public Types | Public Member Functions | Static Public Member Functions | Private Types | Static Private Member Functions | Friends | List of all members
so_5::message_holder_t< Msg, Ownership > Class Template Reference

A class for holding an instance of a message. More...

#include <message_holder.hpp>

Inheritance diagram for so_5::message_holder_t< Msg, Ownership >:

Public Types

using payload_type = typename base_type::payload_type
 
using envelope_type = typename base_type::envelope_type
 

Public Member Functions

template<typename... Args>
 message_holder_t (std::piecewise_construct_t, Args &&...args)
 

Static Public Member Functions

template<typename... Args>
static SO_5_NODISCARD message_holder_t make (Args &&...args)
 Create a new instance of message_holder with a new message inside. More...
 

Private Types

using base_type = details::message_holder_details::accessor_selector_t< details::message_mutability_traits< Msg >::mutability, details::message_holder_details::impl_selector_t< Msg, Ownership > >
 

Static Private Member Functions

template<typename... Args>
static SO_5_NODISCARD intrusive_ptr_t< envelope_typemake_msg_instance (Args &&...args)
 Create a new instance of message. More...
 

Friends

void swap (message_holder_t &a, message_holder_t &b) noexcept
 

Detailed Description

template<typename Msg, message_ownership_t Ownership = message_ownership_t::autodetected>
class so_5::message_holder_t< Msg, Ownership >

A class for holding an instance of a message.

Attention
This class should be used with messages only. Signals are not supported by that class.

This class is intended for simplification of holding message instances for some time and resending them later. For example:

class my_actor final : public so_5::agent_t {
// A stored message.
...
void on_message(mhood_t<my_message> cmd) {
// Store message inside the agent.
stored_ = cmd.make_holder();
...
// Initiate a delayed message to resend the stored message later.
so_5::send_delayed<resend_message>(*this, 10s);
}
...
void on_resend_message(mhood_t<resend_message>) {
// Resend the stored message.
so_5::send(some_target, stored_);
// The stored message is no more needed.
stored_.reset();
// Or we can write:
// so_5::send(some_target, std::move(stored_));
}
};

This class is also intended to be used with preallocated messages:

class prealloc_msg_demo final : public so_5::agent_t {
...
prealloc_msg_demo(
context_t ctx,
... // Some other params.
) : request_{std::piecewise_construct, ...} // Preallocation of message.
{}
void on_some_event(...) {
...
// It is time to send preallocated message.
so_5::send(some_target, request_);
...
}
};

The main benefit of that class is the ability to correctly handle messages of arbitrary user types (e.g. messages not derived from so_5::message_t class) and mutability flags. For example, the following cases are correctly handled by message_holder_t:

struct status_data { // This is message that is not derived from so_5::message_t.
... // Some fields.
};
Methods of message_holder_t class

Class message_holder_t provides the following methods:

// Default constructor. Creates an empty holder.
// Constructs holder for holding the specified message instance.
message_holder_t(so_5::intrusive_ptr<envelope_type> msg);
// Creates a new instance of message from 'args' and constructs holder for it.
template<typename... Args>
message_holder_t(std::piecewise_construct_t, Args && ...args);
// Creates a new instance of message from 'args' and constructs holder for it.
template<typename... Args>
static message_holder_t make(Args && ...args);
// Returns true if message_holder is empty.
bool empty() const noexcept;
bool operator!() const noexcept;
// Returns true if message_holder is not empty.
operator bool() const noexcept;
// Drops the content of message_holder.
void reset() noexcept;

There are also some more methods which are depend on mutability of message and the type of ownership. They are described below.

Getters are depend on mutability of message

If a message_holder holds an immutable message then there are the following getter methods:

const payload_type * get() const noexcept;
const payload_type & operator*() const noexcept;
const payload_type * operator->() const noexcept;

But if message_holder holds a mutable message those getters are still here but they have non-const return type:

payload_type * get() const noexcept;
payload_type & operator*() const noexcept;
payload_type * operator->() const noexcept;
Shared and unique ownership

A message_holder works like a smart pointer. But what kind of smart pointer?

It depends on Ownership template parameters.

But default Ownership is so_5::message_ownership_t::autodetected. In this case the behavior of a message_holder depends of the mutability of message. If message is immutable then message_holders is like std::shared_ptr: several message_holders can hold pointers to the same message instances.

If message is mutable then message_holder is like std::unique_ptr: only one message_holder can hold a pointer to a message instance.

For example:

// Immutable message.
so_5::message_holder_t<my_msg> msg1{std::piecewise_construct, ...};
assert(msg1.get() == msg2.get()); // Now msg1 and msg2 refer to the same msg.
// Mutable message.
so_5::message_holder_t<so_5::mutable_msg<my_msg>> msg4{ msg3 }; // WON'T COMPILE!
assert(msg3.empty()); // Now msg3 is empty.
assert(!msg5.empty()); // And only msg5 holds the message.

The value of Ownership parameter can be specified manually. In that case we can have an unique-holder for an immutable message:

There can also be a shared-holder for a mutable message:

But this approach should be taken with an additional care because it allows to make several sends of the same mutable message instances at the same time.

If a message_holder works as std::shared_ptr then there is the following methods:

// Copy constructor and operator.
message_holder_t & operator=(const message_holder_t &) noexcept;
// Move constructor and operator.
message_holder_t & operator=(message_holder_t &&) noexcept;
// Getter for the underlying smart pointer.
intrusive_ptr_t<envelope_type> make_reference() const noexcept;

If a message_holder works as std::unique_ptr then copy operator/constructors are disabled and make_reference() leaves the message_holder object empty:

// Move constructor and operator.
message_holder_t & operator=(message_holder_t &&) noexcept;
// Extracts the underlying smart pointer.
// Leaves the message_holder object empty.
intrusive_ptr_t<envelope_type> make_reference() noexcept;
Creation of an instance of message to be stored inside a message_holder

There are several ways of creation of a message to be stored inside a message_holder object.

The recommended way is to use the constructor of message_holder with std::piecewise_construct_t argument. This constructor automatically creates an underlying message instance:

struct my_msg {
int a_;
std::string b_;
};
so_5::message_holder_t<my_msg> msg{std::piecewise_construct,
0, // value for my_msg's a_ field.
"hello" // value for my_msg's b_ field.
};

Sometimes a static method make() can be used for similar purpose:

auto make_message() {
}

But sometimes an instance of message is present as raw pointer, std::unique_ptr or so_5::intrusive_ptr_t objects. In that case the constructor that accepts intrusive_ptr_t can be used:

// Somewhere in 3rd-party library.
std::unique_ptr<some_message> make_message() {
return std::make_unique<some_message>(...);
}
// Somewhere in your code.
Since
v.5.6.0
Examples:
so_5/chameneos_prealloc_msgs/main.cpp, so_5/make_pipeline/main.cpp, so_5/prio_work_stealing/main.cpp, and so_5/simple_message_deadline/main.cpp.

Member Typedef Documentation

◆ base_type

template<typename Msg, message_ownership_t Ownership = message_ownership_t::autodetected>
using so_5::message_holder_t< Msg, Ownership >::base_type = details::message_holder_details::accessor_selector_t< details::message_mutability_traits<Msg>::mutability, details::message_holder_details::impl_selector_t<Msg, Ownership> >
private

◆ envelope_type

template<typename Msg, message_ownership_t Ownership = message_ownership_t::autodetected>
using so_5::message_holder_t< Msg, Ownership >::envelope_type = typename base_type::envelope_type

◆ payload_type

template<typename Msg, message_ownership_t Ownership = message_ownership_t::autodetected>
using so_5::message_holder_t< Msg, Ownership >::payload_type = typename base_type::payload_type

Constructor & Destructor Documentation

◆ message_holder_t()

template<typename Msg, message_ownership_t Ownership = message_ownership_t::autodetected>
template<typename... Args>
so_5::message_holder_t< Msg, Ownership >::message_holder_t ( std::piecewise_construct_t  ,
Args &&...  args 
)
inline

Special constructor for constructing message_holder with a new message instance inside.

Usage example:

struct my_message {
int a_;
std::string b_;
std::chrono::millisecons c_;
};
so_5::message_holder_t<my_message> msg{ std::piecewise_construct,
0, // value for my_message's a_ field.
"hello", // value for my_message's b_ field.
15s // value for my_message's c_ field.
};

Member Function Documentation

◆ make()

template<typename Msg, message_ownership_t Ownership = message_ownership_t::autodetected>
template<typename... Args>
static SO_5_NODISCARD message_holder_t so_5::message_holder_t< Msg, Ownership >::make ( Args &&...  args)
inlinestatic

Create a new instance of message_holder with a new message inside.

Usage example:

struct my_message {
int a_;
std::string b_;
std::chrono::millisecons c_;
};
auto make_message() {
0, // value for my_message's a_ field.
"hello", // value for my_message's b_ field.
15s ); // value for my_message's c_ field.
}

◆ make_msg_instance()

template<typename Msg, message_ownership_t Ownership = message_ownership_t::autodetected>
template<typename... Args>
static SO_5_NODISCARD intrusive_ptr_t< envelope_type > so_5::message_holder_t< Msg, Ownership >::make_msg_instance ( Args &&...  args)
inlinestaticprivate

Create a new instance of message.

Friends And Related Function Documentation

◆ swap

template<typename Msg, message_ownership_t Ownership = message_ownership_t::autodetected>
void swap ( message_holder_t< Msg, Ownership > &  a,
message_holder_t< Msg, Ownership > &  b 
)
friend

The documentation for this class was generated from the following file: