diff --git a/examples/basic-system/basic-system.cpp b/examples/basic-system/basic-system.cpp index 6adacae..860e680 100644 --- a/examples/basic-system/basic-system.cpp +++ b/examples/basic-system/basic-system.cpp @@ -1,69 +1,70 @@ /******************************************************************************* * * File: basic-system.cpp * * Contents: A simple example on the basic System and Unit classes of the RoSA * Core library. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #include "rosa/config/version.h" #include "rosa/core/Unit.h" #include "rosa/core/System.hpp" #include "rosa/support/log.h" #include "rosa/support/terminal_colors.h" #include using namespace rosa; using namespace rosa::terminal; // A dummy wrapper for testing System. // NOTE: Since we test System directly here, we need to get access to its // protected members. That we do by imitating to be a decent subclass of // System, while calling protected member functions on an object of a type from // which we actually don't inherit. struct SystemTester : protected System { static constexpr AtomValue UnitKind = atom("unit"); static Unit &createMyUnit(System *S, const std::string &Name = std::string()) { return ((SystemTester *)S) - ->createUnit([&Name](const size_t Id, System &S) noexcept { + ->createUnit([&Name](const rosa::id_t Id, + System &S) noexcept { // NOTE: If Name is empty, construct a name with the number of the // Unit being created right now. const std::string N( Name.empty() ? "Unit_" + std::to_string(S.numberOfConstructedUnits()) : Name); return new Unit(UnitKind, Id, N, S); }); } static void destroyMyUnit(System *S, Unit &U) { ((SystemTester *)S)->destroyUnit(U); } }; int main(void) { LOG_INFO_STREAM << library_string() << " -- " << Color::Red << "basic-system example" << Color::Default << std::endl; std::unique_ptr S = System::createSystem("Sys"); System *SP = S.get(); Unit &Unit1 = SystemTester::createMyUnit(SP), &Unit2 = SystemTester::createMyUnit(SP, "Second"), &Unit3 = SystemTester::createMyUnit(SP); SystemTester::destroyMyUnit(SP, Unit1); SystemTester::destroyMyUnit(SP, Unit3); LOG_INFO_STREAM << "Dumping Unit2" << std::endl << Unit2 << std::endl; SystemTester::destroyMyUnit(SP, Unit2); return 0; } diff --git a/include/rosa/core/Message.hpp b/include/rosa/core/Message.hpp index 88092dc..7448d80 100644 --- a/include/rosa/core/Message.hpp +++ b/include/rosa/core/Message.hpp @@ -1,306 +1,304 @@ /******************************************************************************* * * File: Message.hpp * * Contents: Declaration of Message base-class. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #ifndef ROSA_CORE_MESSAGE_HPP #define ROSA_CORE_MESSAGE_HPP #include "rosa/support/log.h" #include "rosa/support/type_token.hpp" +#include "rosa/core/forward_declarations.h" + #include #include namespace rosa { // Message interface. The interface provides means to check the type of the // stored values, but actual data is to be managed by derived implementations. // Messages are immutable data objects, obtaining their data upon creation and // providing only constant references for the stored values. // NOTE: Any reference obtained from a Message instance remains valid only as // long as the owning Message object is not destroyed. class Message { protected: // Ctor. // NOTE: No implementation for empty list. template Message(const Type &, const Ts &...) noexcept; // No copy and move. Message(const Message &) = delete; Message(Message &&) = delete; Message &operator=(const Message &) = delete; Message &operator=(Message &&) = delete; public: - // Type alias for a smart pointer for Message. - using message_t = std::unique_ptr; - // Factory function to locally create a Message instance from constant // lvalue references. template static message_t create(const Type &T, const Types &... Ts) noexcept; // Factory function to locally create a Message instance from rvalue // references. template static message_t create(Type &&T, Types &&... Ts) noexcept; // A valid, non-empty token representing the types of the values stored in // the Message. const Token T; // The number of types encoded in T, that is the number of values stored in // Message. const size_t Size; // Virtual dtor. virtual ~Message(void); // Tells if the value in position Pos is of type Type. // NOTE: Token encodes atoms as AtomValue and not directly AtomConstants. // PRE: Pos < Size template bool isTypeAt(const size_t Pos) const noexcept; // Gives a constant reference of the value of type Type in position Pos. // PRE: Pos < Size && isTypeAt(Pos) template const Type &valueAt(const size_t Pos) const noexcept; protected: // Provides an untyped pointer for the value in position Pos. // PRE: Pos < Size virtual const void *pointerTo(const size_t Pos) const noexcept = 0; }; // Nested namespace with an implementation for Message, consider it private. namespace { // Template class for an implementation of Message, definition below. template class LocalMessage; // Initializes Arena with values from const lvalue references of Types. // NOTE: Arena needs to be a valid pointer to a memory area big enough for // values of Types. template inline void createMessageElements(void *const Arena, const Types &... Ts) noexcept; // NOTE: This terminal case is used for both constant lvalue references and // value references. template inline void createMessageElement(void *const, const std::vector &Offsets) { ASSERT(Pos == Offsets.size()); } template inline void createMessageElement(void *const Arena, const std::vector &Offsets, const AtomConstant &, const Types &... Ts) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); *static_cast( static_cast(static_cast(Arena) + Offsets[Pos])) = V; createMessageElement(Arena, Offsets, Ts...); } template inline void createMessageElement(void *const Arena, const std::vector &Offsets, const Type &T, const Types &... Ts) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); new (static_cast(static_cast(static_cast(Arena) + Offsets[Pos]))) Type(T); createMessageElement(Arena, Offsets, Ts...); } template inline void createMessageElements(void *const Arena, const Type &T, const Types &... Ts) noexcept { ASSERT(Arena != nullptr); createMessageElement<0>(Arena, LocalMessage::Offsets, T, Ts...); } // Initializes Arena with values from rvalue references of Types. // NOTE: Arena needs to be a valid pointer to a memory area big enough for // values of Types. template inline void createMessageElements(void *const Arena, Types &&... Ts) noexcept; template inline void createMessageElement(void *const Arena, const std::vector &Offsets, AtomConstant &&, Types &&... Ts) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); *static_cast( static_cast(static_cast(Arena) + Offsets[Pos])) = V; createMessageElement(Arena, Offsets, std::move(Ts)...); } template inline void createMessageElement(void *const Arena, const std::vector &Offsets, Type &&T, Types &&... Ts) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); new (static_cast(static_cast( static_cast(Arena) + Offsets[Pos]))) Type(std::move(T)); createMessageElement(Arena, Offsets, std::move(Ts)...); } template inline void createMessageElements(void *const Arena, Type &&T, Types &&... Ts) noexcept { ASSERT(Arena != nullptr); createMessageElement<0>(Arena, LocalMessage::Offsets, std::move(T), std::move(Ts)...); } // Destroyes values of Types stored in Arena. // NOTE: Arena needs to be a valid pointer to a memory area where values of // Types are stored. template inline void destroyMessageElements(void *const Arena) noexcept; template inline void destroyMessageElement(void *const, const std::vector &Offsets) noexcept { ASSERT(Pos == Offsets.size()); } template inline void destroyMessageElement(void *const Arena, const std::vector &Offsets) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); static_cast( static_cast(static_cast(Arena) + Offsets[Pos])) ->~Type(); destroyMessageElement(Arena, Offsets); } template inline void destroyMessageElements(void *const Arena) noexcept { destroyMessageElement<0, Type, Types...>( Arena, LocalMessage::Offsets); } // Implementation of the template LocalMessage. Provides facilities for storing // values as a Message. template class LocalMessage : public Message { public: // Type Token for the stored values. // NOTE: Only for compile-time checks, static member is not defined. static constexpr Token ST = TypeToken::type, typename std::decay::type...>::Value; // Offsets required to access values in Arena. static const std::vector Offsets; private: // A BLOB storing all the values one after the other. void *const Arena; // Generates offset for accessing values in Arena. static std::vector offsets(void) noexcept { Token T = ST; // Need a mutable copy. size_t I = 0; // Start indexing from position 0. std::vector O(lengthOfToken(T)); // Allocate vector of proper size. O[0] = 0; // First offset is always 0. while (!emptyToken(T)) { ASSERT(I < O.size()); // Calculate next offset based on the previous one. O[I + 1] = O[I] + sizeOfHeadOfToken(T); dropHeadOfToken(T), ++I; } ASSERT(I == O.size()); return O; } public: // Ctor for constant lvalue references. LocalMessage(const Type &T, const Types &... Ts) noexcept : Message(T, Ts...), Arena(::operator new(sizeOfValuesOfToken(ST))) { ASSERT(this->T == ST && Size == Offsets.size() && Arena != nullptr); // Sanity check. createMessageElements(Arena, T, Ts...); } // Ctor for rvalue referecens. LocalMessage(Type &&T, Types &&... Ts) noexcept : Message(T, Ts...), Arena(::operator new(sizeOfValuesOfToken(ST))) { ASSERT(this->T == ST && Size == Offsets.size() && Arena != nullptr); // Sanity check. createMessageElements(Arena, std::move(T), std::move(Ts)...); } // Dtor. ~LocalMessage(void) { destroyMessageElements(Arena); ::operator delete(Arena); } // Provides an untyped pointer for the constant value at Pos. const void *pointerTo(const size_t Pos) const noexcept override { ASSERT(Pos < Offsets.size()); return static_cast(Arena) + Offsets[Pos]; } }; // Implementation of the static member field Offsets. template const std::vector LocalMessage::Offsets = LocalMessage::offsets(); } // End namespace template Message::Message(const Type &, const Ts &...) noexcept : T(TypeToken::type, typename std::decay::type...>::Value), Size(lengthOfToken(T)) { ASSERT(validToken(T) && lengthOfToken(T) == (1 + sizeof...(Ts))); // Sanity check. LOG_TRACE("Creating Message with Token(" + to_string(T) + ")"); } template -Message::message_t Message::create(const Type &T, - const Types &... Ts) noexcept { +message_t Message::create(const Type &T, const Types &... Ts) noexcept { return message_t(new LocalMessage(T, Ts...)); } template -Message::message_t Message::create(Type &&T, Types &&... Ts) noexcept { +message_t Message::create(Type &&T, Types &&... Ts) noexcept { return message_t( new LocalMessage(std::move(T), std::move(Ts)...)); } template bool Message::isTypeAt(const size_t Pos) const noexcept { ASSERT(Pos < Size); Token T_ = T; // NOLINT dropNOfToken(T_, Pos); return isHeadOfTokenTheSameType(T_); } template const Type &Message::valueAt(const size_t Pos) const noexcept { ASSERT(Pos < Size && isTypeAt(Pos)); return *static_cast(pointerTo(Pos)); } } // End namespace rosa #endif // ROSA_CORE_MESSAGE_HPP diff --git a/include/rosa/core/System.hpp b/include/rosa/core/System.hpp index 4c47fd4..38fde7b 100644 --- a/include/rosa/core/System.hpp +++ b/include/rosa/core/System.hpp @@ -1,135 +1,134 @@ /******************************************************************************* * * File: System.hpp * * Contents: Declaration of System interface. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #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 namespace rosa { -// Forward declarations. -class Unit; - // Base interface for actual agent-systems, the class provides facilities to // keep track of Units of the system. // NOTE: Any subclasses are 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 SystemBase // implenenting a base feature-set. class System { public: - // Type alias for Ids used by the System to identify Units. - using id_t = size_t; - // Signature of creator functions for Units. - template - using UnitCreator = std::function; + template + using UnitCreator = std::function; // Returns an object implementing the interface defined by the class. static std::unique_ptr createSystem(const std::string &Name) noexcept; protected: // Protected ctor, only subclasses can instantiate. System(void) noexcept = default; // No copy and move. System(const System&) = delete; System(System&&) = delete; System &operator=(const System&) = delete; System &operator=(System&&) = delete; public: // Dtor. // NOTE: Any implementation makes sure that only a cleaned System can be // destroyed. virtual ~System(void) = default; protected: // Tells the next Id to be used for a newly created Unit. // NOTE: Never returs nthe same value twice. virtual id_t nextId(void) noexcept = 0; // Tells if the System has been marked clean and ready for destruction. virtual bool isSystemCleaned(void) const noexcept = 0; // Marks the System cleaned. Can be called only once when the System does not // have any live Units. // PRE: !isSystemCleaned() && empty() // POST: isSystemCleaned() virtual void markCleaned(void) noexcept = 0; // Registers the given Unit instance to the System. // PRE: !isUnitRegistered(U) // POST: isUnitRegistered(U) virtual void registerUnit(Unit &U) noexcept = 0; // Unregisters and destroys the given Unit. // PRE: isUnitRegistered(U) // POST: !isUnitRegistered(U) && 'U is destroyed' virtual void destroyUnit(Unit &U) noexcept = 0; // Returns if the given Unit is registered in the System. virtual bool isUnitRegistered(const Unit &U) const noexcept = 0; - // Creates a Unit instance with the given UnitCreator, using the given Name - // if any, then registers the new Unit instance by calling the virtual - // member function registerUnit. - // STATIC PRE: std::is_base_of::value + // Creates a Unit instance with the given UnitCreator and registers the new + // Unit instance + // NOTE: The type argument S must be the actual subclass whose instance + // creates the Unit. That cannot be statically enforced, it is the + // reponsibility of the caller to provide the proper System subclass. + // STATIC PRE: + // std::is_base_of::value && std::is_base_of::value // PRE: !isSystemCleaned() - template - T &createUnit(UnitCreator C) noexcept; + template T &createUnit(UnitCreator C) noexcept; public: // Tells the textual name of the System. // NOTE: The returned reference remains valid as long as the originating // System is not destroyed. virtual const std::string &name(void) const noexcept = 0; // Returns the number of Units constructed in the System so far, // including those being already destroyed. virtual size_t numberOfConstructedUnits(void) const noexcept = 0; // Returns the number of live Units, that have been constructed and not // destroyed yet. virtual size_t numberOfLiveUnits(void) const noexcept = 0; // Returns if the System has no live Units. virtual bool empty(void) const noexcept = 0; }; -template -T &System::createUnit(UnitCreator C) noexcept { +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, *this); + 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/Unit.h b/include/rosa/core/Unit.h index 1541398..4d7339e 100644 --- a/include/rosa/core/Unit.h +++ b/include/rosa/core/Unit.h @@ -1,86 +1,85 @@ /******************************************************************************* * * File: Unit.h * * Contents: Declaration of Unit base-class. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #ifndef ROSA_CORE_UNIT_H #define ROSA_CORE_UNIT_H #include "rosa/support/atom.hpp" +#include "rosa/core/forward_declarations.h" + #include #include namespace rosa { -// Forward declarations. -class System; - // Base class for every entity in the system that has to be identified and // traced. // NOTE: Life-cycle of Unit instances is supposed to be managed by a System, do // not create and destroy a Unit directly. class Unit { public: // Identifies the kind of the Unit. // NOTE: Kind is dependent on the owning System. const AtomValue Kind; // System-assigned unique identifier of the Unit instance, // based on the static member field CountUnits. - const size_t Id; + const id_t Id; // Textual identifier of the Unit instance. Defaults to a text referring to // the Id value of the Unit, unless otherwise defined via a constructor // argument. The Name of a Unit is not necessarily unique in the system. const std::string Name; protected: // The owning System. System &S; public: // Full qualified name of the Unit instance. const std::string FullName; public: // Ctor. // PRE: !Name.empty() - Unit(const AtomValue Kind, const size_t Id, const std::string &Name, + Unit(const AtomValue Kind, const id_t Id, const std::string &Name, System &S) noexcept; // No copy and move. Unit(const Unit &) = delete; Unit(Unit &&) = delete; Unit &operator=(const Unit &) = delete; Unit &operator=(Unit &&) = delete; // Dtor. virtual ~Unit(void); // Dumping the object into a string for tracing purposes, // subclasses are supposed to override this function. virtual std::string dump(void) const noexcept; protected: // Returns a reference to the owning System. // NOTE: Subclasses may override the function to return a reference of a // special System subtype. virtual System &system() const noexcept; }; // Helper function dumping the given Unit instance to the given output stream. std::ostream &operator<<(std::ostream &OS, const Unit &U); } // End namespace rosa #endif // ROSA_CORE_UNIT_H diff --git a/include/rosa/core/forward_declarations.h b/include/rosa/core/forward_declarations.h new file mode 100644 index 0000000..4367faf --- /dev/null +++ b/include/rosa/core/forward_declarations.h @@ -0,0 +1,36 @@ +/******************************************************************************* + * + * File: forward_declarations.h + * + * Contents: Necessary forward declarations of types in the Core library. + * + * Copyright 2017 + * + * Author: David Juhasz (david.juhasz@tuwien.ac.at) + * + ******************************************************************************/ + +#ifndef ROSA_CORE_FORWARD_DECLARATIONS_H +#define ROSA_CORE_FORWARD_DECLARATIONS_H + +#include + +namespace rosa { + +// Forward declarations of classes. +class Agent; +class Message; +class MessagingSystem; +class System; +class Unit; + +// Type alias used for Unit identifiers. +using id_t = uint64_t; + +// Type of unique pointer for an immutable Message instance. +using message_t = std::unique_ptr; + +} // End namespace rosa + +#endif // ROSA_CORE_FORWARD_DECLARATIONS_H + diff --git a/lib/core/SystemBase.cpp b/lib/core/SystemBase.cpp index a0efac8..17f8016 100644 --- a/lib/core/SystemBase.cpp +++ b/lib/core/SystemBase.cpp @@ -1,59 +1,59 @@ /******************************************************************************* * * File: SystemBase.cpp * * Contents: Implementation of SystemBase. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #include "rosa/core/SystemBase.hpp" namespace rosa { SystemBase::SystemBase(const std::string &Name) noexcept : Name(Name), CountUnits(0), SystemCleaned(false) { LOG_TRACE("Creating System (" + Name + ")"); } SystemBase::~SystemBase(void) { if (!SystemCleaned) { ROSA_CRITICAL("Trying to destroy an uncleaned System (" + Name + ")"); } LOG_TRACE("Destroying System (" + Name + ")"); } const std::string &SystemBase::name() const noexcept { return Name; } -System::id_t SystemBase::nextId(void) noexcept { +id_t SystemBase::nextId(void) noexcept { return ++CountUnits; } bool SystemBase::isSystemCleaned(void) const noexcept { return SystemCleaned; } void SystemBase::markCleaned(void) noexcept { if (SystemCleaned) { ROSA_CRITICAL("System (" + Name + ") has been already mark cleaned"); } else if (!empty()) { ROSA_CRITICAL("Trying to mark a non-empty System (" + Name + ")"); } else { SystemCleaned = true; LOG_TRACE("System (" + Name + ") is marked clean"); } } size_t SystemBase::numberOfConstructedUnits(void) const noexcept { return CountUnits; } } // End namespace rosa diff --git a/lib/core/Unit.cpp b/lib/core/Unit.cpp index a768590..3293501 100644 --- a/lib/core/Unit.cpp +++ b/lib/core/Unit.cpp @@ -1,54 +1,54 @@ /******************************************************************************* * * File: Unit.cpp * * Contents: Implementation of Unit base-class. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #include "rosa/core/Unit.h" #include "rosa/core/System.hpp" #include "rosa/support/debug.hpp" #include "rosa/support/log.h" namespace rosa { // Ctor. Initializing member fields. -Unit::Unit(const AtomValue Kind, const size_t Id, const std::string &Name, +Unit::Unit(const AtomValue Kind, const id_t Id, const std::string &Name, System &S) noexcept : Kind(Kind), Id(Id), Name(Name), S(S), FullName(Name + "@" + S.name()) { ASSERT(!Name.empty()); LOG_TRACE("Constructing Unit (" + FullName + " of kind '" + to_string(Kind) + "')"); } // Dtor. Unit::~Unit(void) { LOG_TRACE("Destroying Unit (" + FullName + ")"); } // Default dump function, emitting the Name of the Unit. std::string Unit::dump(void) const noexcept { LOG_TRACE("Dumping Unit (" + FullName + ")"); return "[Unit] " + FullName; } System &Unit::system(void) const noexcept { return S; } std::ostream &operator<<(std::ostream &OS, const Unit &U) { OS << U.dump(); return OS; } } // End namespace rosa