diff --git a/include/rosa/core/AbstractAgent.hpp b/include/rosa/core/AbstractAgent.hpp index 4ddadfe..bbb1a52 100644 --- a/include/rosa/core/AbstractAgent.hpp +++ b/include/rosa/core/AbstractAgent.hpp @@ -1,139 +1,150 @@ //===-- rosa/core/AbstractAgent.hpp -----------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/core/AbstractAgent.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Declaration of an abstract interface for *Agents*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_ABSTRACTAGENT_HPP #define ROSA_CORE_ABSTRACTAGENT_HPP #include "rosa/core/Message.hpp" #include "rosa/core/forward_declarations.h" #include "rosa/support/debug.hpp" #include namespace rosa { /// Abstract class declaring an interface for *Agents*. /// /// \tparam Ref type of the derived class implementing \c rosa::AbstractAgent /// for referencing \p this object /// /// \note \p Ref is reference for \c rosa::AbstractAgent, whose actual value /// must be a class derived from \c rosa::AbstractAgent. /// /// \note It can be statically checked if \p Ref is derived from /// \c rosa::AbstractAgent, but the static assertion cannot be defined /// directly in the class body. That is because a class \c C derived from /// \c rosa::AbstractAgent is not complete when the static assertion in the /// definition of \c rosa::AbstractAgent would be evaluated. Thus, the static /// assertion is placed in the constructor of \c rosa::AbstractAgent. template class AbstractAgent { protected: /// Creates a new instance of \c rosa::AbstractAgent. /// /// \note The constructor is protected, thus restricting class instantiation /// for derived classes only. /// /// \pre \p Ref is derived from \c rosa::AbstractAgent:\code /// std::is_base_of, Ref>::value /// \endcode AbstractAgent(void) noexcept; public: /// Destroys \p this object. virtual ~AbstractAgent(void) = default; /// Tells if \p this object is in a valid state. /// /// \return if \p this object is in a valid state virtual operator bool(void) const noexcept = 0; /// Tells if a given reference refers to \p this object. /// /// \param R reference to another object /// /// \return if \p R refers to \p this object virtual bool operator==(const Ref &R) const noexcept = 0; + /// Compares \p this object to a given reference. + /// + /// The operator can be used to sort references. Standard containers storing + /// entries in an ordered way use \c std::less for comparing entries, which + /// utilizes this operator. + /// + /// \param R reference to another object + /// + /// \return if \p this object compares less to \p R + virtual bool operator<(const Ref &R) const noexcept = 0; + /// Returns a reference to \p this object. /// /// \return a reference to \p this object virtual Ref self(void) noexcept = 0; /// Sends a \c rosa::message_t instance to \p this object. /// /// \param M message to send /// /// \pre \p this object is in a valid state:\code /// bool(*this) /// \endcode virtual void sendMessage(message_t &&M) noexcept = 0; /// Sends a message -- created from given constant lvalue references -- to /// \p this object. /// /// \note The message must consists of at least one value. /// /// \tparam Type type of the first mandatory value /// \tparam Types types of any further values /// /// \param T the first value to include in the message /// \param Ts optional further values to include in the message /// /// \pre \p this object is in a valid state:\code /// bool(*this) /// \endcode template void send(const Type &T, const Types &... Ts) noexcept; /// Sends a message -- created from given rvalue references -- to \p this /// object. /// /// \note The message must consists of at least one value. /// /// \tparam Type type of the first mandatory value /// \tparam Types types of any further values /// /// \param T the first value to include in the message /// \param Ts optional further values to include in the message /// /// \pre \p this object is in a valid state:\code /// bool(*this) /// \endcode template void send(Type &&T, Types &&... Ts) noexcept; }; template AbstractAgent::AbstractAgent(void) noexcept { STATIC_ASSERT((std::is_base_of, Ref>::value), "not derived Agent"); // Sanity check. } template template void AbstractAgent::send(const Type &T, const Types &... Ts) noexcept { sendMessage(Message::create(T, Ts...)); } template template void AbstractAgent::send(Type &&T, Types &&... Ts) noexcept { sendMessage(Message::create(std::move(T), std::move(Ts)...)); } } // End namespace rosa #endif // ROSA_CORE_ABSTRACTAGENT_HPP diff --git a/include/rosa/core/Agent.hpp b/include/rosa/core/Agent.hpp index 344b8f6..07524fb 100644 --- a/include/rosa/core/Agent.hpp +++ b/include/rosa/core/Agent.hpp @@ -1,137 +1,146 @@ //===-- rosa/core/Agent.hpp -------------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/core/Agent.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Declaration of the base \c rosa::Agent class. /// //===----------------------------------------------------------------------===// #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 \c rosa::Unit owned by a /// \c rosa::MessagingSystem, capable of handling \c rosa::Message instances /// as \c rosa::MessageHandler, and provides the \c rosa::AbstractAgent /// interface with \c rosa::AgentHandle as reference type. class Agent : public Unit, public MessageHandler, public AbstractAgent { friend class AgentHandle; ///< \c rosa::AgentHandle is our friend. protected: /// A handle for \p this object. const AgentHandle Self; public: /// Creates a new instance by instantiating all the base-classes. /// /// \tparam Fun type of the first mandatory function for handling messages /// \tparam Funs types of any further functions for handling messages /// /// \param Kind kind of the new \c rosa::Unit instance /// \param Id unique identifier of the new \c rosa::Unit instance /// \param Name name of the new \c rosa::Unit instance /// \param S \c rosa::MessagingSystem owning the new instance /// \param F the first mandatory function for handling messages /// \param Fs optional further functions for handling messages template Agent(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, Fun &&F, Funs &&... Fs); /// Destroys \p this object. ~Agent(void); /// Tells if \p this object is in a valid state. /// /// \note A \c rosa::Agent instance is always valid. /// /// \return if \p this object is in a valid state operator bool(void) const noexcept override; /// Tells if a given reference refers to \p this object. /// /// \param H reference to another object /// /// \return if \p H refers to \p this object bool operator==(const AgentHandle &H) const noexcept override; + /// Comapres \p this object to a given reference. + /// + /// The comparison is based on the memory addresses. + /// + /// \param H reference to another object + /// + /// \return if \p this object compares less to \p H + bool operator<(const AgentHandle &H) const noexcept override; + /// Returns a reference to \p this object. /// /// \return a reference to \p this object AgentHandle self(void) noexcept override; /// Sends a given \c rosa::message_t instance to \p this object. /// /// \param M message to send /// /// \note Since a \c rosa::Agent instance is always valid, there is no /// precondition for this function. /// \see \c rosa::AbstractAgent::sendMessage and /// `rosa::Agent::operator bool() const` void sendMessage(message_t &&M) noexcept override; /// Dumps \p this object into a \c std::string for tracing purposes. /// /// \return \c std::string representing the state of \p this object std::string dump(void) const noexcept override; protected: /// Returns a reference to the \c rosa::MessagingSystem owning \p this object. /// /// \return reference of \c rosa::Unit::S MessagingSystem &system(void) const noexcept override; /// Gives the referenced \c rosa::Agent instance for a \c rosa::AgentHandle. /// /// \note Intended for derived classes to be able to inspect /// \c rosa::AgentHandle instances. /// /// \param H \c rosa::AgentHandle to take the referenced \c rosa::Agent from /// /// \return reference to the \c rosa::Agent instance from \p H static Agent &unwrapAgent(const AgentHandle &H) noexcept { return H.A; } /// Gives the owning \c rosa::MessagingSystem of a \c rosa::Agent instance /// for a \c rosa::AgentHandle. /// /// \note Intended for for derived classes to be able to inspect /// \c rosa::AgentHandle instances. /// /// \param H \c rosa::AgentHandle to take the owning /// \c rosa::MessagingSystem from /// /// \return reference to the \c rosa::MessagingSystem owning the /// \c rosa::Agent instance from \p H static MessagingSystem &unwrapSystem(const AgentHandle &H) noexcept { return H.S; } }; 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, /*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 68df7b0..0a57953 100644 --- a/include/rosa/core/AgentHandle.hpp +++ b/include/rosa/core/AgentHandle.hpp @@ -1,302 +1,313 @@ //===-- rosa/core/AgentHandle.hpp -------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/core/AgentHandle.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Declaration of a handle for \c rosa::Agent. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_AGENTHANDLE_HPP #define ROSA_CORE_AGENTHANDLE_HPP #include "rosa/core/AbstractAgent.hpp" namespace rosa { /// Wraps an actual \c rosa::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 { /// \c rosa::Agent and \c rosa::MessagingSystem are our friends, they may /// inspect the private member fields of the class. ///@{ friend class Agent; friend class MessagingSystem; ///@} /// The wrapped \c rosa::Agent instance. Agent &A; /// The \c rosa::MessagingSystem owning \c A. MessagingSystem &S; /// Creates a new instance without validating the state of the wrapped /// \c rosa::Agent. /// /// \note Used by a \c rosa::Agent instance to create a reference to itself /// during construction, when its state is not valid yet. /// /// \param A \c rosa::Agent to wrap /// /// \note There a second argument, which is ignored, that is only present to /// separate this constructor from the public constructor taking only a /// \c rosa::Agent to wrap. AgentHandle(Agent &A, bool) noexcept; public: /// Creates a new instance validating the state of the wrapped \p rosa::Agent. /// /// \note The wrapped \c rosa::Agent must be in a valid state to instantiate /// \c rosa::AgentHandle with this constructor. /// /// \param A \c rosa::Agent to wrap /// /// \pre \p A is registered in its owning *system*:\code /// A.system().isUnitRegistered(A) /// \endcode AgentHandle(Agent &A); /// Destroys \p this object. /// /// The destructor has nothing to take care of. ~AgentHandle(void) = default; /// Tells if the wrapped \c rosa::Agent is in a valid state. /// /// \note A \c rosa::AgentHandler belongs to a \c rosa::MessagingSystem. /// Working with a \c rosa::AgentHandler whose originating /// \c rosa::MessagingSystem has already been destroyed results in *undefined* /// behavior. /// /// \return if the wrapped \c rosa::Agent is in a valid state operator bool(void) const noexcept override; /// Tells if another \c rosa::AgentHandle wraps the same \c rosa::Agent as /// \p this object. /// /// \param H \c rosa::AgentHandle whose wrapped \c rosa::Agent to check /// /// \return if \p H wraps \c A like \p this object bool operator==(const AgentHandle &H) const noexcept override; + /// Compares \p this object to another \c rosa::AgentHandle instance. + /// + /// The comparison is based on the memory addresses of the wrapped + /// \c rosa::Agent instances. + /// + /// \param H \c rosa::AgentHandle to compare to + /// + /// \return if \p this object a \c rosa::Agent instance whose address is less + /// than that of \p H + bool operator<(const AgentHandle &H) const noexcept override; + /// Returns a reference to the wrapped \c rosa::Agent. /// /// \return a reference to \c A AgentHandle self(void) noexcept override; /// Sends a given \c rosa::message_t instance to the wrapped \c rosa::Agent. /// /// \param M message to send /// /// \pre The wrapped \c rosa::Agent instance is in a valid state:\code /// bool(*this) /// \endcode void sendMessage(message_t &&M) noexcept override; }; /// Template specialization for optionally storing \c rosa::AgentHandle /// instances. /// /// \ingroup Optional /// /// Due to \c rosa::AgentHandle not supporting copying and moving of instances, /// the member functions in this class fall back to destroying the old stored /// object and creating a new one whenever the stored value is to be changed. template <> class Optional { public: using Type = AgentHandle; /// Creates an instance without value. /// /// \note Use it with its default parameter. Optional(const none_t & = none) : Valid(false) {} /// Creates a valid instance with value. /// /// \param X value to store in the object Optional(AgentHandle X) : Valid(false) { cr(std::move(X)); } /// Creates an instance as a copy of another one. /// /// \param Other the instance whose state to copy Optional(const Optional &Other) : Valid(false) { if (Other.Valid) { cr(Other.Value); } } /// Creates an instance as a copy of another one. /// /// \param Other the instance whose state to obtain Optional(Optional &&Other) noexcept : Valid(false) { if (Other.Valid) { cr(std::move(Other.Value)); } } /// Destroys \p this object. ~Optional(void) { destroy(); } /// Updates \p this object by copying the state of another one. /// /// \param Other the instance whose state to copy /// /// \return reference of the updated instance Optional &operator=(const Optional &Other) { if (Valid) { destroy(); } if (Other.Valid) { cr(Other.Value); } return *this; } /// Updates \p this object by copying the state of another one. /// /// \param Other the instance whose state to obtain /// /// \return reference of the updated instance Optional &operator=(Optional &&Other) noexcept { if (Valid) { destroy(); } if (Other.Valid) { cr(std::move(Other.Value)); } return *this; } /// Checks whether \p this object contains a value. /// /// \return if \p this object contains a value explicit operator bool(void) const { return Valid; } /// Checks whether \p this object does not contain a value. /// /// \return if \p this object does not contain a value bool operator!(void)const { return !Valid; } /// Returns the value stored in \p this object. /// /// \return reference of the stored value /// /// \pre \p this object contains a value AgentHandle &operator*(void) { ASSERT(Valid); return Value; } /// Returns the value stored in \p this object. /// /// \return reference of the stored value /// /// \pre \p this object contains a value const AgentHandle &operator*(void)const { ASSERT(Valid); return Value; } /// Returns the value stored in \p this object. /// /// \return pointer to the stored value /// /// \pre \p this object contains a value const AgentHandle *operator->(void)const { ASSERT(Valid); return &Value; } /// Returns the value stored in \p this object. /// /// \return pointer of the stored value /// /// \pre \p this object contains a value AgentHandle *operator->(void) { ASSERT(Valid); return &Value; } /// Returns the value stored in \p this object. /// /// \return reference of the stored value /// /// \pre \p this object contains a value AgentHandle &value(void) { ASSERT(Valid); return Value; } /// Returns the value stored in \p this object. /// /// \return reference of the stored value /// /// \pre \p this object contains a value const AgentHandle &value(void) const { ASSERT(Valid); return Value; } /// Returns the stored value or a default. /// /// If \p this object contains a value, then the stored value is returned. A /// given default value is returned otherwise. /// /// \param DefaultValue the value to return if \p this object does not contain /// a value /// /// \return reference to either the stored value or \p DefaultValue if \p this /// object does not contain a value const AgentHandle &valueOr(const AgentHandle &DefaultValue) const { return Valid ? Value : DefaultValue; } private: /// Deallocates the stored value if any. void destroy(void) { if (Valid) { Value.~AgentHandle(); Valid = false; } } /// Updates the state of \p this object by copying a value into it. /// /// \tparam V type of \p X /// /// \param X value to copy /// /// \pre \p this object does not contain a value template void cr(V &&X) { ASSERT(!Valid); Valid = true; new (&Value) AgentHandle(std::forward(X)); } /// Denotes if \p this object contains a value. bool Valid; /// Holds the stored value if any. union { AgentHandle Value; ///< The stored value. }; }; } // End namespace rosa #endif // ROSA_CORE_AGENTHANDLE_HPP diff --git a/include/rosa/core/System.hpp b/include/rosa/core/System.hpp index 25661cb..8925692 100644 --- a/include/rosa/core/System.hpp +++ b/include/rosa/core/System.hpp @@ -1,212 +1,238 @@ //===-- rosa/core/System.hpp ------------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/core/System.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Declaration of *System* interface. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_SYSTEM_HPP #define ROSA_CORE_SYSTEM_HPP #include "rosa/config/config.h" #include "rosa/core/forward_declarations.h" #include "rosa/support/debug.hpp" #include "rosa/support/log.h" #include #include #include namespace rosa { /// Base interface for actual agent-systems. /// /// The class provides facilities to keep track of \c rosa::Unit instances owned /// by a \c rosa::System. /// /// \note Any subclass is supposed to provide thread-safe implementation. /// /// \note The class declares only an interface to avoid trouble with multiple /// inheritance in various subclasses as in derived interfaces and derived /// implementations. /// /// \note Actual implementations are supposed to derive from \c rosa::SystemBase /// implenenting a base feature-set. class System { public: /// Signature of creator functions for \c rosa::Unit instances. /// /// \tparam T type derived from \c rosa::Unit /// \tparam S type derived from \c rosa::System template using UnitCreator = std::function; /// Returns an object implementing the \c rosa::System interface. /// /// \param Name name of the new instance /// /// \return \c std::unique_ptr for a new instance of \c rosa::System static std::unique_ptr createSystem(const std::string &Name) noexcept; protected: /// Creates an instance. /// /// \note Protected constructor restricts instantiation for subclasses. System(void) noexcept = default; /// No copying and moving of \c rosa::System. ///@{ System(const System &) = delete; System(System &&) = delete; System &operator=(const System &) = delete; System &operator=(System &&) = delete; ///@} public: /// Destroys \p this object. /// /// \note Any implementation makes sure that a \c rosa::System can be /// destroyed only if it is marked *cleaned* /// \see \c rosa::System::isSystemCleaned virtual ~System(void) = default; + /// Tells whether \p this object is the same as \p Other. + /// + /// \note Whenever checking equality of two objects, use the one with the + /// more specialized static type on the left-hand side of the operator. The + /// static type of the object on the right-hand side is better to be + /// \c rosa::System, ambiguous conversion might happen otherwise. + /// + /// \param Other another \c rosa::System instance to compare to + /// + /// \return whether \p this object and \p Other is the same + virtual bool operator==(const System &Other) const noexcept = 0; + + /// Tells whether \p this object is not the same as \p Other. + /// + /// \note Whenever checking inequality of two objects, use the one with the + /// more specialized static type on the left-hand side of the operator. The + /// static type of the object on the right-hand side is better to be + /// \c rosa::System, ambiguous conversion might happen otherwise. + /// + /// \param Other another \c rosa::System instance to compare to + /// + /// \return whether \p this object and \p Other is not the same + bool operator!=(const System &Other) const noexcept { + return !operator==(Other); + } + protected: /// Tells the next unique identifier to be used for a newly created /// \c rosa::Unit. /// /// \return \c rosa::id_t which is unique within the context of \p this /// object. /// /// \note Never returs the same value twice. virtual id_t nextId(void) noexcept = 0; /// Tells if \p this object has been marked cleaned and is ready for /// destruction. /// /// \return if \p this object is marked clean. virtual bool isSystemCleaned(void) const noexcept = 0; /// Marks \p this object cleaned. /// /// \note Can be called only once when the System does not have any live /// \c rosa::Unit instances. /// /// \pre \p this object has not yet been marked as cleaned and it has no /// \c rosa::Unit instances registered:\code /// !isSystemCleaned() && empty() /// \endcode /// /// \post \p this object is marked cleaned:\code /// isSystemCleaned() /// \encode virtual void markCleaned(void) noexcept = 0; /// Registers a \c rosa::Unit instance to \p this object. /// /// \param U \c rosa::Unit to register /// /// \pre \p this object has not yet been marked as cleaned and \p U is not /// registered yet:\code /// !isSystemCleaned() && !isUnitRegistered(U) /// \endcode /// /// \post \p U is registered:\code /// isUnitRegistered(U) /// \endcode virtual void registerUnit(Unit &U) noexcept = 0; /// Unregisters and destroys a registered \c rosa::Unit instance. /// /// \param U \c rosa::Unit to destroy /// /// \pre \p U is registered:\code /// isUnitRegistered(U) /// \endcode /// /// \post \p U is not registered and also destroyed. virtual void destroyUnit(Unit &U) noexcept = 0; /// Tells if a \c rosa::Unit is registered in \p this object. /// /// \param U \c rosa::Unit to check /// /// \return whether \p U is registered in \p this object virtual bool isUnitRegistered(const Unit &U) const noexcept = 0; /// Creates a \c rosa::Unit instance with the given /// \c rosa::System::UnitCreator and registers the new instance. /// /// \tparam T type of the actual \c rosa::Unit to instantiate /// \tparam S type of the actual \c rosa::System instantiating /// /// \param C function creating an instance of type \p T /// /// \note \p S must be the actual subclass that wants to instantiate /// \c rosa::Unit. That cannot be statically enforced, it is the /// reponsibility of the caller to provide the proper \c rosa::System /// subclass. /// /// \pre Statically, \p T is a subclass of \c rosa::Unit and \p S is a /// subclass of \c rosa::System:\code /// std::is_base_of::value && std::is_base_of::value /// \endcode Dynamically, \p this object has not yet been marked cleaned:\code /// !isSystemCleaned() /// \endcode template T &createUnit(UnitCreator C) noexcept; public: /// Tells the name of \p this object /// /// \note The returned reference remains valid as long as \p this object is /// not destroyed. /// /// \return name of \p this object virtual const std::string &name(void) const noexcept = 0; /// Tells the number of \c rosa::Unit instances constructed in the context of /// \p this object so far, including those being already destroyed. /// /// \return number of \c rosa::Unit instances created so far virtual size_t numberOfConstructedUnits(void) const noexcept = 0; /// Tells the number of live \c rosa::Unit instances in the context \p this /// object, those being constructed and not destroyed yet. /// /// \return number of \c rosa::Unit instances alive virtual size_t numberOfLiveUnits(void) const noexcept = 0; /// Tells if \p this object has no live \c rosa::Unit instances. /// /// \return whether \p this object has any live \c rosa::Unit instances virtual bool empty(void) const noexcept = 0; }; template T &System::createUnit(UnitCreator C) noexcept { STATIC_ASSERT((std::is_base_of::value), "not a Unit"); STATIC_ASSERT((std::is_base_of::value), "not a System"); if (isSystemCleaned()) { ROSA_CRITICAL("Trying to create a Unit in a cleaned System '" + name() + "'"); } const id_t Id = nextId(); T *U = C(Id, static_cast(*this)); registerUnit(*U); LOG_TRACE("Unit created and registered '" + U->FullName + "'"); return *U; } } // End namespace rosa #endif // ROSA_CORE_SYSTEM_HPP diff --git a/include/rosa/core/SystemBase.hpp b/include/rosa/core/SystemBase.hpp index f5fd038..83439e7 100644 --- a/include/rosa/core/SystemBase.hpp +++ b/include/rosa/core/SystemBase.hpp @@ -1,127 +1,138 @@ //===-- rosa/core/SystemBase.hpp -----------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/core/SystemBase.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Base implementation of the \c rosa::System interface. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_SYSTEMBASE_HPP #define ROSA_CORE_SYSTEMBASE_HPP #include "rosa/core/System.hpp" #include namespace rosa { /// Base implementation of the \c rosa::System interface. /// -/// This implementation provides only *name* for \c rosa::System, identifiers -/// for \c rosa::Unit instances, and marking the \c rosa::System cleaned for -/// destruction. +/// This implementation provides only equality checking and *name* for +/// \c rosa::System, identifiers for \c rosa::Unit instances, and marking the +/// \c rosa::System cleaned for destruction. /// /// \note Actual implementations of \c rosa::System and derived interfaces are /// supposed to inherit from this implementation. class SystemBase : public System { protected: /// Creates an instance. /// /// \note Protected constructor restrict instantiation for subclasses. /// /// \param Name name of the new instance SystemBase(const std::string &Name) noexcept; public: /// Destroys \p this object. /// /// \pre \p this object is marked cleaned:\code /// isSystemCleaned() /// \endcode ~SystemBase(void); + /// Tells whether \p this object is the same as \p Other. + /// + /// Two \c rosa::System instances are considered equal if they share a common + /// \c rosa::SystemBase::Name member field. That should do among various + /// subclasses. + /// + /// \param Other another \c rosa::System instance to compare to + /// + /// \return whether \p this object and \p Other is the same + bool operator==(const System &Other) const noexcept override; + protected: /// The textual name of \p this object implementing \c rosa::System. const std::string Name; private: /// Number of \c rosa::Unit instances constructed by \p this object. /// /// \note Should never be decremented! std::atomic UnitCount; /// Indicates that \p this object has been cleaned and is ready for /// destruction. /// /// The field is initialized as \c false and can be set by /// \c rosa::SystemBase::markCleaned. /// /// \note Subclasses must set the flag upon destructing their instances, which /// indicates to the destructor of the base-class that all the managed /// resources has been properly released. std::atomic SystemIsCleaned; public: /// Tells the name of \p this object /// /// \note The returned reference remains valid as long as \p this object is /// not destroyed. /// /// \return reference to \c rosa::SystemBase::Name const std::string &name(void) const noexcept override; protected: /// Tells the next unique identifier to be used for a newly created /// \c rosa::Unit. /// /// The functions takes the current value of the internal counter /// \c rosa::SystemBase::UnitCount and then increments it. /// /// \note This is the only function modifying /// \c rosa::SystemBase::UnitCount. /// /// \return \c rosa::id_t which is unique within the context of \p this /// object. id_t nextId(void) noexcept override; /// Tells if \p this object has been marked cleaned and is ready for /// destruction. /// /// \return if \p this object is marked clean. bool isSystemCleaned(void) const noexcept override; /// Marks \p this object cleaned by setting /// \c rosa::SystemBase::SystemIsCleaned. /// /// \note Can be called only once when the System does not have any live /// \c rosa::Unit instances. /// /// \pre \p this object has not yet been marked as cleaned and it has no /// \c rosa::Unit instances registered:\code /// !isSystemCleaned() && empty() /// \endcode /// /// \post \p this object is marked cleaned:\code /// isSystemCleaned() /// \encode void markCleaned(void) noexcept override; /// Tells the number of \c rosa::Unit instances constructed in the context of /// \p this object so far, including those being already destroyed. /// /// \return current value of \c rosa::SystemBase::UnitCount that is the number /// of \c rosa::Unit instances created so far size_t numberOfConstructedUnits(void) const noexcept override; }; } // End namespace rosa #endif // ROSA_LIB_CORE_SYSTEMBASE_HPP diff --git a/include/rosa/deluxe/DeluxeAgent.hpp b/include/rosa/deluxe/DeluxeAgent.hpp index d71db01..479b09d 100755 --- a/include/rosa/deluxe/DeluxeAgent.hpp +++ b/include/rosa/deluxe/DeluxeAgent.hpp @@ -1,667 +1,680 @@ //===-- rosa/deluxe/DeluxeAgent.hpp -----------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeAgent.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Specialization of \c rosa::Agent for *agent* role of the *deluxe /// interface*. /// /// \see \c rosa::deluxe::DeluxeContext /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXEAGENT_HPP #define ROSA_DELUXE_DELUXEAGENT_HPP #include "rosa/core/Agent.hpp" #include "rosa/deluxe/DeluxeAtoms.hpp" #include /// Local helper macros to deal with built-in types. /// ///@{ /// Creates function name for member functions in \c rosa::deluxe::DeluxeAgent. /// /// \param N name suffix to use #define DAHANDLERNAME(N) handleSlave_##N /// Defines member functions for handling messages from *slaves* in /// \c rosa::deluxe::DeluxeAgent. /// /// \see \c DeluxeAgentInputHandlers /// /// \note No pre- and post-conditions are validated directly by these functions, /// they rather rely on \c rosa::deluxe::DeluxeAgent::saveInput to do that. /// /// \param T the type of input to handle /// \param N name suffix for the function identifier #define DAHANDLERDEFN(T, N) \ void DAHANDLERNAME(N)(atoms::Slave, id_t Id, T Value) noexcept { \ saveInput(Id, Value); \ } /// Convenience macro for \c DAHANDLERDEFN with identical arguments. /// /// \see \c DAHANDLERDEFN /// /// This macro can be used instead of \c DAHANDLERDEFN if the actual value of /// \p T can be used as a part of a valid identifier. /// /// \param T the type of input to handle #define DAHANDLERDEF(T) DAHANDLERDEFN(T, T) /// Results in a \c THISMEMBER reference to a member function defined by /// \c DAHANDLERDEFN. /// /// Used in the constructor of \c rosa::deluxe::DeluxeAgent to initialize super /// class \c rosa::Agent with member function defined by \c DAHANDLERDEFN. /// /// \see \c DAHANDLERDEFN, \c THISMEMBER /// /// \param N name suffix for the function identifier #define DAHANDLERREF(N) THISMEMBER(DAHANDLERNAME(N)) ///@} namespace rosa { namespace deluxe { /// Specialization of \c rosa::Agent for *agent* role of the *deluxe interface*. /// /// \see \c rosa::deluxe::DeluxeContext /// /// \invariant All input-related container objects have a size matching /// \c rosa::deluxe::DeluxeAgent::NumberOfInputs, thus having a corresponding /// entry for each input. Types of input values are consistent throughout all /// the input-related containers. No *slave* is registered at more than one /// input position. *Slave* registrations and corresponding reverse lookup /// information are consistent. /// /// \see Definition of \c rosa::deluxe::DeluxeAgent::inv on the class invariant /// /// \note All member functions validate the class invariant as part of their /// precondition. Moreover, non-const functions validate the invariant before /// return as their postcondition. class DeluxeAgent : public Agent { /// Checks whether \p this object holds the class invariant. /// /// \see Invariant of the class \c rosa::deluxe::DeluxeAgent /// /// \return if \p this object holds the class invariant bool inv(void) const noexcept; public: /// Template alias for function objects used to process input and generate /// output for \c rosa::deluxe::DeluxeAgent. /// + /// The output generated by the function is optional as an agent may decide + /// not to output anything at some situation. + /// /// \tparam T type of output /// \tparam A type of mandatory first input value /// \tparam As types of further optional input values template - using D = - std::function, std::pair...) noexcept>; + using D = std::function(std::pair, + std::pair...) noexcept>; /// The type of values produced by \p this object. /// /// That is the type of values \p this object sends to its *master*. /// /// \see \c rosa::deluxe::DeluxeAgent::master const TypeNumber OutputType; /// Number of inputs processed by \p this object. const size_t NumberOfInputs; private: /// Types of input values produced by *slaves* of \p this object. /// /// \note The \c rosa::TypeNumber values stored here match the corresponding /// values in \c rosa::deluxe::DeluxeAgent::InputValues. /// /// \note The position of a type in the \c std::vector indicates which /// argument of \p this object's processing function it belongs to. See also /// \c rosa::deluxe::DeluxeAgent::D. const std::vector InputTypes; /// Indicates whether any particular input value has been changed since the /// last trigger received from the system. /// /// All the flags are reset to \c false upon handling a trigger and then set /// to \c true by \c rosa::deluxe::DeluxeAgent::saveInput when storing a new /// input value in \c rosa::deluxe::DeluxeAgent::InputValues. /// /// \note The position of a flag in the \c std::vector indicates which /// argument of \p this object's processing function it belongs to. See also /// \c rosa::deluxe::DeluxeAgent::D. std::vector InputChanged; /// Stores the actual input values. /// /// \note The types of stored values match the corresponding /// \c rosa::TypeNumber values in \c rosa::deluxe::DeluxeAgent::InputTypes. /// /// \note The position of a value in the \c rosa::AbstractTokenizedStorage /// indicates which argument of \p this object's processing function it is. /// See also \c rosa::deluxe::DeluxeAgent::D. const std::unique_ptr InputValues; /// Alias for function objects used as trigger handler for /// \c rosa::deluxe::DeluxeAgent. /// /// \see \c rosa::deluxe::DeluxeAgent::FP using H = std::function; /// Handles trigger from the system. /// - /// The actual function processing *slave* inputs and generating output to - /// *master* is captured in a lambda expression that is in turn wrapped in a - /// \c std::function object. The lambda expression calls the processing - /// function with the actual input data and sends its result to *master* by - /// calling \c rosa::deluxe::DeluxeAgent::sendToMaster. Also, all the flags - /// stored in \c rose::deluxe::DeluxeAgent::InputChanged are reset when the - /// current input values are processed. The function + /// The actual function processing *slave* inputs and generating optional + /// output to *master* is captured in a lambda expression that is in turn + /// wrapped in a \c std::function object. The lambda expression calls the + /// processing function with the actual input data and sends its result -- if + /// any -- to *master* by calling \c rosa::deluxe::DeluxeAgent::sendToMaster. + /// Also, all the flags stored in \c rose::deluxe::DeluxeAgent::InputChanged + /// are reset when the current input values are processed. The function /// \c rosa::deluxe::DeluxeAgent::handleTrigger needs only to call the /// function object. /// /// \see \c rosa::deluxe::DeluxeAgent::triggerHandlerFromProcessingFunction const H FP; /// The *master* to send values to. /// /// \note *Masters* are set dynamically, hence it is possible that a /// \c rosa::deluxe::DeluxeAgent instance does not have any *master* at a /// given moment. Optional Master; /// The *slaves* sending input to \p this object. /// /// \note The position of a *slave* in the \c std::vector indicates which /// argument of \p this object's processing function it belongs to. See also /// \c rosa::deluxe::DeluxeAgent::D. /// /// \note *Slaves* are set dynamically, hence it is possible that a /// \c rosa::deluxe::DeluxeAgent instance does have input positions without /// any *slave* associated to them. /// /// \note Reverse lookup information is maintained in /// \c rosa::deluxe::DeluxeAgent::SlaveIds, which is to be kept in sync with /// the *slaves* stored here. std::vector> Slaves; /// Associates \c rosa::id_t values to corresponding indices of registered /// *slaves*. /// /// \see \c rosa::deluxe::DeluxeAgent::Slaves std::map SlaveIds; /// Tells whether types \p As... match the input types of \p this object. /// /// \tparam As types to match against values in /// \c rosa::deluxe::DeluxeAgent::InputTypes /// /// \return if types \p As... match \c rosa::TypeNumber values stored in /// \c rosa::deluxe::DeluxeAgent::InputTypes template bool inputTypesMatch(void) const noexcept; /// Gives an \c std::tuple containing the current input values and their /// change flags so that they can be used for the processing function. /// /// \tparam As types of the input values /// \tparam S0 indices for accessing input values and their change flags /// /// \note The only argument provides indices statically as template arguments /// \p S0..., so its actual value is ignored. /// /// \return current input values and their change flags prepared for invoking /// the processing function with them /// /// \pre The type arguments \p As... match the input types of \p this object /// and the provided indices \p S0... constitute a proper sequence for /// accessing input values and their change flags: \code /// inputTypesMatch() && sizeof...(As) == sizeof...(S0) /// \endcode template std::tuple...> prepareCurrentInputs(Seq) const noexcept; /// Invokes a processing function matching the output and input types of \p /// this object with actual arguments provided in a \c std::tuple. /// /// \note \p Args providing the actual arguments for \p F is to be created by /// \c rosa::deluxe::DeluxeAgent::prepareCurrentInputs. /// /// \tparam T output type of the processing function /// \tparam A type of the first mandatory input for the processing function /// \tparam As types of further optional inputs for the processing function /// \tparam S1 indices starting with `1` for extracting actual arguments from /// \p Args /// /// \param F the processing function to invoke /// \param Args the actual arguments to invoke \p F with /// /// \note The last argument provides indices statically as template arguments /// \p S1..., so its actual value is ignored. /// /// \return the result of \p F for actual arguments \p Args /// /// \pre The provided sequence of indices \p S1... prefixed with the value /// `0` constitutes a proper sequence for extracting all actual arguments for /// \p F from \p Args: \code /// sizeof...(As) == sizeof...(S1) /// \endocde template - static T + static Optional invokeWithTuple(D F, std::tuple, std::pair...> Args, Seq<0, S1...>) noexcept; /// Wraps a processing function into a trigger handler. /// /// \see \c rosa::deluxe::DeluxeAgent::FP /// /// \note The function cannot be const qualified because the lambda /// expression defined in it needs to capture \p this object by a non-const /// reference /// /// \tparam T type of output /// \tparam A type of the first mandatory input value /// \tparam As types of further optional input values /// /// \param F function processing inputs and generating output /// /// \pre Template arguments \p T, \p A and \p As... match the corresponding /// types \p this object was created with: \code /// OutputType == TypeNumberOf::Value && inputTypesMatch() /// \endcode template H triggerHandlerFromProcessingFunction(D &&F) noexcept; public: /// Creates a new instance. /// /// The constructor instantiates the base-class with functions to handle /// messages as defined for the *deluxe interface*. /// /// \note Template argument deduction for this constructor breaks older Clang /// versions, the minimal working version is 3.9.0. The issue and minimal /// version requirement are recorded in the documentation. Using the named /// constructor idiom was also investigated to no avail. Explicit /// specification of actual template arguments does not stop Clang 3.8.0 from /// breaking on a call to a corresponding named constructor. /// /// \tparam T type of output of \p F /// \tparam A type of mandatory first input value of \p F /// \tparam As types of further optional input values of \p F /// /// \note Instantiation fails if any of the type arguments \p T, \p A, and /// \p As... is not a built-in type. /// /// \param Kind kind of the new \c rosa::Unit instance /// \param Id unique identifier of the new \c rosa::Unit instance /// \param Name name of the new \c rosa::Unit instance /// \param S \c rosa::MessagingSystem owning the new instance /// \param F function to process input values and generate output with /// /// \pre Statically, all of the type arguments \p T, \p A, and \p As... is a /// built-in type: \code /// TypeListSubsetOf, BuiltinTypes>::Value /// \endcode Dynamically, the instance is created as of kind /// \c rosa::deluxe::atoms::AgentKind: \code /// Kind == rosa::deluxe::atoms::AgentKind /// \endcode template , BuiltinTypes>::Value>> DeluxeAgent(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, D &&F) noexcept; /// Destroys \p this object. ~DeluxeAgent(void) noexcept; /// The *master* of \p this object, if any is registered. /// /// \see \c rosa::deluxe::DeluxeAgent::registerMaster /// /// \return the *master* registered for \p this object Optional master(void) const noexcept; /// Registers a *master* for \p this object. /// /// The new *master* is registered by overwriting the reference to any /// already registered *master*. One can clear the registered reference by /// passing an *empty* \c rosa::Optional object as actual argument. /// /// \note The role of the referred *master* is validated by checking its /// *kind*. /// /// \param Master the *master* to register /// /// \pre \p Master is empty or of kind \c rosa::deluxe::atoms::AgentKind: /// \code /// !Master || unwrapAgent(*Master).Kind == rosa::deluxe::atoms::AgentKind /// \endcode void registerMaster(const Optional Master) noexcept; /// Tells the type of values consumed from the *slave* at a position. /// /// That is the type of values \p this object expect to be sent to it by its /// *slave* registered at position \p Pos. /// /// \see \c rosa::deluxe::DeluxeAgent::slave /// /// \param Pos position of *slave* /// /// \return \c rosa::TypeNumber representing the type of values consumed from /// the *slave* at position \p Pos /// /// \pre \p Pos is a valid index of input: \code /// Pos < NumberOfInputs /// \endcode TypeNumber inputType(const size_t Pos) const noexcept; /// The *slave* of \p this object registered at a position, if any. /// /// \see \c rosa::deluxe::DeluxeAgent::registerSlave /// /// \param Pos position of *slave* /// /// \return the *slave* registered for \p this object at position \p Pos /// /// \pre \p Pos is a valid index of input: \code /// Pos < NumberOfInputs /// \endcode Optional slave(const size_t Pos) const noexcept; /// Registers a *slave* for \p this object at a position. /// /// The new *slave* is registered by overwriting the reference to any already /// registered *slave* at position \p Pos. One can clear the registered /// reference by passing an *empty* \c rosa::Optional object as actual /// argument. If \p Slave is already registered for another position, the /// other position gets cleared. /// /// \note The role of the referred *slave* is validated by checking its /// *kind*. /// /// \note The type of values produced by the referred *slave* is validated by /// matching its `OutputType` against the corresponding value in /// \c rosa::deluxe::DeluxeAgent::InputTypes. /// /// \param Pos position to register \p Slave at /// \param Slave the *slave* to register /// /// \pre \p Pos is a valid index of input, \p Slave is empty or of kind /// \c rosa::deluxe::atoms::AgentKind or \c rosa::deluxe::atoms::SensorKind, /// and \p Slave -- if not empty -- produces values of types matching the /// expected input type at position \p Pos: /// \code /// Pos < NumberOfInputs && /// (!Slave || /// (unwrapAgent(*Slave.)Kind == rosa::deluxe::atoms::SensorKind && /// static_cast(unwrapAgent(*Slave)).OutputType == /// InputTypes[Pos]) || /// (unwrapAgent(*Slave).Kind == rosa::deluxe::atoms::AgentKind && /// static_cast(unwrapAgent(*Slave)).OutputType == /// InputTypes[Pos])) /// \endcode void registerSlave(const size_t Pos, const Optional Slave) noexcept; + /// Tells the position of a registered *slave*. + /// + /// \param Slave \c rosa::AgentHandle for the *slave* to check + /// + /// \return position of \p Slave if it is registered and found, + /// \c rosa::deluxe::DeluxeAgent::NumberOfInputs otherwise. + size_t positionOfSlave(AgentHandle Slave) const noexcept; + private: /// Sends a value to the *master* of \p this object. /// /// \p Value is getting sent to \c rosa::deluxe::DeluxeAgent::Master if it /// contains a valid handle for a \c rosa::deluxe::DeluxeAgent. The function /// does nothing otherwise. /// /// \tparam T type of the value to send /// /// \param Value value to send /// /// \pre \p T matches \c rosa::deluxe::DeluxeiAgent::OutputType: \code /// OutputType == TypeNumberOf::Value /// \endcode template void sendToMaster(const T &Value) noexcept; /// Generates the next output by processing current input values upon trigger /// from the system. /// /// Executes \c rosa::deluxe::DeluxeAgent::FP. /// /// \note The only argument is a \c rosa::AtomConstant, hence its actual /// value is ignored. void handleTrigger(atoms::Trigger) noexcept; /// Stores a new input value from a *slave*. /// /// The function stores \p Value in \c rosa::deluxe::DeluxeAgent::InputValues /// at the position associated to \p Id in /// \c rosa::deluxe::DeluxeAgent::SlaveIds and also sets the corresponding /// flag in \c rosa::deluxe::DeluxeAgent::InputChanged. /// /// \note Utilized by member functions of group \c DeluxeAgentInputHandlers. /// /// \tparam T type of input to store /// /// \param Id unique identifier of *slave* /// \param Value the input value to store /// /// \pre The *slave* with \p Id is registered and the input from it is /// expected to be of type \p T: \code /// SlaveIds.find(Id) != SlaveIds.end() && /// InputTypes[SlaveIds.find(Id)->second] == TypeNumberOf::Value /// \endcode template void saveInput(id_t Id, T Value) noexcept; /// \defgroup DeluxeAgentInputHandlers /// /// Definition of member functions handling messages from *slaves* with /// different types of input /// /// A *master* generally needs to be prepared to deal with values of any /// built-in type. Each type requires a separate message handler, which are /// implemented by these functions. The functions instantiate /// \c rosa::deluxe::DeluxeAgent::saveInput with the proper template argument /// and pass the content of the message on for processing. /// /// \note The member functions in this group are defined by \c DAHANDLERDEF. /// /// \note Keep these definitions in sync with \c rosa::BuiltinTypes. /// ///@{ DAHANDLERDEF(AtomValue) DAHANDLERDEF(int16_t) DAHANDLERDEF(int32_t) DAHANDLERDEF(int64_t) DAHANDLERDEF(int8_t) DAHANDLERDEFN(long double, long_double) DAHANDLERDEFN(std::string, std__string) DAHANDLERDEF(uint16_t) DAHANDLERDEF(uint32_t) DAHANDLERDEF(uint64_t) DAHANDLERDEF(uint8_t) DAHANDLERDEF(unit_t) DAHANDLERDEF(bool) DAHANDLERDEF(double) DAHANDLERDEF(float) /// @} }; /// Anonymous namespace with implementation for /// \c rosa::deluxe::DeluxeAgent::inputTypesMatch, consider it private. namespace { /// Template \c struct whose specializations provide a recursive implementation /// for \c rosa::deluxe::DeluxeAgent::inputTypesMatch. /// /// \note Matching a list of types \p As... against a \c std::vector of /// \c rosa::TypeNumber values, \c InputTypes, like \code /// bool match = InputTypesMatchImpl::f(InputTypes, 0); /// \endcode /// /// \tparam As types to match template struct InputTypesMatchImpl; /// Template specialization for the general case, when at least one type is to /// be matched. /// /// \tparam A first type to match /// \tparam As further types to match template struct InputTypesMatchImpl { /// Tells whether types \p A, \p As... match \c rosa::TypeNumber values /// stored in \p InputTypes starting at position \p Pos. /// /// The function has got a recursive implementation: it matches the first /// type \p A against \c rosa::TypeNumber at position \p Pos of \p /// InputTypes, then further types \p As.. are matched recursively starting /// at position \c (Pos + 1). /// /// \param InputTypes container of \c rosa::TypeNumber values to match /// types against /// \param Pos position in \p InputTypes to start matching at /// /// \return if types \p A, \p As... match \c rosa::TypeNumber values stored /// in \p InputTypes starting at position \p Pos static bool f(const std::vector &InputTypes, size_t Pos) noexcept { return Pos < InputTypes.size() && TypeNumberOf::Value == InputTypes[Pos] && InputTypesMatchImpl::f(InputTypes, Pos + 1); } }; /// Template specialization for the terminal case, when no type remains to /// check. template <> struct InputTypesMatchImpl<> { /// Tells whether \p Pos is the number of values stored in \p InputTypes. /// /// In this terminal case, there is no more types to matchi because all the /// types are supposed to be already matched successfully. The whole list of /// types already matched is a complete match if it covers all values in /// \p InputTypes. That is true if \p Pos points exactly to the end of /// \p InputTypes. /// /// \param InputTypes container of \c rosa::TypeNumber values to match /// types against /// \param Pos position in \p InputTypes to start matching at /// /// \return if \p Pos is the number of values stored in \p InputTypes static bool f(const std::vector &InputTypes, size_t Pos) noexcept { return Pos == InputTypes.size(); } }; } // End namespace template bool DeluxeAgent::inputTypesMatch(void) const noexcept { return InputTypesMatchImpl::f(InputTypes, 0); } template std::tuple...> DeluxeAgent::prepareCurrentInputs(Seq) const noexcept { // Need to indirectly reference \c rosa::deluxe::DeluxeAgent::inputTypesMatch // inside \c ASSERT because of the comma in its template argument list. auto MFP = &DeluxeAgent::inputTypesMatch; ASSERT(inv() && (this->*MFP)() && sizeof...(As) == sizeof...(S0)); return std::make_tuple( std::make_pair(*static_cast(InputValues->pointerTo(S0)), InputChanged[S0])...); } template -T DeluxeAgent::invokeWithTuple( +Optional DeluxeAgent::invokeWithTuple( D F, std::tuple, std::pair...> Args, Seq<0, S1...>) noexcept { ASSERT(sizeof...(As) == sizeof...(S1)); return F(std::get<0>(Args), std::get(Args)...); } template DeluxeAgent::H DeluxeAgent::triggerHandlerFromProcessingFunction(D &&F) noexcept { // Need to indirectly reference \c rosa::deluxe::DeluxeAgent::inputTypesMatch // inside \c ASSERT because of the comma in its template argument list. auto MFP = &DeluxeAgent::inputTypesMatch; ASSERT(OutputType == TypeNumberOf::Value && (this->*MFP)()); return [this, F]() noexcept { using Indices = typename GenSeq::Type; auto Args = prepareCurrentInputs(Indices()); std::fill(InputChanged.begin(), InputChanged.end(), false); - T R = invokeWithTuple(F, Args, Indices()); - sendToMaster(R); + Optional R = invokeWithTuple(F, Args, Indices()); + if (R) { + sendToMaster(*R); + } }; } template DeluxeAgent::DeluxeAgent(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, D &&F) noexcept : Agent(Kind, Id, Name, S, THISMEMBER(handleTrigger), DAHANDLERREF(AtomValue), DAHANDLERREF(int16_t), DAHANDLERREF(int32_t), DAHANDLERREF(int64_t), DAHANDLERREF(int8_t), DAHANDLERREF(long_double), DAHANDLERREF(std__string), DAHANDLERREF(uint16_t), DAHANDLERREF(uint32_t), DAHANDLERREF(uint64_t), DAHANDLERREF(uint8_t), DAHANDLERREF(unit_t), DAHANDLERREF(bool), DAHANDLERREF(double), DAHANDLERREF(float)), OutputType(TypeNumberOf::Value), NumberOfInputs(1 + sizeof...(As)), InputTypes({TypeNumberOf::Value, TypeNumberOf::Value...}), InputChanged(NumberOfInputs, false), InputValues(new TokenizedStorage()), FP(triggerHandlerFromProcessingFunction(std::move(F))), Slaves(NumberOfInputs) { ASSERT(Kind == atoms::AgentKind); LOG_TRACE("DeluxeAgent is created."); ASSERT(inv()); } template void DeluxeAgent::sendToMaster(const T &Value) noexcept { ASSERT(inv() && OutputType == TypeNumberOf::Value); // There is a handle and the referred *master* is in a valid state. if (Master && *Master) { Master->sendMessage(Message::create(atoms::Slave::Value, Id, Value)); } } template void DeluxeAgent::saveInput(id_t Id, T Value) noexcept { ASSERT(inv() && SlaveIds.find(Id) != SlaveIds.end() && InputTypes[SlaveIds.find(Id)->second] == TypeNumberOf::Value); size_t Pos = SlaveIds.at(Id); *static_cast(InputValues->pointerTo(Pos)) = Value; InputChanged[Pos] = true; ASSERT(inv()); } } // End namespace deluxe } // End namespace rosa #undef DAHANDLEREF #undef DAHANDLEDEF #undef DAHANDLEDEFN #undef DAHANDLENAME #endif // ROSA_DELUXE_DELUXEAGENT_HPP diff --git a/include/rosa/deluxe/DeluxeContext.hpp b/include/rosa/deluxe/DeluxeContext.hpp index 52e9a72..d083d28 100755 --- a/include/rosa/deluxe/DeluxeContext.hpp +++ b/include/rosa/deluxe/DeluxeContext.hpp @@ -1,282 +1,292 @@ //===-- rosa/deluxe/DeluxeContext.hpp ---------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeContext.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Public interface for the *deluxe interface* for working with agent /// systems. /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXECONTEXT_HPP #define ROSA_DELUXE_DELUXECONTEXT_HPP #include "rosa/deluxe/DeluxeSystem.hpp" #include "rosa/support/types.hpp" #include #include #include /// Local helper macro to log and return a /// \c rosa::deluxe::DeluxeContext::ErrorCode value. /// /// Creates a debug message with the stringified value and returns the value. /// /// \param Err \c rosa::deluxe::DeluxeContext::ErrorCode value to log and /// return #define DCRETERROR(Err) \ { \ LOG_DEBUG(#Err); \ return Err; \ } namespace rosa { namespace deluxe { /// Defines the *deluxe interface*. class DeluxeContext { /// A system owned by \p this object. /// /// \note The reference is kept in a \c std::shared_ptr because of the member /// function \c rosa::deluxe::DeluxeContext::getSystem. std::shared_ptr System; /// References to all *sensors* and *agents* created by \p this object. std::set DeluxeUnits; public: /// Errors that may be resulted by some of the member functions of the class. enum struct ErrorCode { NoError, TypeMismatch, NotSensor, NotAgent, WrongPosition, AlreadyHasSlave, AlreadyHasMaster, AlreadyHasValueStream }; /// Returns a new instance of \c rosa::deluxe::DeluxeContext. /// /// \param Name name of the underlying \c rosa::DeluxeSystem /// /// \return \c std::unique_ptr for the new instance of /// \c rosa::deluxe::DeluxeContext with a new, empty \c rosa::DeluxeSystem static std::unique_ptr create(const std::string &Name) noexcept; private: /// Creates a new instance. /// /// \note Private constructor restricts instantiation to member functions of /// the class. /// /// \param Name name of the underlying \c rosa::MessagingSystem DeluxeContext(const std::string &Name) noexcept; public: /// Destroys \p this object. ~DeluxeContext(void) noexcept; /// Returns a reference for the underlying \c rosa::MessagingSystem. /// /// \note One cannot do much with a \c rosa::MessagingSystem currently, this /// is for future use. /// /// \return reference for the underlying \c rosa::MessagingSystem. std::weak_ptr getSystem(void) const noexcept; /// Creates a new *sensor* in the context of \p this object. /// /// \tparam T type of data the new *sensor* operates on /// /// \param Name name of the new *sensor* /// \param F function for the new *sensor* to generate the next value with /// during normal operation /// /// \note \p F is not used during simulation, in which case /// \c rosa::deluxe::DeluxeContext::registerSensorValues is used to register /// an alternative simulation data source with /// \c rosa::deluxe::DeluxeSensor::registerSimulationDataSource. One may /// safely keep relying on the default value of \p F as long as only /// simulation of the system is to be done. /// /// \return \c rosa::AgentHandle for the new *sensor* template AgentHandle createSensor(const std::string &Name, DeluxeSensor::D &&F = [](void) { return T(); }) noexcept; /// Creates a new *agent* in the context of \p this object. /// /// \tparam T type of data the new *agent* outputs /// \tparam A type of mandatory first input the new *agent* takes /// \tparam As types of futher optional inputs the new *agent* takes /// /// \param Name name of the new *agent* /// \param F function for the new *agent* to process input values and /// generate output with /// /// \return \c rosa::AgentHandle for the new *agent* template AgentHandle createAgent(const std::string &Name, DeluxeAgent::D &&F) noexcept; /// Connectes a *sensor* to an *agent* in the context of \p this object. /// /// \param Agent the *agent* to connect to /// \param Pos the index of slot of \p Agent to connect \p Sensor to /// \param Sensor the *sensor* to connect /// \param Description optional textual description of the connection /// /// \return how successfull connecting \p Sensor to \p Agent at slot index /// \p Pos was /// /// \note The function may return the following /// \c rosa::deluxe::DeluxeContext::ErrorCode values: /// `ErrorCode` | Comment /// ----------- | ------- /// `NoError` | Success /// `NotAgent` | Referred \p Agent is not \c rosa::deluxe::DeluxeAgent /// `NotSensor` | Referred \p Sensor is not \c rosa::deluxe::DeluxeSensor /// `WrongPosition` | \p Pos is not a valid input position of \p Agent /// `TypeMismatch` | Expected input type at position \p Pos of \p Agent is other than the output type of \p Sensor /// `AlreadyHasSlave` | \p Agent at position \p Pos already has a *slave* registered /// `AlreadyHasMaster` | \p Sensor already has a *master* registered ErrorCode connectSensor(AgentHandle &Agent, const size_t Pos, AgentHandle &Sensor, const std::string &Description = "") noexcept; /// Connectes two *agents* in the context of \p this object. /// /// \param Master the *agent* to connect to /// \param Pos the index of slot of \p Master to connect \p Slave to /// \param Slave the *agent* to connect /// \param Description optional textual description of the connection /// /// \return how succesfull connecting \p Slave to \p Master at slot index /// \p Pos was /// /// \note The function may return the following /// \c rosa::deluxe::DeluxeContext::ErrorCode values: /// `ErrorCode` | Comment /// ----------- | ------- /// `NoError` | Success /// `NotAgent` | Referred \p Master or \p Slave is not \c rosa::deluxe::DeluxeAgent /// `WrongPosition` | \p Pos is not a valid input position of \p Master /// `TypeMismatch` | Expected input type at position \p Pos of \p Master is other than the output type of \p Slave /// `AlreadyHasSlave` | \p Master at position \p Pos already has a *slave* registered /// `AlreadyHasMaster` | \p Slave already has a *master* registered ErrorCode connectAgents(AgentHandle &Master, const size_t Pos, AgentHandle &Slave, const std::string &Description = "") noexcept; /// Initializes \c this object and others managed by \p this object for /// setting up and performing simulation. /// /// \see \c rosa::deluxe::DeluxeContext::registerSensorValues, /// \c rosa::deluxe::DeluxeContext::simulate /// /// Need to clear simulation data sources from all the *sensors*. void initializeSimulation(void) noexcept; /// Registers a stream providing values for a *sensor* during simulation. /// - /// \tparam T type of values \p Sensor is operating on + /// \tparam Iterator type of iterator providing values for \p Sensor + /// \tparam T type of values \p Sensor is operating on, always use default! /// /// \param Sensor the *sensor* to register values for /// \param Start provides values for \p Sensor /// \param End denotes the end of stream of values /// \param Default value to be used when input stream is depleted during /// simulation /// /// \return how successful registering \p Source for \p Sensor /// /// \note The function may return the following /// \c rosa::deluxe::DeluxeContext::ErrorCode values: /// `ErrorCode` | Comment /// ----------- | ------- /// `NoError` | Success /// `TypeMismatch` | \p Sensor generates values of a type other than \p T /// `NotSensor` | Referred \p Sensor is not \c rosa::deluxe::DeluxeSensor /// `AlreadyHasValueStream` | \p Sensor already has simulation data source set - template - ErrorCode - registerSensorValues(AgentHandle Sensor, std::istream_iterator &&Start, - std::istream_iterator &&End, T Default = {}) noexcept; + template + ErrorCode registerSensorValues(AgentHandle Sensor, Iterator &&Start, + const Iterator &End, T Default = {}) noexcept; /// Performs the system contained by \p this object. /// /// The function performs \p NumCycles cycle of simulation. In each cycle, /// all the *agents* and *sensors* registered in /// \c rosa::deluxe::DeluxeContext::DeluxeUnits are trigged for execution. /// /// \param NumCycles number of cycles to perform /// /// \pre All the *sensors* in the system contained by \p this object generate /// their output from simulation data sources. void simulate(const size_t NumCycles) const noexcept; }; template AgentHandle DeluxeContext::createSensor(const std::string &Name, DeluxeSensor::D &&F) noexcept { - return System->createSensor(Name, std::move(F)); + AgentHandle H = System->createSensor(Name, std::move(F)); + DeluxeUnits.emplace(H); + return H; } template AgentHandle DeluxeContext::createAgent(const std::string &Name, DeluxeAgent::D &&F) noexcept { - return System->createAgent(Name, std::move(F)); + AgentHandle H = System->createAgent(Name, std::move(F)); + DeluxeUnits.emplace(H); + return H; } -template -DeluxeContext::ErrorCode DeluxeContext::registerSensorValues( - AgentHandle Sensor, std::istream_iterator &&Start, - std::istream_iterator &&End, T Default) noexcept { +template +DeluxeContext::ErrorCode +DeluxeContext::registerSensorValues(AgentHandle Sensor, Iterator &&Start, + const Iterator &End, T Default) noexcept { + // Get the type of values provided by \p Iterator. + STATIC_ASSERT((std::is_same::value), + "type mismatch"); + // Make sure preconditions are met. if (!System->isDeluxeSensor(Sensor)) { DCRETERROR(ErrorCode::NotSensor); } auto S = System->getDeluxeSensor(Sensor); ASSERT(S); // Sanity check. if (S->OutputType != TypeNumberOf::Value) { DCRETERROR(ErrorCode::TypeMismatch); } else if (S->simulationDataSourceIsSet()) { DCRETERROR(ErrorCode::AlreadyHasValueStream); } // Register input stream. - S->registerSimulationDataSource([&](void) noexcept { + S->registerSimulationDataSource(DeluxeSensor::D([&](void) noexcept { if (Start != End) { + LOG_TRACE("Reading next value for sensor '" + S->FullName + "'"); return *Start++; } else { + LOG_TRACE("Providing default value for sensor '" + S->FullName + "'"); return Default; } - }); + })); return ErrorCode::NoError; } } // End namespace deluxe } // End namespace rosa // Undef local macro if not used in the corresponding implementation. #ifndef ROSA_LIB_DELUXE_DELUXECONTEXT_CPP #undef DCRETERROR #endif #endif // ROSA_DELUXE_DELUXECONTEXT_HPP diff --git a/include/rosa/deluxe/DeluxeSensor.hpp b/include/rosa/deluxe/DeluxeSensor.hpp index 9e4b61d..134e1f4 100755 --- a/include/rosa/deluxe/DeluxeSensor.hpp +++ b/include/rosa/deluxe/DeluxeSensor.hpp @@ -1,250 +1,248 @@ //===-- rosa/deluxe/DeluxeSensor.hpp ----------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeSensor.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Specialization of \c rosa::Agent for *sensor* role of the the *deluxe /// interface*. /// /// \see \c rosa::deluxe::DeluxeContext /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXESENSOR_HPP #define ROSA_DELUXE_DELUXESENSOR_HPP #include "rosa/core/Agent.hpp" #include "rosa/deluxe/DeluxeAtoms.hpp" namespace rosa { namespace deluxe { /// Specialization of \c rosa::Agent for *sensor* role of the *deluxe /// interface*. /// /// \see \c rosa::deluxe::DeluxeContext class DeluxeSensor : public Agent { public: /// Template alias for function objects used as data source for /// \c rosa::deluxe::DeluxeSensor. /// /// \tparam T type of data provided by the function template using D = std::function; /// The type of values produced by \p this object. /// /// That is the type of values \p this object sends to its *master*. /// /// \see \c rosa::deluxe::DeluxeSensor::master const TypeNumber OutputType; private: /// Alias for function objects used as trigger handler for /// \c rosa::deluxe::DeluxeSensor. /// /// \see \c DeluxeSensorTriggerHandlers using H = std::function; /// \defgroup DeluxeSensorTriggerHandlers /// /// \brief Trigger handler functions of \c rosa::deluxe::DeluxeSensor /// /// The actual data source functions are captured in a lambda expression that /// is in turn wrapped in a \c std::function object. The lambda expression /// calls the data source function to obtain the next sensory value and sends /// it to *master* by calling \c rosa::deluxe::DeluxeSensor::sendToMaster. The /// function \c rosa::deluxe::DeluxeSensor::handleTrigger needs only to call /// the proper function object. /// Handles trigger during normal execution. /// /// \ingroup DeluxeSensorTriggerHandlers /// /// The function is used during normal execution. During simulation, the /// simulation environment sets \c rosa::deluxe::DeluxeSensor::SFP, which is /// used instead of \c rosa::deluxe::DeluxeSensor::FP. const H FP; /// Handles trigger during simulation. /// /// \ingroup DeluxeSensorTriggerHandlers /// /// The function is empty by default. The simulation environment sets it to be /// used during simulation. H SFP; /// The *master* to send values to. /// /// \note *Masters* are set dynamically, hence it is possible that a /// \c rosa::deluxe::DeluxeSensor instance does not have any *master* at a /// given moment. Optional Master; /// Wraps a data source function into a trigger handler. /// /// \see \c DeluxeSensorTriggerHandlers /// /// \tparam T type of data provided by \p F /// /// \param F function to generate value with /// /// \pre \p T matches \c rosa::deluxe::DeluxeSensor::OutputType: \code /// OutputType == TypeNumberOf::Value /// \endcode - template - H triggerHandlerFromDataSource(D &&F) const noexcept; + template H triggerHandlerFromDataSource(D &&F) noexcept; public: /// Creates a new instance. /// /// The constructor instantiates the base-class with functions to handle /// messages as defined for the *deluxe interface*. /// /// \tparam T type of data to operate on /// /// \param Kind kind of the new \c rosa::Unit instance /// \param Id unique identifier of the new \c rosa::Unit instance /// \param Name name of the new \c rosa::Unit instance /// \param S \c rosa::MessagingSystem owning the new instance /// \param F function to generate the next value with during normal operation /// /// \pre Statically \code /// TypeListContains::Value /// \endcode Dynamically, the instance is created as of kind /// \c rosa::deluxe::atoms::SensorKind \code /// Kind == rosa::deluxe::atoms::SensorKind /// \endcode template ::Value>> DeluxeSensor(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, D &&F) noexcept; /// Destroys \p this object. ~DeluxeSensor(void) noexcept; /// The *master* of \p this object, if any. /// /// \see \c rosa::deluxe::DeluxeSensor::registerMaster /// /// \return the *master* registered for \p this object Optional master(void) const noexcept; /// Registers a *master* for \p this object. /// /// The new *master* is registered by overwriting the reference to any /// already registered *master*. One can clear the registered reference by /// passing an *empty* \c rosa::Optional object as actual argument. /// /// \note The role of the referred *master* is validated by checking its /// *kind*. /// /// \param Master the *master* to register /// /// \pre \p Master is empty or of kind \c rosa::deluxe::atoms::AgentKind: /// \code /// !Master || unwrapAgent(*Master).Kind == rosa::deluxe::atoms::AgentKind /// \code void registerMaster(const Optional Master) noexcept; /// Clears the simulation trigger handler of \p this object. /// /// The function assigns \c rosa::deluxe::DeluxeSensor::SFP with \c nullptr. void clearSimulationDataSource(void) noexcept; /// Tells whether a simulation trigger handler is set for \p this object. /// /// The function returns whether \c rosa::deluxe::DeluxeSensor::SFP is not /// \c nullptr. /// /// \return if a simulation trigger handler is set for \p this object. bool simulationDataSourceIsSet(void) const noexcept; /// Registers a simulation data source for \p this object. /// /// A new simulation trigger handler wrapping \p SF is stored in /// \c rosa::deluxe::DeluxeSensor::SFP by overwriting any already registered /// simulation data source. /// /// \tparam T type of data provided by \p SF /// /// \param SF function to generate value with /// /// \pre \p T matches \c rosa::deluxe::DeluxeSensor::OutputType: \code /// OutputType == TypeNumberOf::Value /// \endcode template void registerSimulationDataSource(D &&SF) noexcept; private: /// Sends a value to the *master* of \p this object. /// /// \p Value is getting sent to \c rosa::deluxe::DeluxeSensor::Master if it /// contains a valid handle for a \c rosa::deluxe::DeluxeAgent. The function /// does nothing otherwise. /// /// \tparam T type of the value to send /// /// \param Value value to send /// /// \pre \p T matches \c rosa::deluxe::DeluxeSensor::OutputType: \code /// OutputType == TypeNumberOf::Value /// \endcode template void sendToMaster(const T &Value) noexcept; /// Generates the next sensory value upon trigger from the system. /// /// Executes \c rosa::deluxe::DeluxeSensor::FP or /// \c rosa::deluxe::DeluxeSensor::SFP if set. /// /// \note The only argument is a \c rosa::AtomConstant, hence its actual /// value is ignored. void handleTrigger(atoms::Trigger) noexcept; }; template -DeluxeSensor::H DeluxeSensor::triggerHandlerFromDataSource(D &&F) const - noexcept { +DeluxeSensor::H DeluxeSensor::triggerHandlerFromDataSource(D &&F) noexcept { ASSERT(OutputType == TypeNumberOf::Value); return [this, F](void) noexcept { sendToMaster(F()); }; } template DeluxeSensor::DeluxeSensor(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, D &&F) noexcept : Agent(Kind, Id, Name, S, THISMEMBER(handleTrigger)), OutputType(TypeNumberOf::Value), FP(triggerHandlerFromDataSource(std::move(F))), SFP(nullptr) { ASSERT(Kind == atoms::SensorKind); LOG_TRACE("DeluxeSensor is created."); } template void DeluxeSensor::registerSimulationDataSource(D &&SF) noexcept { ASSERT(OutputType == TypeNumberOf::Value); SFP = triggerHandlerFromDataSource(std::move(SF)); } template void DeluxeSensor::sendToMaster(const T &Value) noexcept { ASSERT(OutputType == TypeNumberOf::Value); // There is a handle and the referred *master* is in a valid state. if (Master && *Master) { Master->sendMessage(Message::create(atoms::Slave::Value, Id, Value)); } } } // End namespace deluxe } // End namespace rosa #endif // ROSA_DELUXE_DELUXESENSOR_HPP diff --git a/lib/core/Agent.cpp b/lib/core/Agent.cpp index 630df6c..ebfd576 100644 --- a/lib/core/Agent.cpp +++ b/lib/core/Agent.cpp @@ -1,50 +1,55 @@ //===-- core/Agent.cpp ------------------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file core/Agent.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Implementation of rosa/core/Agent.hpp. /// //===----------------------------------------------------------------------===// #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 \c rosa::Agent wrapped by \p H is this very object. return this == &H.A; } +bool Agent::operator<(const AgentHandle &H) const noexcept { + // Compare memory addresses. + 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 \c rosa::System the \c rosa::Unit is created with is a // \c rosa::MessagingSystem. return static_cast(Unit::system()); } } // End namespace rosa diff --git a/lib/core/AgentHandle.cpp b/lib/core/AgentHandle.cpp index 5f3fda9..5a15789 100644 --- a/lib/core/AgentHandle.cpp +++ b/lib/core/AgentHandle.cpp @@ -1,50 +1,55 @@ //===-- core/AgentHandle.cpp ------------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file core/AgentHandle.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Implementation of rosa/core/AgentHandle.hpp. /// //===----------------------------------------------------------------------===// #include "rosa/core/AgentHandle.hpp" #include "rosa/core/Agent.hpp" namespace rosa { AgentHandle::AgentHandle(Agent &A, bool) noexcept : A(A), S(A.system()) {} AgentHandle::AgentHandle(Agent &A) : A(A), S(A.system()) { ASSERT(S.isUnitRegistered(A)); } AgentHandle::operator bool(void) const noexcept { // \note The referred \c rosa::MessageSystem is supposed to be still alive. return S.isUnitRegistered(A); } bool AgentHandle::operator==(const AgentHandle &H) const noexcept { // Return if the referred \c rosa::Agent is the same object in both // \c rosa::AgentHandler instances. return &A == &H.A; } +bool AgentHandle::operator<(const AgentHandle &H) const noexcept { + // Compare memory addresses of the referred \c rosa::Agent instances. + return &A < &H.A; +} + AgentHandle AgentHandle::self(void) noexcept { // Return a copy of \p this object. 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 f8efa60..ab28444 100644 --- a/lib/core/MessagingSystemImpl.cpp +++ b/lib/core/MessagingSystemImpl.cpp @@ -1,34 +1,34 @@ //===-- core/MessageingSystemImpl.cpp ---------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file core/MessagingSystemImpl.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Implementation of core/MessagingSystemImpl.hpp. /// //===----------------------------------------------------------------------===// #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(&unwrapSystem(H) == this && isUnitRegistered(unwrapAgent(H))); + ASSERT(*this == unwrapSystem(H) && isUnitRegistered(unwrapAgent(H))); unwrapAgent(H)(*M); } } // End namespace rosa diff --git a/lib/core/MessagingSystemImpl.hpp b/lib/core/MessagingSystemImpl.hpp index b8d4cb4..fc0f823 100644 --- a/lib/core/MessagingSystemImpl.hpp +++ b/lib/core/MessagingSystemImpl.hpp @@ -1,105 +1,109 @@ //===-- core/MessagingSystemImpl.hpp ----------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file core/MessagingSystemImpl.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Declaration of a basic implementation of the \c rosa::MessagingSystem /// interface. /// //===----------------------------------------------------------------------===// #ifndef ROSA_LIB_CORE_MESSAGINGSYSTEMIMPL_HPP #define ROSA_LIB_CORE_MESSAGINGSYSTEMIMPL_HPP #include "SystemImpl.hpp" #include "rosa/core/MessagingSystem.hpp" namespace rosa { /// Implements \c rosa::MessagingSystem by extending \c rosa::SystemImpl with /// adding a simple implementation of sending messages: directly invoking /// \c rosa::Agent instances with given \c rosa::Message objects. /// /// \note Keep in mind that sending a \c rosa::Message object with this /// implementation translates into a direct function call. class MessagingSystemImpl : public MessagingSystem, public SystemImpl { /// Alies for the base-class \c rosa::SystemImpl. using Base = SystemImpl; public: /// Creates an instance. /// /// \param Name name of the new instance MessagingSystemImpl(const std::string &Name) noexcept; -protected: /// \defgroup MessagingSystemImplCallForwarding /// /// \c rosa::MessagingSystemImpl call forwardings /// /// \note Simply forwarding calls to implementations provided by /// \c rosa::MessagingSystem::Base for the \c rosa::System interface. /// /// \todo How could we use the inherited implementations in a simpler way? ///@{ + bool operator==(const System &Other) const noexcept override { + return Base::operator==(Other); + } + +protected: id_t nextId(void) noexcept override { return Base::nextId(); } bool isSystemCleaned(void) const noexcept override { return Base::isSystemCleaned(); } void markCleaned(void) noexcept override { Base::markCleaned(); } void registerUnit(Unit &U) noexcept override { Base::registerUnit(U); } void destroyUnit(Unit &U) noexcept override { Base::destroyUnit(U); } bool isUnitRegistered(const Unit &U) const noexcept override { return Base::isUnitRegistered(U); } public: const std::string &name(void) const noexcept override { return Base::name(); } size_t numberOfConstructedUnits(void) const noexcept override { return Base::numberOfConstructedUnits(); } size_t numberOfLiveUnits(void) const noexcept override { return Base::numberOfLiveUnits(); } bool empty(void) const noexcept override { return Base::empty(); } ///@} /// Sends a \c rosa::message_t instance to the \c rosa::Agent instance /// referred by a \c rosa::AgentHandle -- by directly invoking the /// \c rosa::Agent instance with the \c rosa::Message object. /// /// \note If the given \c rosa::Message object cannot be handled by the /// referred \c rosa::Agent instance, the \c rosa::Message object is simply /// ignored. /// /// \param H refers to the \c rosa::Agent instance to send to /// \param M message to send /// /// \pre The referred \c rosa::Agent instance is owned by \p this object and /// also registered: \code /// &unwrapSystem(H) == this && isUnitRegistered(unwrapAgent(H)) /// \endcode void send(const AgentHandle &H, message_t &&M) noexcept override; }; } // End namespace rosa #endif // ROSA_LIB_CORE_MESSAGINGSYSTEMIMPL_HPP diff --git a/lib/core/SystemBase.cpp b/lib/core/SystemBase.cpp index c75e8b8..2674c2b 100644 --- a/lib/core/SystemBase.cpp +++ b/lib/core/SystemBase.cpp @@ -1,58 +1,62 @@ //===-- core/SystemBase.cpp -------------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file core/SystemBase.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Implementation of rosa/core/SystemBase.hpp. /// //===----------------------------------------------------------------------===// #include "rosa/core/SystemBase.hpp" namespace rosa { SystemBase::SystemBase(const std::string &Name) noexcept : Name(Name), UnitCount(0), SystemIsCleaned(false) { LOG_TRACE("Creating System '" + Name + "'"); } SystemBase::~SystemBase(void) { if (!SystemIsCleaned) { ROSA_CRITICAL("Trying to destroy an uncleaned System '" + Name + "'"); } LOG_TRACE("Destroying System '" + Name + "'"); } +bool SystemBase::operator==(const System &Other) const noexcept { + return &Name == &Other.name(); +} + const std::string &SystemBase::name() const noexcept { return Name; } id_t SystemBase::nextId(void) noexcept { return ++UnitCount; } bool SystemBase::isSystemCleaned(void) const noexcept { return SystemIsCleaned; } void SystemBase::markCleaned(void) noexcept { if (SystemIsCleaned) { ROSA_CRITICAL("System '" + Name + "' has been already mark cleaned"); } else if (!empty()) { ROSA_CRITICAL("Trying to mark a non-empty System '" + Name + "'"); } else { SystemIsCleaned = true; LOG_TRACE("System '" + Name + "' is marked cleaned"); } } size_t SystemBase::numberOfConstructedUnits(void) const noexcept { return UnitCount; } } // End namespace rosa diff --git a/lib/deluxe/DeluxeAgent.cpp b/lib/deluxe/DeluxeAgent.cpp index 48ae72f..6d8af42 100755 --- a/lib/deluxe/DeluxeAgent.cpp +++ b/lib/deluxe/DeluxeAgent.cpp @@ -1,157 +1,205 @@ //===-- deluxe/DeluxeAgent.cpp ----------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file deluxe/DeluxeAgent.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Implementation of rosa/deluxe/DeluxeAgent.hpp. /// //===----------------------------------------------------------------------===// #include "rosa/deluxe/DeluxeAgent.hpp" + #include "rosa/deluxe/DeluxeSensor.hpp" #include namespace rosa { namespace deluxe { bool DeluxeAgent::inv(void) const noexcept { // Check container sizes. if (!(InputTypes.size() == NumberOfInputs && InputChanged.size() == NumberOfInputs && InputValues->size() == NumberOfInputs && - Slaves.size() == NumberOfInputs)) + Slaves.size() == NumberOfInputs)) { return false; + } // Check *slave* types and validate *slave* registrations and reverse lookup // information. std::map RefIds; // Build up a reference of SlaveIds in this. for (size_t I = 0; I < NumberOfInputs; ++I) { // First, validate input types at position \c I. const TypeNumber T = InputTypes[I]; - if (InputValues->typeAt(I) != T) + if (InputValues->typeAt(I) != T) { return false; + } // Check the registered *slave* at position \c I. const auto &S = Slaves[I]; // If \c S is empty, nothing to check. if (!S) continue; // \c S is not empty here. // Check the `OutputType` of the registered *slave*. const auto &A = unwrapAgent(*S); if (!((A.Kind == atoms::SensorKind && static_cast(A).OutputType == T) || (A.Kind == atoms::AgentKind && - static_cast(A).OutputType == T))) + static_cast(A).OutputType == T))) { return false; + } // Validate that the *slave* is not registered more than once. if (std::any_of( - Slaves.begin() + I, Slaves.end(), - [&S](const Optional &O) { return O && *S == *O; })) + Slaves.begin() + I + 1, Slaves.end(), + [&S](const Optional &O) { return O && *S == *O; })) { return false; + } // Build the content of \c RefIds. RefIds.emplace(A.Id, I); } // Validate *slave* reverse lookup information against our reference. - if (RefIds != SlaveIds) + if (RefIds != SlaveIds) { return false; + } // All checks were successful, the invariant is held. return true; } DeluxeAgent::~DeluxeAgent(void) noexcept { ASSERT(inv()); LOG_TRACE("Destroying DeluxeAgent..."); + + // Make sure \p this object is not a registered *slave*. + if (Master) { + ASSERT(unwrapAgent(*Master).Kind == atoms::AgentKind); // Sanity check. + DeluxeAgent &M = static_cast(unwrapAgent(*Master)); + ASSERT(M.positionOfSlave(self()) != M.NumberOfInputs); // Sanity check. + M.registerSlave(M.positionOfSlave(self()), {}); + Master = {}; + } + + // Also, make sure \p this object is no acting *master*. + for (size_t Pos = 0; Pos < NumberOfInputs; ++Pos) { + registerSlave(Pos, {}); + } + + // Now there is no connection with other entities, safe to destroy. } Optional DeluxeAgent::master(void) const noexcept { ASSERT(inv()); return Master; } void DeluxeAgent::registerMaster(const Optional Master) noexcept { ASSERT(inv() && (!Master || unwrapAgent(*Master).Kind == atoms::AgentKind)); this->Master = Master; ASSERT(inv()); } TypeNumber DeluxeAgent::inputType(const size_t Pos) const noexcept { ASSERT(inv() && Pos < NumberOfInputs); return InputTypes[Pos]; } Optional DeluxeAgent::slave(const size_t Pos) const noexcept { ASSERT(inv() && Pos < NumberOfInputs); return Slaves[Pos]; } void DeluxeAgent::registerSlave(const size_t Pos, const Optional Slave) noexcept { ASSERT(inv() && Pos < NumberOfInputs && (!Slave || (unwrapAgent(*Slave).Kind == atoms::SensorKind && static_cast(unwrapAgent(*Slave)).OutputType == InputTypes[Pos]) || (unwrapAgent(*Slave).Kind == atoms::AgentKind && static_cast(unwrapAgent(*Slave)).OutputType == InputTypes[Pos]))); // If registering an actual *slave*, not just clearing the slot, make sure // the same *slave* is not registered to another slot. if (Slave) { auto It = SlaveIds.find(unwrapAgent(*Slave).Id); if (It != SlaveIds.end()) { Slaves[It->second] = {};//Optional(); SlaveIds.erase(It); } } // Obtain the place whose content is to be replaced with \p Slave auto &S = Slaves[Pos]; // If there is already a *slave* registered at \p Pos, clear reverse lookup - // information for it. + // information for it, and make sure it no longer has \p this object as + // *master*. if (S) { - ASSERT(SlaveIds.find(unwrapAgent(*S).Id) != - SlaveIds.end()); // Sanity check. - SlaveIds.erase(unwrapAgent(*S).Id); + auto &A = unwrapAgent(*S); + ASSERT(SlaveIds.find(A.Id) != SlaveIds.end()); // Sanity check. + SlaveIds.erase(A.Id); + + if (A.Kind == atoms::AgentKind) { + static_cast(A).registerMaster({}); + } else { + ASSERT(A.Kind == atoms::SensorKind); // Sanity check. + static_cast(A).registerMaster({}); + } } // Register \p Slave at \p Pos. S = Slave; // If registering an actual *slave*, not just clearing the slot, register // reverse lookup information for the new *slave*. if (Slave) { SlaveIds.emplace(unwrapAgent(*Slave).Id, Pos); } ASSERT(inv()); } +size_t DeluxeAgent::positionOfSlave(const AgentHandle Slave) const noexcept { + ASSERT(inv()); + + bool Found = false; + size_t Pos = 0; + while (!Found && Pos < NumberOfInputs) { + auto &S = Slaves[Pos]; + if (S && *S == Slave) { + Found = true; + } else { + ++Pos; + } + } + ASSERT(Found || Pos == NumberOfInputs); // Sanity check. + + return Pos; +} + void DeluxeAgent::handleTrigger(atoms::Trigger) noexcept { ASSERT(inv()); FP(); ASSERT(inv()); } } // End namespace deluxe } // End namespace rosa diff --git a/lib/deluxe/DeluxeContext.cpp b/lib/deluxe/DeluxeContext.cpp index 7ec7ec8..ef3434d 100755 --- a/lib/deluxe/DeluxeContext.cpp +++ b/lib/deluxe/DeluxeContext.cpp @@ -1,151 +1,151 @@ //===-- deluxe/DeluxeContext.cpp --------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file deluxe/DeluxeContext.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Implementation for rosa/deluxe/DeluxeContext.hpp. /// //===----------------------------------------------------------------------===// #define ROSA_LIB_DELUXE_DELUXECONTEXT_CPP // For including helper macros. #include "rosa/deluxe/DeluxeContext.hpp" #include namespace rosa { namespace deluxe { std::unique_ptr DeluxeContext::create(const std::string &Name) noexcept { return std::unique_ptr(new DeluxeContext(Name)); } DeluxeContext::DeluxeContext(const std::string &Name) noexcept : System(DeluxeSystem::createSystem(Name)) { LOG_TRACE("DeluxeContext for '" + System->name() + "' is created."); } DeluxeContext::~DeluxeContext(void) noexcept { // \c rosa::deluxe::DeluxeContext::System is not used outside, just clean it. for(auto U : DeluxeUnits) { System->destroyAgent(U); } - System->markCleaned(); + // \note \c System will be marked clean by SystemImpl::~SystemImpl. LOG_TRACE("DeluxeContext for '" + System->name() + "' prepared for destruction."); } DeluxeContext::ErrorCode DeluxeContext::connectSensor(AgentHandle &Agent, const size_t Pos, AgentHandle &Sensor, const std::string &Description) noexcept { // Generate trace log. auto &Trace = LOG_TRACE_STREAM; Trace << "Establishing connection"; if (!Description.empty()) { Trace << " '" << Description << "'"; } Trace << " between '" << System->unwrapAgent(Sensor).FullName << "' and '" << System->unwrapAgent(Agent).FullName << "'\n"; // Make sure preconditions are met. if (!System->isDeluxeAgent(Agent)) { DCRETERROR(ErrorCode::NotAgent); } else if (!System->isDeluxeSensor(Sensor)) { DCRETERROR(ErrorCode::NotSensor); } auto A = System->getDeluxeAgent(Agent); auto S = System->getDeluxeSensor(Sensor); ASSERT(A && S); // Sanity check. if (Pos >= A->NumberOfInputs) { DCRETERROR(ErrorCode::WrongPosition); } else if (A->inputType(Pos) != S->OutputType) { DCRETERROR(ErrorCode::TypeMismatch); } else if (A->slave(Pos)) { DCRETERROR(ErrorCode::AlreadyHasSlave); } else if (S->master()) { DCRETERROR(ErrorCode::AlreadyHasMaster); } // Do register. A->registerSlave(Pos, {Sensor}); S->registerMaster({Agent}); return ErrorCode::NoError; } DeluxeContext::ErrorCode DeluxeContext::connectAgents(AgentHandle &Master, const size_t Pos, AgentHandle &Slave, const std::string &Description) noexcept { // Generate trace log. auto &Trace = LOG_TRACE_STREAM; Trace << "Establishing connection"; if (!Description.empty()) { Trace << " '" << Description << "'"; } Trace << " between '" << System->unwrapAgent(Slave).FullName << "' and '" << System->unwrapAgent(Master).FullName << "'\n"; // Make sure preconditions are met. if (!(System->isDeluxeAgent(Master) && System->isDeluxeAgent(Slave))) { DCRETERROR(ErrorCode::NotAgent); } auto M = System->getDeluxeAgent(Master); auto S = System->getDeluxeSensor(Slave); ASSERT(M && S); // Sanity check. if (Pos >= M->NumberOfInputs) { DCRETERROR(ErrorCode::WrongPosition); } else if (M->inputType(Pos) != S->OutputType) { DCRETERROR(ErrorCode::TypeMismatch); } else if (M->slave(Pos)) { DCRETERROR(ErrorCode::AlreadyHasSlave); } else if (S->master()) { DCRETERROR(ErrorCode::AlreadyHasMaster); } // Do register. M->registerSlave(Pos, {Slave}); S->registerMaster({Master}); return ErrorCode::NoError; } std::weak_ptr DeluxeContext::getSystem(void) const noexcept { return std::weak_ptr(System); } void DeluxeContext::initializeSimulation(void) noexcept { // Clear simulation data sources from sensors. for (auto U : DeluxeUnits) { if (auto S = System->getDeluxeSensor(U)) { S->clearSimulationDataSource(); } } } void DeluxeContext::simulate(const size_t NumCycles) const noexcept { ASSERT(std::all_of( DeluxeUnits.begin(), DeluxeUnits.end(), [&](const AgentHandle &H) { return System->isDeluxeAgent(H) || System->isDeluxeSensor(H) && System->getDeluxeSensor(H)->simulationDataSourceIsSet(); })); for (size_t I = 1; I <= NumCycles; ++I) { LOG_TRACE("Simulation cycle: " + std::to_string(I)); for (auto U : DeluxeUnits) { U.sendMessage(Message::create(atoms::Trigger::Value)); } } } } // End namespace deluxe } // End namespace rosa diff --git a/lib/deluxe/DeluxeSensor.cpp b/lib/deluxe/DeluxeSensor.cpp index 31172ec..0074532 100755 --- a/lib/deluxe/DeluxeSensor.cpp +++ b/lib/deluxe/DeluxeSensor.cpp @@ -1,52 +1,63 @@ //===-- deluxe/DeluxeSensor.cpp ---------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file deluxe/DeluxeSensor.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Implementation of rosa/deluxe/DeluxeSensor.hpp. /// //===----------------------------------------------------------------------===// #include "rosa/deluxe/DeluxeSensor.hpp" +#include "rosa/deluxe/DeluxeAgent.hpp" + namespace rosa { namespace deluxe { DeluxeSensor::~DeluxeSensor(void) noexcept { LOG_TRACE("Destroying DeluxeSensor..."); + + // Make sure \p this object is not a registered *slave*. + if (Master) { + ASSERT(unwrapAgent(*Master).Kind == atoms::AgentKind); // Sanity check. + DeluxeAgent &M = static_cast(unwrapAgent(*Master)); + ASSERT(M.positionOfSlave(self()) != M.NumberOfInputs); // Sanity check. + M.registerSlave(M.positionOfSlave(self()), {}); + Master = {}; + } } Optional DeluxeSensor::master(void) const noexcept { return Master; } void DeluxeSensor::registerMaster(const Optional Master) noexcept { ASSERT(!Master || unwrapAgent(*Master).Kind == atoms::AgentKind); this->Master = Master; } void DeluxeSensor::clearSimulationDataSource(void) noexcept { SFP = nullptr; } bool DeluxeSensor::simulationDataSourceIsSet(void) const noexcept { return SFP != nullptr; } void DeluxeSensor::handleTrigger(atoms::Trigger) noexcept { // Use \c rosa::deluxe::DeluxeSensor::SFP if set, otherwise // \c rosa::deluxe::DeluxeSensor::FP. const H &F = SFP ? SFP : FP; F(); } } // End namespace deluxe } // End namespace rosa diff --git a/lib/deluxe/DeluxeSystemImpl.cpp b/lib/deluxe/DeluxeSystemImpl.cpp index e5d0220..0f24882 100755 --- a/lib/deluxe/DeluxeSystemImpl.cpp +++ b/lib/deluxe/DeluxeSystemImpl.cpp @@ -1,37 +1,37 @@ //===-- deluxe/DeluxeSystemImpl.cpp -----------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file deluxe/DeluxeSystemImpl.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Implementation of deluxe/DeluxeSystemImpl.hpp. /// //===----------------------------------------------------------------------===// #include "DeluxeSystemImpl.hpp" namespace rosa { namespace deluxe { DeluxeSystemImpl::DeluxeSystemImpl(const std::string &Name) noexcept : DeluxeSystem(), MessagingSystemImpl(Name) { - LOG_TRACE("System '" + Name + "' also has a deluxe flavor"); + LOG_TRACE("System '" + Name + "' has a deluxe flavor"); } bool DeluxeSystemImpl::isDeluxeSensor(const AgentHandle &H) const noexcept { return unwrapAgent(H).Kind == atoms::SensorKind; } bool DeluxeSystemImpl::isDeluxeAgent(const AgentHandle &H) const noexcept { return unwrapAgent(H).Kind == atoms::AgentKind; } } // End namespace deluxe } // End namespace rosa diff --git a/lib/deluxe/DeluxeSystemImpl.hpp b/lib/deluxe/DeluxeSystemImpl.hpp index 9e50354..60131f2 100755 --- a/lib/deluxe/DeluxeSystemImpl.hpp +++ b/lib/deluxe/DeluxeSystemImpl.hpp @@ -1,112 +1,114 @@ //===-- deluxe/DeluxeSystemImpl.hpp -----------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file deluxe/DeluxeSystemImpl.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Declaration of a basic implementation of the /// \c rosa::deluxe::DeluxeSystem interface. /// //===----------------------------------------------------------------------===// #ifndef ROSA_LIB_DELUXE_DELUXESYSTEMIMPL_HPP #define ROSA_LIB_DELUXE_DELUXESYSTEMIMPL_HPP #include "../core/MessagingSystemImpl.hpp" #include "rosa/deluxe/DeluxeSystem.hpp" namespace rosa { namespace deluxe { /// Implements \c rosa::deluxe::DeluxeSystem by extending /// \c rosa::MessagingSystemImpl. class DeluxeSystemImpl : public DeluxeSystem, public MessagingSystemImpl { /// Alies for the base-class \c rosa::MessagingSystemImpl. using Base = MessagingSystemImpl; public: /// Creates an instance. /// /// \param Name name of the new instance DeluxeSystemImpl(const std::string &Name) noexcept; - /// - ~DeluxeSystemImpl(void) noexcept; -protected: /// \defgroup DeluxeSystemImplCallForwardings /// /// \c rosa::deluxe::DeluxeSystemImpl call forwardings /// /// \note Simply forwarding calls to implementations provided by /// \c rosa::deluxe::DeluxeSystem::Base for the \c rosa::MessagingSystem /// interface. /// /// \todo How could we use the inherited implementations in a simpler way? ///@{ + bool operator==(const System &Other) const noexcept override { + return Base::operator==(Other); + } + +protected: id_t nextId(void) noexcept override { return Base::nextId(); } bool isSystemCleaned(void) const noexcept override { return Base::isSystemCleaned(); } void markCleaned(void) noexcept override { Base::markCleaned(); } void registerUnit(Unit &U) noexcept override { Base::registerUnit(U); } void destroyUnit(Unit &U) noexcept override { Base::destroyUnit(U); } bool isUnitRegistered(const Unit &U) const noexcept override { return Base::isUnitRegistered(U); } public: const std::string &name(void) const noexcept override { return Base::name(); } size_t numberOfConstructedUnits(void) const noexcept override { return Base::numberOfConstructedUnits(); } size_t numberOfLiveUnits(void) const noexcept override { return Base::numberOfLiveUnits(); } bool empty(void) const noexcept override { return Base::empty(); } void send(const AgentHandle &H, message_t &&M) noexcept override { Base::send(H, std::move(M)); } ///@} /// Tells whether a \c rosa::AgentHandle refers to a /// \c rosa::deluxe::DeluxeSensor owned by \p this object. /// /// \param H \c rosa::AgentHandle to check /// /// \return whether \p H refers to a \c rosa::deluxe::DeluxeSensor owned by /// \p this object bool isDeluxeSensor(const AgentHandle &H) const noexcept override; /// Tells whether a \c rosa::AgentHandle refers to a /// \c rosa::deluxe::DeluxeAgent owned by \p this object. /// /// \param H \c rosa::AgentHandle to check /// /// \return whether \p H refers to a \c rosa::deluxe::DeluxeAgent owned by /// \p this object bool isDeluxeAgent(const AgentHandle &H) const noexcept override; }; } // End namespace deluxe } // End namespace rosa #endif // ROSA_LIB_DELUXE_DELUXESYSTEMIMPL_HPP