Skip to content

v.5.8.1

Yauheni Akhotnikau edited this page Dec 15, 2023 · 2 revisions

This page describes changes and new features of v.5.8.1.

Table of Contents

A new message sink implementation

The v.5.8.1 provides a ready-to use implementation of a message sink that transforms and redirects a message/signal.

The easiest way to use this functionality is to use the so_5::bind_transformer helper function:

so_5::multi_sink_binding_t binding; // Can't use single_sink_binding_t here because
                                    // binding object will hold several bindings.

so_5::bind_transformer(binding, source_mbox,
    // Type of the incoming message will be deduced from a lambda parameter.
    [dest_mbox](const source_msg_type & msg) {
        return so_5::make_transformed<result_msg_type>(dest_mbox,
            ... /* some values from msg */ );
    });
    
// Type of the source message is specified explicitly.
// This is required for mutable messages and signals.
so_5::bind_transformer< so_5::mutable_msg<source_msg_type> >(binding, source_mbox,
    [dest_mbox](source_msg_type & msg) // Note the use of non-const reference, it is
                                       // allowed here because the message is mutable.
    {
        return so_5::make_transformed<result_msg_type>(dest_mbox,
            ... /* some values from msg */ );
    });
    
so_5::bind_transformer< source_signal_type >(binding, source_mbox,
    [dest_mbox]() // Note that transformer lambda without a parameter has to
                  // be used for signals.
    {
        return so_5::make_transformed<result_signal_type>(dest_mbox);
    });

The so_5::bind_transformer allows to specify an optional delivery filter (note that a delivery filter can only be specified for messages, not signals):

so_5::bind_transformer< so_5::mutable_msg<source_msg_type> >(binding, source_mbox,
    // The transformer.
    [dest_mbox](source_msg_type & msg) // Note the use of non-const reference, it is
                                       // allowed here because the message is mutable.
    {
        return so_5::make_transformed<result_msg_type>(dest_mbox,
            ... /* some values from msg */ );
    },
    // The delivery filter. The delivery filter will be called first and only
    // if the delivery filter return `true` the transformer will be invoked.
    [](const auto & msg) // Note the use of const-reference.
                         // Delivery filters always receive const-references,
                         // even for mutable messages.
    {
        return ... /* some condition that uses the content of the `msg` */;
    });

There is a new example bind_transformer in the sample folder that shows the use of so_5::bind_transformer function.

Under the hood the so_5::bind_transformer uses so_5::msinks::transform_then_redirect factory function(s). The so_5::msinks::transform_then_redirect is also a new feature of v.5.8.1. It can be used by a user if simple so_5::bind_transformer is not enough.

New methods so_5::agent_t::so_this_agent_disp_binder() and so_5::agent_t::so_this_coop_disp_binder()

Two new methods of so_5::agent_t class simplify creation of a child coop with binding agents from it to the dispatcher already used for the parent agent (or for the cooperation of the parent agent).

For example, for so_this_agent_disp_binder:

class parent_agent final : public so_5::agent_t
{
    void create_new_child_coop() {
        so_5::introduce_child_coop(*this,
            // Use the parent agent dispatcher as the default dispatcher
            // for agents from a new coop.
            so_this_agent_disp_binder(),
            [this](so_5::coop_t & coop) {
                ... /* Creation of agents */
            });
    }
    ...
};

...
// Registration of the coop with the parent agent.
env.introduce_coop(
    // Use active_obj dispatcher as the default for this coop.
    so_5::disp::active_obj::make_dispatcher(env).binder(),
    [](so_5::coop_t & coop) {
        // The parent agent will use the active_obj dispatcher by default.
        coop.make_agent<parent_agent>(...);
        ... /* Creation of other agents. */
    });

In that case agents from a child coop will use the same active_obj dispatcher as the parent agent.

Sometimes it's necessary to distinguish the dispatcher for the parent agent and for the parent coop. In that case so_this_coop_disp_binder will help:

class parent_agent final : public so_5::agent_t
{
    void create_new_child_coop() {
        so_5::introduce_child_coop(*this,
            // Use the parent coop dispatcher as the default dispatcher
            // for agents from a new coop.
            so_this_coop_disp_binder(),
            [this](so_5::coop_t & coop) {
                ... /* Creation of agents */
            });
    }
    ...
};

...
// Registration of the coop with the parent agent.
env.introduce_coop(
    // Use thread_pool dispatcher as the default for this coop.
    so_5::disp::thread_pool::make_dispatcher(env, 9u).binder(),
    [](so_5::coop_t & coop) {
        // The parent agent will use a separate one_thread dispatcher.
        coop.make_agent_with_binder<parent_agent>(
            so_5::disp::one_thread::make_dispatcher(env).binder(),
            ...);
        ... /* Creation of other agents. */
    });

In that case agents from a child coop will be bound to a thread_pool dispatcher, not to one_thread dispatcher used for the parent agent.

A fix for limit_then_transform and mutable messages

Up to v.5.8.1 there was a flaw in the SObjectizer implementation: the limit_then_transform overload reaction could not be used with mutable messages. This problem is fixed in v.5.8.1, not it is possible to write like this:

some_agent::some_agent(context_t ctx)
    : so_5::agent_t{ ctx + limit_then_transform< so_5::mutable_msg< my_msg > >(
        [this](my_msg & msg) /* Note the use of non-const reference */ {
            return so_5::make_transformed< another_msg >(...);
        })
      }
{...}

The specificator so_5::immutable_msg<M> is also supported now for limit_then_transform:

some_agent::some_agent(context_t ctx)
    : so_5::agent_t{ ctx + limit_then_transform< so_5::immutable_msg< my_msg > >(
        [this](const my_msg & msg) /* Note the use of const reference */ {
            return so_5::make_transformed< another_msg >(...);
        })
      }
{...}

Support for so_5::immutable_msg<M> and so_5::mutable_msg<M> is important for case when agent is a template:

template<typename Src_Msg>
class some_message_handler final : public so_5::agent_t {
public:
    some_message_handler(context_t ctx)
        : so_5::agent_t{ctx
            + limit_then_transform<Src_Msg>([this](const auto & msg) {...})
            + ... /* other limits */
          }
    {...}
};
...
coop.make_agent< some_message_handler< so_5::immutable_msg<received_data> > >(...);
coop.make_agent< some_message_handler< so_5::mutable_msg<parsed_data> > >(...);
Clone this wiki locally