diff --git a/examples/messaging-system/messaging-system.cpp b/examples/messaging-system/messaging-system.cpp index cc53fbf..8049395 100644 --- a/examples/messaging-system/messaging-system.cpp +++ b/examples/messaging-system/messaging-system.cpp @@ -1,144 +1,144 @@ /******************************************************************************* * * File: messaging-system.cpp * * Contents: A simple example on the MessagingSystem and Agent classes of the * RoSA Core library. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #include "rosa/config/version.h" #include "rosa/core/Agent.hpp" #include "rosa/core/MessagingSystem.hpp" #include "rosa/support/log.h" #include "rosa/support/terminal_colors.h" #include using namespace rosa; using namespace rosa::terminal; // A dummy wrapper for testing MessagingSystem. // NOTE: Since we test MessagingSystem directly here, we need to get access to // its protected members. That we do by imitating to be a decent subclass of // MessagingSystem, while calling protected member functions on an object of a // type from which we actually don't inherit. struct SystemTester : protected MessagingSystem { template static AgentHandle createMyAgent(MessagingSystem *S, const std::string &Name, Funs &&... Fs) { return ((SystemTester *)S)->createAgent(Name, std::move(Fs)...); } static void destroyMyAgent(MessagingSystem *S, const AgentHandle &H) { - ((SystemTester *)S)->destroyUnit(H.agent()); + ((SystemTester *)S)->destroyUnit(unwrapAgent(H)); } }; // A special Agent with its own state. class MyAgent : public Agent { public: using Tick = AtomConstant; using Report = AtomConstant; private: size_t Counter; public: void handler(Tick) noexcept { LOG_INFO_STREAM << "MyAgent Tick count: " << ++Counter << std::endl; } MyAgent(const AtomValue Kind, const rosa::id_t Id, const std::string &Name, MessagingSystem &S) : Agent(Kind, Id, Name, S, Invoker::F([this](Report) noexcept { LOG_INFO_STREAM << "MyAgent count: " << Counter << std::endl; }), THISMEMBER(handler)), Counter(0) {} }; int main(void) { LOG_INFO_STREAM << library_string() << " -- " << Color::Red << "messaging-system example" << Color::Default << std::endl; std::unique_ptr S = MessagingSystem::createSystem("Sys"); MessagingSystem *SP = S.get(); LOG_INFO_STREAM << std::endl << std::endl << "** Stateless Agents" << std::endl << std::endl; AgentHandle Agent1 = SystemTester::createMyAgent( SP, "Agent1", Invoker::F([](const std::string &M) noexcept { LOG_INFO("Agent1: " + M); })); using Print = AtomConstant; using Forward = AtomConstant; AgentHandle Agent2 = SystemTester::createMyAgent( SP, "Agent2", Invoker::F([](Print, uint8_t N) noexcept { LOG_INFO("Agent2: " + std::to_string(N)); }), Invoker::F([&Agent1](Forward, uint8_t N) noexcept { if (Agent1) { Agent1.send(std::to_string(N)); } else { LOG_INFO("Agent2 cannot forward: Agent1 is not valid"); } })); LOG_INFO_STREAM << std::endl << "Agent1 is valid: " << bool(Agent1) << std::endl << "Agent2 is valid: " << bool(Agent2) << std::endl; LOG_INFO("Sending a print-message to Agent2..."); SP->send(Agent2, Print::Value, 42); LOG_INFO("Sending a forward-message to Agent2..."); SP->send(Agent2, Forward::Value, 42); LOG_INFO("Sending an unexpected message to Agent2..."); SP->send(Agent2, unit); SystemTester::destroyMyAgent(SP, Agent1); LOG_INFO_STREAM << std::endl << "Agent1 is valid: " << bool(Agent1) << std::endl << "Agent2 is valid: " << bool(Agent2) << std::endl; LOG_INFO("Sending a forward-message to Agent2..."); SP->send(Agent2, Forward::Value, 42); SystemTester::destroyMyAgent(SP, Agent2); LOG_INFO_STREAM << std::endl << std::endl << "** Stateful Agents" << std::endl << std::endl; AgentHandle Agent3 = SystemTester::createMyAgent(SP, "Agent3"); for (size_t I = 0; I < 2; ++I) { LOG_INFO("Sending report-message to Agent3..."); Agent3.send(MyAgent::Report::Value); LOG_INFO("Sending tick-message to Agent3..."); Agent3.send(MyAgent::Tick::Value); } SystemTester::destroyMyAgent(SP, Agent3); LOG_INFO_STREAM << std::endl << std::endl; return 0; } diff --git a/include/rosa/core/Agent.hpp b/include/rosa/core/Agent.hpp index 2ca9088..9c4a97d 100644 --- a/include/rosa/core/Agent.hpp +++ b/include/rosa/core/Agent.hpp @@ -1,77 +1,77 @@ /******************************************************************************* * * File: Agent.hpp * * Contents: Declaration of the Agent class. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #ifndef ROSA_CORE_AGENT_HPP #define ROSA_CORE_AGENT_HPP #include "rosa/core/AgentHandle.hpp" #include "rosa/core/MessageHandler.hpp" #include "rosa/core/MessagingSystem.hpp" #include "rosa/core/Unit.h" #include "rosa/support/log.h" namespace rosa { // Implements an Agent that is a special Unit owned by a MessagingSystem, // capable of handling Messages as MessageHandler, and provides the // AbstractAgent interface with AgentHandle as reference type. class Agent : public Unit, public MessageHandler, public AbstractAgent { friend class AgentHandle; // AgentHandle is our friend. protected: // A handle for this Agent. const AgentHandle Self; public: // Ctor for instantiating all the base-classes. template Agent(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, Fun &&F, Funs &&... Fs); // Dtor. ~Agent(void); // Tells if the Agent is in valid state. operator bool(void) const noexcept override; // Tells if the given reference refers to this Agent. bool operator==(const AgentHandle &H) const noexcept override; // Returns a reference to this Agent. AgentHandle self(void) noexcept override; // Sends the given Message to this Agent. void sendMessage(message_t &&M) noexcept override; // Dumping the Agent into a string for tracing purposes. std::string dump(void) const noexcept override; protected: // Returns the owning MessagingSystem. MessagingSystem &system(void) const noexcept override; }; template Agent::Agent(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, Fun &&F, Funs &&... Fs) : Unit(Kind, Id, Name, S), MessageHandler(std::move(F), std::move(Fs)...), - Self(*this) { + Self(*this, /*valid*/ true) { LOG_TRACE("Agent is created."); } } // End namespace rosa #endif // ROSA_CORE_AGENT_HPP diff --git a/include/rosa/core/AgentHandle.hpp b/include/rosa/core/AgentHandle.hpp index a5b5313..c17737d 100644 --- a/include/rosa/core/AgentHandle.hpp +++ b/include/rosa/core/AgentHandle.hpp @@ -1,61 +1,69 @@ /******************************************************************************* * * File: AgentHandle.hpp * * Contents: Declaration of AgentHandle. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #ifndef ROSA_CORE_AGENTHANDLE_HPP #define ROSA_CORE_AGENTHANDLE_HPP #include "rosa/core/AbstractAgent.hpp" namespace rosa { // Wraps an actual Agent to decouple its public interface. // NOTE: Such decoupling might be necessary when operating with remote // Systems, sometime in the future. class AgentHandle : public AbstractAgent { + // Agent and MessagingSystem are our friends, they may inspect the + // private member fields of the class. + friend class Agent; + friend class MessagingSystem; + // The wrapped Agent instance. Agent &A; // The owning MessagingSystem of A. MessagingSystem &S; + // Ctor used by Agents creating a reference to itself during construction. + // NOTE: The state of the Agent is not validated by this constructor. + AgentHandle(Agent &A, bool); + public: - // Ctor. + // Public ctor for normal use. + // NOTE: The Agent must be in a valid state to create an AgentHandle with. + // PRE: A.system().isUnitRegistered(A) AgentHandle(Agent &A); // Dtor, nothing to take care of. ~AgentHandle(void) = default; - // Gives a reference to the wrapped Agent. - Agent &agent(void) const noexcept; - // Tells if the AgentHandle is valid, referring to an existing Agent. // NOTE: AgentHandlers belong to MessagingSystems. Working with an // AgentHandler whose originating MessagingSystem has already been destroyed // results in undefined behavior. operator bool(void) const noexcept override; // Tells if the given reference refers to this Agent. bool operator==(const AgentHandle &H) const noexcept override; // Returns a reference to this Agent. AgentHandle self(void) noexcept override; // Sends the given Message to this Agent. // PRE: bool(*this) void sendMessage(message_t &&M) noexcept override; }; } // End namespace rosa #endif // ROSA_CORE_AGENTHANDLE_HPP diff --git a/include/rosa/core/MessagingSystem.hpp b/include/rosa/core/MessagingSystem.hpp index 53acddd..fcc2bcb 100644 --- a/include/rosa/core/MessagingSystem.hpp +++ b/include/rosa/core/MessagingSystem.hpp @@ -1,96 +1,109 @@ /******************************************************************************* * * File: MessagingSystem.hpp * * Contents: Declaration of the class MessagingSystem. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #ifndef ROSA_CORE_MESSAGINGSYSTEM_HPP #define ROSA_CORE_MESSAGINGSYSTEM_HPP #include "rosa/core/AgentHandle.hpp" #include "rosa/core/System.hpp" #include "rosa/support/atom.hpp" namespace rosa { // Extends the System interface with features to create Agents and register // Messages for them. class MessagingSystem : public System { friend class AgentHandle; // AgentHandle is our friend. public: // Returns an object implementing the interface defined by the class. static std::unique_ptr createSystem(const std::string &Name) noexcept; private: // Kind used for Unit categorization of Agents. static constexpr AtomValue AgentKind = atom("agent"); protected: // Ctor. MessagingSystem(void) noexcept = default; protected: // Creates an Agent owned by the MessagingSystem and returns a handle for it. // NOTE: Agent requires at least one Fun for its constructor, but derived // classes may do not need that. That's the reason of allowing even zero Funs // for this template function. // STATIC PRE: std::is_base_of::value template AgentHandle createAgent(const std::string &Name, Funs &&... Fs); + // Gives the wrapped Agent from the given AgentHandle for derived classes + // to be able to inspect the AgentHandle. + static inline Agent &unwrapAgent(const AgentHandle &H) noexcept { + return H.A; + } + + // Gives the original owning MessagingSystem of the wrapped Agent from the + // given AgentHandle for derived classes to be able to inspect the + // AgentHandle. + static inline MessagingSystem &unwrapSystem(const AgentHandle &H) noexcept { + return H.S; + } + public: // Sends the given Message to the Agent referred by the given AgentHandle. // NOTE: If the given Message cannot be handled by the referred Agent, the // Message is simply ignored. - // PRE: isUnitRegistered(H.agent()) + // PRE: &unwrapSystem(H) == this && isUnitRegistered(unwrapAgent(H)) virtual void send(const AgentHandle &H, message_t &&M) noexcept = 0; // Convenience template, which creates the Message from the given constant // lvalue references and sends to the Agent. - // PRE: isUnitRegistered(H.agent()) + // PRE: &unwrapSystem(H) == this && isUnitRegistered(unwrapAgent(H)) template void send(const AgentHandle &H, const Type &T, const Types &... Ts) noexcept; // Convenience template, which creates the Message from the given rvalue // references and sends to the Agent. - // PRE: isUnitRegistered(H.agent()) + // PRE: &unwrapSystem(H) == this && isUnitRegistered(unwrapAgent(H)) template void send(const AgentHandle &H, Type &&T, Types &&... Ts) noexcept; }; template AgentHandle MessagingSystem::createAgent(const std::string &Name, Funs &&... Fs) { STATIC_ASSERT((std::is_base_of::value), "not an Agent"); Agent &A = createUnit([&](const id_t Id, MessagingSystem &S) noexcept { return new T(AgentKind, Id, Name, S, std::move(Fs)...); }); return {A}; } template void MessagingSystem::send(const AgentHandle &H, const Type &T, const Types &... Ts) noexcept { send(H, Message::create(T, Ts...)); } template void MessagingSystem::send(const AgentHandle &H, Type &&T, Types &&... Ts) noexcept { send(H, Message::create(std::move(T), std::move(Ts)...)); } } // End namespace rosa #endif // ROSA_CORE_MESSAGINGSYSTEM_HPP diff --git a/lib/core/Agent.cpp b/lib/core/Agent.cpp index b3a673c..558117e 100644 --- a/lib/core/Agent.cpp +++ b/lib/core/Agent.cpp @@ -1,48 +1,48 @@ /******************************************************************************* * * File: Agent.cpp * * Contents: Implementation of non-template part of the Agent class. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #include "rosa/core/Agent.hpp" namespace rosa { Agent::~Agent(void) { LOG_TRACE("Destroying Agent..."); } Agent::operator bool(void) const noexcept { // An Agent itself is always valid. return true; } bool Agent::operator==(const AgentHandle &H) const noexcept { // Return if the Agent wrapped by H is this very object. - return this == &H.agent(); + return this == &H.A; } AgentHandle Agent::self(void) noexcept { return Self; } void Agent::sendMessage(message_t &&M) noexcept { system().send(Self, std::move(M)); } std::string Agent::dump(void) const noexcept { LOG_TRACE("Dumping Agent (" + FullName + ")"); return "[Agent] " + FullName; } MessagingSystem &Agent::system(void) const noexcept { // NOTE: The System the Unit is created with is a MessagingSystem. return static_cast(Unit::system()); } } // End namespace rosa diff --git a/lib/core/AgentHandle.cpp b/lib/core/AgentHandle.cpp index b0bf4bc..2c618a7 100644 --- a/lib/core/AgentHandle.cpp +++ b/lib/core/AgentHandle.cpp @@ -1,46 +1,46 @@ /******************************************************************************* * * File: AgentHandle.cpp * * Contents: Implementation of AgentHandle. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #include "rosa/core/AgentHandle.hpp" #include "rosa/core/Agent.hpp" namespace rosa { -AgentHandle::AgentHandle(Agent &A) : A(A), S(A.system()) {} +AgentHandle::AgentHandle(Agent &A, bool) : A(A), S(A.system()) {} -Agent &AgentHandle::agent(void) const noexcept { - return A; +AgentHandle::AgentHandle(Agent &A) : A(A), S(A.system()) { + ASSERT(S.isUnitRegistered(A)); } AgentHandle::operator bool(void) const noexcept { // NOTE: The referred MessageSystem is supposed to be still alive. return S.isUnitRegistered(A); } bool AgentHandle::operator==(const AgentHandle &H) const noexcept { // Return if the referred Agent is the same object in both AgentHandlers. return &A == &H.A; } AgentHandle AgentHandle::self(void) noexcept { // Return a copy of this AgentHandle. return *this; } void AgentHandle::sendMessage(message_t &&M) noexcept { ASSERT(bool(*this)); S.send(*this, std::move(M)); } } // End namespace rosa diff --git a/lib/core/MessagingSystemImpl.cpp b/lib/core/MessagingSystemImpl.cpp index 475889a..c0b9af5 100644 --- a/lib/core/MessagingSystemImpl.cpp +++ b/lib/core/MessagingSystemImpl.cpp @@ -1,31 +1,31 @@ /******************************************************************************* * * File: MessagingSystemImpl.cpp * * Contents: Implementation of the MessagingSystemImpl class. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #include "MessagingSystemImpl.hpp" #include "rosa/core/Agent.hpp" namespace rosa { MessagingSystemImpl::MessagingSystemImpl(const std::string &Name) noexcept : MessagingSystem(), SystemImpl(Name) { LOG_TRACE("System '" + Name + "' is a MessagingSystem"); } void MessagingSystemImpl::send(const AgentHandle &H, message_t &&M) noexcept { - ASSERT(isUnitRegistered(H.agent())); - H.agent()(*M); + ASSERT(&unwrapSystem(H) == this && isUnitRegistered(unwrapAgent(H))); + unwrapAgent(H)(*M); } } // End namespace rosa diff --git a/lib/core/MessagingSystemImpl.hpp b/lib/core/MessagingSystemImpl.hpp index 526d21a..c28c2ca 100644 --- a/lib/core/MessagingSystemImpl.hpp +++ b/lib/core/MessagingSystemImpl.hpp @@ -1,80 +1,80 @@ /******************************************************************************* * * File: MessagingSystemImpl.hpp * * Contents: Declaration of a basic implementation of the MessagingSystem * interface. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #ifndef ROSA_LIB_CORE_MESSAGINGSYSTEMIMPL_HPP // NOLINT #define ROSA_LIB_CORE_MESSAGINGSYSTEMIMPL_HPP #include "SystemImpl.hpp" #include "rosa/core/MessagingSystem.hpp" namespace rosa { // Implements MessagingSystem by extending SystemImpl by adding a // simple implementation of Message sending: directly invoking the Agent with // the given Message. // NOTE: Keep in mind that sending a Message with this implementation // translates into a direct function call. class MessagingSystemImpl : public MessagingSystem, public SystemImpl { // Referring to SystemImpl as Base inside the class. using Base = SystemImpl; public: // Ctor. MessagingSystemImpl(const std::string &Name) noexcept; protected: // NOTE: Simply forwarding calls to implementations from Base for the System // interface. // FIXME: How could we use the inherited implementations in a simpler way? inline id_t nextId(void) noexcept override { return Base::nextId(); } inline bool isSystemCleaned(void) const noexcept override { return Base::isSystemCleaned(); } inline void markCleaned(void) noexcept override { Base::markCleaned(); } inline void registerUnit(Unit &U) noexcept override { Base::registerUnit(U); } inline void destroyUnit(Unit &U) noexcept override { Base::destroyUnit(U); } inline bool isUnitRegistered(const Unit &U) const noexcept override { return Base::isUnitRegistered(U); } public: inline const std::string &name(void) const noexcept override { return Base::name(); } inline size_t numberOfConstructedUnits(void) const noexcept override { return Base::numberOfConstructedUnits(); } inline size_t numberOfLiveUnits(void) const noexcept override { return Base::numberOfLiveUnits(); } inline bool empty(void) const noexcept override { return Base::empty(); } // Directly invokes the Agent with the Message. - // PRE: isUnitRegistered(H.agent()) + // PRE: &unwrapSystem(H) == this && isUnitRegistered(unwrapAgent(H)) void send(const AgentHandle &H, message_t &&M) noexcept override; }; } // End namespace rosa #endif // ROSA_LIB_CORE_MESSAGINGSYSTEMIMPL_HPP