diff --git a/examples/agent-modules/agent-modules.cpp b/examples/agent-modules/agent-modules.cpp index 5f5d41e..4ec7f59 100644 --- a/examples/agent-modules/agent-modules.cpp +++ b/examples/agent-modules/agent-modules.cpp @@ -1,108 +1,112 @@ -/***************************************************************************//** - * - * \file examples/agent-modules/agent-modules.cpp - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief A simple example on defining `rosa::Agent` instances using - * `rosa::agent::Module` object as components. - * - ******************************************************************************/ +//===-- examples/agent-modules/agent-modules.cpp ----------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file examples/agent-modules/agent-modules.cpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2017 +/// +/// \brief A simple example on defining `rosa::Agent` instances using +/// `rosa::agent::Module` object as components. +/// +//===----------------------------------------------------------------------===// #include "rosa/agent/Abstraction.hpp" #include "rosa/agent/Confidence.hpp" #include "rosa/config/version.h" #include "rosa/core/Agent.hpp" #include "rosa/core/MessagingSystem.hpp" #include "rosa/support/log.h" #include "rosa/support/terminal_colors.h" #include #include using namespace rosa; using namespace rosa::agent; using namespace rosa::terminal; /// A dummy wrapper for testing `rosa::MessagingSystem`. /// /// \note Since we test `rosa::MessagingSystem` directly here, we need to get /// access to its protected members. That we do by imitating to be a decent /// subclass of `rosa::MessagingSystem`, while calling protected member /// functions on an object of a type from which we actually don't inherit. struct SystemTester : protected MessagingSystem { template static AgentHandle createMyAgent(MessagingSystem *S, const std::string &Name, Funs &&... Fs) { return ((SystemTester *)S)->createAgent(Name, std::move(Fs)...); } static void destroyMyAgent(MessagingSystem *S, const AgentHandle &H) { ((SystemTester *)S)->destroyUnit(unwrapAgent(H)); } }; /// A special `rosa::Agent` with its own state. class MyAgent : public Agent { public: using Tick = AtomConstant; private: enum class Categories { Bad, Normal, Good }; static const std::map CategoryNames; History H; Confidence C; RangeAbstraction A; public: void handler(Tick, uint8_t V) noexcept { // Record V to the History, then print state info. H << V; ASSERT(H.entry() == V); // Sanity check. LOG_INFO_STREAM << std::endl << "Next value: " << PRINTABLE(V) << ", confidence: " << C(H) << ", category: " << CategoryNames.at(A(H.entry())) << std::endl; } MyAgent(const AtomValue Kind, const rosa::id_t Id, const std::string &Name, MessagingSystem &S) : Agent(Kind, Id, Name, S, THISMEMBER(handler)), H(), C(5, 20, 1), A({{{10, 14}, Categories::Normal}, {{15, 17}, Categories::Good}, {{18, 19}, Categories::Normal}}, Categories::Bad) {} }; const std::map MyAgent::CategoryNames{ {Categories::Bad, "Bad"}, {Categories::Normal, "Normal"}, {Categories::Good, "Good"}}; int main(void) { LOG_INFO_STREAM << library_string() << " -- " << Color::Red << "agent-modules example" << Color::Default << std::endl; std::unique_ptr S = MessagingSystem::createSystem("Sys"); MessagingSystem *SP = S.get(); AgentHandle A = SystemTester::createMyAgent(SP, "MyAgent"); std::vector Vs{4, 5, 6, 7, 9, 10, 11, 13, 15, 14, 15, 16, 19, 20, 21}; for (auto I = Vs.begin(); I != Vs.end(); ++I) { A.send(MyAgent::Tick::Value, *I); } SystemTester::destroyMyAgent(SP, A); return 0; } diff --git a/examples/basic-system/basic-system.cpp b/examples/basic-system/basic-system.cpp index 9203634..4074754 100644 --- a/examples/basic-system/basic-system.cpp +++ b/examples/basic-system/basic-system.cpp @@ -1,70 +1,75 @@ -/***************************************************************************//** - * - * \file examples/basic-system/basic-system.cpp - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief A simple example on the basic `rosa::System` and `rosa::Unit` classes. - * - ******************************************************************************/ +//===-- examples/basic-system/basic-system.cpp ------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file examples/basic-system/basic-system.cpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2017 +/// +/// \brief A simple example on the basic `rosa::System` and `rosa::Unit` +/// classes. +/// +//===----------------------------------------------------------------------===// #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 `rosa::MessagingSystem`. /// /// \note Since we test `rosa::MessagingSystem` directly here, we need to get /// access to its protected members. That we do by imitating to be a decent /// subclass of `rosa::MessagingSystem`, 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 rosa::id_t Id, System &S) noexcept { // \note If `Name` is empty, construct a name with the number of the // `rosa::Unit` instance 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/examples/messaging-system/messaging-system.cpp b/examples/messaging-system/messaging-system.cpp index 4fe596a..fe01e79 100644 --- a/examples/messaging-system/messaging-system.cpp +++ b/examples/messaging-system/messaging-system.cpp @@ -1,145 +1,148 @@ -/***************************************************************************//** - * - * \file examples/messaging-system/messaging-system.cpp - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief A simple example on the `rosa::MessagingSystem` and `rosa::Agent` - * classes. - * - ******************************************************************************/ +//===-- examples/messaging-system/messaging-system.cpp ----------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file examples/messaging-system/messaging-system.cpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2017 +/// +/// \brief A simple example on the `rosa::MessagingSystem` and `rosa::Agent` +/// classes. +//===----------------------------------------------------------------------===// #include "rosa/config/version.h" #include "rosa/core/Agent.hpp" #include "rosa/core/MessagingSystem.hpp" #include "rosa/support/log.h" #include "rosa/support/terminal_colors.h" #include using namespace rosa; using namespace rosa::terminal; /// A dummy wrapper for testing `rosa::MessagingSystem`. /// /// \note Since we test `rosa::MessagingSystem` directly here, we need to get /// access to its protected members. That we do by imitating to be a decent /// subclass of `rosa::MessagingSystem`, while calling protected member /// functions on an object of a type from which we actually don't inherit. struct SystemTester : protected MessagingSystem { template static AgentHandle createMyAgent(MessagingSystem *S, const std::string &Name, Funs &&... Fs) { return ((SystemTester *)S)->createAgent(Name, std::move(Fs)...); } static void destroyMyAgent(MessagingSystem *S, const AgentHandle &H) { ((SystemTester *)S)->destroyUnit(unwrapAgent(H)); } }; /// A special `rosa::Agent` subclass with its own state. class MyAgent : public Agent { public: using Tick = AtomConstant; using Report = AtomConstant; private: size_t Counter; public: void handler(Tick) noexcept { LOG_INFO_STREAM << "MyAgent Tick count: " << ++Counter << std::endl; } MyAgent(const AtomValue Kind, const rosa::id_t Id, const std::string &Name, MessagingSystem &S) : Agent(Kind, Id, Name, S, Invoker::F([this](Report) noexcept { LOG_INFO_STREAM << "MyAgent count: " << Counter << std::endl; }), THISMEMBER(handler)), Counter(0) {} }; int main(void) { LOG_INFO_STREAM << library_string() << " -- " << Color::Red << "messaging-system example" << Color::Default << std::endl; std::unique_ptr S = MessagingSystem::createSystem("Sys"); MessagingSystem *SP = S.get(); LOG_INFO_STREAM << std::endl << std::endl << "** Stateless Agents" << std::endl << std::endl; AgentHandle Agent1 = SystemTester::createMyAgent( SP, "Agent1", Invoker::F([](const std::string &M) noexcept { LOG_INFO("Agent1: " + M); })); using Print = AtomConstant; using Forward = AtomConstant; AgentHandle Agent2 = SystemTester::createMyAgent( SP, "Agent2", Invoker::F([](Print, uint8_t N) noexcept { LOG_INFO("Agent2: " + std::to_string(N)); }), Invoker::F([&Agent1](Forward, uint8_t N) noexcept { if (Agent1) { Agent1.send(std::to_string(N)); } else { LOG_INFO("Agent2 cannot forward: Agent1 is not valid"); } })); LOG_INFO_STREAM << std::endl << "Agent1 is valid: " << bool(Agent1) << std::endl << "Agent2 is valid: " << bool(Agent2) << std::endl; LOG_INFO("Sending a print-message to Agent2..."); SP->send(Agent2, Print::Value, 42); LOG_INFO("Sending a forward-message to Agent2..."); SP->send(Agent2, Forward::Value, 42); LOG_INFO("Sending an unexpected message to Agent2..."); SP->send(Agent2, unit); SystemTester::destroyMyAgent(SP, Agent1); LOG_INFO_STREAM << std::endl << "Agent1 is valid: " << bool(Agent1) << std::endl << "Agent2 is valid: " << bool(Agent2) << std::endl; LOG_INFO("Sending a forward-message to Agent2..."); SP->send(Agent2, Forward::Value, 42); SystemTester::destroyMyAgent(SP, Agent2); LOG_INFO_STREAM << std::endl << std::endl << "** Stateful Agents" << std::endl << std::endl; AgentHandle Agent3 = SystemTester::createMyAgent(SP, "Agent3"); for (size_t I = 0; I < 2; ++I) { LOG_INFO("Sending report-message to Agent3..."); Agent3.send(MyAgent::Report::Value); LOG_INFO("Sending tick-message to Agent3..."); Agent3.send(MyAgent::Tick::Value); } SystemTester::destroyMyAgent(SP, Agent3); LOG_INFO_STREAM << std::endl << std::endl; return 0; } diff --git a/examples/messaging/messaging.cpp b/examples/messaging/messaging.cpp index b64a689..9f34c42 100644 --- a/examples/messaging/messaging.cpp +++ b/examples/messaging/messaging.cpp @@ -1,142 +1,146 @@ -/***************************************************************************//** - * - * \file examples/messaging/messaging.cpp - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief An example showcasing features related to the `rosa::Message` class. - * - ******************************************************************************/ +//===-- examples/messaging/messaging.cpp ------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file examples/messaging/messaging.cpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2017 +/// +/// \brief An example showcasing features related to the `rosa::Message` class. +/// +//===----------------------------------------------------------------------===// #include "rosa/config/version.h" #include "rosa/core/MessageHandler.hpp" #include "rosa/support/log.h" #include "rosa/support/terminal_colors.h" #include using namespace rosa; using namespace rosa::terminal; int main(void) { LOG_INFO_STREAM << library_string() << " -- " << Color::Red << "messaging" << Color::Default << std::endl; auto &Log = LOG_INFO_STREAM << std::endl; // Message interface. auto PMsg = Message::create(1, 2); auto &Msg = *PMsg; Log << "Checking on a 'Message with TypeList<>':" << std::endl << " Size: " << Msg.Size << std::endl << " Pos 0 is uint8_t: " << Msg.isTypeAt(0) << std::endl << " Pos 1 is uint16_t: " << Msg.isTypeAt(1) << std::endl << " Pos 2 is uint32_t: " << Msg.isTypeAt(1) << std::endl << " Value at pos 0: " << PRINTABLE(Msg.valueAt(0)) << std::endl << " Value at pos 1: " << Msg.valueAt(1) << std::endl << std::endl; // MessageMatcher. using MyMatcher = MsgMatcher; Log << "Matching against 'TypeList':" << std::endl << " matching: " << MyMatcher::doesStronglyMatch(Msg) << std::endl; auto Vs = MyMatcher::extractedValues(Msg); Log << " value: '(" << PRINTABLE(std::get<0>(Vs)) << ", " << std::get<1>(Vs) << ")'" << std::endl << std::endl; using MyWrongMatcher = MsgMatcher; Log << "Matching against 'TypeList':" << std::endl << " matching: " << MyWrongMatcher::doesStronglyMatch(Msg) << std::endl << std::endl; using MyAtom = AtomConstant; const MyAtom &A = MyAtom::Value; using MyNAtom = AtomConstant; auto PAMsg = Message::create(A); auto &AMsg = *PAMsg; Log << "Checking on a 'Message with TypeList>':" << std::endl << " Size: " << AMsg.Size << std::endl << " Pos 0 is 'AtomValue': " << AMsg.isTypeAt(0) << std::endl << " Pos 0 is 'AtomConstant': " << AMsg.isTypeAt(0) << std::endl << std::endl; using MyAtomMatcher = MsgMatcher; Log << "Matching against 'TypeList>':" << std::endl << " matching: " << MyAtomMatcher::doesStronglyMatch(AMsg) << std::endl << " value: '(" << to_string(std::get<0>(MyAtomMatcher::extractedValues(AMsg))) << ")'" << std::endl << std::endl; using MyWrongAtomMatcher = MsgMatcher; Log << "Matching against 'TypeList>':" << std::endl << " matching: " << MyWrongAtomMatcher::doesStronglyMatch(AMsg) << std::endl << std::endl; // Invoker. auto IP = Invoker::wrap(Invoker::F([&Log](MyAtom) noexcept->void { Log << "** Handling 'Message with TypeList>'." << std::endl; })); auto &I = *IP; // Get a reference from the pointer. Log << "Invoking a function of signature 'void(AtomConstant) " "noexcept':" << std::endl << " with 'Message with TypeList'" << std::endl << " does Message match Invoker: " << I.match(Msg) << std::endl << " invoking..." << std::endl; I(Msg); Log << " with 'Message with TypeList>'..." << std::endl << " does Message match Invoker: " << I.match(AMsg) << std::endl << " invoking..." << std::endl; I(AMsg); Log << std::endl; // MessageHandler. MessageHandler Handler{ Invoker::F([&Log](uint8_t, uint16_t) { Log << "** Handling 'Message with TypeList'" << std::endl; }), Invoker::F([&Log](MyAtom) { Log << "** Handling 'Message with " "TypeList>'" << std::endl; })}; auto PANMsg = Message::create(MyNAtom::Value); auto &ANMsg = *PANMsg; Log << "Handling Messages with 'MessageHandler " "{ Invoker::F, " "Invoker::F> }':" << std::endl << " 'Message with TypeList'" << std::endl << " can handle: " << Handler.canHandle(Msg) << std::endl << " handling..." << std::endl; Handler(Msg); Log << " 'Message with TypeList>'" << std::endl << " can handle: " << Handler.canHandle(AMsg) << std::endl << " handling..." << std::endl; Handler(AMsg); Log << " 'Message with TypeList>'" << std::endl << " can handle: " << Handler.canHandle(ANMsg) << std::endl << " handling..." << std::endl; Handler(ANMsg); Log << std::endl; Log << "Terminating, destroying automatic variables." << std::endl; return 0; } diff --git a/examples/type-facilities/type-facilities.cpp b/examples/type-facilities/type-facilities.cpp index 3cc15ca..a189fed 100644 --- a/examples/type-facilities/type-facilities.cpp +++ b/examples/type-facilities/type-facilities.cpp @@ -1,98 +1,102 @@ -/***************************************************************************//** - * - * \file examples/type-facilities/type-facilities.cpp - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief An example showcasing various type-related support facilities. - * - ******************************************************************************/ +//===-- examples/type-facilities/type-facilities.cpp ------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file examples/type-facilities/type-facilities.cpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2017 +/// +/// \brief An example showcasing various type-related support facilities. +/// +//===----------------------------------------------------------------------===// #include "rosa/config/version.h" #include "rosa/support/log.h" #include "rosa/support/terminal_colors.h" #include "rosa/support/type_token.hpp" #include using namespace rosa; using namespace rosa::terminal; int main(void) { LOG_INFO_STREAM << library_string() << " -- " << Color::Red << "type facilities" << Color::Default << std::endl; auto &Log = LOG_TRACE_STREAM << std::endl; Log << "NumberOfBuiltinTypes: " << NumberOfBuiltinTypes << std::endl << "TokenBits: " << token::TokenBits << std::endl << "RepresentationBits: " << token::RepresentationBits << std::endl << "MaxTokenizableListSize: " << token::MaxTokenizableListSize << std::endl << std::endl; Log << "Type number information on 'uint8_t':" << std::endl; constexpr TypeNumber TN = TypeNumberOf::Value; Log << " type number: " << PRINTABLE_TN(TN) << std::endl << " size: " << TypeForNumber::Size << std::endl << " name: " << TypeForNumber::Name << std::endl << std::endl; Log << "Type number information on 'std::string':" << std::endl; constexpr TypeNumber TNS = TypeNumberOf::Value; Log << " type number: " << PRINTABLE_TN(TNS) << std::endl << " size: " << TypeForNumber::Size << std::endl << " name: " << TypeForNumber::Name << std::endl << std::endl; Log << "Type number information of AtomConstants:" << std::endl; using Atom1 = AtomConstant; using Atom2 = AtomConstant; Log << " std::is_same::value: " << std::is_same::value << std::endl << " TypeNumberOf::Value: " << PRINTABLE_TN(TypeNumberOf::Value) << std::endl << " TypeNumberOf::Value: " << PRINTABLE_TN(TypeNumberOf::Value) << std::endl << " name: " << TypeForNumber::Value>::Name << std::endl << std::endl; Log << "Type token information on 'TypeList':" << std::endl; // Token is generated statically. constexpr Token T = TypeToken::Value; STATIC_ASSERT( (T == TypeListToken>::Value), "alias template definition is wrong"); Token T_ = T; // We need a non-const value for dropping head later. // Iterate over encoded entries in Token. while (!emptyToken(T_)) { Log << " token: " << PRINTABLE_TOKEN(T_) << std::endl << " valid: " << validToken(T_) << std::endl << " empty: " << emptyToken(T_) << std::endl << " length: " << lengthOfToken(T_) << std::endl << " full size: " << sizeOfValuesOfToken(T_) << std::endl << " head type number: " << PRINTABLE_TN(typeNumberOfHeadOfToken(T_)) << std::endl << " size of head: " << sizeOfHeadOfToken(T_) << std::endl << " name of head: " << nameOfHeadOfToken(T_) << std::endl << " is head uint8_t: " << isHeadOfTokenTheSameType(T_) << std::endl << " is head uint16_t: " << isHeadOfTokenTheSameType(T_) << std::endl << " is head std::string: " << isHeadOfTokenTheSameType(T_) << std::endl << "Dropping head..." << std::endl; dropHeadOfToken(T_); } // Here when Token became empty. Log << " token: " << PRINTABLE_TOKEN(T_) << std::endl << " empty: " << emptyToken(T_) << std::endl; return 0; } diff --git a/include/rosa/agent/Abstraction.hpp b/include/rosa/agent/Abstraction.hpp index a343ec8..cb7330c 100644 --- a/include/rosa/agent/Abstraction.hpp +++ b/include/rosa/agent/Abstraction.hpp @@ -1,190 +1,194 @@ -/***************************************************************************//** - * - * \file rosa/agent/Abstraction.hpp - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief Definition of *abstraction* *functionality*. - * - ******************************************************************************/ +//===-- rosa/agent/Abstraction.hpp ------------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/agent/Abstraction.hpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2017 +/// +/// \brief Definition of *abstraction* *functionality*. +/// +//===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_ABSTRACTION_HPP #define ROSA_AGENT_ABSTRACTION_HPP #include "rosa/agent/Module.h" #include "rosa/support/debug.hpp" #include #include namespace rosa { namespace agent { /// Abstracts values from a type to another one. /// /// \tparam T type to abstract from /// \tparam A type to abstract to template class Abstraction : public Module { protected: /// Value to abstract to by default. const A Default; public: /// Creates an instance. /// /// \param Default value to abstract to by default Abstraction(const A Default) noexcept : Default(Default) {} /// Destroys `this` object. ~Abstraction(void) = default; /// Abstracts a value from type `T` to type `A`. /// /// \note The default implementation always returns /// `rosa::agent::Abstraction::Default`, hence the actual argument is ignored. /// /// \return the abstracted value virtual A operator()(const T &) const noexcept { return Default; } }; /// Implements `rosa::agent::Abstraction` as a `std::map` from a type to another /// one. /// /// \note This implementation is supposed to be used to abstract between /// enumeration types, which is statically enforced. /// /// \tparam T type to abstract from /// \tparam A type to abstract to template class MapAbstraction : public Abstraction, private std::map { // Make sure the actual type arguments are enumerations. STATIC_ASSERT((std::is_enum::value && std::is_enum::value), "mapping not enumerations"); // Bringing into scope inherited members. using Abstraction::Default; using std::map::end; using std::map::find; public: /// Creates an instance by initializing the underlying `std::map`. /// /// \param Map the mapping to do abstraction according to /// \param Default value to abstract to by default MapAbstraction(const std::map &Map, const A Default) noexcept : Abstraction(Default), std::map(Map) {} /// Destroys `this` object. ~MapAbstraction(void) = default; /// Abstracts a value from type `T` to type `A` based on the set mapping. /// /// Results in the value associated by the set mapping to the argument, or /// `rosa::agent::MapAbstraction::Default` if the actual argument is not /// associated with anything by the set mapping. /// /// \param V value to abstract /// /// \return the abstracted value based on the set mapping A operator()(const T &V) const noexcept override { const auto I = find(V); return I == end() ? Default : *I; } }; /// Implements `rosa::agent::Abstraction` as a `std::map` from ranges of a type /// to values of another type. /// /// \note This implementation is supposed to be used to abstract ranges of /// arithmetic types into enumerations, which is statically enforced. /// /// \invariant The keys in the underlying `std::map` define valid ranges /// `(first <= second)` and there are no overlapping ranges defined by the keys. /// /// \tparam T type to abstract from /// \tparam A type to abstract to template class RangeAbstraction : public Abstraction, private std::map, A> { // Make sure the actual type arguments are matching our expectations. STATIC_ASSERT((std::is_arithmetic::value), "abstracting not arithmetic"); STATIC_ASSERT((std::is_enum::value), "abstracting not to enumeration"); // Bringing into scope inherited members. using Abstraction::Default; using std::map, A>::begin; using std::map, A>::end; using std::map, A>::find; public: /// Creates an instance by Initializing the unserlying `std::map`. /// /// \param Map the mapping to do abstraction according to /// \param Default value to abstract to by default /// /// \pre Each key defines a valid range `(first <= second)` and there are no /// overlapping ranges defined by the keys. RangeAbstraction(const std::map, A> &Map, const A &Default) : Abstraction(Default), std::map, A>(Map) { // Sanity check. ASSERT(std::all_of( begin(), end(), [this](const std::pair, A> &P) { return P.first.first <= P.first.second && std::all_of(++find(P.first), end(), [&P](const std::pair, A> &R) { // NOTE: Values in Map are sorted. return P.first.first < P.first.second && P.first.second <= R.first.first || P.first.first == P.first.second && P.first.second < R.first.first; }); })); } /// Destroys `this` object. ~RangeAbstraction(void) = default; // Gives a value of type A associated by the underlying map to V of type T, // or Default if no range containing V is defined. /// Abstracts a value from type `T` to type `A` based on the set mapping. /// /// Results in the value associated by the set mapping to the argument, or /// `rosa::agent::RangeAbstraction::Default` if the actual argument is not /// included in any of the ranges in the set mapping. /// /// \param V value to abstract /// /// \return the abstracted value based on the set mapping A operator()(const T &V) const noexcept override { auto I = begin(); bool Found = false; // Indicates if I refers to a matching range. bool Failed = false; // Indicates if it is pointless to continue searching. while (!Found && !Failed && I != end()) { if (V < I->first.first) { // No match so far and V is below the next range, never will match. // \note Keys are sorted in the map. Failed = true; } else if (I->first.first <= V && V < I->first.second) { // Matching range found. Found = true; } else { // Cannot conclude in this step, move to the next range. ++I; } } ASSERT(!Found || I != end()); return Found ? I->second : Default; } }; } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_ABSTRACTION_HPP diff --git a/include/rosa/agent/Confidence.hpp b/include/rosa/agent/Confidence.hpp index 454d0f1..48a1f56 100644 --- a/include/rosa/agent/Confidence.hpp +++ b/include/rosa/agent/Confidence.hpp @@ -1,199 +1,203 @@ -/***************************************************************************//** - * - * \file rosa/agent/Confidence.hpp - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief Definition of the *confidence* *functionality*. - * - ******************************************************************************/ +//===-- rosa/agent/Confidence.hpp -------------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/agent/Confidence.hpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2017 +/// +/// \brief Definition of *confidence* *functionality*. +/// +//===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_CONFIDENCE_HPP #define ROSA_AGENT_CONFIDENCE_HPP #include "rosa/agent/History.hpp" #include "rosa/support/debug.hpp" #include namespace rosa { namespace agent { /// Confidence validator. /// /// Checks the plausibility of given values by validating if a valid region /// contains them. It also capable of checking consistency by validating the /// rate of change recorded by a `rosa::agent::History` object against a maximal /// absolute valid rate of change. /// /// \tparam T type of values to validate /// /// \note The template is defined only for arithmetic types. /// /// \note The lower bound is inclusive and the upper bound is exclusive. /// /// \invariant The bounds are defined in a meaningful way:\code /// LowerBound <= UpperBound /// \endcode template class Confidence : public Module { // Make sure the actual type argument is an arithmetic type. STATIC_ASSERT(std::is_arithmetic::value, "not arithmetic Confidence"); public: /// Unsigned type corresponding to `T`. using UT = unsigned_t; /// The minimal value of type `T`. /// \note Not exist `V` of type `T` such that `V < Min`. static constexpr T Min = std::is_integral::value ? std::numeric_limits::min() : std::numeric_limits::lowest(); /// The maximal value of type `T`. /// \note Not exist `V` of type `T` such that `V > Max`. static constexpr T Max = (std::is_integral::value || !std::numeric_limits::has_infinity) ? std::numeric_limits::max() : std::numeric_limits::infinity(); /// The maximal value of type `UT`. /// \note Not exist `V` of type `UT` such that `V > UnsignedMax`. static constexpr UT UnsignedMax = (std::is_integral::value || !std::numeric_limits::has_infinity) ? std::numeric_limits::max() : std::numeric_limits::infinity(); private: /// The inclusive lower bound for plausibility check. T LowerBound; /// The exclusive upper bound for plausibility check. T UpperBound; /// The maximal absolute rate of change for consistency check. UT ChangeRate; public: /// Creates an instance by setting the validator variables. /// /// \param LowerBound the lower bound for plausability check /// \param UpperBound the upper bound for plausability check /// \param ChangeRate maximal absolute rate of change for consistency check /// /// \pre The bounds are defined in a meaningful way:\code /// LowerBound <= UpperBound /// \endcode Confidence(const T LowerBound = Min, const T UpperBound = Max, const UT ChangeRate = UnsignedMax) noexcept : LowerBound(LowerBound), UpperBound(UpperBound), ChangeRate(ChangeRate) { // Make sure Confidence is created in a valid state. if (LowerBound > UpperBound) { ROSA_CRITICAL("Confidence with LowerBound higher than UpperBound"); } } /// Destroys `this` object. ~Confidence(void) = default; /// Gives a snapshot of the current state of the validator variables. /// /// \param [out] LowerBound to copy `rosa::agent::Confidence::LowerBound` into /// \param [out] UpperBound to copy `rosa::agent::Confidence::UpperBound` into /// \param [out] ChangeRate to copy `rosa::agent::Confidence::ChangeRate` into void getParameters(T &LowerBound, T &UpperBound, UT &ChangeRate) const noexcept { // Copy members to the given references. LowerBound = this->LowerBound; UpperBound = this->UpperBound; ChangeRate = this->ChangeRate; } /// Sets the lower bound for plausability check. /// /// Beyond setting the lower bound, the function also adjusts the upper bound /// to the given lower bound if the new lower bound would be higher than the /// upper bound. /// /// \param LowerBound the new lower bound to set void setLowerBound(const T LowerBound) noexcept { // Adjust UpperBound if necessary, then set LowerBound. if (UpperBound < LowerBound) { UpperBound = LowerBound; } this->LowerBound = LowerBound; } /// Sets the upper bound for plausability check. /// /// Beyond setting the upper bound, the function also adjusts the lower bound /// to the given upper bound if the new upper bound would be lower than the /// lower bound. /// /// \param UpperBound the new upper bound to set void setUpperBound(const T UpperBound) noexcept { // Adjust LowerBound if necessary, then set UpperBound. if (UpperBound < LowerBound) { LowerBound = UpperBound; } this->UpperBound = UpperBound; } /// Sets the maximal rate of change for consistency check. /// /// \param ChangeRate the new rate of change to set void setChangeRate(const UT ChangeRate) noexcept { // Set ChangeRate. this->ChangeRate = ChangeRate; } /// Tells the binary confidence on the plausibility of a value. /// /// \param V value to check /// /// \return whether `V` is within the range defined by /// `rosa::agent::Confidence::LowerBound` and /// `rosa::agent::Confidence::UpperBound`. bool operator()(const T V) const noexcept { // Return if V is plausible. return LowerBound <= V && V < UpperBound; } /// Tells the binary confidence on the plausibility and consistency of the /// last value recorded by a `rosa::agent::History` instance. /// /// Consistency of the last value is checked by validating the difference with /// its preceding entry. /// /// \note The `rosa::agent::History` instance needs to store values of type /// `T`. /// /// \note An empty `rosa::agent::History` instance results in full confidence. /// /// \tparam N number of values `H` is able to store /// \tparam P retention policy followed by `H` when capacity is reached /// /// \param H *history* whose last entry to check template bool operator()(const History &H) const noexcept { if (H.empty()) { // No entry to validate. return true; } else { // Validate the last entry and the one step average absolute difference. return (*this)(H.entry()) && H.averageAbsDiff(1) <= ChangeRate; } } }; } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_CONFIDENCE_HPP diff --git a/include/rosa/agent/History.hpp b/include/rosa/agent/History.hpp index 826a64d..d8e979f 100644 --- a/include/rosa/agent/History.hpp +++ b/include/rosa/agent/History.hpp @@ -1,292 +1,296 @@ -/***************************************************************************//** - * - * \file rosa/agent/History.hpp - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief Definition of the *history* *functionality*. - * - ******************************************************************************/ +//===-- rosa/agent/History.hpp ----------------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/agent/History.hpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2017 +/// +/// \brief Definition of *history* *functionality*. +/// +//===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_HISTORY_HPP #define ROSA_AGENT_HISTORY_HPP #include "rosa/agent/Module.h" #include "rosa/config/config.h" #include "rosa/support/debug.hpp" #include "rosa/support/type_helper.hpp" #include namespace rosa { namespace agent { /// Retention policies defining what a `rosa::agent::History` instance should do /// when the number of recorded entries reached its capacity. enum class HistoryPolicy { SRWF, ///< Stop Recording When Full -- no new entry is recorded when full FIFO ///< First In First Out -- overwrite the earliest entry with a new one }; /// Implements *history* by recording and storing values. /// /// \note Not thread-safe implementation, which should not be a problem as any /// instance of `rosa::agent::Module` is an internal component of a /// `rosa::Agent`, which is the basic unit of concurrency. /// /// \tparam T type of values to store /// \tparam N number of values to store at most /// \tparam P retention policy to follow when capacity is reached /// /// \invariant The size of the underlying `std::array` is `N + 1`:\code /// max_size() == N + 1 && N == max_size() - 1 /// \endcode template class History : public Module, private std::array { // Bring into scope inherited functions that are used. using std::array::max_size; using std::array::operator[]; /// The index of the first data element in the circular buffer. size_t Data; /// The index of the first empty slot in the circular buffer. size_t Space; public: /// Creates an instances by initializing the indices for the circular buffer. History(void) noexcept : Data(0), Space(0) {} /// Destroys `this` object. ~History(void) = default; /// Tells the retention policy applied to `this` object. /// /// \return `rosa::agent::History::P` static constexpr HistoryPolicy policyOfHistory(void) noexcept { return P; } /// Tells how many entries may be recorded by `this` object. /// /// \note The number of entries that are actually recorded may be smaller. /// /// \return `rosa::agent::History::N` static constexpr size_t lengthOfHistory(void) noexcept { return N; } /// Tells how many entries are currently recorded by `this` object. /// /// \return number of entries currently recorded by `this` object. /// /// \post The returned value cannot be larger than the capacity of `this` /// object:\code /// 0 <= numberOfEntries() && numberOfEntries <= lengthOfHistory() /// \endcode inline size_t numberOfEntries(void) const noexcept { return Data <= Space ? Space - Data : max_size() - Data + Space; } /// Tells if `this` object has not recorded anything yet. /// /// \return if `this` object has no entries recorded inline bool empty(void) const noexcept { return numberOfEntries() == 0; } /// Gives a constant lvalue reference to an entry stored in `this` object. /// /// \note The recorded entries are indexed starting from the latest one. /// /// \param I the index at which the stored entry to take from /// /// \pre `I` is a valid index:\code /// 0 <= I && I <= numberOfEntries() /// \endcode const T &entry(const size_t I = 0) const noexcept { ASSERT(0 <= I && I < numberOfEntries()); // Boundary check. // Position counted back from the last recorded entry. typename std::make_signed::type Pos = Space - (1 + I); // Actual index wrapped around to the end of the buffer if negative. return (*this)[Pos >= 0 ? Pos : max_size() + Pos]; } private: /// Tells if the circular buffer is full. /// /// \return if the circular buffer is full. inline bool full(void) const noexcept { return numberOfEntries() == N; } /// Pushes a new entry into the circular buffer. /// /// \note The earliest entry gets overwritten if the buffer is full. /// /// \param V value to push into the buffer void pushBack(const T& V) noexcept { // Store value to the first empty slot and step Space index. (*this)[Space] = V; Space = (Space + 1) % max_size(); if (Data == Space) { // Buffer was full, step Data index. Data = (Data + 1) % max_size(); } } public: /// Adds a new entry to `this` object and tells if the operation was /// successful. /// /// \note Success of the operation depends on the actual policy. /// /// \param V value to store /// /// \return if `V` was successfully stored bool addEntry(const T &V) noexcept { switch (P) { default: ROSA_CRITICAL("unkown HistoryPolicy"); case HistoryPolicy::SRWF: if (full()) { return false; } // \note Fall through to FIFO which unconditionally pushes the new entry. case HistoryPolicy::FIFO: // FIFO and SRWF not full. pushBack(V); return true; } } /// Tells the trend set by the entries recorded by `this` object. /// /// The number of steps to go back when calculating the trend is defined as /// argument to the function. /// /// \note The number of steps that can be made is limited by the number of /// entries recorded by `this` object. /// /// \note The function is made a template only to be able to use /// `std::enable_if`. /// /// \tparam X always use the default! /// /// \param D number of steps to go back in *history* /// /// \return trend set by analyzed entries /// /// \pre Statically, `this` object stores signed arithmetic values:\code /// std::is_arithmetic::value && std::is_signed::value /// \endcode Dynamically, `D` is a valid number of steps to take:\code /// 0 <= D && D < N /// \endcode template typename std::enable_if< std::is_arithmetic::value && std::is_signed::value, X>::type trend(const size_t D = N - 1) const noexcept { STATIC_ASSERT((std::is_same::value), "not default template arg"); ASSERT(0 <= D && D < N); // Boundary check. if (numberOfEntries() < 2 || D < 1) { // No entries for computing trend. return {}; // Zero element of T } else { // Here at least two entries. // `S` is the number of steps that can be done. const size_t S = std::min(numberOfEntries() - 1, D); size_t I = S; // Compute trend with linear regression. size_t SumIndices = 0; T SumEntries = {}; T SumSquareEntries = {}; T SumProduct = {}; while (I > 0) { // \note Indexing for the regression starts in the past. const size_t Index = S - I; const T Entry = entry(--I); SumIndices += Index; SumEntries += Entry; SumSquareEntries += Entry * Entry; SumProduct += Entry * Index; } return (SumProduct * S - SumEntries * SumIndices) / (SumSquareEntries * S - SumEntries * SumEntries); } } /// Tells the average absolute difference between consequtive entries recorded /// by `this` object /// The number of steps to go back when calculating the average is defined as /// argument to the function. /// /// \note The number of steps that can be made is limited by the number of /// entries recorded by `this` object. /// /// \note The function is made a template only to be able to use /// `std::enable_if`. /// /// \tparam X always use the default! /// /// \param D number of steps to go back in *history* /// /// \pre Statically, `this` object stores arithmetic values:\code /// std::is_arithmetic::value /// \endcode Dynamically, `D` is a valid number of steps to take:\code /// 0 <= D && D < N /// \endcode template typename std::enable_if::value, unsigned_t>::type averageAbsDiff(const size_t D = N - 1) const noexcept { STATIC_ASSERT((std::is_same::value), "not default template arg"); ASSERT(0 <= D && D < N); // Boundary check. if (numberOfEntries() < 2 || D < 1) { // No difference to average. return {}; // Zero element of T } else { // Here at least two entries. // `S` is the number of steps that can be done. const size_t S = std::min(numberOfEntries() - 1, D); // Sum up differences as non-negative values only, hence using an // unsigned variable for that. unsigned_t Diffs = {}; // Init to zero. // Count down entry indices and sum up all the absolute differences. size_t I = S; T Last = entry(I); while (I > 0) { T Next = entry(--I); Diffs += Last < Next ? Next - Last : Last - Next; Last = Next; } // Return the average of the summed differences. return Diffs / S; } } }; /// Adds a new entry to a `rosa::agent::History` instance. /// /// \note The result of `rosa::agent::History::addEntry` is ignored. /// /// \tparam T type of values stored in `H` /// \tparam N number of values `H` is able to store /// \tparam P retention policy followed by `H` when capacity is reached /// /// \param H to add a new entry to /// \param V value to add to `H` /// /// \return `H` after adding `V` to it template History &operator<<(History &H, const T &V) noexcept { H.addEntry(V); return H; } } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_HISTORY_HPP diff --git a/include/rosa/agent/Module.h b/include/rosa/agent/Module.h index ff6a58a..f2a949a 100644 --- a/include/rosa/agent/Module.h +++ b/include/rosa/agent/Module.h @@ -1,31 +1,35 @@ -/***************************************************************************//** - * - * \file rosa/agent/Module.h - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief Declaration of `rosa::Module` base-class. - * - ******************************************************************************/ +//===-- rosa/agent/Module.h -------------------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/agent/Module.h +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2017 +/// +/// \brief Declaration of `rosa::Module` base-class. +/// +//===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_MODULE_H #define ROSA_AGENT_MODULE_H namespace rosa { namespace agent { /// Base class for actual *functionalities* that implement various concepts of /// self-awareness and are supposed to be used in implementing *agents*. class Module { public: Module(void) noexcept = default; virtual ~Module(void) = default; }; } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_MODULE_H diff --git a/include/rosa/config/config.h b/include/rosa/config/config.h index 81d82ed..66b467c 100644 --- a/include/rosa/config/config.h +++ b/include/rosa/config/config.h @@ -1,69 +1,73 @@ -/***************************************************************************//** - * - * \file rosa/config/config.h - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief Configuration information on the build of the library. - * - ******************************************************************************/ +//===-- rosa/config/config.h ------------------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/config/config.h +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2017 +/// +/// \brief Configuration information on the build of the library. +/// +//===----------------------------------------------------------------------===// #ifndef ROSA_CONFIG_CONFIG_H #define ROSA_CONFIG_CONFIG_H #include "rosa/config/rosa_config.h" #include // This OS-specific block defines one of the following: // - ROSA_LINUX // - ROSA_WINDOWS // It also defines ROSA_POSIX for POSIX-compatible systems. #if defined(__linux__) #define ROSA_LINUX #elif defined(WIN32) || defined(_WIN32) #define ROSA_WINDOWS #else #error Platform and/or compiler not supported #endif #if defined(ROSA_LINUX) #define ROSA_POSIX #endif // Defining filenames in a project-relative way based on absolute paths. #include "rosa/config/project_path.hpp" /// The project-relative path of the current source file. #define __FILENAME__ (__FILE__ + project_relative_path_index(__FILE__)) // Convenience macros. /// No-op. #define ROSA_VOID_STMT static_cast(0) /// Ignors anything. /// /// \param x anything #define ROSA_IGNORE_UNUSED(x) static_cast(x) /// Prints an error message and aborts execution. /// /// \param error the error message to print #define ROSA_CRITICAL(error) \ do { \ std::cerr << "[FATAL] " << __func__ << "@" << __FILENAME__ << ":" \ << __LINE__ << ": critical error: '" << (error) << "'" \ << std::endl; \ ::abort(); \ } while (false) /// Raises a runtime error in the program. /// /// \param msg message describing the error /// /// \throws std::runtime_error #define ROSA_RAISE_ERROR(msg) throw std::runtime_error(msg) #endif // ROSA_CONFIG_CONFIG_H diff --git a/include/rosa/config/namespaces.h b/include/rosa/config/namespaces.h index 9c23e55..2aea3a6 100755 --- a/include/rosa/config/namespaces.h +++ b/include/rosa/config/namespaces.h @@ -1,27 +1,31 @@ -/***************************************************************************//** - * - * \file rosa/config/namespaces.h - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief Documentation for namespaces that are scattered into more then one - * header file. - * - ******************************************************************************/ +//===-- rosa/config/namespaces.h --------------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/config/namespaces.h +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2017 +/// +/// \brief Documentation for namespaces that are scattered into more then one +/// header file. +/// +//===----------------------------------------------------------------------===// #ifndef ROSA_CONFIG_NAMESPACES_H #define ROSA_CONFIG_NAMESPACES_H /// Base namespace used by the RoSA framework. namespace rosa { /// Contains facilities that are supposed to be useful for implementing /// *agents*. namespace agent {} } // End namespace rosa #endif // ROSA_CONFIG_NAMESPACES_H diff --git a/include/rosa/config/project_path.hpp b/include/rosa/config/project_path.hpp index 37516ff..8375d0a 100644 --- a/include/rosa/config/project_path.hpp +++ b/include/rosa/config/project_path.hpp @@ -1,63 +1,67 @@ -/***************************************************************************//** - * - * \file rosa/config/project_path.hpp - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief Facility for compile-time manipulation of paths. - * - ******************************************************************************/ +//===-- rosa/config/project_path.hpp ----------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/config/project_path.hpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2017 +/// +/// \brief Facility for compile-time manipulation of paths. +/// +//===----------------------------------------------------------------------===// #ifndef ROSA_CONFIG_PROJECT_PATH_HPP #define ROSA_CONFIG_PROJECT_PATH_HPP #include "rosa/config/rosa_config.h" #include namespace rosa { /// Nested namespace with implementation of the provided features, /// consider it private. namespace { /// Tells the index of the project-relative part of an absolute path. /// /// \param path absolute path to check /// \param project the absolute path of the project /// \param index number of leading characters already checked and found /// matching /// /// \return index of the `project`-relative part of `path`; `0` if `path` is not /// under `project` constexpr size_t project_relative_path_index_impl(const char *const path, const char *const project = ROSA_SRC_DIR, const size_t index = 0) { return project[index] == '\0' ? index // Found it. : (path[index] == '\0' || project[index] != path[index]) ? 0 // Path is not under project. : project_relative_path_index_impl( path, project, index + 1); // Continue searching... } } // End namespace /// Tells the index of the project-relative part of an absolute path. /// /// \param path absolute path to check /// /// \return index of the project-relative part of `path`; `0` if `path` is not /// under the RoSA siource directory. constexpr size_t project_relative_path_index(const char * const path) { return project_relative_path_index_impl(path); } } // End namespace rosa #endif // ROSA_CONFIG_PROJECT_PATH_HPP diff --git a/include/rosa/config/rosa_config.h.cmake b/include/rosa/config/rosa_config.h.cmake index bee01fb..8a60994 100644 --- a/include/rosa/config/rosa_config.h.cmake +++ b/include/rosa/config/rosa_config.h.cmake @@ -1,44 +1,54 @@ +//===-- rosa/config/rosa_config.h -------------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +// +// The file enumerates variables from the RoSA configurations so that they can +// be in exported headers. +// +//===----------------------------------------------------------------------===// #ifndef ROSA_CONFIG_ROSA_CONFIG_H #define ROSA_CONFIG_ROSA_CONFIG_H #define CMAKE_SYSTEM "${CMAKE_SYSTEM}" #define CMAKE_GENERATOR "${CMAKE_GENERATOR}" #define CMAKE_C_COMPILER_ID "${CMAKE_C_COMPILER_ID}" #define CMAKE_C_COMPILER_VERSION "${CMAKE_C_COMPILER_VERSION}" #define CMAKE_CXX_COMPILER_ID "${CMAKE_CXX_COMPILER_ID}" #define CMAKE_CXX_COMPILER_VERSION "${CMAKE_CXX_COMPILER_VERSION}" #define ROSA_VERSION_MAJOR ${ROSA_VERSION_MAJOR} #define ROSA_VERSION_MINOR ${ROSA_VERSION_MINOR} #define ROSA_VERSION_PATCH ${ROSA_VERSION_PATCH} #define PACKAGE_NAME "${PACKAGE_NAME}" #define PACKAGE_STRING "${PACKAGE_STRING}" #define PACKAGE_VERSION "${PACKAGE_VERSION}" #define PACKAGE_BUGREPORT "${PACKAGE_BUGREPORT}" #define BUILD_DATE __DATE__ " " __TIME__ #define ROSA_SRC_DIR "${ROSA_MAIN_SRC_DIR}" #if ${ROSA_LOG_LEVEL_INT} != -1 #define ROSA_LOG_LEVEL ${ROSA_LOG_LEVEL} #endif #if ${ROSA_ENABLE_ASSERTIONS_INT} != -1 #define ROSA_ENABLE_ASSERTIONS #endif #endif // ROSA_CONFIG_ROSA_CONFIG_H diff --git a/include/rosa/config/version.h b/include/rosa/config/version.h index 0ebfa8b..6e6dca3 100644 --- a/include/rosa/config/version.h +++ b/include/rosa/config/version.h @@ -1,34 +1,38 @@ -/***************************************************************************//** - * - * \file rosa/config/version.h - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief Version information about the build of the library. - * - ******************************************************************************/ +//===-- rosa/config/version.h -----------------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/config/version.h +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2017 +/// +/// \brief Version information about the build of the library. +/// +//===----------------------------------------------------------------------===// #ifndef ROSA_CONFIG_VERSION_H #define ROSA_CONFIG_VERSION_H #include "rosa/config/rosa_config.h" #include // NOLINT namespace rosa { /// Returns a string containing the name of the library followed by its version. std::string library_string(void); /// Returns a string containing the version number of the library. std::string version(void); /// Returns a multi-line string containing verbose information on the library. std::string verbose_version(void); } // End namespace rosa #endif // ROSA_CONFIG_VERSION_H diff --git a/include/rosa/core/AbstractAgent.hpp b/include/rosa/core/AbstractAgent.hpp index 2d89640..f1d4539 100644 --- a/include/rosa/core/AbstractAgent.hpp +++ b/include/rosa/core/AbstractAgent.hpp @@ -1,136 +1,140 @@ -/***************************************************************************//** - * - * \file rosa/core/AbstractAgent.hpp - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief Declaration of an abstract interface for *Agents*. - * - ******************************************************************************/ +//===-- 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 `rosa::AbstractAgent` for /// referencing `this` object /// /// \note `Ref` is reference for `rosa::AbstractAgent`, whose actual value must /// be a class derived from `rosa::AbstractAgent`. /// /// \note It can be statically checked if `Ref` is derived from /// `rosa::AbstractAgent`, but the static assertion cannot be defined /// directly in the class body. That is because a class `C` derived from /// `rosa::AbstractAgent` is not complete when the static assertion in the /// definition of `rosa::AbstractAgent` would be evaluated. Thus, the static /// assertion is placed in the constructor of `rosa::AbstractAgent`. template class AbstractAgent { protected: /// Creates a new instance of `rosa::AbstractAgent`. /// /// \note The constructor is protected, thus restricting class instantiation /// for derived classes only. /// /// \pre `Ref` is derived from `rosa::AbstractAgent`:\code /// std::is_base_of, Ref>::value /// \endcode AbstractAgent(void) noexcept; public: /// Destroys `this` object. virtual ~AbstractAgent(void) = default; /// Tells if `this` object is in a valid state. /// /// \return if `this` object is in a valid state virtual operator bool(void) const noexcept = 0; /// Tells if a given reference refers to `this` object. /// /// \param R reference to another object /// /// \return if `R` refers to `this` object virtual bool operator==(const Ref &R) const noexcept = 0; /// Returns a reference to `this` object. /// /// \return a reference to `this` object virtual Ref self(void) noexcept = 0; /// Sends a `rosa::message_t` instance to `this` object. /// /// \param M message to send /// /// \pre `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 /// `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 `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 `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 `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 d7b6a57..7360f56 100644 --- a/include/rosa/core/Agent.hpp +++ b/include/rosa/core/Agent.hpp @@ -1,109 +1,113 @@ -/***************************************************************************//** - * - * \file rosa/core/Agent.hpp - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief Declaration of the base `rosa::Agent` class. - * - ******************************************************************************/ +//===-- 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 `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 `rosa::Unit` owned by a /// `rosa::MessagingSystem`, capable of handling `rosa::Message` instances /// as `rosa::MessageHandler`, and provides the `rosa::AbstractAgent` interface /// with `rosa::AgentHandle` as reference type. class Agent : public Unit, public MessageHandler, public AbstractAgent { friend class AgentHandle; ///< `rosa::AgentHandle` is our friend. protected: /// A handle for `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 `rosa::Unit` instance /// \param Id unique identifier of the new `rosa::Unit` instance /// \param Name name of the new `rosa::Unit` instance /// \param S `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 `this` object. ~Agent(void); /// Tells if `this` object is in a valid state. /// /// \note A `rosa::Agent` instance is always valid. /// /// \return if `this` object is in a valid state operator bool(void) const noexcept override; /// Tells if a given reference refers to `this` object. /// /// \param H reference to another object /// /// \return if `H` refers to `this` object bool operator==(const AgentHandle &H) const noexcept override; /// Returns a reference to `this` object. /// /// \return a reference to `this` object AgentHandle self(void) noexcept override; /// Sends a given `rosa::message_t` instance to `this` object. /// /// \param M message to send /// /// \note Since a `rosa::Agent` instance is always valid, there is no /// precondition for this function. /// \see rosa::AbstractAgent::sendMessage and /// `rosa::Agent::operator bool() const` void sendMessage(message_t &&M) noexcept override; /// Dumps `this` object into a `std::string` for tracing purposes. /// /// \return `string` representing the state of `this` object std::string dump(void) const noexcept override; protected: /// Returns a reference to the `rosa::MessagingSystem` owning `this` object. /// /// \return reference of `S` MessagingSystem &system(void) const noexcept override; }; template Agent::Agent(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, Fun &&F, Funs &&... Fs) : Unit(Kind, Id, Name, S), MessageHandler(std::move(F), std::move(Fs)...), Self(*this, /*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 49ef714..46c3a9a 100644 --- a/include/rosa/core/AgentHandle.hpp +++ b/include/rosa/core/AgentHandle.hpp @@ -1,105 +1,109 @@ -/***************************************************************************//** - * - * \file rosa/core/AgentHandle.hpp - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief Declaration of a handle for `rosa::Agent`. - * - ******************************************************************************/ +//===-- 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 `rosa::Agent`. +/// +//===----------------------------------------------------------------------===// #ifndef ROSA_CORE_AGENTHANDLE_HPP #define ROSA_CORE_AGENTHANDLE_HPP #include "rosa/core/AbstractAgent.hpp" namespace rosa { /// Wraps an actual `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 { /// `rosa::Agent` and `rosa::MessagingSystem` are our friends, they may /// inspect the private member fields of the class. ///@{ friend class Agent; friend class MessagingSystem; ///@} /// The wrapped `rosa::Agent` instance. Agent &A; /// The `rosa::MessagingSystem` owning `A`. MessagingSystem &S; /// Creates a new instance without validating the state of the wrapped /// `rosa::Agent`. /// /// \note Used by a `rosa::Agent` instance to create a reference to itself /// during construction, when its state is not valid yet. /// /// \param A `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 /// `rosa::Agent` to wrap. AgentHandle(Agent &A, bool); public: /// Creates a new instance validating the state of the wrapped `rosa::Agent`. /// /// \note The wrapped `rosa::Agent` must be in a valid state to instantiate /// `rosa::AgentHandle` with this constructor. /// /// \param A `Agent` to wrap /// /// \pre `A` is registered in its owning *system*:\code /// A.system().isUnitRegistered(A) /// \endcode AgentHandle(Agent &A); /// Destroys `this` object. /// /// The destructor has nothing to take care of. ~AgentHandle(void) = default; /// Tells if the wrapped `rosa::Agent` is in a valid state. /// /// \note A `rosa::AgentHandler` belongs to a `rosa::MessagingSystem`. Working /// with a `rosa::AgentHandler` whose originating `rosa::MessagingSystem` has /// already been destroyed results in *undefined* behavior. /// /// \return if the wrapped `rosa::Agent` is in a valid state operator bool(void) const noexcept override; /// Tells if another `rosa::AgentHandle` wraps the same `rosa::Agent` as /// `this` object. /// /// \param H `AgentHandle` whose wrapped `Agent` to check /// /// \return if `H` wraps `A` like `this` object bool operator==(const AgentHandle &H) const noexcept override; /// Returns a reference to the wrapped `rosa::Agent`. /// /// \return a reference to `A` AgentHandle self(void) noexcept override; /// Sends a given `rosa::message_t` instance to the wrapped `rosa::Agent`. /// /// \param M message to send /// /// \pre The wrapped `rosa::Agent` instance is in a valid state:\code /// bool(*this) /// \endcode void sendMessage(message_t &&M) noexcept override; }; } // End namespace rosa #endif // ROSA_CORE_AGENTHANDLE_HPP diff --git a/include/rosa/core/Invoker.hpp b/include/rosa/core/Invoker.hpp index 74bb11b..9f85bb4 100644 --- a/include/rosa/core/Invoker.hpp +++ b/include/rosa/core/Invoker.hpp @@ -1,265 +1,269 @@ -/***************************************************************************//** - * - * \file rosa/core/Invoker.hpp - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief Facilities for providing actual arguments for functions as - * `rosa::Message`objects. - * - ******************************************************************************/ +//===-- rosa/core/Invoker.hpp -----------------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/core/Invoker.hpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2017 +/// +/// \brief Facilities for providing actual arguments for functions as +/// `rosa::Message`objects. +/// +//===----------------------------------------------------------------------===// #ifndef ROSA_CORE_INVOKER_HPP #define ROSA_CORE_INVOKER_HPP #include "rosa/core/MessageMatcher.hpp" #include "rosa/support/log.h" #include #include namespace rosa { /// Wraps a function and provides a simple interface to invoke the stored /// function by passing actual arguments as a `rosa::Message` object. /// /// \note A `rosa::Invoker` instance is supposed to be owned by a /// `rosa::MessageHandler` instance, and not being used directly from user code. class Invoker { protected: /// Creates an instance. /// /// \note Protected constructor restricts instantiation to derived classes. Invoker(void) noexcept; public: /// Destroys `this` object. virtual ~Invoker(void); /// Possible results of an invocation. enum class Result { NoMatch, ///< The wrapped function could not be invoked Invoked ///< The wrapped function has been invoked }; /// Type alias for a smart-pointer for `rosa::Invoker`. using invoker_t = std::unique_ptr; /// Type alias for `rosa::Invoker::Result`. using result_t = Result; /// Tells if a `rosa::Message` object can be used to invoke the function /// wrapped in `this` object. /// /// \param Msg `rosa::Message` to check /// /// \return whether `Msg` can be used to invoke the wrapped function virtual bool match(const Message &Msg) const noexcept = 0; /// Tries to invoke the wrapped function with a `rosa::Message` object. /// /// The wrapped function is invoked if the actual `rosa::Message` object can /// be used to invoke it. /// /// \param Msg `rosa::Message` to try to invoke the wrapped function with /// /// \return whether the wrapped function could be invoked with `Msg` virtual result_t operator()(const Message &Msg) const noexcept = 0; /// Instantiates an implementation of `rosa::Invoker` with the given function. /// /// \note As there is no empty `rosa::Message`, no `rosa::Invoker` wraps a /// function without any argument. /// /// \tparam T type of the first mandatory argument /// \tparam Ts types of any further arguments /// /// \param F function to wrap /// /// \return new `invoker_t` object created from the given function template static invoker_t wrap(std::function &&F) noexcept; /// Convenience template alias for casting callable stuff to function objects /// for wrapping. /// /// \tparam Ts types of arguments /// /// \todo Should make it possible to avoid using an explicit conversion for /// the arguments of wrap. template using F = std::function; /// Convenience template for preparing non-static member functions into /// function objects for wrapping. /// /// \tparam C type whose non-static member the function is /// \tparam Ts types of arguments /// /// \see THISMEMBER template static inline F M(C *O, void (C::*Fun)(Ts...) noexcept) noexcept; }; /// Convenience preprocessor macro for the typical use of `rosa::Invoker::M`. It /// can be used inside a class to turn a non-static member function into a /// function object capturing this pointer, so using the actual object when /// handling a `rosa::Message`. /// /// \param FUN the non-static member function to wrap /// /// \note Inside the class `MyClass`, use\code /// THISMEMBER(fun) /// \endcode instead of\code /// Invoker::M(this, &MyClass::fun) /// \endcode #define THISMEMBER(FUN) \ Invoker::M(this, &std::decay::type::FUN) /// Nested namespace with implementation of `rosa::Invoker` and helper /// templates, consider it private. namespace { /// \defgroup Seq /// ///@{ /// Template with an empty struct to store a sequence of numbers in compile time /// as template arguments. template struct Seq {}; /// Sequence generator, the general case when counting down by extending the /// sequence. template struct GenSeq : GenSeq {}; /// Sequence generator, the terminal case when storing the generated sequence /// into `rosa::Seq`. template struct GenSeq<0, S...> { using Type = Seq; }; ///@} /// \defgroup InvokerImpl /// /// Implements the `rosa::Invoker` interface for functions with different /// signatures. /// ///@{ /// Declaration of `rosa::InvokerImpl` implementing `rosa::Invoker`. /// /// \tparam Fun function to wrap template class InvokerImpl; /// Implementation of `rosa::InvokerImpl` for `std::function`. /// /// \tparam T type of the first mandatory argument /// \tparam Ts types of further arguments /// /// \note As there is no empty `rosa::Message`, no `rosa::Invoker` wraps a /// function without any argument, i.e., no std::function. template class InvokerImpl> final : public Invoker { /// Type alias for the stored function. using function_t = std::function; /// Type alias for correctly typed argument-tuples as obtained from /// `rosa::Message`. using args_t = std::tuple; /// Alias for `rosa::MessageMatcher` for the arguments of the stored function. using Matcher = MsgMatcher; /// The wrapped function. const function_t F; /// Invokes `F` by unpacking arguments from a `std::tuple` with the help of /// the actual template arguments. /// /// \tparam S sequence of numbers indexing `std::tuple` for arguments /// /// \param Args arguments to invoke `F` with /// /// \pre the length of `S` and size of `Args` are matching:\code /// sizeof...(S) == std::tuple_size::value /// \endcode template inline void invokeFunction(Seq, const args_t &Args) const noexcept; public: /// Creates an instance. /// /// \param F function to wrap /// /// \pre `F` is valid:\code /// bool(F) /// \endcode InvokerImpl(function_t &&F) noexcept : F(F) { ASSERT(bool(F)); // Sanity check. } /// Destroys `this` object. ~InvokerImpl(void) = default; /// Tells if a `rosa::Message` object can be used to invoke the function /// wrapped in `this` object. /// /// \param Msg `rosa::Message` to check /// /// \return whether `Msg` can be used to invoke the wrapped function bool match(const Message &Msg) const noexcept override { return Matcher::doesStronglyMatch(Msg); }; /// Tries to invoke the wrapped function with a `rosa::Message` object. /// /// The wrapped function is invoked if the actual `rosa::Message` object can /// be used to invoke it. /// /// \param Msg `rosa::Message` to try to invoke the wrapped function with /// /// \return whether the wrapped function could be invoked with `Msg` result_t operator()(const Message &Msg) const noexcept override { if (match(Msg)) { LOG_TRACE("Invoking with matching arguments"); invokeFunction(typename GenSeq::Type(), Matcher::extractedValues(Msg)); return result_t::Invoked; } else { LOG_TRACE("Tried to invoke with non-matching arguments"); return result_t::NoMatch; } } }; template template void InvokerImpl>::invokeFunction( Seq, const args_t &Args) const noexcept { ASSERT(sizeof...(S) == std::tuple_size::value); // Sanity check. F(std::get(Args)...); } ///@} } // End namespace template Invoker::invoker_t Invoker::wrap(std::function &&F) noexcept { return std::unique_ptr( new InvokerImpl>(std::move(F))); } template Invoker::F Invoker::M(C *O, void (C::*Fun)(Ts...) noexcept) noexcept { return [ O, Fun ](Ts... Vs) noexcept->void { (O->*Fun)(Vs...); }; } } // End namespace rosa #endif // ROSA_CORE_INVOKER_HPP diff --git a/include/rosa/core/Message.hpp b/include/rosa/core/Message.hpp index ca3d5de..a2e8927 100644 --- a/include/rosa/core/Message.hpp +++ b/include/rosa/core/Message.hpp @@ -1,477 +1,481 @@ -/***************************************************************************//** - * - * \file rosa/core/Message.hpp - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief Declaration of `rosa::Message` base-class. - * - ******************************************************************************/ +//===-- rosa/core/Message.hpp -----------------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/core/Message.hpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2017 +/// +/// \brief Declaration of `rosa::Message` base-class. +/// +//===----------------------------------------------------------------------===// #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. /// /// A `rosa::Message` instance is an immutable data object that obtains its data /// upon creation and provides only constant references for the stored values. /// /// \note Any reference obtained from a `rosa::Message` instance remains valid /// only as long as the owning `rosa::Message` object is not destroyed. class Message { protected: /// Creates a new instance. /// /// \note No implementation for empty list. /// /// \tparam Type type of the mandatory first argument /// \tparam Types types of any further arguments /// /// \note the actual arguments are ignored by the constructor it is only /// their type that matters. The actual values are supposed to be handled by /// any implementation derived from `rosa::Message`. /// /// \pre `Type` and `Types` are all built-in types and the number of stored /// values does not exceed `rosa::token::MaxTokenizableListSize`. template Message(const Type &, const Types &...) noexcept; /// No copying and moving of `rosa::Message` instances. ///@{ Message(const Message &) = delete; Message(Message &&) = delete; Message &operator=(const Message &) = delete; Message &operator=(Message &&) = delete; ///@} public: /// Creates a `rosa::message_t` object from constant lvalue references. /// /// \tparam Type type of the mandatory first argument /// \tparam Types types of any further arguments /// /// \param T the first value to include in the `rosa::Message` /// \param Ts optional further values to include in the `rosa::Message` /// /// \return new `message_t` object created from the given arguments template static message_t create(const Type &T, const Types &... Ts) noexcept; /// Creates a `rosa::message_t` object from rvalue references. /// /// \tparam Type type of the mandatory first argument /// \tparam Types types of any further arguments /// /// \param T the first value to include in the `rosa::Message` /// \param Ts optional further values to include in the `rosa::Message` /// /// \return new `message_t` object created from the given arguments template static message_t create(Type &&T, Types &&... Ts) noexcept; /// Represents the types of the values stored in `this` object. /// /// A valid, non-empty `rosa::Token` representing the types of the values /// stored in `this` object. const Token T; /// The number of values stored in `this` object. /// /// That is the number of types encoded in `T`. const size_t Size; /// Destroys `this` object. virtual ~Message(void); /// Tells if the value stored at a given index is of a given type. /// /// \note Any `rosa::AtomConstant` is encoded in `rosa::Token` as /// the `rosa::AtomValue` wrapped into it. /// /// \tparam Type type to match against /// /// \param Pos index the type of the value at is to be matched against `Type` /// /// \return if the value at index `Pos` of type `Type` /// /// \pre `Pos` is a valid index:\code /// Pos < Size /// \endcode template bool isTypeAt(const size_t Pos) const noexcept; /// Gives a constant reference of a value of a given type stored at a given /// index. /// /// \tparam Type type to give a reference of /// /// \param Pos index to set the reference for /// /// \return constant reference of `Type` for the value stored at index `Pos` /// /// \pre `Pos` is a valid index and the value at index `Pos` is of type /// `Type`:\code /// Pos < Size && isTypeAt(Pos) /// \endcode template const Type &valueAt(const size_t Pos) const noexcept; protected: /// Provides an untyped pointer for the value at a given index. /// /// \param Pos index to take a pointer for /// /// \return untyped pointer for the value stored at index `Pos` /// /// \pre `Pos` is a valid index:\code /// Pos < Size /// \endcode virtual const void *pointerTo(const size_t Pos) const noexcept = 0; }; /// Nested namespace with implementation for `rosa::Message`, consider it /// private. namespace { /// Template class for an implementation of `rosa::Message`. /// /// \tparam Types types whose values are to be stored template class LocalMessage; /// Initializes a pre-allocated memory area with values from constant lvalue /// references. /// /// \tparam Types types whose values are to be stored /// /// \param Arena pre-allocated memory area to store values to /// \param Ts the values to store in `Arena` /// /// \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; /// \defgroup createMessageElement from const lvalue references /// /// Stores values from constant lvalue references into a pre-allocated memory /// area. /// /// \note To be used by the implementation of `rosa::createMessageElements`. /// /// \todo Document these functions. ///@{ /// \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 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 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...); } ///@} /// Implementation of the template. /// /// \tparam Type the type of the mandatory first value to store /// \tparam Types types of any further values to store /// /// \param Arena pre-allocated memory area to store values to /// \param T the first value to store in `Arena`˛ /// \param Ts optional further values to store in `Arena` /// /// \pre `Arena` is not `nullptr`. 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 a pre-allocated memory area with values from rvalue references. /// /// \tparam Types types whose values are to be stored /// /// \param Arena pre-allocated memory area to store values to /// \param Ts the values to store in `Arena` /// /// \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; /// \defgroup createMessageElement from rvalue references /// /// Stores values from rvalue references into a pre-allocated memory area. /// /// \note To be used by the implementation of `rosa::createMessageElements`. /// /// \todo Document these functions. ///@{ 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 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)...); } ///@} /// Implementation of the template. /// /// \tparam Type the type of the mandatory first value to store /// \tparam Types types of any further values to store /// /// \param Arena pre-allocated memory area to store values to /// \param T the first value to store in `Arena`˛ /// \param Ts optional further values to store in `Arena` /// /// \pre `Arena` is not `nullptr`. 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)...); } /// Destroys values allocated by `rosa::createMessageElements`. /// /// \tparam Type type of the mandatory first value stored in `Arena` /// \tparam Types futher types whose values are stored in `Arena` /// /// \param Arena the memory area to destroy values from /// /// \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; /// \defgroup destroyMessageElement /// /// Destroys values from a memory area. /// /// \note To be used by the implementation of `rosa::destroyMessageElements`. /// /// \todo Document these functions. ///@{ 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); } ///@} /// Implementation of the template. /// /// \tparam Type the type of the mandatory first value to destroy /// \tparam Types types of any further values to destroy /// /// \param Arena the memory area to destroy values from /// /// \pre `Arena` is not `nullptr`. template inline void destroyMessageElements(void *const Arena) noexcept { ASSERT(Arena != nullptr); destroyMessageElement<0, Type, Types...>( Arena, LocalMessage::Offsets); } /// Implementation of the template `rosa::LocalMessage` providing facilities for /// storing values as a `rosa::Message` object. /// /// \tparam Type type of the first mandatory value of the `Message` /// \tparam Types of any further values template class LocalMessage : public Message { public: /// `rosa::Token` for the stored values. /// /// \note Only for compile-time checks! This static member is not defined /// because it must be the same as `rosa::Message::T`. static constexpr Token ST = TypeToken::type, typename std::decay::type...>::Value; /// Byte offsets to access stored values in `Arena`. static const std::vector Offsets; private: /// A BLOB storing all the values one after the other. void *const Arena; /// Generates byte offsets for accessing values stored in `Arena`. /// /// \return `std::vector` containing byte offsets for accessing values stored /// in `Arena` static std::vector offsets(void) noexcept { Token T = ST; // Need a mutable copy. const size_t N = lengthOfToken(T); // Number of types encoded in T. size_t I = 0; // Start indexing from position 0. std::vector O(N); // Allocate vector of proper size. O[0] = 0; // First offset is always 0. while (I < N - 1) { ASSERT(I + 1 < O.size() && lengthOfToken(T) == N - I); // Calculate next offset based on the previous one. // \note The offset of the last value is stored at O[N - 1], which is set // when I == N - 2. Hence the limit of the loop. O[I + 1] = O[I] + sizeOfHeadOfToken(T); dropHeadOfToken(T), ++I; } ASSERT(I + 1 == O.size() && lengthOfToken(T) == 1); return O; } public: /// Creates an instance from constant lvalue references. /// /// \param T the mandatory first value to store in the `rosa::Message` object /// \param Ts optional further values to store in the `rosa::Message` object 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...); } /// Creates an instance from rvalue references. /// /// \param T the mandatory first value to store in the `rosa::Message` object /// \param Ts optional further values to store in the `rosa::Message` object 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)...); } // Destroys `this` object. ~LocalMessage(void) { destroyMessageElements(Arena); ::operator delete(Arena); } /// Provides an untyped pointer for the constant value stored at a position. /// /// \param Pos the index of the value to return an untyped pointer for /// /// \return untyped pointer for the constant value stored at index `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 `rosa::LocalMessage::Offsets`. template const std::vector LocalMessage::Offsets = LocalMessage::offsets(); } // End namespace template Message::Message(const Type &, const Types &...) noexcept : T(TypeToken::type, typename std::decay::type...>::Value), Size(lengthOfToken(T)) { ASSERT(validToken(T) && lengthOfToken(T) == (1 + sizeof...(Types))); // Sanity check. LOG_TRACE("Creating Message with Token(" + to_string(T) + ")"); } /// \note The implementation instantiates a private local template class /// `LocalMessage`. template message_t Message::create(const Type &T, const Types &... Ts) noexcept { return message_t(new LocalMessage(T, Ts...)); } /// \note The implementation instantiates a private local template class /// `LocalMessage`. template 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/MessageHandler.hpp b/include/rosa/core/MessageHandler.hpp index aba1e14..abdc848 100644 --- a/include/rosa/core/MessageHandler.hpp +++ b/include/rosa/core/MessageHandler.hpp @@ -1,166 +1,170 @@ -/***************************************************************************//** - * - * \file rosa/core/MessageHandler.hpp - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief Facility for combining `rosa::Invoker` instances and applying - * `rosa::Message` intances to them. - * - ******************************************************************************/ +//===-- rosa/core/MessageHandler.hpp ----------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/core/MessageHandler.hpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2017 +/// +/// \brief Facility for combining `rosa::Invoker` instances and applying +/// `rosa::Message` intances to them. +/// +//===----------------------------------------------------------------------===// #ifndef ROSA_CORE_MESSAGEHANDLER_HPP #define ROSA_CORE_MESSAGEHANDLER_HPP #include "rosa/core/Invoker.hpp" #include "rosa/support/log.h" #include namespace rosa { /// Handles `rosa::Message` instances. /// /// A `rosa::MessageHandler` stores `rosa::Invoker` instances and tries to /// apply `rosa::Message` objects to them in the order of definition.The first /// matching `rosa::Invoker` instance is invoked with the `rosa::Message` /// object, after which handling of that `rosa::Message` object is completed. /// /// For example, consider the following snippet: \code /// rosa::MessageHandler { /// rosa::Invoker::F([](uint8_t) { /* ... */ }), /// rosa::Invoker::F([](uint8_t) { /* Never invoked */ }) /// }; /// \endcode /// Applying a `rosa::Message` with `rosa::TypeList` invokes the first /// function, and the second function would never be invoked because any /// matching `rosa::Message` object had already been handled by the first one. class MessageHandler { /// Type alias to bring `rosa::Invoker::invoker_t` to the local scope. using invoker_t = Invoker::invoker_t; /// Type alias for a `std::vector` storing `rosa::Invoker` instances. using invokers_t = std::vector; /// Stores `rosa::Invoker` instances. const invokers_t Invokers; /// Creates a container with `rosa::Invoker` instances from functions. /// /// \tparam Fun type of the mandatory first function /// \tparam Funs types of further functions /// /// \param F the mandatory first function /// \param Fs optional further functions /// /// \return `invokers_t` object storing `rosa::Invoker` instances created /// from the `F` and `Fs...` template static inline invokers_t createInvokers(Fun &&F, Funs &&... Fs) noexcept; /// Updates an `invokers_t` object with a new `rosa::Invoker` instance and /// handles further functions recursively. /// /// \tparam Fun type of the first function /// \tparam Funs types of further functions /// /// \param I `invokers_t` to update /// \param Pos index at which to store the new `rosa::Invoker` instance /// \param F function to wrap and store into `I` at index `Pos` /// \param Fs further functions to handle later /// /// \pre `Pos` is a valid index:\code /// Pos < I.size() /// \endcode template static inline void wrapFun(invokers_t &I, const size_t Pos, Fun &&F, Funs &&... Fs) noexcept; /// Terminal case for the template `rosa::MessageHandler::wrapFun`. /// /// \param I `invokers_t` which is now complete /// \param Pos size of `I` /// /// \pre `Pos` is the size of `I`:\code /// Pos == I.size(); /// \endcode static inline void wrapFun(invokers_t &I, const size_t Pos) noexcept; public: /// Creates an instance. /// /// The constructor stores the given functions into the new /// `rosa::MessageHandler` instance. /// /// \tparam Fun type of the mandatory first function /// \tparam Funs types of further functions /// /// \param F the first function to store /// \param Fs optional further functions to store template MessageHandler(Fun &&F, Funs &&... Fs) noexcept; /// Destroys `this` object. virtual ~MessageHandler(void); /// Tells if a `rosa::Message` object can be handled by `this` object. /// /// \param Msg `rosa::Message`to check /// /// \return whether `this` object stores a `rosa::Invoker` instance that can /// handle `Msg` bool canHandle(const Message &Msg) const noexcept; /// Applies a `rosa::Message` object to the first stored `rosa::Invoker` that /// can handle it, and tells if there was any. /// /// \note This operator finds the first applicable `rosa::Invoker` and invokes /// it with the given `rosa::Message` object, while the member function /// `rosa::MessageHandler::canHandle` only checks if there is any /// `rosa::Invoker` that can be invoked with `rosa::Message` object. /// /// \param Msg `rosa::Message` to use in invoking a matching `rosa::Invoker` /// /// \return whether there was a matching `rosa::Invoker` found and invoked /// with `Msg`. bool operator()(const Message &Msg) const noexcept; }; template MessageHandler::MessageHandler(Fun &&F, Funs &&... Fs) noexcept : Invokers(createInvokers(std::move(F), std::move(Fs)...)) { LOG_TRACE("MessageHandler is created"); } template MessageHandler::invokers_t MessageHandler::createInvokers(Fun &&F, Funs &&... Fs) noexcept { // Create a container with the required size and get all the functions // wrapped. invokers_t I(1 + sizeof...(Funs)); wrapFun(I, 0, std::move(F), std::move(Fs)...); return I; } template void MessageHandler::wrapFun(invokers_t &I, const size_t Pos, Fun &&F, Funs &&... Fs) noexcept { ASSERT(Pos < I.size()); // Sanity check. // Wrap the current function and continue with the rest. I[Pos] = Invoker::wrap(std::move(F)); wrapFun(I, Pos + 1, std::move(Fs)...); } void MessageHandler::wrapFun(invokers_t &I, const size_t Pos) noexcept { ASSERT(Pos == I.size()); // Sanity check. // Nothing to do here. } } // End namespace rosa #endif // ROSA_CORE_MESSAGEHANDLER_HPP diff --git a/include/rosa/core/MessageMatcher.hpp b/include/rosa/core/MessageMatcher.hpp index e4e02d4..74138b1 100644 --- a/include/rosa/core/MessageMatcher.hpp +++ b/include/rosa/core/MessageMatcher.hpp @@ -1,199 +1,203 @@ -/***************************************************************************//** - * - * \file rosa/core/MessageMatcher.hpp - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief Facilities for checking and matching types of values stored in - * `rosa::Message` instances. - * - ******************************************************************************/ +//===-- rosa/core/MessageMatcher.hpp ----------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/core/MessageMatcher.hpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2017 +/// +/// \brief Facilities for checking and matching types of values stored in +/// `rosa::Message` instances. +/// +//===----------------------------------------------------------------------===// #ifndef ROSA_CORE_MESSAGEMATCHER_HPP #define ROSA_CORE_MESSAGEMATCHER_HPP #include "rosa/core/Message.hpp" #include namespace rosa { /// Provides features to type-check a `rosa::Message` instance and extract /// stored values from it into an `std::tuple` instance with matching type /// arguments. /// /// \tparam List `rosa::TypeList` to check the stored values against template struct MessageMatcher; /// Definition of `rosa::MessageMatcher` for non-empty lists of types, like /// `rosa::Message` itself. /// /// \tparam Type first mandatory type /// \tparam Types any further types template struct MessageMatcher> { /// `rosa::Token` associated to the give `rosa::TypeList`. static constexpr Token T = TypeToken::Value; /// Tells if the values stored in a `rosa::Message` instance are matching /// types given as `rosa::TypeList`, considering /// `rosa::AtomConstant` instead of `rosa::AtomValue`. /// /// \param Msg `Message` to match /// /// \return whether the types of values stored in `Msg` matches /// `rosa::TypeList` static inline bool doesStronglyMatch(const Message &Msg) noexcept; /// Gives a `std::tuple` with references to the values stored in a /// type-matching instance of `rosa::Message`. /// /// \param Msg `Message` to extract values from /// /// \return `tuple` with references to the values stored in `Msg` /// /// \pre Types of the values stored in `Msg` matches /// `rosa::TypeList`:\code /// doesStronglyMatch(Msg) /// \endcode static inline std::tuple extractedValues(const Message &Msg) noexcept; }; /// Turns a list of types into a `rosa::TypeList` for `rosa::MessageMatcher`. template using MsgMatcher = MessageMatcher>; /// Nested namespace with implementation for features of `rosa::MessageMatcher`, /// consider it private. namespace { /// \defgroup MessageMatcherImpl /// /// An implementation of type-checking and value extraction for /// `rosa::MessageMatcher`. /// ///@{ /// Template declaration of `rosa::MessageMatcherImpl. /// /// \tparam List `rosa::TypeList` to match against template struct MessageMatcherImpl; /// Specialization for `rosa::EmptyTypeList`. template <> struct MessageMatcherImpl { static inline bool doesStronglyMatchFrom(const Message &Msg, const size_t Pos) noexcept { // Matching EmptyTypeList only if reached the end of the stored types. return Pos == Msg.Size; } static inline std::tuple<> extractedValuesFrom(const Message &Msg, const size_t Pos) noexcept { // It is valid to extract an empty list only if we reached the end of // stored values. ASSERT(doesStronglyMatchFrom(Msg, Pos)); return std::tie(); } }; /// Specialization for `rosa::AtomValue` in the head. template struct MessageMatcherImpl, Ts...>> { static inline bool doesHeadStronglyMatchAt(const Message &Msg, const size_t Pos) noexcept { // Matching an AtomConstant in the head if there is a type stored at Pos, // the stored type is AtomValue, and the corresponding value matches the // AtomValue V. return Pos < Msg.Size && Msg.isTypeAt(Pos) && Msg.valueAt(Pos) == V; } static inline bool doesStronglyMatchFrom(const Message &Msg, const size_t Pos) noexcept { // Matching a non-empty list if the head is matching and the rest of the // list is matching. return doesHeadStronglyMatchAt(Msg, Pos) && MessageMatcherImpl>::doesStronglyMatchFrom(Msg, Pos + 1); } static inline std::tuple &, const Ts &...> extractedValuesFrom(const Message &Msg, const size_t Pos) noexcept { // Extracting for a non-empty list with a matching AtomConstant in the head // by getting the encoded AtomConstant and concatenating it with values // extracted for the rest of the list. ASSERT(doesHeadStronglyMatchAt(Msg, Pos)); return std::tuple_cat( std::tie(AtomConstant::Value), MessageMatcherImpl>::extractedValuesFrom(Msg, Pos + 1)); } }; /// Definition for the general case when a regular built-in type (not a /// `rosa::AtomConstant`) is in the head. template struct MessageMatcherImpl> { static inline bool doesHeadStronglyMatchAt(const Message &Msg, const size_t Pos) noexcept { // Matching the head if there is a type stored at Pos, and the stored type // is T. return Pos < Msg.Size && Msg.isTypeAt(Pos); } static inline bool doesStronglyMatchFrom(const Message &Msg, const size_t Pos) noexcept { // Matching a non-empty list if the head is matching and the rest of the // list is matching. return doesHeadStronglyMatchAt(Msg, Pos) && MessageMatcherImpl>::doesStronglyMatchFrom(Msg, Pos + 1); } static inline std::tuple extractedValuesFrom(const Message &Msg, const size_t Pos) noexcept { // Extracting for a non-empty list with a matching head by getting the // value for the head and concatenating it with values extracted for the // rest of the list. ASSERT(doesHeadStronglyMatchAt(Msg, Pos)); return std::tuple_cat( std::tie(Msg.valueAt(Pos)), MessageMatcherImpl>::extractedValuesFrom(Msg, Pos + 1)); } }; ///@} } // End namespace template bool MessageMatcher>::doesStronglyMatch( const Message &Msg) noexcept { // \note Fail quick on T, then match against list with squashed integers the // way `rosa::Token` is generated. return T == Msg.T && MessageMatcherImpl>::Type>::doesStronglyMatchFrom(Msg, 0); } template std::tuple MessageMatcher>::extractedValues( const Message &Msg) noexcept { ASSERT(doesStronglyMatch(Msg)); // \note Match against a list with squashed integers as `rosa::Token` is // generated. return MessageMatcherImpl>::Type>::extractedValuesFrom(Msg, 0); } } // End namespace rosa #endif // ROSA_CORE_MESSAGEMATCHER_HPP diff --git a/include/rosa/core/MessagingSystem.hpp b/include/rosa/core/MessagingSystem.hpp index 0c564ea..7ce5b53 100644 --- a/include/rosa/core/MessagingSystem.hpp +++ b/include/rosa/core/MessagingSystem.hpp @@ -1,185 +1,189 @@ -/***************************************************************************//** - * - * \file rosa/core/MessagingSystem.hpp - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief Declaration of an interface extending `rosa::System` with messaging. - * - ******************************************************************************/ +//===-- rosa/core/MessagingSystem.hpp ---------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/core/MessagingSystem.hpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2017 +/// +/// \brief Declaration of an interface extending `rosa::System` with messaging. +/// +//===----------------------------------------------------------------------===// #ifndef ROSA_CORE_MESSAGINGSYSTEM_HPP #define ROSA_CORE_MESSAGINGSYSTEM_HPP #include "rosa/core/AgentHandle.hpp" #include "rosa/core/System.hpp" #include "rosa/support/atom.hpp" namespace rosa { /// Extends the `rosa::System` interface with features to create `rosa::Agent` /// instancess and register `rosa::Message` objects for them. class MessagingSystem : public System { friend class AgentHandle; ///< `rosa::AgentHandle` is our friend. public: /// Returns an object implementing the `rosa::MessagingSystem` interface. /// /// \param Name name of the new instance /// /// \return `std::unique_ptr` for the new instance of /// `rosa::MessagingSystem` static std::unique_ptr createSystem(const std::string &Name) noexcept; private: /// Kind for categorizing `rosa::Unit` instances as *agents*. static constexpr AtomValue AgentKind = atom("agent"); protected: /// Creates a new instance. /// /// \note Protected constructor restricts instantiation for subclasses. MessagingSystem(void) noexcept = default; protected: /// Creates a `rosa::Agent` instance owned by `this` object and returns a /// `rosa::AgentHandle` for it. /// /// \tparam T type of the actual `rosa::Agent` to instantiate /// \tparam Funs types of the functions to instantiate `rosa::Agent` with /// /// \note `rosa::Agent` requires at least one function for its constructor, /// but derived classes may do not need that. That's the reason of allowing /// zero `Funs` for this template function. /// /// \param Name name of the new `rosa::Unit` instance /// \param Fs functions to instantiate `rosa::Unit` with /// /// \pre Statically, `T` is a subclass of `rosa::Agent`:\code /// std::is_base_of::value /// \endcode template AgentHandle createAgent(const std::string &Name, Funs &&... Fs); /// Gives the references `rosa::Agent` instance for a `rosa::AgentHandle`. /// /// \note Intended for derived classes to be able to inspect /// `rosa::AgentHandle` instances. /// /// \param H `rosa::AgentHandle` to take the referenced `rosa::Agent` from /// /// \return reference to the `rosa::Agent` instance from `H` static inline Agent &unwrapAgent(const AgentHandle &H) noexcept { return H.A; } /// Gives the owning `rosa::MessagingSystem` of a `rosa::Agent` instance /// for a `rosa::AgentHandle`. /// /// \note Intended for for derived classes to be able to inspect /// `rosa::AgentHandle` instances. /// /// \param H `rosa::AgentHandle` to take the owning /// `rosa::MessagingSystem` from /// /// \return reference to the `rosa::MessagingSystem` owning the /// `rosa::agent` instance from `H` static inline MessagingSystem &unwrapSystem(const AgentHandle &H) noexcept { return H.S; } public: /// Sends a `rosa::message_t` instance to the `rosa::Agent`instance referred /// by a `rosa::AgentHandle`. /// /// \note If the given `rosa::Message` object cannot be handled by the /// referred `rosa::Agent` instance, the `rosa::Message` object is simply /// ignored. /// /// \param H refers to the `rosa::Agent` instance to send to /// \param M message to send /// /// \pre The referred `rosa::Agent` instance is owned by `this` object and /// also registered: \code /// &unwrapSystem(H) == this && isUnitRegistered(unwrapAgent(H)) /// \endcode virtual void send(const AgentHandle &H, message_t &&M) noexcept = 0; /// Sends a message -- created from given constant lvalue references -- /// to the `rosa::Agent`instance referred by a `rosa::AgentHandle`. /// /// \note If the given `rosa::Message` object cannot be handled by the /// referred `rosa::Agent` instance, the `rosa::Message` object is simply /// ignored. /// /// \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 H refers to the `rosa::Agent` instance to send to /// \param T the first value to include in the message /// \param Ts optional further values to include in the message /// /// \pre The referred `rosa::Agent` instance is owned by `this` object and /// also registered: \code /// &unwrapSystem(H) == this && isUnitRegistered(unwrapAgent(H)) /// \endcode template void send(const AgentHandle &H, const Type &T, const Types &... Ts) noexcept; /// Sends a message -- created from given rvalue references -- /// to the `rosa::Agent`instance referred by a `rosa::AgentHandle`. /// /// \note If the given `rosa::Message` object cannot be handled by the /// referred `rosa::Agent` instance, the `rosa::Message` object is simply /// ignored. /// /// \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 H refers to the `rosa::Agent` instance to send to /// \param T the first value to include in the message /// \param Ts optional further values to include in the message /// /// \pre The referred `rosa::Agent` instance is owned by `this` object and /// also registered: \code /// &unwrapSystem(H) == this && isUnitRegistered(unwrapAgent(H)) /// \endcode template void send(const AgentHandle &H, Type &&T, Types &&... Ts) noexcept; }; template AgentHandle MessagingSystem::createAgent(const std::string &Name, Funs &&... Fs) { STATIC_ASSERT((std::is_base_of::value), "not an Agent"); Agent &A = createUnit([&](const id_t Id, MessagingSystem &S) noexcept { return new T(AgentKind, Id, Name, S, std::move(Fs)...); }); return {A}; } template void MessagingSystem::send(const AgentHandle &H, const Type &T, const Types &... Ts) noexcept { send(H, Message::create(T, Ts...)); } template void MessagingSystem::send(const AgentHandle &H, Type &&T, Types &&... Ts) noexcept { send(H, Message::create(std::move(T), std::move(Ts)...)); } } // End namespace rosa #endif // ROSA_CORE_MESSAGINGSYSTEM_HPP diff --git a/include/rosa/core/System.hpp b/include/rosa/core/System.hpp index ea701be..f575745 100644 --- a/include/rosa/core/System.hpp +++ b/include/rosa/core/System.hpp @@ -1,209 +1,213 @@ -/***************************************************************************//** - * - * \file rosa/core/System.hpp - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief Declaration of *System* interface. - * - ******************************************************************************/ +//===-- 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 `rosa::Unit` instances owned /// by `this` `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 `rosa::SystemBase` /// implenenting a base feature-set. class System { public: /// Signature of creator functions for `rosa::Unit` instances. /// /// \tparam T type derived from `rosa::Unit` /// \tparam S type derived from `rosa::System` template using UnitCreator = std::function; /// Returns an object implementing the `rosa::System` interface. /// /// \param Name name of the new instance /// /// \return `std::unique_ptr` for a new instance of `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 `rosa::System`. ///@{ System(const System&) = delete; System(System&&) = delete; System &operator=(const System&) = delete; System &operator=(System&&) = delete; ///@} public: /// Destroys `this` object. /// /// \note Any implementation makes sure that a `rosa::System` can be /// destroyed only if it is marked *cleaned* /// \see rosa::System::isSystemCleaned virtual ~System(void) = default; protected: /// Tells the next unique identifier to be used for a newly created /// `rosa::Unit`. /// /// \return `id_t` which is unique within the context of `this` object. /// /// \note Never returs the same value twice. virtual id_t nextId(void) noexcept = 0; /// Tells if `this` object has been marked cleaned and is ready for /// destruction. /// /// \return if `this` object is marked clean. virtual bool isSystemCleaned(void) const noexcept = 0; /// Marks `this` object cleaned. /// /// \note Can be called only once when the System does not have any live /// `rosa::Unit` instances. /// /// \pre `this` object has not yet been marked as cleaned and it has no /// `rosa::Unit` instances registered:\code /// !isSystemCleaned() && empty() /// \endcode /// /// \post `this` object is marked cleaned:\code /// isSystemCleaned() /// \encode virtual void markCleaned(void) noexcept = 0; /// Registers a `rosa::Unit` instance to `this` object. /// /// \param U `rosa::Unit` to register /// /// \pre `this` object has not yet been marked as cleaned and `U` is not /// registered yet:\code /// !isSystemCleaned() && !isUnitRegistered(U) /// \endcode /// /// \post `U` is registered:\code /// isUnitRegistered(U) /// \endcode virtual void registerUnit(Unit &U) noexcept = 0; /// Unregisters and destroys a registered `rosa::Unit` instance. /// /// \param U `rosa::Unit` to destroy /// /// \pre `U` is registered:\code /// isUnitRegistered(U) /// \endcode /// /// \post `U` is not registered:\code /// !isUnitRegistered(U) /// \endcode Moreover, `U` is destroyed. virtual void destroyUnit(Unit &U) noexcept = 0; /// Tells if a `rosa::Unit` is registered in `this` object. /// /// \param U `rosa::Unit` to check /// /// \return whether `U` is registered in `this` object virtual bool isUnitRegistered(const Unit &U) const noexcept = 0; /// Creates a `rosa::Unit` instance with the given `rosa::System::UnitCreator` /// and registers the new instance. /// /// \tparam T type of the actual `rosa::Unit` to instantiate /// \tparam S type of the actual `rosa::System` instantiating /// /// \param C function creating an instance of type `T` /// /// \note `S` must be the actual subclass that wants to instantiate /// `rosa::Unit`. That cannot be statically enforced, it is the /// reponsibility of the caller to provide the proper `rosa::System` subclass. /// /// \pre Statically, `T` is a subclass of `rosa::Unit` and `S` is a subclass /// of `rosa::System`:\code /// std::is_base_of::value && std::is_base_of::value /// \endcode Dynamically, `this` object has not yet been marked cleaned:\code /// !isSystemCleaned() /// \endcode template T &createUnit(UnitCreator C) noexcept; public: /// Tells the name of `this` object /// /// \note The returned reference remains valid as long as `this` object is not /// destroyed. /// /// \return name of `this` object virtual const std::string &name(void) const noexcept = 0; /// Tells the number of `rosa::Unit` instances constructed in the context of /// `this`object so far, including those being already destroyed. /// /// \return number of `rosa::Unit`instances created so far virtual size_t numberOfConstructedUnits(void) const noexcept = 0; /// Tells the number of live `rosa::Unit` instances in the context `this` /// object, those being constructed and not destroyed yet. /// /// \return number of `rosa::Unit` instances alive virtual size_t numberOfLiveUnits(void) const noexcept = 0; /// Tells if `this` object has no live `rosa::Unit` instances. /// /// \return whether `this` object has any live `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 62e4a90..df861a8 100644 --- a/include/rosa/core/SystemBase.hpp +++ b/include/rosa/core/SystemBase.hpp @@ -1,121 +1,125 @@ -/***************************************************************************//** - * - * \file rosa/core/SystemBase.hpp - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief Base implementation of the `rosa::System` interface. - * - ******************************************************************************/ +//===-- 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 `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 `rosa::System` interface. /// /// This implementation provides only *name* for `rosa::System`, identifiers for /// `rosa::Unit` instances, and marking the `rosa::System` cleaned for /// destruction. /// /// \note Actual implementations of `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 `this` object. /// /// \pre `this`object is marked cleaned:\code /// isSystemCleaned() /// \endcode ~SystemBase(void); protected: /// The textual name of `this` object implementing `rosa::System`. const std::string Name; private: /// Number of `rosa::Unit` instances constructed by `this` object. /// /// \note Should never be decremented! std::atomic UnitCount; /// Indicates that `this` object has been cleaned and is ready for /// destruction. /// /// The field is initialized as 'false' and can be set by /// `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 `this` object /// /// \note The returned reference remains valid as long as `this` object is not /// destroyed. /// /// \return reference to `Name` const std::string &name(void) const noexcept override; protected: /// Tells the next unique identifier to be used for a newly created /// `rosa::Unit` and increments the internal counter /// `rosa::SystemBase::UnitCount`. /// /// \note This is the only function modifying /// `rosa::SystemBase::UnitCount`. /// /// \return `id_t` which is unique within the context of `this` object. id_t nextId(void) noexcept override; /// Tells if `this` object has been marked cleaned and is ready for /// destruction. /// /// \return if `this` object is marked clean. bool isSystemCleaned(void) const noexcept override; /// Marks `this` object cleaned by setting /// `rosa::SystemBase::SystemIsCleaned`. /// /// \note Can be called only once when the System does not have any live /// `rosa::Unit` instances. /// /// \pre `this` object has not yet been marked as cleaned and it has no /// `rosa::Unit` instances registered:\code /// !isSystemCleaned() && empty() /// \endcode /// /// \post `this` object is marked cleaned:\code /// isSystemCleaned() /// \encode void markCleaned(void) noexcept override; /// Tells the number of `rosa::Unit` instances constructed in the context of /// `this`object so far, including those being already destroyed. /// /// \return current value of `rosa::SystemBase::UnitCount` that is the number /// of `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/core/Unit.h b/include/rosa/core/Unit.h index d9e479d..0a5b61d 100644 --- a/include/rosa/core/Unit.h +++ b/include/rosa/core/Unit.h @@ -1,116 +1,120 @@ -/***************************************************************************//** - * - * \file rosa/core/Unit.h - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief Declaration of `rosa::Unit` base-class. - * - ******************************************************************************/ +//===-- rosa/core/Unit.h ----------------------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/core/Unit.h +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2017 +/// +/// \brief Declaration of `rosa::Unit` base-class. +/// +//===----------------------------------------------------------------------===// #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 { /// Base class for every entity in a `rosa::System` that has to be identified /// and traced. /// /// \note Life-cycle of `rosa::Unit` instances is supposed to be managed by the /// `rosa::System` owning the instance, do not create and destroy any /// `rosa::Unit` directly. class Unit { public: /// Identifies the *kind* of `this` object. /// /// \note Kind is dependent on the `rosa::System` owning `this` object. const AtomValue Kind; /// Unique identifier for `this` object. /// /// The unique identifier is assigned by the `rosa::System` owning `this` /// object and is based on `rosa::System::CountUnits` of the owning /// `rosa::System`. const id_t Id; /// Textual identifier of `this` object. /// /// The textual identifier defaults to a text referring to `Id`, unless /// otherwise defined via an argument for the constructor. /// /// \note `Name` of a `rosa::Unit` instance is not necessarily unique in the /// owning `rosa::System`. const std::string Name; protected: /// The `rosa::System` owning `this` object. System &S; public: /// Full qualified name of `this` object. const std::string FullName; public: /// Creates a new instnace. /// /// \param Kind the kind of the new instance /// \param Id the unique identifier of the new instance /// \param Name the name of the new instance /// \param S `rosa::System` owning the new instance /// /// \pre `Name` is not empty:\code /// !Name.empty() /// \endcode Unit(const AtomValue Kind, const id_t Id, const std::string &Name, System &S) noexcept; /// No copying and moving of `rosa::Unit` instances is possible. ///@{ Unit(const Unit &) = delete; Unit(Unit &&) = delete; Unit &operator=(const Unit &) = delete; Unit &operator=(Unit &&) = delete; ///@} /// Destroys `this` object. virtual ~Unit(void); /// Dumps `this` object into a `std::string` for tracing purposes. /// /// Subclasses are supposed to override this function. /// /// \return `string` representing the state of `this` object virtual std::string dump(void) const noexcept; protected: /// Returns a reference to the `rosa::System` owning `this` object. /// /// \note Subclasses may override the function to return a reference of a /// subtype of `rosa::System`. /// /// \return reference of `S` virtual System &system() const noexcept; }; /// Dumps a `rosa::Unit` isntance to a given `std::ostream`. /// /// \param [in,out] OS output stream to dump to /// \param U `Unit` to dump /// /// \return `OS` after dumping `U` to it 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 index c478649..360aa56 100644 --- a/include/rosa/core/forward_declarations.h +++ b/include/rosa/core/forward_declarations.h @@ -1,37 +1,41 @@ -/***************************************************************************//** - * - * \file rosa/core/forward_declarations.h - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief Necessary forward declarations of types in the *Core* library. - * - ******************************************************************************/ +//===-- rosa/core/forward_declarations.h ------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/core/forward_declarations.h +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2017 +/// +/// \brief Necessary forward declarations of types in the *Core* library. +/// +//===----------------------------------------------------------------------===// #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 `rosa::Unit` identifiers. using id_t = uint64_t; /// Type of a `std::unique_ptr` for an immutable *Message*, `rosa::Message` /// instance. using message_t = std::unique_ptr; } // End namespace rosa #endif // ROSA_CORE_FORWARD_DECLARATIONS_H diff --git a/include/rosa/support/atom.hpp b/include/rosa/support/atom.hpp index 45bdb51..09216d7 100644 --- a/include/rosa/support/atom.hpp +++ b/include/rosa/support/atom.hpp @@ -1,175 +1,179 @@ -/***************************************************************************//** - * - * \file rosa/support/atom.hpp - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief Facility for `atom`s, short strings statically encoded as integers. - * - * \note This implementation is based on the `atom` implementation of CAF. - * \todo Check license. - * - * `Atom`s can be used to turn short string literals into statically generated - * types. The literals may consist of at most `10` non-special characters, legal - * characters are `_0-9A-Za-z` and the whitespace character. Special characters - * are turned into whitespace, which may result in different string literals - * being encoded into the same integer value, if any of those contain at least - * one special character. - * - * \note The usage of special characters in the string literals used to create - * `atoms` cannot be checked by the compiler. - * - * Example: - * - * \code - * constexpr AtomValue NameValue = atom("name"); - * using NameAtom = AtomConstant; - * - * [](NameAtom){ std::cout << "Argument of type NameAtom"; }(NameAtom::Value) - * \endcode - * - ******************************************************************************/ +//===-- rosa/support/atom.hpp -----------------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/support/atom.hpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2017 +/// +/// \brief Facility for `atom`s, short strings statically encoded as integers. +/// +/// \note This implementation is based on the `atom` implementation of CAF. +/// \todo Check license. +/// +/// `Atom`s can be used to turn short string literals into statically generated +/// types. The literals may consist of at most `10` non-special characters, legal +/// characters are `_0-9A-Za-z` and the whitespace character. Special characters +/// are turned into whitespace, which may result in different string literals +/// being encoded into the same integer value, if any of those contain at least +/// one special character. +/// +/// \note The usage of special characters in the string literals used to create +/// `atoms` cannot be checked by the compiler. +/// +/// Example: +/// +/// \code +/// constexpr AtomValue NameValue = atom("name"); +/// using NameAtom = AtomConstant; +/// +/// [](NameAtom){ std::cout << "Argument of type NameAtom"; }(NameAtom::Value) +/// \endcode +/// +//===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_ATOM_HPP #define ROSA_SUPPORT_ATOM_HPP #include "rosa/support/debug.hpp" namespace rosa { /// Maximal length of valid atom strings. constexpr size_t MaxAtomLength = 10; /// Underlying integer type of atom values. using atom_t = uint64_t; /// Turn `rosa::atom_t` into a strongly typed enumeration. /// /// Values of `rosa::atom_t` casted to `rosa::AtomValue` may be used in a /// type-safe way. enum class AtomValue : atom_t {}; /// Anonymous namespace with implementational details, consider it private. namespace { /// Encodes ASCII characters to 6-bit encoding. constexpr unsigned char AtomEncodingTable[] = { /* ..0 ..1 ..2 ..3 ..4 ..5 ..6 ..7 ..8 ..9 ..A ..B ..C ..D ..E ..F */ /* 0.. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1.. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2.. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 3.. */ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0, 0, /* 4.. */ 0, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, /* 5.. */ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 0, 0, 0, 0, 37, /* 6.. */ 0, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, /* 7.. */ 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 0, 0, 0, 0, 0}; /// Decodes 6-bit characters to ASCII constexpr char AtomDecodingTable[] = " 0123456789" "ABCDEFGHIJKLMNOPQRSTUVWXYZ_" "abcdefghijklmnopqrstuvwxyz"; /// Encodes one character and updates the integer representation. /// /// \param Current an encoded value /// \param CharCode a character to add to `Current` /// /// \return `Current` updated with `CharCode` constexpr atom_t nextInterim(atom_t Current, size_t CharCode) { return (Current << 6) | AtomEncodingTable[(CharCode <= 0x7F) ? CharCode : 0]; } /// Encodes a C-string into an integer value to be used as `rosa::AtomValue`. /// /// \param CStr a string to encode /// \param Interim encoded value to add `CStr` to it /// /// \return `Interim` updated with `CStr` constexpr atom_t atomValue(const char *CStr, atom_t Interim = 0xF) { return (*CStr == '\0') ? Interim : atomValue(CStr + 1, nextInterim(Interim, static_cast(*CStr))); } } // End namespace /// Converts a `rosa::AtomValue` into `std::string`. /// /// \param What value to convert /// /// \return the `string` encoded in `What` std::string to_string(const AtomValue &What); /// Converts a `std::string` into a `rosa::AtomValue`. /// /// \param S the `string` to convert /// /// \return `AtomValue` representation of `S` AtomValue atom_from_string(const std::string &S); /// Converts a string-literal into a `rosa::AtomValue`. /// /// \tparam Size the length of `Str` /// /// \param Str the string-literal to convert /// /// \return `AtomValue` representation of `Str` /// /// \pre `Str` is not too long:\code /// Size <= MaxAtomLength + 1 /// \endcode template constexpr AtomValue atom(char const (&Str)[Size]) { // Last character is the NULL terminator. STATIC_ASSERT(Size <= MaxAtomLength + 1, "Too many characters in atom definition"); return static_cast(atomValue(Str)); } /// Lifts a `rosa::AtomValue` to a compile-time constant. /// /// \tparam V the `AtomValue` to lift template struct AtomConstant { /// Ctor, has to do nothing. constexpr AtomConstant(void) {} /// Returns the wrapped value. /// /// \return `V` constexpr operator AtomValue(void) const { return V; } /// Returns the wrapped value as of type `rosa::atom_t`. /// /// \return `atom_t` value from `V` static constexpr atom_t value() { return static_cast(V); } /// An instance *of this constant* (*not* an `rosa::AtomValue`). static const AtomConstant Value; }; // Implementation of the static member field `Value` of `rosa::AtomConstant`. template const AtomConstant AtomConstant::Value = AtomConstant{}; /// Converts a `rosa::AtomConstant` into `std::string`. /// /// \tparam V `AtomValue` to convert /// /// \note The actual argument of type `const AtomConstant` is ignored /// because the `AtomValue` to convert is encoded in the type itself. /// /// \return the original string encoded in `V` template std::string to_string(const AtomConstant &) { return to_string(V); } } // End namespace rosa #endif // ROSA_SUPPORT_ATOM_HPP diff --git a/include/rosa/support/debug.hpp b/include/rosa/support/debug.hpp index a60c05e..279ac71 100644 --- a/include/rosa/support/debug.hpp +++ b/include/rosa/support/debug.hpp @@ -1,124 +1,128 @@ -/***************************************************************************//** - * - * \file rosa/support/debug.hpp - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief Facility for debugging - * - ******************************************************************************/ +//===-- rosa/support/debug.hpp ----------------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/support/debug.hpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2017 +/// +/// \brief Facility for debugging +/// +//===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_DEBUG_HPP #define ROSA_SUPPORT_DEBUG_HPP #include "rosa/config/config.h" #include "rosa/support/type_helper.hpp" #include "rosa/support/terminal_colors.h" #include #include namespace rosa { /// Returns an output stream to use for debugging. std::ostream &dbgs(void); /// Prints an `std::array` to an `std::ostream`. /// /// \tparam T type of values stored in `A` /// \tparam Size number of elements in `A` /// /// \param [in,out] OS `ostream` to print to /// \param A `array` to print to `OS` /// /// \return `OS` after printing `A` template std::ostream &operator<<(std::ostream &OS, const std::array &A) { OS << '['; for (unsigned I = 0; I < Size; ++I) { if (I) { OS << ','; } OS << PRINTABLE(A[I]); } OS << ']'; return OS; } } // End namespace rosa /// \def ASSERT(stmt) /// \brief Enforces an assertion. /// /// \note Takes effect only when `ROSA_ENABLE_ASSERTIONS` is defined. /// /// Checks if `stmt` evaluates to true. If not, prints an error message about /// violating the assertion and aborts execution. /// /// \param stmt statement to evaluate, needs to be convertable to `bool` #ifndef ROSA_ENABLE_ASSERTIONS #define ASSERT(stmt) ROSA_IGNORE_UNUSED(stmt) #elif defined(ROSA_WINDOWS) #define ASSERT(stmt) \ if (static_cast(stmt) == false) { \ rosa::dbgs() << rosa::terminal::Color::Default << __FILENAME__ << ":" \ << __LINE__ << ": requirement failed: '" << #stmt << "'" \ << std::endl; \ ::abort(); \ } \ ROSA_VOID_STMT #else // defined(ROSA_LINUX) #include #define ASSERT(stmt) \ if (static_cast(stmt) == false) { \ rosa::dbgs() << rosa::terminal::Color::Default << __FILENAME__ << ":" \ << __LINE__ << ": requirement failed: '" << #stmt << "'" \ << std::endl; \ void *array[20]; \ auto bt_size = ::backtrace(array, 20); \ ::backtrace_symbols_fd(array, bt_size, 2); \ ::abort(); \ } \ ROSA_VOID_STMT #endif // defined(ROSA_ENABLE_ASSERTIONS) /// \def DEBUG(X) /// \brief Executes the given piece of code only if `NDEBUG` is not defined. /// /// \param X the code to execute /// \def DEBUGVAR(V) /// \brief Dumps the given variable to the default debug output. /// /// \param V the variable to dump /// /// \sa rosa::dbgs() #ifndef NDEBUG #define DEBUG(X) \ do { \ X; \ } while (false) #define DEBUGVAR(V) \ do { \ rosa::dbgs() << rosa::terminal::Color::Default << #V << " (" \ << __FILENAME__ << ":" << __LINE__ << "): " << (V) \ << std::endl; \ } while (false) #else // defined(NDEBUG) #define DEBUG(X) ROSA_IGNORE_UNUSED(X) #define DEBUGVAR(X) ROSA_IGNORE_UNUSED(X) #endif // defined(NDEBUG) /// Enforces static assertion. /// /// \param COND the condition to evaluate, must be statically evaluable /// \param DIAG error message if `COND` does not evaluate to `true` #define STATIC_ASSERT(COND, DIAG) static_assert((COND), DIAG) #endif // ROSA_SUPPORT_DEBUG_HPP diff --git a/include/rosa/support/log.h b/include/rosa/support/log.h index 9067ada..584435e 100644 --- a/include/rosa/support/log.h +++ b/include/rosa/support/log.h @@ -1,257 +1,261 @@ -/***************************************************************************//** - * - * \file rosa/support/log.h - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief Facility for logging. - * - * \note One call for the various logging macros is supposed to be used - * for registering one log entry. That goes natural with the non-stream - * implementations, which accept one string as argument. A more flexible way - * for printing log entries, for example for colorizing text, is to use macros - * providing a log stream. It is important to note, however, that the stream - * obtained from one macro evaluation fits for printing one log entry, without - * nested/overlapping log entry emissions and the entry being closed with a - * newline. Should this simple recommendation not being followed, the result - * becomes hard to read due to missing line breaks and overlapping entries. - * - * \todo Thread-safety is another issue, which need to be addressed for proper - * logging. - * - ******************************************************************************/ +//===-- rosa/support/log.h --------------------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/support/log.h +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2017 +/// +/// \brief Facility for logging. +/// +/// \note One call for the various logging macros is supposed to be used +/// for registering one log entry. That goes natural with the non-stream +/// implementations, which accept one string as argument. A more flexible way +/// for printing log entries, for example for colorizing text, is to use macros +/// providing a log stream. It is important to note, however, that the stream +/// obtained from one macro evaluation fits for printing one log entry, without +/// nested/overlapping log entry emissions and the entry being closed with a +/// newline. Should this simple recommendation not being followed, the result +/// becomes hard to read due to missing line breaks and overlapping entries. +/// +/// \todo Thread-safety is another issue, which need to be addressed for proper +/// logging. +/// +//===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_LOG_H #define ROSA_SUPPORT_LOG_H #include "rosa/config/config.h" #include "rosa/support/terminal_colors.h" #include #include /* **************************************************************************** * Log Levels * * ****************************************************************************/ namespace rosa { /// Type-safe definition of log levels, use this in code. /// \note Keep values in sync with the corresponding preprocessor definitions. enum class LogLevel { Error, ///< Log errors only Warning, ///< Like `rosa::LogLevel::Error` and also log warnings Info, ///< Like `rosa::LogLevel::Warning` and also log general infos Debug, ///< Like `rosa::LogLevel::Info` and also log debug infos Trace, ///< Like `rosa::LogLevel::Debug` and also log trace infos NumLogLevels ///< Number of log levels }; /// Converts a `rosa::LogLevel` to its textual representation. /// /// \param logLevel the `LogLevel` to convert /// /// \return `string` representing `logLevel` /// /// \pre `logLevel` is valid:\code /// logLevel != LogLevel::NumLogLevels /// \endcode std::string logLevelToString(const LogLevel logLevel); /// Prints colorized tag for the given `rosa::LogLevel`. /// /// \param [in,out] OS `ostream` to print to /// \param logLevel the `LogLevel` to print tag for /// /// \return `OS` after printing a tag for `logLevel` /// /// \pre `logLevel` is valid:\code /// logLevel != LogLevel::NumLogLevels /// \endcode std::ostream &operator<<(std::ostream &OS, const LogLevel logLevel); } // End namespace rosa /// \name Valid log level constants /// \note Only for preprocessor definitions in this file. /// \note Keep the defintions in sync with the values of `rosa::LogLevel`. ///@{ #define ROSA_LOG_LEVEL_ERROR \ 0 ///< Value corresponding to `rosa::LogLevel::Error` #define ROSA_LOG_LEVEL_WARNING \ 1 ///< Value corresponding to `rosa::LogLevel::Warning` #define ROSA_LOG_LEVEL_INFO \ 2 ///< Value corresponding to `rosa::LogLevel::Info` #define ROSA_LOG_LEVEL_DEBUG \ 3 ///< Value corresponding to `rosa::LogLevel::Debug` #define ROSA_LOG_LEVEL_TRACE \ 4 ///< Value corresponding to `rosa::LogLevel::Trace` ///@} /* **************************************************************************** * Logger Implementation * * ****************************************************************************/ /// Stream to print logs to /// /// \todo Make it configurable, e.g. printing into a file. #define ROSA_LOG_OSTREAM std::clog /// Prints a log message to `ROSA_LOG_OSTREAM`. /// /// \param level `LogLevel` of the log entry /// \param output message to print #define ROSA_LOG_IMPL(level, output) \ do { \ ROSA_LOG_OSTREAM << (level) << " " << __func__ << "@" << __FILENAME__ \ << ":" << __LINE__ << ": " << (output) << std::endl; \ } while (false) /// Returns a stream to print a log message to. /// /// \param level `LogLevel`of the log entry that is about to be printed #define ROSA_LOG_STREAM_IMPL(level) \ ([](void) -> std::ostream & { \ return ROSA_LOG_OSTREAM << (level) << " " << __func__ << "@" \ << __FILENAME__ << ":" << __LINE__ << ": "; \ }()) namespace rosa { /// Dummy `std::ostream` printing to nowhere. extern std::ostream LogSink; } // End namespace rosa /// An output stream ignoring all its input. #define ROSA_LOG_STREAM_IGNORE rosa::LogSink /* **************************************************************************** * Logging Interface * * ****************************************************************************/ /// \name Logging interface /// /// Preprocesser macros for convenience. ///@{ /// \def LOG_ERROR_STREAM /// \brief Returns a stream to print a log entry of level /// `rosa::LogLevel::Error`. /// \note Takes effect only if RoSA is built with a log level not smaller than /// `rosa::LogLevel::Error`. /// \def LOG_ERROR(output) /// \brief Prints a log entry of level `rosa::LogLevel::Error`. /// \param output the message to print /// \note Takes effect only if RoSA is built with a log level not smaller than /// `rosa::LogLevel::Error`. /// \def LOG_WARNING_STREAM /// \brief Returns a stream to print a log entry of level /// `rosa::LogLevel::Warning`. /// \note Takes effect only if RoSA is built with a log level not smaller than /// `rosa::LogLevel::Warning`. /// \def LOG_WARNING(output) /// \brief Prints a log entry of level `rosa::LogLevel::Warning`. /// \param output the message to print /// \note Takes effect only if RoSA is built with a log level not smaller than /// `rosa::LogLevel::Warning`. /// \def LOG_INFO_STREAM /// \brief Returns a stream to print a log entry of level /// `rosa::LogLevel::Info`. /// \note Takes effect only if RoSA is built with a log level not smaller than /// `rosa::LogLevel::Info`. /// \def LOG_INFO(output) /// \brief Prints a log entry of level `rosa::LogLevel::Info`. /// \param output the message to print /// \note Takes effect only if RoSA is built with a log level not smaller than /// `rosa::LogLevel::Info`. /// \def LOG_DEBUG_STREAM /// \brief Returns a stream to print a log entry of level /// `rosa::LogLevel::Debug`. /// \note Takes effect only if RoSA is built with a log level not smaller than /// `rosa::LogLevel::Debug`. /// \def LOG_DEBUG(output) /// \brief Prints a log entry of level `rosa::LogLevel::Debug`. /// \param output the message to print /// \note Takes effect only if RoSA is built with a log level not smaller than /// `rosa::LogLevel::Debug`. /// \def LOG_TRACE_STREAM /// \brief Returns a stream to print a log entry of level /// `rosa::LogLevel::Trace`. /// \note Takes effect only if RoSA is built with a log level not smaller than /// `rosa::LogLevel::Trace`. /// \def LOG_TRACE(output) /// \brief Prints a log entry of level `rosa::LogLevel::Trace`. /// \param output the message to print /// \note Takes effect only if RoSA is built with a log level not smaller than /// `rosa::LogLevel::Trace`. ///@} // Define logging macros if logging is enabled. #ifdef ROSA_LOG_LEVEL #define LOG_ERROR_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Error) #define LOG_ERROR(output) ROSA_LOG_IMPL(rosa::LogLevel::Error, output) #if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_WARNING #define LOG_WARNING_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Warning) #define LOG_WARNING(output) ROSA_LOG_IMPL(rosa::LogLevel::Warning, output) #endif #if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_INFO #define LOG_INFO_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Info) #define LOG_INFO(output) ROSA_LOG_IMPL(rosa::LogLevel::Info, output) #endif #if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_DEBUG #define LOG_DEBUG_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Debug) #define LOG_DEBUG(output) ROSA_LOG_IMPL(rosa::LogLevel::Debug, output) #endif #if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_TRACE #define LOG_TRACE_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Trace) #define LOG_TRACE(output) ROSA_LOG_IMPL(rosa::LogLevel::Trace, output) #endif #endif // defined ROSA_LOG_LEVEL // Define all disabled logging features as void. #ifndef LOG_ERROR #define LOG_ERROR_STREAM ROSA_LOG_STREAM_IGNORE #define LOG_ERROR(output) ROSA_IGNORE_UNUSED(output) #endif #ifndef LOG_WARNING #define LOG_WARNING_STREAM ROSA_LOG_STREAM_IGNORE #define LOG_WARNING(output) ROSA_IGNORE_UNUSED(output) #endif #ifndef LOG_INFO #define LOG_INFO_STREAM ROSA_LOG_STREAM_IGNORE #define LOG_INFO(output) ROSA_IGNORE_UNUSED(output) #endif #ifndef LOG_DEBUG #define LOG_DEBUG_STREAM ROSA_LOG_STREAM_IGNORE #define LOG_DEBUG(output) ROSA_IGNORE_UNUSED(output) #endif #ifndef LOG_TRACE #define LOG_TRACE_STREAM ROSA_LOG_STREAM_IGNORE #define LOG_TRACE(output) ROSA_IGNORE_UNUSED(output) #endif #endif // ROSA_SUPPORT_LOG_H diff --git a/include/rosa/support/math.hpp b/include/rosa/support/math.hpp index ca1a2e5..7abb757 100644 --- a/include/rosa/support/math.hpp +++ b/include/rosa/support/math.hpp @@ -1,32 +1,36 @@ -/***************************************************************************//** - * - * \file rosa/support/math.hpp - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief Math helpers. - * - ******************************************************************************/ +//===-- rosa/support/math.hpp -----------------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/support/math.hpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2017 +/// +/// \brief Math helpers. +/// +//===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_MATH_HPP #define ROSA_SUPPORT_MATH_HPP #include namespace rosa { /// Computes log base 2 of a number. /// /// \param N the number to compute log base 2 for /// /// \return log base 2 of `N` constexpr size_t log2(const size_t N) { return ((N < 2) ? 1 : 1 + log2(N / 2)); } } // End namespace rosa #endif // ROSA_SUPPORT_MATH_HPP diff --git a/include/rosa/support/squashed_int.hpp b/include/rosa/support/squashed_int.hpp index b686ce6..868cbd3 100644 --- a/include/rosa/support/squashed_int.hpp +++ b/include/rosa/support/squashed_int.hpp @@ -1,129 +1,133 @@ -/**************************************************************************//** - * - * \file rosa/support/squashed_int.hpp - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief Facilities for squashing integer types into standard equivalents. - * - * \note This implementation is partially based on the `squashed_int` - * implementation of CAF. - * \todo Check license. - * - ******************************************************************************/ +//===-- rosa/support/squashed_int.hpp ---------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/support/squashed_int.hpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2017 +/// +/// \brief Facilities for squashing integer types into standard equivalents. +/// +/// \note This implementation is partially based on the `squashed_int` +/// implementation of CAF. +/// \todo Check license. +/// +//===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_SQUASHED_INT_HPP #define ROSA_SUPPORT_SQUASHED_INT_HPP #include "rosa/support/type_list.hpp" #include "rosa/support/type_pair.hpp" namespace rosa { /// Compile-time list of integer types. /// /// \note This list is used to select a proper type as `rosa::type_nr_t`, always /// make sure that `rosa::type_nr_t` remains correct whenever changing the list. using IntegerTypesBySize = TypeList< // bytes none_t, // 0 TypePair, // 1 TypePair, // 2 none_t, // 3 TypePair, // 4 none_t, // 5 none_t, // 6 none_t, // 7 TypePair // 8 >; /// Squashes integer types into `[u]int_[8|16|32|64]_t` equivalents. /// /// The squashed type for a type `T` can be obtained as \code /// typename SquashedInt::Type /// \endcode /// /// \tparam T the integer type to squash /// /// \pre `T` is an integral type:\code /// std::is_integral::value /// \endcode template struct SquashedInt { STATIC_ASSERT((std::is_integral::value), "squashing a non-integral type"); using TPair = typename TypeListAt::Type; using Type = typename std::conditional::value, typename TPair::First, typename TPair::Second>::type; }; /// Convenience alias for obtaining a squashed integer type. template using squashed_int_t = typename SquashedInt::Type; /// \defgroup SquashedType /// \brief Squashes a type. /// /// The squashed type for a type `T` can be obtained as \code /// typename SquashedType::Type /// \endcode /// The used type `Type` is squashed with `SquashedInt` if `T` is integral, /// and remains `T` otherwise. ///@{ /// Definition for the general case, used when `T` is not integral. /// /// \tparam T the type to squash /// \tparam IsIntegral Always use the default value! template ::value> struct SquashedType { using Type = T; }; /// Specialization for the case when `T` is integral. /// /// \tparam T the type to squash template struct SquashedType { using Type = squashed_int_t; }; ///@} /// Convenience alias for obtaining a squashed type. template using squashed_t = typename SquashedType::Type; /// \defgroup SquashedTypeList /// \brief Squashes a `rosa::TypeList` elementwise. /// /// Replaces all types in a `rosa::TypeList` with their corresponding squashed /// types by using `rosa::SquashedType`. The squashed `rosa::TypeList` /// corresponding to `List` can be obtained as \code /// typename SquashedTypeList::Type /// \endcode ///@{ /// Declaration of the template. /// /// \tparam List `TypeList` to squash template struct SquashedTypeList; // Specialization for the case of `rosa::EmptyTypeList`. template <> struct SquashedTypeList { using Type = EmptyTypeList; }; /// Specialization for non-empty `rosa::TypeList`. template struct SquashedTypeList> { using Type = typename TypeListPush< squashed_t, typename SquashedTypeList>::Type>::Type; }; ///@} } // End namespace rosa #endif // ROSA_SUPPORT_SQUASHED_INT_HPP diff --git a/include/rosa/support/terminal_colors.h b/include/rosa/support/terminal_colors.h index 3deda36..aa23501 100644 --- a/include/rosa/support/terminal_colors.h +++ b/include/rosa/support/terminal_colors.h @@ -1,65 +1,69 @@ -/***************************************************************************//** - * - * \file rosa/support/terminal_colors.h - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief Facility for printing colorized text to terminals supporting it. - * - ******************************************************************************/ +//===-- rosa/support/terminal_colors.h --------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/support/terminal_colors.h +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2017 +/// +/// \brief Facility for printing colorized text to terminals supporting it. +/// +//===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_TERMINAL_COLORS_H #define ROSA_SUPPORT_TERMINAL_COLORS_H #include namespace rosa { /// Encloses entities related to terminal I/O. namespace terminal { /// Text colors for colorizable terminals. enum class Color { Default, Black, Red, Green, Yellow, Blue, Magenta, Cyan, Lightgrey, Darkgrey, Lightred, Lightgreen, Lightyellow, Lightblue, LightMagenta, Lightcyan, White, NumColors ///< Number of `rosa::terminal::Color` values }; /// Handles `rosa::terminal::Color` values sent to output streams. /// /// The operator sends terminal commands through `os` to the /// associated terminal to change text color to `color`. /// /// \note If `os` is not a terminal output, the terminal commands simply appear /// as text in the stream. /// /// \param [in,out] os `ostream` to apply `color` to /// \param color `Color` to apply for `os` /// /// \pre `color` is valid:\code /// color != Color::NumColors /// \endcoed std::ostream &operator<<(std::ostream &os, const Color color); } // End namespace terminal } // End namespace rosa #endif // ROSA_SUPPORT_TERMINAL_COLORS_H diff --git a/include/rosa/support/type_helper.hpp b/include/rosa/support/type_helper.hpp index 2a9d4eb..2e4e8a7 100644 --- a/include/rosa/support/type_helper.hpp +++ b/include/rosa/support/type_helper.hpp @@ -1,139 +1,143 @@ -/***************************************************************************//** - * - * \file rosa/support/type_helper.hpp - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief Helper facilities for type-related stuff. - * - ******************************************************************************/ +//===-- rosa/support/type_helper.hpp ----------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/support/type_helper.hpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2017 +/// +/// \brief Helper facilities for type-related stuff. +/// +//===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_TYPE_HELPER_HPP #define ROSA_SUPPORT_TYPE_HELPER_HPP #include #include namespace rosa { /* ************************************************************************** * * Printable * * ************************************************************************** */ /// \defgroup PrintableType /// /// A value of type `[u]int8_t` is treated as a character when being put to an /// output stream, which can result in invisible characters being printed. To /// avoid that, such a value needs to be casted to a wider type. It can be done /// by using the following template to find a target type to cast our value to. /// The template also turns enumerations into their underlying types. Moreover, /// any reference type is turned into the referred type and any non-const type /// is turned into their const-qualified type. /// /// \note It is important to remove references before checking /// const-qualification because constant references are not const-qualified /// types. /// /// The corresponding printable type for a type `T` can be obtained as \code /// typename PrintableType::Type /// \endcode ///@{ /// Definition for the general case /// /// \tparam T type to cast /// \tparam IsReference always use the default value! /// \tparam IsConst always use the default value! /// \tparam IsEnum always use the default value! template ::value, bool IsConst = std::is_const::value, bool IsEnum = std::is_enum::value> struct PrintableType { using Type = T; }; /// Specialization for reference types. template struct PrintableType { using Type = typename PrintableType::type>::Type; }; /// Specialization for non-reference, non-const types. template struct PrintableType { using Type = typename PrintableType::Type; }; /// Specialization for non-reference, const, enum types. template struct PrintableType { using Type = typename PrintableType::type>::Type; }; /// Specialization for `const uint8_t`. template <> struct PrintableType { using Type = const unsigned int; }; /// Specialization for `const int8_t`. template <> struct PrintableType { using Type = const int; }; ///@} /// Convenience template alias for using `rosa::PrintableType`. template using printable_t = typename PrintableType::Type; /// Casts values to their corresponding printable types. /// /// \param V value to cast #define PRINTABLE(V) static_cast>(V) /* ************************************************************************** * * Unsigned * * ************************************************************************** */ /// \defgroup Unsigned /// \brief Converts integral types to their corresponding unsigned type. /// /// Provides the unsigned integer type corresponding to `T` if `T` is an /// integral (except bool) or enumeration type. Keeps `T` otherwise. /// /// The corresponding unsigned type for a type `T` can be obtained as \code /// typename Unsigned::Type /// \endcode ///@{ /// Definition for the general case, used when `T` is not integral. /// /// \tparam T type to convert /// \tparam IsIntegral always use the default value! template ::value> struct Unsigned { using Type = T; }; /// Specialization for the case when `T` is integral. template struct Unsigned { using Type = typename std::make_unsigned::type; }; ///@} /// Convenience template alias for using `rosa::Unsigned`. template using unsigned_t = typename Unsigned::Type; } // End namespace rosa #endif // ROSA_SUPPORT_TYPE_HELPER_HPP diff --git a/include/rosa/support/type_list.hpp b/include/rosa/support/type_list.hpp index 2553692..20d0e85 100644 --- a/include/rosa/support/type_list.hpp +++ b/include/rosa/support/type_list.hpp @@ -1,433 +1,437 @@ -/***************************************************************************//** - * - * \file rosa/support/type_list.hpp - * - * \author David Juhasz (david.juhasz@tuwien.ac.at) - * - * \date 2017 - * - * \brief Facilities for types representing lists of types. - * - * \note This implementation is partially based on the `type_list` - * implementation of CAF. - * \todo Check license. - * - ******************************************************************************/ +//===-- rosa/support/type_list.hpp ------------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/support/type_list.hpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2017 +/// +/// \brief Facilities for types representing lists of types. +/// +/// \note This implementation is partially based on the `type_list` +/// implementation of CAF. +/// \todo Check license. +/// +//===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_TYPE_LIST_HPP #define ROSA_SUPPORT_TYPE_LIST_HPP #include "rosa/support/debug.hpp" #include "rosa/support/types.hpp" #include namespace rosa { /// A list of types. /// /// \tparam Ts types to make a list of template struct TypeList { /// Ctor, needs to do nothing. constexpr TypeList(void) {} }; /// The empty `rosa::Typelist`. using EmptyTypeList = TypeList<>; /// \defgroup TypeListAtImpl /// \brief Gets the type at index `Pos` from a list of types. /// /// \note Only to be used by the implementation of `rosa::TypeListAt`. ///@{ /// Declaration of the template. /// /// \tparam Pos index to take the element from /// \tparam Ts types template struct TypeListAtImpl; /// Definition for the general case when `Pos` is not `0` and there is type in /// the list. template struct TypeListAtImpl { using Type = typename TypeListAtImpl::Type; }; /// Specialization for the case when `Pos` is `0`˙. template struct TypeListAtImpl<0, T, Ts...> { using Type = T; }; /// Specialization for the case when there is no more type. /// /// In this case, the found type is `rosa::none_t`. template struct TypeListAtImpl { using Type = none_t; }; ///@} /// \defgroup TypeListAt /// \brief Gets the element at index `Pos` of `List`. /// /// /// The type at index `Pos` in a `rosa::TypeList` `List` can be obtained as /// \code /// typename TypeListAt::Type /// \endcode /// /// \note The resulting type is `rosa::none_t` if \code /// TypeListSize::Value < Pos /// \endcode ///@{ /// Declaration of the template. /// /// \tparam List `TypeList` to take an element from /// \tparam Pos index to take the element from template struct TypeListAt; /// Implementation using `rosa::TypeListAtImpl`. template struct TypeListAt, Pos> { using Type = typename TypeListAtImpl::Type; }; ///@} /// \defgroup TypeListIndexOfImpl /// \brief Tells the index of the first occurence of a type in a list of types. /// /// \note Only to be used by the implementation of `rosa::TypeListIndexOf`. ///@{ /// Declaration of the template. /// /// \tparam Pos the number types already being checked from the beginning of the /// list /// \tparam X type to search for /// \tparam Ts remaining list of types template struct TypeListIndexOfImpl; /// Specialization for the case when the list is over. /// /// In this case, the found index is `-1`. template struct TypeListIndexOfImpl { static constexpr int Value = -1; }; /// Specialization for the case when the first type in the remaining list /// is a match. template struct TypeListIndexOfImpl { static constexpr int Value = Pos; }; /// Implementation for the general case when need to continue looking. template struct TypeListIndexOfImpl { static constexpr int Value = TypeListIndexOfImpl::Value; }; ///@} /// \defgroup TypeListIndexOf /// \brief Tells the index of the first occurence of type in a `rosa::TypeList`. /// /// The index of the first occurence of type `T` in `rosa::TypeList` `List` can /// be obtained as \code /// TypeListIndexOf::Value /// \endcode /// /// \note The resulting index is `-1` if `T` is not present in `List`. ///@{ /// Declaration of the template. /// /// \tparam List `TypeList` to search in /// \tparam T type to search for template struct TypeListIndexOf; /// Implementation of the template using `rosa::TypeListIndexOfImpl`. template struct TypeListIndexOf, T> { static constexpr int Value = TypeListIndexOfImpl<0, T, Ts...>::Value; }; ///@} /// \defgroup TypeListHead /// \brief Gets the first element of a `rosa::TypeList`. /// /// The first element of a `rosa::TypeList` `List` can be obtained as \code /// typename TypeListHead::Type /// \endcode /// /// \note The resulting type is `rosa::none_t` if `List` is the /// `rosa::EmptyTypeList`. ///@{ /// Declaration of the template. /// /// \tparam List `TypeList` to get the first element of template struct TypeListHead; /// Specialization for `rosa::EmptyTypeList`. /// /// In this case, the found type is `rosa::none_t`. template <> struct TypeListHead { using Type = none_t; }; /// Implementation for a non-empty `rosa::TypeList`. template struct TypeListHead> { using Type = T; }; ///@} /// \defgroup TypeListTail /// /// \brief Gets the tail of a `rosa::TypeList`. /// /// The tail of a `rosa::TypeList` `List`, that is `List` except for its first /// element, can be obtained as \code /// typename TypeListTail::Type /// \endcode /// /// \note If `List` is the `rosa::EmptyTypeList`, then the resulting type is /// also `rosa::EmptyTypeList`. ///@{ /// Declaration of the template. /// /// \tparam List `TypeList` to take the tail of template struct TypeListTail; /// Specialization for the `rosa::EmptyTypeList`. /// /// In this case, the resulting type is `rosa::EmptyTypeList`. template <> struct TypeListTail { using Type = EmptyTypeList; }; /// Implementation for a non-empty `rosa::TypeList`. template struct TypeListTail> { using Type = TypeList; }; ///@} /// \defgroup TypeListPush /// \brief Extends a `rosa::TypeList` with a type. /// /// Whether the new type is pushed in the front or in the back of the /// `rosa::TypeList` depends on the order of template arguments, as shown in the /// following example: \code /// using List = TypeList /// typename TypeListPush::Type; // TypeList /// typename TypeListPush::Type; // TypeList /// \endcode ///@{ /// Declaration of the template. /// /// \tparam P a type if `Q` is a `rosa::TypeList`, a `rosa::TypeList` otherwise /// \tparam Q a type if `P` is a `rosa::TypeList`, a `rosa::TypeList` otherwise template struct TypeListPush; /// Implementation for the case when pushing at the back of the /// `rosa::TypeList`. template struct TypeListPush, T> { using Type = TypeList; }; /// Implementation for the case when pushing to the front of the /// `rosa::TypeList`. template struct TypeListPush> { using Type = TypeList; }; ///@} /// \defgroup TypeListDrop /// \brief Drops some elements from the beginning of a `rosa::TypeList`. /// /// The first `N` types of a `rosa::TypeList` `List` can be dropped as \code /// typename TypeListDrop::Type /// \endcode ///@{ /// Declaration of the template. /// /// \tparam N number of types to drop /// \tparam List `TypeList` to drop the first `N` element of template struct TypeListDrop; /// Specialization for the `rosa::EmptyTypeList`. template struct TypeListDrop { using Type = EmptyTypeList; }; /// Implementation for a non-empty `rosa::TypeList`. template struct TypeListDrop> { using Type = typename std::conditional< N == 0, TypeList, typename TypeListDrop>::Type>::type; }; ///@} /// \defgroup TypeListSize /// \brief Tells the number of types stored in a `rosa::TypeList`. /// /// The size of a `rosa::TypeList` `List` can be obtained as \code /// TypeListSize::Value /// \endcode ///@{ /// Declaration of the template. /// /// \tparam List `TypeList` to get the size of template struct TypeListSize; /// Implementation of the template. template struct TypeListSize> { static constexpr size_t Value = sizeof...(Ts); }; template constexpr size_t TypeListSize>::Value; ///@} /// Tests whether a `rosa::TypeList` is empty. /// /// \tparam List the `TypeList` to check template struct TypeListEmpty { /// Denotes whether `List` is an empty `rosa::TypeList` or not. static constexpr bool Value = std::is_same::value; }; /// \defgroup TypeListContains /// \brief Tells if a `rosa::TypeList` contains a type. /// /// Whether a `rosa::TypeList` `List` contains the type `T` can be checked as /// \code /// TypeListContains::Value /// \endcode ///@{ /// Declaration of the template. /// /// \tparam List `TypeList` to search in /// \tparam T type to search for template struct TypeListContains; /// Implementation of the template. template struct TypeListContains, T> { static constexpr bool Value = std::conditional, T>::Value == -1, std::false_type, std::true_type>::type::value; }; ///@} /// \defgroup TypeListSubsetOf /// \brief Tells if a `rosa::TypeList` is a subset of another one. /// /// Whether a `rosa::TypeList` `ListA` is a subset of another `rosa::TypeList` /// `ListB` can be checked as \code /// TypeListSubsetOf::Value /// \endcode ///@{ /// Declaration of the template. /// /// \tparam ListA `TypeList` to check if is a subset of `ListB` /// \tparam ListB `TypeList` to check if is a superset of `ListA` /// \tparam Fwd always use the default value! template struct TypeListSubsetOf; /// Specialization for the case when all the elements of the original `ListA` /// was found in `ListB`. template struct TypeListSubsetOf { static constexpr bool Value = true; }; /// Specializaton for the case when an element of the original `ListA` cannot be /// found in `ListB`. template struct TypeListSubsetOf { static constexpr bool Value = false; }; /// Definition for the general case. template struct TypeListSubsetOf, List> : TypeListSubsetOf, List, TypeListContains::Value> {}; ///@} /// \defgroup TypeListFindImpl /// \brief Finds the first type in a list of types that satisfies a predicate. /// /// \note Only to be used by the implementation of `rosa::TypeListFind`. ///@{ /// Declaration of the template. /// /// \tparam Pred the predicate to check types against /// \tparam Ts list of types to check template