Skip to content

Commit

Permalink
Just a backup commit.
Browse files Browse the repository at this point in the history
The first draft of agent_identity_t class.
  • Loading branch information
eao197 committed Mar 14, 2024
1 parent c493af0 commit 964d23e
Show file tree
Hide file tree
Showing 12 changed files with 349 additions and 0 deletions.
49 changes: 49 additions & 0 deletions dev/so_5/agent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,51 @@

#include <algorithm>
#include <sstream>
#include <cstdint>
#include <cstdlib>
#include <limits>

namespace so_5
{

//
// agent_identity_t::pointer_only_t
//
std::array<char, agent_identity_t::pointer_only_t::c_string_size>
agent_identity_t::pointer_only_t::make_c_string() const noexcept
{
static constexpr std::array<char, 16> hex_symbols{
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};

constexpr std::string_view prefix{ "<noname:" };

std::array<char, c_string_size> result;
auto it = std::copy( prefix.begin(), prefix.end(), result.begin() );

// NOTE: this code won't compile if there is no std::uintptr_t type,
// but we don't care about such platforms at the moment.
std::uintptr_t ptr_as_uint = reinterpret_cast<std::uintptr_t>(m_pointer_value);

// Handle by 4 bits portions from the most significant bit.
// Leading zeros are not skipped (for the simplicity of implementation).
unsigned bits_to_process = sizeof(void *) * 8u;
while( bits_to_process >= 4u )
{
const auto v = (ptr_as_uint >> (bits_to_process - 4u)) & 0x0Fu;
const std::size_t index = static_cast<std::size_t>( v );
*(it++) = hex_symbols[ index ];

bits_to_process -= 4u;
}

*(it++) = '>'; ++it;
*it = 0;

return result;
}

namespace
{

Expand Down Expand Up @@ -642,6 +681,7 @@ agent_t::agent_t(
, m_working_thread_id( so_5::query_current_thread_id() )
, m_agent_coop( nullptr )
, m_priority( ctx.options().query_priority() )
, m_name( ctx.options().giveout_agent_name() )
{
}

Expand Down Expand Up @@ -951,6 +991,15 @@ agent_t::so_this_coop_disp_binder() const
return m_agent_coop->coop_disp_binder();
}

agent_identity_t
agent_t::so_agent_name() const noexcept
{
if( m_name.has_value() )
return { m_name.as_string_view() };
else
return { static_cast<const void *>(this) };
}

void
agent_t::destroy_all_subscriptions_and_filters() noexcept
{
Expand Down
14 changes: 14 additions & 0 deletions dev/so_5/agent.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

#include <so_5/agent_ref_fwd.hpp>
#include <so_5/agent_context.hpp>
#include <so_5/agent_identity.hpp>
#include <so_5/mbox.hpp>
#include <so_5/agent_state_listener.hpp>
#include <so_5/event_queue.hpp>
Expand Down Expand Up @@ -2708,6 +2709,11 @@ class SO_5_TYPE agent_t
disp_binder_shptr_t
so_this_coop_disp_binder() const;

//FIXME: document this!
[[nodiscard]]
agent_identity_t
so_agent_name() const noexcept;

private:
const state_t st_default{ self_ptr(), "<DEFAULT>" };

Expand Down Expand Up @@ -2873,6 +2879,14 @@ class SO_5_TYPE agent_t
*/
disp_binder_shptr_t m_disp_binder;

//FIXME: document this!
/*!
* Empty value means that the name for the agent wasn't specified.
*
* \since v.5.8.2
*/
name_for_agent_t m_name;

//! Destroy all agent's subscriptions.
/*!
* \note
Expand Down
9 changes: 9 additions & 0 deletions dev/so_5/agent_context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,15 @@ operator+(
ctx.options().custom_direct_mbox_factory( std::move(factory) );
return ctx;
}

inline agent_context_t
operator+(
agent_context_t ctx,
name_for_agent_t agent_name )
{
ctx.options().agent_name( std::move(agent_name) );
return ctx;
}
/*!
* \}
*/
Expand Down
154 changes: 154 additions & 0 deletions dev/so_5/agent_identity.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/*
* SObjectizer-5
*/

/*!
* \file
* \brief Types related to agents identity (name).
*/

#pragma once

#include <so_5/declspec.hpp>

#include <so_5/fwd.hpp>

#include <array>
#include <iostream>
#include <string>
#include <string_view>
#include <variant>

namespace so_5
{

//FIXME: document this!
class agent_identity_t
{
friend class agent_t;

//! Type for case when agent has no user-provided name.
struct pointer_only_t
{
//! Capacity of c-string for holding string representation.
//!
//! Size of hex representation of a pointer + "<noname:>".length() + 1.
static constexpr std::size_t c_string_size =
(sizeof(void*) * 2u)
+ std::string_view{ "<noname:>" }.size()
+ 1u /* terminating 0-symbol */;

//! Value.
const void * m_pointer_value;

// NOTE: this method is implemented in agent.cpp source file.
//! Make a c-string with text representation of a value.
SO_5_FUNC [[nodiscard]]
std::array<char, c_string_size>
make_c_string() const noexcept;
};

//! Type for case when agent has user-provided name.
struct actual_name_t
{
//! Value.
std::string_view m_name;
};

//FIXME: document this!
struct to_string_visitor_t
{
[[nodiscard]]
std::string
operator()( const pointer_only_t & v ) const
{
const auto c_str = v.make_c_string();
return std::string( c_str.data(), pointer_only_t::c_string_size - 1u );
}

[[nodiscard]]
std::string
operator()( const actual_name_t & v ) const
{
return std::string( v.m_name );
}
};

//FIXME: document this!
struct to_ostream_visitor_t
{
std::ostream & m_to;

to_ostream_visitor_t( std::ostream & to ) : m_to{ to } {}

void
operator()( const pointer_only_t & v ) const
{
const auto c_str = v.make_c_string();
m_to << c_str.data();
}

void
operator()( const actual_name_t & v ) const
{
m_to << v.m_name;
}
};

//! Type of identity holder.
using value_t = std::variant< pointer_only_t, actual_name_t >;

//! Agent's identity.
value_t m_value;

//! Initializing constructor for case when agent has no user specified name.
agent_identity_t( const void * pointer ) noexcept
: m_value{ pointer_only_t{ pointer } }
{}

//! Initializing constructor for case when agent has a user specified name.
agent_identity_t( std::string_view name ) noexcept
: m_value{ actual_name_t{ name } }
{}

public:
//! Does agent have an actual name?
[[nodiscard]]
bool
has_actual_name() const noexcept
{
return std::holds_alternative< actual_name_t >( m_value );
}

//! Attempt to get an agent name.
/*!
* \return empty std::string_view if agent has no an actual name.
*/
[[nodiscard]]
std::string_view
actual_name() const noexcept
{
if( const auto * an = std::get_if< actual_name_t >( &m_value ) )
return an->m_name;
else
return std::string_view{};
}

//! Transform identity into a string.
[[nodiscard]]
std::string
to_string() const
{
return std::visit( to_string_visitor_t{}, m_value );
}

friend std::ostream &
operator<<( std::ostream & to, const agent_identity_t & what )
{
std::visit( to_ostream_visitor_t{ to }, what.m_value );
return to;
}
};

} /* namespace so_5 */

2 changes: 2 additions & 0 deletions dev/test/so_5/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ add_subdirectory(timer_thread)

add_subdirectory(mpsc_queue_traits)

add_subdirectory(agent)

add_subdirectory(disp)

add_subdirectory(event_handler)
Expand Down
1 change: 1 addition & 0 deletions dev/test/so_5/agent/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
add_subdirectory(agent_name)
2 changes: 2 additions & 0 deletions dev/test/so_5/agent/agent_name/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
set(UNITTEST _unit.test.agent.agent_name)
include(${CMAKE_SOURCE_DIR}/cmake/unittest.cmake)
86 changes: 86 additions & 0 deletions dev/test/so_5/agent/agent_name/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* A unit-test for agent_t::so_agent_name().
*/

#include <so_5/all.hpp>

#include <test/3rd_party/various_helpers/ensure.hpp>
#include <test/3rd_party/various_helpers/time_limited_execution.hpp>

namespace test
{

class anonymous_agent_t final : public so_5::agent_t
{
public:
using so_5::agent_t::agent_t;

void
so_evt_start() override
{
const auto id = so_agent_name();
std::cout << "anonymous_agent_t: " << id << std::endl;

ensure_or_die( !id.has_actual_name(), "agent should not have a name!" );

const auto str_id = id.to_string();
ensure_or_die( "<noname:" == str_id.substr( 0u, 8u ),
"unexpected prefix!" );

so_deregister_agent_coop_normally();
}
};

class named_agent_t final : public so_5::agent_t
{
public:
named_agent_t( context_t ctx )
: so_5::agent_t{ ctx + so_5::name_for_agent_t{ "Alice" } }
{}

void
so_evt_start() override
{
const auto id = so_agent_name();
std::cout << "named_agent_t: " << id << std::endl;

ensure_or_die( id.has_actual_name(), "agent should have a name!" );

constexpr std::string_view expected_value{ "Alice" };
ensure_or_die( id.actual_name() == expected_value,
"unexpected result of id.actual_name()!" );

const auto str_id = id.to_string();
ensure_or_die( str_id == expected_value,
"unexpected result of id.to_string()!" );

so_deregister_agent_coop_normally();
}
};

void
init( so_5::environment_t & env )
{
env.register_agent_as_coop( env.make_agent< anonymous_agent_t >() );
env.register_agent_as_coop( env.make_agent< named_agent_t >() );
}

} /* namespace test */

int
main()
{
try
{
run_with_time_limit( []{ so_5::launch( test::init ); }, 5 );

return 0;
}
catch( const std::exception & x )
{
std::cerr << "Exception: " << x.what() << std::endl;
}

return 2;
}

11 changes: 11 additions & 0 deletions dev/test/so_5/agent/agent_name/prj.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
require 'mxx_ru/cpp'

MxxRu::Cpp::exe_target {

required_prj 'so_5/prj.rb'

target '_unit.test.agent.agent_name'

cpp_source 'main.cpp'
}

Loading

0 comments on commit 964d23e

Please sign in to comment.