SO 5.8 InDepth Optional Agent Name - Stiffstream/sobjectizer GitHub Wiki

Since v.5.8.2 it is possible to specify a name of an agent:

class my_agent final : public so_5::agent_t
{
public:
  my_agent(context_t ctx, std::string_view name)
    : so_5::agent_t{ctx + name_for_agent(name)}
  {}
};

This name is then accessible via the agent_t::so_agent_name() method introduced in v.5.8.2:

class my_agent final : public so_5::agent_t
{
public:
  my_agent(context_t ctx, std::string_view name)
    : so_5::agent_t{ctx + name_for_agent(name)}
  {}
  ...
  void so_evt_start() override {
    std::cout << so_agent_name() << ": started" << std::endl;
    ...
  }
  void so_evt_finish() override {
    ...
    std::cout << so_agent_name() << ": finished" << std::endl;
  }
};

Note that the agent_t::so_agent_name() method returns a special type so_5::agent_identity_t that can be seen as a synonym for:

std::variant<std::string_view, const so_5::agent_t*>

It means that if a name is specified for an agent in the constructor, then so_5::agent_identity_t will hold a std::string_view that references the name.

But if name is not specified, then so_5::agent_identity_t will hold just a pointer to the agent.

The so_5::agent_identity_t has to_string() method that returns a std::string with the following value:

  • the name of the agent if a name is set for the agent. So if we specify "Alice" as the agent name, then a std::string with "Alice" value will be returned;
  • a string like <noname:0000025A55B4B540> will be returned if there is no name for the agent. This pseudo-name is constructed by SObjectizer by using the pointer to the agent.

So it's always safe to call agent_identity_t::to_string() or to print the value of agent_identity_t into a std::ostream, even if an agent has no actual name.

agent_identity_t is a lightweight object

Special care has to be taken working with agent_identity_t. It's a lightweight object that holds just references/pointers and doesn't copy anything. Because of that, the instances of agent_identity_t should not be stored for a long time. For example, such code can be dangerous:

struct agent_started {
  so_5::agent_identity_t m_name;
};

class my_agent final : public so_5::agent_t {
public:
  my_agent(context_t ctx, std::string_view name)
    : so_5::agent_t{ctx + name_for_agent(name)}
  {}
  ...
  void so_evt_start() override {
    so_5::send<agent_started>(so_environment().create_mbox("registry"),
      so_agent_name());
    ...
  }
};

class agent_registry final : public so_5::agent_t {
  // Names of live agents.
  std::vector<so_5::agent_identity_t> m_agents;
  ...
  void evt_agent_started(mhood_t<agent_started> cmd) {
    m_agents.push_back(cmd->m_name);
  }
  void evt_show_live_agents(mhood_t<show_live_agents>) {
    for(const auto & name : m_agents)
      std::cout << name << std::endl; // (1)
  }
  ...
  void so_define_agent() override {
    so_subscribe(so_evironment().create_mbox("registry"))
      .event(agent_registry::evt_agent_started)
      .event(agent_registry::evt_show_live_agents)
      ...
      ;
  }
};

The problem is that a reference inside a agent_identity_t may be invalid at the point (1) if the agent was already deregistered.

To avoid such a problem it's recommended to use std::string returned by the agent_identity_t::to_string() method. Like in this example:

struct agent_started {
  std::string m_name;
};

class my_agent final : public so_5::agent_t {
public:
  my_agent(context_t ctx, std::string_view name)
    : so_5::agent_t{ctx + name_for_agent(name)}
  {}
  ...
  void so_evt_start() override {
    so_5::send<agent_started>(so_environment().create_mbox("registry"),
      so_agent_name().to_string());
    ...
  }
};

class agent_registry final : public so_5::agent_t {
  // Names of live agents.
  std::vector<std::string> m_agents;
  ...
  void evt_agent_started(mhood_t<agent_started> cmd) {
    m_agents.push_back(cmd->m_name);
  }
  void evt_show_live_agents(mhood_t<show_live_agents>) {
    for(const auto & name : m_agents)
      std::cout << name << std::endl; // (2)
  }
  ...
};

In that case we'll print values of our local std::string objects and those objects remain valid even if the corresponding agents are already deregistered and destroyed.