-
Notifications
You must be signed in to change notification settings - Fork 46
SO 5.8 InDepth Optional Agent Name
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.
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.