diff --git a/examples/agent-modules/agent-modules.cpp b/examples/agent-modules/agent-modules.cpp index fc0195d..d50ad83 100644 --- a/examples/agent-modules/agent-modules.cpp +++ b/examples/agent-modules/agent-modules.cpp @@ -1,112 +1,111 @@ //===-- 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 \c rosa::Agent instances using /// \c 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 \c rosa::MessagingSystem. /// /// \note Since we test \c rosa::MessagingSystem directly here, we need to get /// access to its protected members. That we do by imitating to be a decent /// subclass of \c 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 \c 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 \p V to the \c rosa::agent::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 d405a5f..4adc37e 100644 --- a/examples/basic-system/basic-system.cpp +++ b/examples/basic-system/basic-system.cpp @@ -1,73 +1,72 @@ //===-- 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 \c rosa::System and \c rosa::Unit /// classes. /// //===----------------------------------------------------------------------===// #include "rosa/config/version.h" -#include "rosa/core/Unit.h" #include "rosa/core/System.hpp" +#include "rosa/core/Unit.h" #include "rosa/support/log.h" #include "rosa/support/terminal_colors.h" #include using namespace rosa; using namespace rosa::terminal; /// A dummy wrapper for testing \c rosa::System. /// /// \note Since we test \c rosa::System directly here, we need to get access to /// its protected members. That we do by imitating to be a decent subclass of /// \c rosa::System, while calling protected member functions on an object of a /// type from which we actually don't inherit. struct SystemTester : protected System { static constexpr AtomValue UnitKind = atom("unit"); static Unit &createMyUnit(System *S, const std::string &Name = std::string()) { return ((SystemTester *)S) ->createUnit([&Name](const rosa::id_t Id, System &S) noexcept { 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 a26b7cb..d3843dd 100644 --- a/examples/messaging-system/messaging-system.cpp +++ b/examples/messaging-system/messaging-system.cpp @@ -1,148 +1,146 @@ //===-- 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 \c rosa::MessagingSystem and \c 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 \c rosa::MessagingSystem. /// /// \note Since we test \c rosa::MessagingSystem directly here, we need to get /// access to its protected members. That we do by imitating to be a decent /// subclass of \c 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 \c 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 7d83013..e40e55e 100644 --- a/examples/messaging/messaging.cpp +++ b/examples/messaging/messaging.cpp @@ -1,146 +1,146 @@ //===-- 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 \c 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; + 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 + 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; + "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 b3ce553..731c276 100644 --- a/examples/type-facilities/type-facilities.cpp +++ b/examples/type-facilities/type-facilities.cpp @@ -1,102 +1,100 @@ //===-- 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; + 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; // \c rosa::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 \c T_. 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 c31d0f9..3b72448 100644 --- a/include/rosa/agent/Abstraction.hpp +++ b/include/rosa/agent/Abstraction.hpp @@ -1,194 +1,191 @@ //===-- 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 \p this object. ~Abstraction(void) = default; /// Abstracts a value from type \p T to type \p A. /// /// \note The default implementation always returns /// \c rosa::agent::Abstraction::Default, hence the actual argument is /// ignored. /// /// \return the abstracted value virtual A operator()(const T &) const noexcept { return Default; } }; /// Implements \c rosa::agent::Abstraction as a \c 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 \c 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 \p this object. ~MapAbstraction(void) = default; /// Abstracts a value from type \p T to type \p A based on the set mapping. /// /// Results in the value associated by the set mapping to the argument, or /// \c 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 \c rosa::agent::Abstraction as a \c 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 \c std::map define valid ranges /// such that `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 \c 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 such that `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 \c 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 \p this object. ~RangeAbstraction(void) = default; /// Abstracts a value from type \p T to type \p A based on the set mapping. /// /// Results in the value associated by the set mapping to the argument, or /// \c 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 \c I refers to a matching range. + bool Found = false; // Indicates if \c 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 \p 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 dc59e3a..960c712 100644 --- a/include/rosa/agent/Confidence.hpp +++ b/include/rosa/agent/Confidence.hpp @@ -1,207 +1,204 @@ //===-- 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 \c 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 \p T. using UT = unsigned_t; /// The minimal value of type \p T. /// \note Not exist \c V of type \p 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 \p T. /// \note Not exist \c V of type \p 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 \c UT. /// \note Not exist \c V of type \c 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 \p this object. ~Confidence(void) = default; /// Gives a snapshot of the current state of the validator variables. /// /// \param [out] LowerBound to copy \c rosa::agent::Confidence::LowerBound /// into /// \param [out] UpperBound to copy \c rosa::agent::Confidence::UpperBound /// into /// \param [out] ChangeRate to copy \c 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 \c V is within the range defined by /// \c rosa::agent::Confidence::LowerBound and /// \c rosa::agent::Confidence::UpperBound. bool operator()(const T V) const noexcept { // Return if \c V is plausible. return LowerBound <= V && V < UpperBound; } /// Tells the binary confidence on the plausibility and consistency of the /// last value recorded by a \c rosa::agent::History instance. /// /// Consistency of the last value is checked by validating the difference with /// its preceding entry. /// /// \note The \c rosa::agent::History instance needs to store values of type /// \p T. /// /// \note An empty \c rosa::agent::History instance results in full /// confidence. /// /// \tparam N number of values \p H is able to store /// \tparam P retention policy followed by \p 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 e3279ac..9c7c0ea 100644 --- a/include/rosa/agent/History.hpp +++ b/include/rosa/agent/History.hpp @@ -1,296 +1,292 @@ //===-- 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 \c 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 \c rosa::agent::Module is an internal component of a /// \c 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 \c 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 \p this object. ~History(void) = default; /// Tells the retention policy applied to \p this object. /// /// \return \c rosa::agent::History::P static constexpr HistoryPolicy policyOfHistory(void) noexcept { return P; } /// Tells how many entries may be recorded by \c this object. /// /// \note The number of entries that are actually recorded may be smaller. /// /// \return \c rosa::agent::History::N static constexpr size_t lengthOfHistory(void) noexcept { return N; } /// Tells how many entries are currently recorded by \p this object. /// /// \return number of entries currently recorded by \p this object. /// /// \post The returned value cannot be larger than the capacity of \p 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 \p this object has not recorded anything yet. /// /// \return if \p this object has no entries recorded - inline bool empty(void) const noexcept { - return numberOfEntries() == 0; - } + inline bool empty(void) const noexcept { return numberOfEntries() == 0; } /// Gives a constant lvalue reference to an entry stored in \p 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 \p 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 { + 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 \p 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 \p 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. + // \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 \p 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 \p this object. /// /// \note The function is made a template only to be able to use /// \c 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, \p this object stores signed arithmetic values:\code /// std::is_arithmetic::value && std::is_signed::value /// \endcode Dynamically, \p 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 \p T } else { // Here at least two entries. // \c 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 \p 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 \p this object. /// /// \note The function is made a template only to be able to use /// \c std::enable_if. /// /// \tparam X always use the default! /// /// \param D number of steps to go back in *history* /// /// \pre Statically, \p this object stores arithmetic values:\code /// std::is_arithmetic::value /// \endcode Dynamically, \p 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 \p T } else { // Here at least two entries. // \c 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 \c rosa::agent::History instance. /// /// \note The result of \c rosa::agent::History::addEntry is ignored. /// /// \tparam T type of values stored in \p H /// \tparam N number of values \p H is able to store /// \tparam P retention policy followed by \p H when capacity is reached /// /// \param H to add a new entry to /// \param V value to add to \p H /// /// \return \p H after adding \p 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 f2a949a..7961bfe 100644 --- a/include/rosa/agent/Module.h +++ b/include/rosa/agent/Module.h @@ -1,35 +1,34 @@ //===-- 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 66b467c..89794fa 100644 --- a/include/rosa/config/config.h +++ b/include/rosa/config/config.h @@ -1,73 +1,72 @@ //===-- 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 2aea3a6..c7f12f3 100755 --- a/include/rosa/config/namespaces.h +++ b/include/rosa/config/namespaces.h @@ -1,31 +1,30 @@ //===-- 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 91a6629..b4849ae 100644 --- a/include/rosa/config/project_path.hpp +++ b/include/rosa/config/project_path.hpp @@ -1,67 +1,66 @@ //===-- 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 part relative to \c project in \p path; \c 0 if \p path /// is not under \p 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 \p path; \c 0 if \p path is /// not under the RoSA siource directory. -constexpr size_t project_relative_path_index(const char * const path) { +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 8a60994..933d260 100644 --- a/include/rosa/config/rosa_config.h.cmake +++ b/include/rosa/config/rosa_config.h.cmake @@ -1,54 +1,57 @@ //===-- 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 +// clang-format off + #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 +// clang-format on +#endif // ROSA_CONFIG_ROSA_CONFIG_H diff --git a/include/rosa/config/version.h b/include/rosa/config/version.h index 6e6dca3..3583d92 100644 --- a/include/rosa/config/version.h +++ b/include/rosa/config/version.h @@ -1,38 +1,37 @@ //===-- 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 96e754a..4ddadfe 100644 --- a/include/rosa/core/AbstractAgent.hpp +++ b/include/rosa/core/AbstractAgent.hpp @@ -1,140 +1,139 @@ //===-- rosa/core/AbstractAgent.hpp -----------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/core/AbstractAgent.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Declaration of an abstract interface for *Agents*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_ABSTRACTAGENT_HPP #define ROSA_CORE_ABSTRACTAGENT_HPP #include "rosa/core/Message.hpp" #include "rosa/core/forward_declarations.h" #include "rosa/support/debug.hpp" #include namespace rosa { /// Abstract class declaring an interface for *Agents*. /// /// \tparam Ref type of the derived class implementing \c rosa::AbstractAgent /// for referencing \p this object /// /// \note \p Ref is reference for \c rosa::AbstractAgent, whose actual value /// must be a class derived from \c rosa::AbstractAgent. /// /// \note It can be statically checked if \p Ref is derived from /// \c rosa::AbstractAgent, but the static assertion cannot be defined /// directly in the class body. That is because a class \c C derived from /// \c rosa::AbstractAgent is not complete when the static assertion in the /// definition of \c rosa::AbstractAgent would be evaluated. Thus, the static /// assertion is placed in the constructor of \c rosa::AbstractAgent. template class AbstractAgent { protected: /// Creates a new instance of \c rosa::AbstractAgent. /// /// \note The constructor is protected, thus restricting class instantiation /// for derived classes only. /// /// \pre \p Ref is derived from \c rosa::AbstractAgent:\code /// std::is_base_of, Ref>::value /// \endcode AbstractAgent(void) noexcept; public: /// Destroys \p this object. virtual ~AbstractAgent(void) = default; /// Tells if \p this object is in a valid state. /// /// \return if \p this object is in a valid state virtual operator bool(void) const noexcept = 0; /// Tells if a given reference refers to \p this object. /// /// \param R reference to another object /// /// \return if \p R refers to \p this object virtual bool operator==(const Ref &R) const noexcept = 0; /// Returns a reference to \p this object. /// /// \return a reference to \p this object virtual Ref self(void) noexcept = 0; /// Sends a \c rosa::message_t instance to \p this object. /// /// \param M message to send /// /// \pre \p this object is in a valid state:\code /// bool(*this) /// \endcode virtual void sendMessage(message_t &&M) noexcept = 0; /// Sends a message -- created from given constant lvalue references -- to /// \p this object. /// /// \note The message must consists of at least one value. /// /// \tparam Type type of the first mandatory value /// \tparam Types types of any further values /// /// \param T the first value to include in the message /// \param Ts optional further values to include in the message /// /// \pre \p this object is in a valid state:\code /// bool(*this) /// \endcode template void send(const Type &T, const Types &... Ts) noexcept; /// Sends a message -- created from given rvalue references -- to \p this /// object. /// /// \note The message must consists of at least one value. /// /// \tparam Type type of the first mandatory value /// \tparam Types types of any further values /// /// \param T the first value to include in the message /// \param Ts optional further values to include in the message /// /// \pre \p this object is in a valid state:\code /// bool(*this) /// \endcode template void send(Type &&T, Types &&... Ts) noexcept; }; template AbstractAgent::AbstractAgent(void) noexcept { STATIC_ASSERT((std::is_base_of, Ref>::value), "not derived Agent"); // Sanity check. } template template void AbstractAgent::send(const Type &T, const Types &... Ts) noexcept { sendMessage(Message::create(T, Ts...)); } template template void AbstractAgent::send(Type &&T, Types &&... Ts) noexcept { sendMessage(Message::create(std::move(T), std::move(Ts)...)); } } // End namespace rosa #endif // ROSA_CORE_ABSTRACTAGENT_HPP - diff --git a/include/rosa/core/Agent.hpp b/include/rosa/core/Agent.hpp index 457e1f7..eebe905 100644 --- a/include/rosa/core/Agent.hpp +++ b/include/rosa/core/Agent.hpp @@ -1,113 +1,112 @@ //===-- rosa/core/Agent.hpp -------------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/core/Agent.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Declaration of the base \c rosa::Agent class. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_AGENT_HPP #define ROSA_CORE_AGENT_HPP #include "rosa/core/AgentHandle.hpp" #include "rosa/core/MessageHandler.hpp" #include "rosa/core/MessagingSystem.hpp" #include "rosa/core/Unit.h" #include "rosa/support/log.h" namespace rosa { /// Implements an *Agent* that is a special \c rosa::Unit owned by a /// \c rosa::MessagingSystem, capable of handling \c rosa::Message instances /// as \c rosa::MessageHandler, and provides the \c rosa::AbstractAgent /// interface with \c rosa::AgentHandle as reference type. class Agent : public Unit, public MessageHandler, public AbstractAgent { friend class AgentHandle; ///< \c rosa::AgentHandle is our friend. protected: /// A handle for \p this object. const AgentHandle Self; public: /// Creates a new instance by instantiating all the base-classes. /// /// \tparam Fun type of the first mandatory function for handling messages /// \tparam Funs types of any further functions for handling messages /// /// \param Kind kind of the new \c rosa::Unit instance /// \param Id unique identifier of the new \c rosa::Unit instance /// \param Name name of the new \c rosa::Unit instance /// \param S \c rosa::MessagingSystem owning the new instance /// \param F the first mandatory function for handling messages /// \param Fs optional further functions for handling messages template Agent(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, Fun &&F, Funs &&... Fs); /// Destroys \p this object. ~Agent(void); /// Tells if \p this object is in a valid state. /// /// \note A \c rosa::Agent instance is always valid. /// /// \return if \p this object is in a valid state operator bool(void) const noexcept override; /// Tells if a given reference refers to \p this object. /// /// \param H reference to another object /// /// \return if \p H refers to \p this object bool operator==(const AgentHandle &H) const noexcept override; /// Returns a reference to \p this object. /// /// \return a reference to \p this object AgentHandle self(void) noexcept override; /// Sends a given \c rosa::message_t instance to \p this object. /// /// \param M message to send /// /// \note Since a \c rosa::Agent instance is always valid, there is no /// precondition for this function. /// \see \c rosa::AbstractAgent::sendMessage and /// `rosa::Agent::operator bool() const` void sendMessage(message_t &&M) noexcept override; /// Dumps \p this object into a \c std::string for tracing purposes. /// /// \return \c std::string representing the state of \p this object std::string dump(void) const noexcept override; protected: /// Returns a reference to the \c rosa::MessagingSystem owning \p this object. /// /// \return reference of \c rosa::Unit::S MessagingSystem &system(void) const noexcept override; }; 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 8aa7ffe..6e0f4b4 100644 --- a/include/rosa/core/AgentHandle.hpp +++ b/include/rosa/core/AgentHandle.hpp @@ -1,110 +1,108 @@ //===-- rosa/core/AgentHandle.hpp -------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/core/AgentHandle.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Declaration of a handle for \c rosa::Agent. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_AGENTHANDLE_HPP #define ROSA_CORE_AGENTHANDLE_HPP #include "rosa/core/AbstractAgent.hpp" namespace rosa { /// Wraps an actual \c rosa::Agent to decouple its public interface. /// \note Such decoupling might be necessary when operating with remote /// *systems*, sometime in the future. class AgentHandle : public AbstractAgent { /// \c rosa::Agent and \c rosa::MessagingSystem are our friends, they may /// inspect the private member fields of the class. ///@{ friend class Agent; friend class MessagingSystem; ///@} /// The wrapped \c rosa::Agent instance. Agent &A; /// The \c rosa::MessagingSystem owning \c A. MessagingSystem &S; /// Creates a new instance without validating the state of the wrapped /// \c rosa::Agent. /// /// \note Used by a \c rosa::Agent instance to create a reference to itself /// during construction, when its state is not valid yet. /// /// \param A \c rosa::Agent to wrap /// /// \note There a second argument, which is ignored, that is only present to /// separate this constructor from the public constructor taking only a /// \c rosa::Agent to wrap. AgentHandle(Agent &A, bool); public: /// Creates a new instance validating the state of the wrapped \p rosa::Agent. /// /// \note The wrapped \c rosa::Agent must be in a valid state to instantiate /// \c rosa::AgentHandle with this constructor. /// /// \param A \c rosa::Agent to wrap /// /// \pre \p A is registered in its owning *system*:\code /// A.system().isUnitRegistered(A) /// \endcode AgentHandle(Agent &A); /// Destroys \p this object. /// /// The destructor has nothing to take care of. ~AgentHandle(void) = default; /// Tells if the wrapped \c rosa::Agent is in a valid state. /// /// \note A \c rosa::AgentHandler belongs to a \c rosa::MessagingSystem. /// Working with a \c rosa::AgentHandler whose originating /// \c rosa::MessagingSystem has already been destroyed results in *undefined* /// behavior. /// /// \return if the wrapped \c rosa::Agent is in a valid state operator bool(void) const noexcept override; /// Tells if another \c rosa::AgentHandle wraps the same \c rosa::Agent as /// \p this object. /// /// \param H \c rosa::AgentHandle whose wrapped \c rosa::Agent to check /// /// \return if \p H wraps \c A like \p this object bool operator==(const AgentHandle &H) const noexcept override; /// Returns a reference to the wrapped \c rosa::Agent. /// /// \return a reference to \c A AgentHandle self(void) noexcept override; /// Sends a given \c rosa::message_t instance to the wrapped \c rosa::Agent. /// /// \param M message to send /// /// \pre The wrapped \c rosa::Agent instance is in a valid state:\code /// bool(*this) /// \endcode void sendMessage(message_t &&M) noexcept override; - }; } // End namespace rosa #endif // ROSA_CORE_AGENTHANDLE_HPP - diff --git a/include/rosa/core/Invoker.hpp b/include/rosa/core/Invoker.hpp index d5703e7..ad6399e 100644 --- a/include/rosa/core/Invoker.hpp +++ b/include/rosa/core/Invoker.hpp @@ -1,274 +1,273 @@ //===-- 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 /// \c rosa::Messageobjects. /// //===----------------------------------------------------------------------===// #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 \c rosa::Message object. /// /// \note A \c rosa::Invoker instance is supposed to be owned by a /// \c 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 \p 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 \c rosa::Invoker. using invoker_t = std::unique_ptr; /// Type alias for \c rosa::Invoker::Result. using result_t = Result; /// Tells if a \c rosa::Message object can be used to invoke the function /// wrapped in \p this object. /// /// \param Msg \c rosa::Message to check /// /// \return whether \p 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 \c rosa::Message object. /// /// The wrapped function is invoked if the actual \c rosa::Message object can /// be used to invoke it. /// /// \param Msg \c rosa::Message to try to invoke the wrapped function with /// /// \return whether the wrapped function could be invoked with \p Msg virtual result_t operator()(const Message &Msg) const noexcept = 0; /// Instantiates an implementation of \c rosa::Invoker with the given /// function. /// /// \note As there is no empty \c rosa::Message, no \c 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 \c rosa::Invoker::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 \c THISMEMBER template static inline F M(C *O, void (C::*Fun)(Ts...) noexcept) noexcept; }; /// Convenience preprocessor macro for the typical use of \c 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 \c rosa::Message. /// /// \param FUN the non-static member function to wrap /// /// \note Inside the class \c 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 \c 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 \c Seq. template struct GenSeq<0, S...> { using Type = Seq; }; ///@} /// \defgroup InvokerImpl /// /// Implements the \c rosa::Invoker interface for functions with different /// signatures. /// ///@{ /// Declaration of \c rosa::InvokerImpl implementing \c rosa::Invoker. /// /// \tparam Fun function to wrap template class InvokerImpl; /// Implementation of \c rosa::InvokerImpl for \c std::function. /// /// \tparam T type of the first mandatory argument /// \tparam Ts types of further arguments /// /// \note As there is no empty \c rosa::Message, no \c rosa::Invoker wraps a /// function without any argument, i.e., no /// \c 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 /// \c rosa::Message. using args_t = std::tuple; /// Alias for \c rosa::MessageMatcher for the arguments of the stored /// function. using Matcher = MsgMatcher; /// The wrapped function. const function_t F; /// Invokes \c InvokerImpl::F by unpacking arguments from a \c std::tuple with /// the help of the actual template arguments. /// /// \tparam S sequence of numbers indexing \c std::tuple for arguments /// /// \param Args arguments to invoke \c InvokerImpl::F with /// /// \pre the length of \p S and size of \p 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 \p F is valid:\code /// bool(F) /// \endcode InvokerImpl(function_t &&F) noexcept : F(F) { ASSERT(bool(F)); // Sanity check. } /// Destroys \p this object. ~InvokerImpl(void) = default; /// Tells if a \c rosa::Message object can be used to invoke the function /// wrapped in \p this object. /// /// \param Msg \c rosa::Message to check /// /// \return whether \p 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 \c rosa::Message object. /// /// The wrapped function is invoked if the actual \c rosa::Message object can /// be used to invoke it. /// /// \param Msg \c rosa::Message to try to invoke the wrapped function with /// /// \return whether the wrapped function could be invoked with \p 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 58e2d2a..8e7b66e 100644 --- a/include/rosa/core/Message.hpp +++ b/include/rosa/core/Message.hpp @@ -1,483 +1,482 @@ //===-- 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 \c 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 \c 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 \c rosa::Message instance remains valid /// only as long as the owning \c 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 \c rosa::Message. /// /// \pre \p Type and \p Types are all built-in types and the number of stored /// values does not exceed \c rosa::token::MaxTokenizableListSize. template Message(const Type &, const Types &...) noexcept; /// No copying and moving of \c rosa::Message instances. ///@{ Message(const Message &) = delete; Message(Message &&) = delete; Message &operator=(const Message &) = delete; Message &operator=(Message &&) = delete; ///@} public: /// Creates a \c 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 \c rosa::Message /// \param Ts optional further values to include in the \c rosa::Message /// /// \return new \c rosa::message_t object created from the given arguments template static message_t create(const Type &T, const Types &... Ts) noexcept; /// Creates a \c 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 \c rosa::Message /// \param Ts optional further values to include in the \c rosa::Message /// /// \return new \c rosa::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 \p this object. /// /// A valid, non-empty \c rosa::Token representing the types of the values /// stored in \p this object. const Token T; /// The number of values stored in \p this object. /// /// That is the number of types encoded in \c rosa::Message::T. const size_t Size; /// Destroys \p this object. virtual ~Message(void); /// Tells if the value stored at a given index is of a given type. /// /// \note Any \c rosa::AtomConstant is encoded in \c rosa::Token as /// the \c 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 \p Type /// /// \return if the value at index \p Pos of type \p Type /// /// \pre \p 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 \p Type for the value stored at index \p Pos /// /// \pre \p Pos is a valid index and the value at index \p Pos is of type /// \p 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 \p Pos /// /// \pre \p 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 \c rosa::Message, consider it /// private. namespace { /// Template class for an implementation of \c 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 \p Arena /// /// \note \p Arena needs to be a valid pointer to a memory area big enough for /// values of \p 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 \c 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 \p ArenaË› /// \param Ts optional further values to store in \p Arena /// /// \pre \p Arena is not \p 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 \p Arena /// /// \note \p Arena needs to be a valid pointer to a memory area big enough for /// values of \p 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 \c 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 \p Arena /// \param Ts optional further values to store in \p Arena /// /// \pre \p Arena is not \c 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 \c createMessageElements. /// /// \tparam Type type of the mandatory first value stored in \p Arena /// \tparam Types futher types whose values are stored in \p Arena /// /// \param Arena the memory area to destroy values from /// /// \note \p Arena needs to be a valid pointer to a memory area where values of /// \p 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 \c 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 \p Arena is not \c nullptr. template inline void destroyMessageElements(void *const Arena) noexcept { ASSERT(Arena != nullptr); destroyMessageElement<0, Type, Types...>( Arena, LocalMessage::Offsets); } /// Implementation of the template \c rosa::LocalMessage providing facilities /// for storing values as a \c rosa::Message object. /// /// \tparam Type type of the first mandatory value of the \c rosa::Message /// \tparam Types of any further values template class LocalMessage : public Message { public: /// \c 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 \c rosa::Message::T. static constexpr Token ST = TypeToken::type, typename std::decay::type...>::Value; /// Byte offsets to access stored values in \c LocalMessage::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 /// \c LocalMessage::Arena. /// /// \return \c std::vector containing byte offsets for accessing values stored /// in \c LocalMessage::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 \c T. size_t I = 0; // Start indexing from position \c 0. std::vector O(N); // Allocate vector of proper size. O[0] = 0; // First offset is always \c 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 \c rosa::Message object /// \param Ts optional further values to store in the \c 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 \c rosa::Message object /// \param Ts optional further values to store in the \c 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 \p 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 \p 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 \c 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 /// \c 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 /// \c 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_); + Token TT = T; + dropNOfToken(TT, Pos); + return isHeadOfTokenTheSameType(TT); } 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 452e451..3a19c57 100644 --- a/include/rosa/core/MessageHandler.hpp +++ b/include/rosa/core/MessageHandler.hpp @@ -1,170 +1,169 @@ //===-- 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 \c rosa::Invoker instances and applying /// \c 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 \c rosa::Message instances. /// /// A \c rosa::MessageHandler stores \c rosa::Invoker instances and tries to /// apply \c rosa::Message objects to them in the order of definition.The first /// matching \c rosa::Invoker instance is invoked with the \c rosa::Message /// object, after which handling of that \c 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 \c rosa::Message with \c rosa::TypeList invokes the /// first function, and the second function would never be invoked because any /// matching \c rosa::Message object had already been handled by the first one. class MessageHandler { /// Type alias to bring \c rosa::Invoker::invoker_t to the local scope. using invoker_t = Invoker::invoker_t; /// Type alias for a \c std::vector storing \c rosa::Invoker instances. using invokers_t = std::vector; /// Stores \c rosa::Invoker instances. const invokers_t Invokers; /// Creates a container with \c 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 \c rosa::MessageHandler::invokers_t object storing /// \c rosa::Invoker instances created from the \p F and \p Fs... template static inline invokers_t createInvokers(Fun &&F, Funs &&... Fs) noexcept; /// Updates an \c rosa::MessageHandler::invokers_t object with a new /// \c rosa::Invoker instance and handles further functions recursively. /// /// \tparam Fun type of the first function /// \tparam Funs types of further functions /// /// \param I \c rosa::MessageHandler::invokers_t to update /// \param Pos index at which to store the new \c rosa::Invoker instance /// \param F function to wrap and store into \p I at index \p Pos /// \param Fs further functions to handle later /// /// \pre \p 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 \c rosa::MessageHandler::wrapFun. /// /// \param I \c rosa::MessageHandler::invokers_t which is now complete /// \param Pos size of \p I /// /// \pre \p Pos is the size of \p 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 /// \c 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 + template MessageHandler(Fun &&F, Funs &&... Fs) noexcept; /// Destroys \p this object. virtual ~MessageHandler(void); /// Tells if a \c rosa::Message object can be handled by \p this object. /// /// \param Msg \c rosa::Message to check /// /// \return whether \p this object stores a \c rosa::Invoker instance that can /// handle \p Msg bool canHandle(const Message &Msg) const noexcept; /// Applies a \c rosa::Message object to the first stored \c rosa::Invoker /// that can handle it, and tells if there was any. /// /// \note This operator finds the first applicable \c rosa::Invoker and /// invokes it with the given \c rosa::Message object, while the member /// function \c rosa::MessageHandler::canHandle only checks if there is any /// \c rosa::Invoker that can be invoked with a given \c rosa::Message object. /// /// \param Msg \c rosa::Message to use in invoking a matching \c rosa::Invoker /// /// \return whether there was a matching \c rosa::Invoker found and invoked /// with \p 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 4407418..0b79096 100644 --- a/include/rosa/core/MessageMatcher.hpp +++ b/include/rosa/core/MessageMatcher.hpp @@ -1,203 +1,201 @@ //===-- 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 /// \c 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 \c rosa::Message instance and extract /// stored values from it into an \c std::tuple instance with matching type /// arguments. /// /// \tparam List \c rosa::TypeList to check the stored values against template struct MessageMatcher; /// Definition of \c rosa::MessageMatcher for non-empty lists of types, like /// \c rosa::Message itself. /// /// \tparam Type first mandatory type /// \tparam Types any further types template struct MessageMatcher> { /// \c rosa::Token associated to the given \c rosa::TypeList. static constexpr Token T = TypeToken::Value; /// Tells if the values stored in a \c rosa::Message instance are matching /// types given as \c rosa::TypeList, considering /// \c rosa::AtomConstant instead of \c rosa::AtomValue. /// /// \param Msg \c rosa::Message to match /// /// \return whether the types of values stored in \p Msg matches /// \c rosa::TypeList static inline bool doesStronglyMatch(const Message &Msg) noexcept; /// Gives a \c std::tuple with references to the values stored in a /// type-matching instance of \c rosa::Message. /// /// \param Msg \c rosa::Message to extract values from /// /// \return \c std::tuple with references to the values stored in \p Msg /// /// \pre Types of the values stored in \p Msg matches /// \c rosa::TypeList:\code /// doesStronglyMatch(Msg) /// \endcode static inline std::tuple extractedValues(const Message &Msg) noexcept; }; /// Turns a list of types into a \c rosa::TypeList for \c rosa::MessageMatcher. -template +template using MsgMatcher = MessageMatcher>; /// Nested namespace with implementation for features of /// \c rosa::MessageMatcher, consider it private. namespace { /// \defgroup MessageMatcherImpl /// /// An implementation of type-checking and value extraction for /// \c rosa::MessageMatcher. /// ///@{ /// Template declaration of \c MessageMatcherImpl. /// /// \tparam List \c rosa::TypeList to match against template struct MessageMatcherImpl; /// Specialization for \c 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 \c rosa::AtomValue in the head. template struct MessageMatcherImpl, Ts...>> { static inline bool doesHeadStronglyMatchAt(const Message &Msg, const size_t Pos) noexcept { // Matching a \c rosa::AtomConstant in the head if there is a type stored at // \p Pos, the stored type is \c rosa::AtomValue, and the corresponding // value matches the \c rosa::AtomValue \p 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 \c rosa::AtomConstant in // the head by getting the encoded \c rosa::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 /// \c 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 \p Pos, and the stored // type is \p 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 \c rosa::MessageMatcher::T, then match against list // with squashed integers the way \c 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 \c 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 62d0920..b0bab8b 100644 --- a/include/rosa/core/MessagingSystem.hpp +++ b/include/rosa/core/MessagingSystem.hpp @@ -1,189 +1,188 @@ //===-- 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 \c 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 \c rosa::System interface with features to create \c rosa::Agent /// instancess and register \c rosa::Message objects for them. class MessagingSystem : public System { friend class AgentHandle; ///< \c rosa::AgentHandle is our friend. public: /// Returns an object implementing the \c rosa::MessagingSystem interface. /// /// \param Name name of the new instance /// /// \return \c std::unique_ptr for the new instance of /// \c rosa::MessagingSystem static std::unique_ptr createSystem(const std::string &Name) noexcept; private: /// Kind for categorizing \c 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 \c rosa::Agent instance owned by \p this object and returns a /// \c rosa::AgentHandle for it. /// /// \tparam T type of the actual \c rosa::Agent to instantiate /// \tparam Funs types of the functions to instantiate \c rosa::Agent with /// /// \note \c 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 \p Funs for this template function. /// /// \param Name name of the new \c rosa::Unit instance /// \param Fs functions to instantiate \c rosa::Unit with /// /// \pre Statically, \p T is a subclass of \c rosa::Agent:\code /// std::is_base_of::value /// \endcode template AgentHandle createAgent(const std::string &Name, Funs &&... Fs); /// Gives the references \c rosa::Agent instance for a \c rosa::AgentHandle. /// /// \note Intended for derived classes to be able to inspect /// \c rosa::AgentHandle instances. /// /// \param H \c rosa::AgentHandle to take the referenced \c rosa::Agent from /// /// \return reference to the \c rosa::Agent instance from \p H static inline Agent &unwrapAgent(const AgentHandle &H) noexcept { return H.A; } /// Gives the owning \c rosa::MessagingSystem of a \c rosa::Agent instance /// for a \c rosa::AgentHandle. /// /// \note Intended for for derived classes to be able to inspect /// \c rosa::AgentHandle instances. /// /// \param H \c rosa::AgentHandle to take the owning /// \c rosa::MessagingSystem from /// /// \return reference to the \c rosa::MessagingSystem owning the /// \c rosa::Agent instance from \p H static inline MessagingSystem &unwrapSystem(const AgentHandle &H) noexcept { return H.S; } public: /// Sends a \c rosa::message_t instance to the \c rosa::Agent instance /// referred by a \c rosa::AgentHandle. /// /// \note If the given \c rosa::Message object cannot be handled by the /// referred \c rosa::Agent instance, the \c rosa::Message object is simply /// ignored. /// /// \param H refers to the \c rosa::Agent instance to send to /// \param M message to send /// /// \pre The referred \c rosa::Agent instance is owned by \p this object and /// also registered: \code /// &unwrapSystem(H) == this && isUnitRegistered(unwrapAgent(H)) /// \endcode virtual void send(const AgentHandle &H, message_t &&M) noexcept = 0; /// Sends a message -- created from given constant lvalue references -- /// to the \c rosa::Agent instance referred by a \c rosa::AgentHandle. /// /// \note If the given \c rosa::Message object cannot be handled by the /// referred \c rosa::Agent instance, the \c rosa::Message object is simply /// ignored. /// /// \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 \c 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 \c rosa::Agent instance is owned by \p 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 \c rosa::Agent instance referred by a \c rosa::AgentHandle. /// /// \note If the given \c rosa::Message object cannot be handled by the /// referred \c rosa::Agent instance, the \c rosa::Message object is simply /// ignored. /// /// \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 \c 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 \c rosa::Agent instance is owned by \p 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 98d6608..3470bcb 100644 --- a/include/rosa/core/System.hpp +++ b/include/rosa/core/System.hpp @@ -1,215 +1,214 @@ //===-- rosa/core/System.hpp ------------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/core/System.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Declaration of *System* interface. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_SYSTEM_HPP #define ROSA_CORE_SYSTEM_HPP #include "rosa/config/config.h" #include "rosa/core/forward_declarations.h" #include "rosa/support/debug.hpp" #include "rosa/support/log.h" #include #include #include namespace rosa { /// Base interface for actual agent-systems. /// /// The class provides facilities to keep track of \c rosa::Unit instances owned /// by a \c rosa::System. /// /// \note Any subclass is supposed to provide thread-safe implementation. /// /// \note The class declares only an interface to avoid trouble with multiple /// inheritance in various subclasses as in derived interfaces and derived /// implementations. /// /// \note Actual implementations are supposed to derive from \c rosa::SystemBase /// implenenting a base feature-set. class System { public: /// Signature of creator functions for \c rosa::Unit instances. /// /// \tparam T type derived from \c rosa::Unit /// \tparam S type derived from \c rosa::System template using UnitCreator = std::function; /// Returns an object implementing the \c rosa::System interface. /// /// \param Name name of the new instance /// /// \return \c std::unique_ptr for a new instance of \c rosa::System static std::unique_ptr createSystem(const std::string &Name) noexcept; protected: /// Creates an instance. /// /// \note Protected constructor restricts instantiation for subclasses. System(void) noexcept = default; /// No copying and moving of \c rosa::System. ///@{ - System(const System&) = delete; - System(System&&) = delete; - System &operator=(const System&) = delete; - System &operator=(System&&) = delete; + System(const System &) = delete; + System(System &&) = delete; + System &operator=(const System &) = delete; + System &operator=(System &&) = delete; ///@} public: /// Destroys \p this object. /// /// \note Any implementation makes sure that a \c rosa::System can be /// destroyed only if it is marked *cleaned* /// \see \c rosa::System::isSystemCleaned virtual ~System(void) = default; protected: /// Tells the next unique identifier to be used for a newly created /// \c rosa::Unit. /// /// \return \c rosa::id_t which is unique within the context of \p this /// object. /// /// \note Never returs the same value twice. virtual id_t nextId(void) noexcept = 0; /// Tells if \p this object has been marked cleaned and is ready for /// destruction. /// /// \return if \p this object is marked clean. virtual bool isSystemCleaned(void) const noexcept = 0; /// Marks \p this object cleaned. /// /// \note Can be called only once when the System does not have any live /// \c rosa::Unit instances. /// /// \pre \p this object has not yet been marked as cleaned and it has no /// \c rosa::Unit instances registered:\code /// !isSystemCleaned() && empty() /// \endcode /// /// \post \p this object is marked cleaned:\code /// isSystemCleaned() /// \encode virtual void markCleaned(void) noexcept = 0; /// Registers a \c rosa::Unit instance to \p this object. /// /// \param U \c rosa::Unit to register /// /// \pre \p this object has not yet been marked as cleaned and \p U is not /// registered yet:\code /// !isSystemCleaned() && !isUnitRegistered(U) /// \endcode /// /// \post \p U is registered:\code /// isUnitRegistered(U) /// \endcode virtual void registerUnit(Unit &U) noexcept = 0; /// Unregisters and destroys a registered \c rosa::Unit instance. /// /// \param U \c rosa::Unit to destroy /// /// \pre \p U is registered:\code /// isUnitRegistered(U) /// \endcode /// /// \post \p U is not registered:\code /// !isUnitRegistered(U) /// \endcode Moreover, \p U is destroyed. virtual void destroyUnit(Unit &U) noexcept = 0; /// Tells if a \c rosa::Unit is registered in \p this object. /// /// \param U \c rosa::Unit to check /// /// \return whether \p U is registered in \p this object virtual bool isUnitRegistered(const Unit &U) const noexcept = 0; /// Creates a \c rosa::Unit instance with the given /// \c rosa::System::UnitCreator and registers the new instance. /// /// \tparam T type of the actual \c rosa::Unit to instantiate /// \tparam S type of the actual \c rosa::System instantiating /// /// \param C function creating an instance of type \p T /// /// \note \p S must be the actual subclass that wants to instantiate /// \c rosa::Unit. That cannot be statically enforced, it is the /// reponsibility of the caller to provide the proper \c rosa::System /// subclass. /// /// \pre Statically, \p T is a subclass of \c rosa::Unit and \p S is a /// subclass of \c rosa::System:\code /// std::is_base_of::value && std::is_base_of::value /// \endcode Dynamically, \p this object has not yet been marked cleaned:\code /// !isSystemCleaned() /// \endcode template T &createUnit(UnitCreator C) noexcept; public: /// Tells the name of \p this object /// /// \note The returned reference remains valid as long as \p this object is /// not destroyed. /// /// \return name of \p this object virtual const std::string &name(void) const noexcept = 0; /// Tells the number of \c rosa::Unit instances constructed in the context of /// \p this object so far, including those being already destroyed. /// /// \return number of \c rosa::Unit instances created so far virtual size_t numberOfConstructedUnits(void) const noexcept = 0; /// Tells the number of live \c rosa::Unit instances in the context \p this /// object, those being constructed and not destroyed yet. /// /// \return number of \c rosa::Unit instances alive virtual size_t numberOfLiveUnits(void) const noexcept = 0; /// Tells if \p this object has no live \c rosa::Unit instances. /// /// \return whether \p this object has any live \c rosa::Unit instances virtual bool empty(void) const noexcept = 0; }; template T &System::createUnit(UnitCreator C) noexcept { STATIC_ASSERT((std::is_base_of::value), "not a Unit"); STATIC_ASSERT((std::is_base_of::value), "not a System"); if (isSystemCleaned()) { ROSA_CRITICAL("Trying to create a Unit in a cleaned System (" + name() + ")"); } const id_t Id = nextId(); T *U = C(Id, static_cast(*this)); registerUnit(*U); LOG_TRACE("Unit created and registered (" + U->FullName + ")"); return *U; } } // End namespace rosa #endif // ROSA_CORE_SYSTEM_HPP - diff --git a/include/rosa/core/SystemBase.hpp b/include/rosa/core/SystemBase.hpp index 4e50b62..f5fd038 100644 --- a/include/rosa/core/SystemBase.hpp +++ b/include/rosa/core/SystemBase.hpp @@ -1,128 +1,127 @@ //===-- rosa/core/SystemBase.hpp -----------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/core/SystemBase.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Base implementation of the \c rosa::System interface. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_SYSTEMBASE_HPP #define ROSA_CORE_SYSTEMBASE_HPP #include "rosa/core/System.hpp" #include namespace rosa { /// Base implementation of the \c rosa::System interface. /// /// This implementation provides only *name* for \c rosa::System, identifiers /// for \c rosa::Unit instances, and marking the \c rosa::System cleaned for /// destruction. /// /// \note Actual implementations of \c rosa::System and derived interfaces are /// supposed to inherit from this implementation. class SystemBase : public System { protected: /// Creates an instance. /// /// \note Protected constructor restrict instantiation for subclasses. /// /// \param Name name of the new instance SystemBase(const std::string &Name) noexcept; public: /// Destroys \p this object. /// /// \pre \p this object is marked cleaned:\code /// isSystemCleaned() /// \endcode ~SystemBase(void); protected: /// The textual name of \p this object implementing \c rosa::System. const std::string Name; private: /// Number of \c rosa::Unit instances constructed by \p this object. /// /// \note Should never be decremented! std::atomic UnitCount; /// Indicates that \p this object has been cleaned and is ready for /// destruction. /// /// The field is initialized as \c false and can be set by /// \c rosa::SystemBase::markCleaned. /// /// \note Subclasses must set the flag upon destructing their instances, which /// indicates to the destructor of the base-class that all the managed /// resources has been properly released. std::atomic SystemIsCleaned; public: /// Tells the name of \p this object /// /// \note The returned reference remains valid as long as \p this object is /// not destroyed. /// /// \return reference to \c rosa::SystemBase::Name const std::string &name(void) const noexcept override; protected: /// Tells the next unique identifier to be used for a newly created /// \c rosa::Unit. /// /// The functions takes the current value of the internal counter /// \c rosa::SystemBase::UnitCount and then increments it. /// /// \note This is the only function modifying /// \c rosa::SystemBase::UnitCount. /// /// \return \c rosa::id_t which is unique within the context of \p this /// object. id_t nextId(void) noexcept override; /// Tells if \p this object has been marked cleaned and is ready for /// destruction. /// /// \return if \p this object is marked clean. bool isSystemCleaned(void) const noexcept override; /// Marks \p this object cleaned by setting /// \c rosa::SystemBase::SystemIsCleaned. /// /// \note Can be called only once when the System does not have any live /// \c rosa::Unit instances. /// /// \pre \p this object has not yet been marked as cleaned and it has no /// \c rosa::Unit instances registered:\code /// !isSystemCleaned() && empty() /// \endcode /// /// \post \p this object is marked cleaned:\code /// isSystemCleaned() /// \encode void markCleaned(void) noexcept override; /// Tells the number of \c rosa::Unit instances constructed in the context of /// \p this object so far, including those being already destroyed. /// /// \return current value of \c rosa::SystemBase::UnitCount that is the number /// of \c rosa::Unit instances created so far size_t numberOfConstructedUnits(void) const noexcept override; }; } // End namespace rosa #endif // ROSA_LIB_CORE_SYSTEMBASE_HPP - diff --git a/include/rosa/core/Unit.h b/include/rosa/core/Unit.h index a443b08..377443a 100644 --- a/include/rosa/core/Unit.h +++ b/include/rosa/core/Unit.h @@ -1,116 +1,114 @@ //===-- 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 \c 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 \c rosa::System that has to be identified /// and traced. /// /// \note Life-cycle of \c rosa::Unit instances is supposed to be managed by the /// \c rosa::System owning the instance, do not create and destroy any /// \c rosa::Unit directly. class Unit { public: - /// Identifies the *kind* of \p this object. /// /// \note Kind is dependent on the \c rosa::System owning \p this object. const AtomValue Kind; /// Unique identifier for \p this object. /// /// \note The unique identifier is assigned by the \c rosa::System owning /// \p this object upon creation. const id_t Id; /// Textual identifier of \p this object. /// /// \note Textual identifiers of \c rosa::Unit instances are not necessarily /// unique in their owning \c rosa::System. const std::string Name; protected: /// The \c rosa::System owning \p this object. System &S; public: /// Full qualified name of \p 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 \c rosa::System owning the new instance /// /// \pre \p 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 \c rosa::Unit instances is possible. ///@{ Unit(const Unit &) = delete; Unit(Unit &&) = delete; Unit &operator=(const Unit &) = delete; Unit &operator=(Unit &&) = delete; ///@} /// Destroys \p this object. virtual ~Unit(void); /// Dumps \p this object into a \c std::string for tracing purposes. /// /// Subclasses are supposed to override this function. /// /// \return \c std::string representing the state of \p this object virtual std::string dump(void) const noexcept; protected: /// Returns a reference to the \c rosa::System owning \p this object. /// /// \note Subclasses may override the function to return a reference of a /// subtype of \c rosa::System. /// /// \return reference of \c rosa::Unit::S virtual System &system() const noexcept; }; /// Dumps a \c rosa::Unit instance to a given \c std::ostream. /// /// \param [in,out] OS output stream to dump to /// \param U \c rosa::Unit to dump /// /// \return \p OS after dumping \p 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 f812700..1fcc8e1 100644 --- a/include/rosa/core/forward_declarations.h +++ b/include/rosa/core/forward_declarations.h @@ -1,41 +1,40 @@ //===-- 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 \c rosa::Unit identifiers. using id_t = uint64_t; /// Type of a \c std::unique_ptr for an immutable *Message*, \c 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 6762801..4369bc5 100644 --- a/include/rosa/support/atom.hpp +++ b/include/rosa/support/atom.hpp @@ -1,183 +1,179 @@ //===-- 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 *atoms*, short strings statically encoded as integers. /// /// \note This implementation is based on the \c atom implementation of CAF. /// \todo Check license. /// /// *Atoms* can be used to turn short string literals into statically generated /// types. The literals may consist of at most \c 10 non-special characters, /// legal characters are \c _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 \c rosa::atom_t into a strongly typed enumeration. /// /// Values of \c rosa::atom_t casted to \c rosa::AtomValue may be used in a /// type-safe way. enum class AtomValue : atom_t {}; /// Anonymous namespace with implementational details, consider it private. namespace { // clang-format off /// 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}; // clang-format on /// 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 \p Current /// /// \return \p Current updated with \p 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 \c rosa::AtomValue. /// /// \param CStr a string to encode /// \param Interim encoded value to add \p CStr to it /// /// \return \p Interim updated with \p 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 \c rosa::AtomValue into \c std::string. /// /// \param What value to convert /// /// \return \c std::string encoded in \p What std::string to_string(const AtomValue &What); /// Converts a \c std::string into a \c rosa::AtomValue. /// /// \param S \c std::string to convert /// /// \return \c rosa::AtomValue representing \p S AtomValue atom_from_string(const std::string &S); /// Converts a string-literal into a \c rosa::AtomValue. /// /// \tparam Size the length of \p Str /// /// \param Str the string-literal to convert /// /// \return \c rosa::AtomValue representating \p Str /// /// \pre \P Str is not too long:\code /// Size <= MaxAtomLength + 1 /// \endcode -template -constexpr AtomValue atom(char const (&Str)[Size]) { +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 \c rosa::AtomValue to a compile-time constant. /// /// \tparam V \c rosa::AtomValue to lift -template -struct AtomConstant { +template struct AtomConstant { /// Constructor has to do nothing. constexpr AtomConstant(void) {} /// Returns the wrapped value. /// /// \return \p V constexpr operator AtomValue(void) const { return V; } /// Returns the wrapped value as of type \c rosa::atom_t. /// /// \return \c rosa::atom_t value from \p V static constexpr atom_t value() { return static_cast(V); } /// An instance *of this constant* (*not* a \c rosa::AtomValue). static const AtomConstant Value; }; // Implementation of the static member field \c rosa::AtomConstant::Value. template const AtomConstant AtomConstant::Value = AtomConstant{}; /// Converts a \c rosa::AtomConstant into \c std::string. /// /// \tparam V \c rosa::AtomValue to convert /// /// \note The actual argument of type `const rosa::AtomConstant` is ignored /// because the \c rosa::AtomValue to convert is encoded in the type itself. /// /// \return the original string encoded in \p V -template -std::string to_string(const AtomConstant &) { +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 3f7872e..ca3b966 100644 --- a/include/rosa/support/debug.hpp +++ b/include/rosa/support/debug.hpp @@ -1,128 +1,127 @@ //===-- 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 "rosa/support/type_helper.hpp" #include #include namespace rosa { /// Returns an output stream to use for debugging. std::ostream &dbgs(void); /// Prints a \c std::array to a \c std::ostream. /// /// \tparam T type of values stored in \p A /// \tparam Size number of elements in \p A /// /// \param [in,out] OS \c std::ostream to print to /// \param A \c std::array to print to \p OS /// /// \return \p OS after printing \p 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 \c ROSA_ENABLE_ASSERTIONS is defined. /// /// Checks if \p 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 \c 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 \c 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 \c 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 \p COND does not evaluate to \c 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 c616098..1ed7b02 100644 --- a/include/rosa/support/log.h +++ b/include/rosa/support/log.h @@ -1,261 +1,259 @@ //===-- 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 \c rosa::LogLevel::Error and also log warnings Info, ///< Like \c rosa::LogLevel::Warning and also log general infos Debug, ///< Like \c rosa::LogLevel::Info and also log debug infos Trace, ///< Like \c rosa::LogLevel::Debug and also log trace infos NumLogLevels ///< Number of log levels }; /// Converts a \c rosa::LogLevel to its textual representation. /// /// \param logLevel \c rosa::LogLevel to convert /// /// \return \c std::string representing \p logLevel /// /// \pre `logLevel` is valid:\code /// logLevel != LogLevel::NumLogLevels /// \endcode std::string logLevelToString(const LogLevel logLevel); /// Prints colorized tag for the given \c rosa::LogLevel. /// /// \param [in,out] OS \c std::ostream to print to /// \param logLevel \c rosa::LogLevel to print tag for /// /// \return \p OS after printing a tag for \p logLevel /// /// \pre \p 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 \c rosa::LogLevel. ///@{ #define ROSA_LOG_LEVEL_ERROR \ 0 ///< Value corresponding to \c rosa::LogLevel::Error #define ROSA_LOG_LEVEL_WARNING \ 1 ///< Value corresponding to \c rosa::LogLevel::Warning #define ROSA_LOG_LEVEL_INFO \ 2 ///< Value corresponding to \c rosa::LogLevel::Info #define ROSA_LOG_LEVEL_DEBUG \ 3 ///< Value corresponding to \c rosa::LogLevel::Debug #define ROSA_LOG_LEVEL_TRACE \ 4 ///< Value corresponding to \c 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 \c ROSA_LOG_OSTREAM. /// /// \param level \c rosa::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 \c rosa::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 \c 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 /// \c rosa::LogLevel::Error. /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Error. /// \def LOG_ERROR(output) /// \brief Prints a log entry of level \c rosa::LogLevel::Error. /// \param output the message to print /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Error. /// \def LOG_WARNING_STREAM /// \brief Returns a stream to print a log entry of level /// \c rosa::LogLevel::Warning. /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Warning. /// \def LOG_WARNING(output) /// \brief Prints a log entry of level \c rosa::LogLevel::Warning. /// \param output the message to print /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Warning. /// \def LOG_INFO_STREAM /// \brief Returns a stream to print a log entry of level /// \c rosa::LogLevel::Info. /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Info. /// \def LOG_INFO(output) /// \brief Prints a log entry of level \c rosa::LogLevel::Info. /// \param output the message to print /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Info. /// \def LOG_DEBUG_STREAM /// \brief Returns a stream to print a log entry of level /// \c rosa::LogLevel::Debug. /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Debug. /// \def LOG_DEBUG(output) /// \brief Prints a log entry of level \c rosa::LogLevel::Debug. /// \param output the message to print /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Debug. /// \def LOG_TRACE_STREAM /// \brief Returns a stream to print a log entry of level /// \c rosa::LogLevel::Trace. /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Trace. /// \def LOG_TRACE(output) /// \brief Prints a log entry of level \c rosa::LogLevel::Trace. /// \param output the message to print /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c 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 65ed36f..768079f 100644 --- a/include/rosa/support/math.hpp +++ b/include/rosa/support/math.hpp @@ -1,36 +1,35 @@ //===-- 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 \p 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 017e8fa..8609424 100644 --- a/include/rosa/support/squashed_int.hpp +++ b/include/rosa/support/squashed_int.hpp @@ -1,134 +1,130 @@ //===-- 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 \c 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 \c rosa::type_nr_t, /// always make sure that \c 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 \c [u]int_[8|16|32|64]_t equivalents. /// /// The squashed type for a type \c T can be obtained as \code /// typename SquashedInt::Type /// \endcode /// /// \tparam T the integer type to squash /// /// \pre \p 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; +template using squashed_int_t = typename SquashedInt::Type; /// \defgroup SquashedType /// \brief Squashes a type. /// /// The squashed type for a type \c T can be obtained as \code /// typename SquashedType::Type /// \endcode /// The resulting type is squashed with \c rosa::SquashedInt if \c T is /// integral, and remains \p T otherwise. ///@{ /// Definition for the general case, when squashing a non-integral type. /// /// \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 squashing an integral type. /// /// \tparam T the type to squash -template -struct SquashedType { +template struct SquashedType { using Type = squashed_int_t; }; ///@} /// Convenience alias for obtaining a squashed type. -template -using squashed_t = typename SquashedType::Type; +template using squashed_t = typename SquashedType::Type; /// \defgroup SquashedTypeList /// \brief Squashes a \c rosa::TypeList elementwise. /// /// Replaces all types in a \c rosa::TypeList with their corresponding squashed /// types by using \c rosa::SquashedType. The squashed \c rosa::TypeList /// corresponding to \c List can be obtained as \code /// typename SquashedTypeList::Type /// \endcode ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to squash template struct SquashedTypeList; // Specialization for \c rosa::EmptyTypeList. template <> struct SquashedTypeList { using Type = EmptyTypeList; }; /// Specialization for non-empty \c 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 7d144a2..9c9d7e8 100644 --- a/include/rosa/support/terminal_colors.h +++ b/include/rosa/support/terminal_colors.h @@ -1,71 +1,70 @@ //===-- 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 \c rosa::terminal::Color values }; /// Handles \c rosa::terminal::Color values sent to output streams. /// /// The operator sends terminal commands through \p os to the /// associated terminal to change text color to \p color. /// /// \note If \p os is not a terminal output, the terminal commands simply appear /// as text in the stream. /// /// \param [in,out] os \c std::ostream to apply \p color to /// \param color \c rosa::terminal::Color to apply for \p os /// /// \return \p os after applying \p color to it /// /// \pre \p 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 ab84689..b54f9c9 100644 --- a/include/rosa/support/type_helper.hpp +++ b/include/rosa/support/type_helper.hpp @@ -1,143 +1,136 @@ //===-- 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 \c [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 \c 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 { +template struct PrintableType { using Type = typename PrintableType::type>::Type; }; /// Specialization for \c const uint8_t. -template <> -struct PrintableType { +template <> struct PrintableType { using Type = const unsigned int; }; /// Specialization for \c const int8_t. -template <> -struct PrintableType { +template <> struct PrintableType { using Type = const int; }; ///@} /// Convenience template alias for using \c rosa::PrintableType. -template -using printable_t = typename PrintableType::Type; +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 \c T` if \c T is an /// integral (except \c bool) or enumeration type. Keeps \c T otherwise. /// /// The corresponding unsigned type for a type \c T can be obtained as \code /// typename Unsigned::Type /// \endcode ///@{ /// Definition for the general case when converting a non-integral type. /// /// \tparam T type to convert /// \tparam IsIntegral always use the default value! template ::value> struct Unsigned { using Type = T; }; /// Specialization for the case when converting an integral type. -template -struct Unsigned { +template struct Unsigned { using Type = typename std::make_unsigned::type; }; ///@} /// Convenience template alias for using \c rosa::Unsigned. -template -using unsigned_t = typename Unsigned::Type; +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 f8c255f..3edec32 100644 --- a/include/rosa/support/type_list.hpp +++ b/include/rosa/support/type_list.hpp @@ -1,453 +1,444 @@ //===-- 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 \c 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 { /// Constructor, needs to do nothing. constexpr TypeList(void) {} }; /// The empty \c rosa::Typelist. using EmptyTypeList = TypeList<>; /// \defgroup TypeListAtImpl /// /// \brief Gets the type at index \p Pos from a list of types. /// /// \note Only to be used by the implementation of \c 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 \p Pos is not \c 0 and there is type in /// the list. template struct TypeListAtImpl { using Type = typename TypeListAtImpl::Type; }; /// Specialization for the case when \p Pos is \c 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 \c rosa::none_t. template struct TypeListAtImpl { using Type = none_t; }; ///@} /// \defgroup TypeListAt /// /// \brief Gets the element at index \p Pos of \p List. /// /// /// The type at index \c Pos in a \c rosa::TypeList \c List can be obtained as /// \code /// typename TypeListAt::Type /// \endcode /// /// \note The resulting type is \c rosa::none_t if \code /// TypeListSize::Value < Pos /// \endcode ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to take an element from /// \tparam Pos index to take the element from template struct TypeListAt; /// Implementation using \c 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 \c 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 \c -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 /// \c rosa::TypeList. /// /// The index of the first occurence of type \c T in \c rosa::TypeList \c List /// can be obtained as \code /// TypeListIndexOf::Value /// \endcode /// /// \note The resulting index is \c -1 if \c T is not present in \c List. ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to search in /// \tparam T type to search for template struct TypeListIndexOf; /// Implementation of the template using \c rosa::TypeListIndexOfImpl. template struct TypeListIndexOf, T> { static constexpr int Value = TypeListIndexOfImpl<0, T, Ts...>::Value; }; ///@} /// \defgroup TypeListHead /// /// \brief Gets the first element of a \c rosa::TypeList. /// /// The first element of a \c rosa::TypeList \c List can be obtained as \code /// typename TypeListHead::Type /// \endcode /// /// \note The resulting type is \c rosa::none_t if \c List is /// \c rosa::EmptyTypeList. ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to get the first element of template struct TypeListHead; /// Specialization for \c rosa::EmptyTypeList. /// /// In this case, the found type is \c rosa::none_t. template <> struct TypeListHead { using Type = none_t; }; /// Implementation for a non-empty \c rosa::TypeList. -template -struct TypeListHead> { +template struct TypeListHead> { using Type = T; }; ///@} /// \defgroup TypeListTail /// /// \brief Gets the tail of a \c rosa::TypeList. /// /// The tail of a \c rosa::TypeList \c List, that is \c List except for its /// first element, can be obtained as \code /// typename TypeListTail::Type /// \endcode /// /// \note If \c List is \c rosa::EmptyTypeList, then the resulting type is also /// \c rosa::EmptyTypeList. ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to take the tail of template struct TypeListTail; /// Specialization for \c rosa::EmptyTypeList. /// /// In this case, the resulting type is \c rosa::EmptyTypeList. template <> struct TypeListTail { using Type = EmptyTypeList; }; /// Implementation for a non-empty \c rosa::TypeList. -template -struct TypeListTail> { +template struct TypeListTail> { using Type = TypeList; }; ///@} /// \defgroup TypeListPush /// /// \brief Extends a \c rosa::TypeList with a type. /// /// Whether the new type is pushed in the front or in the back of the /// \c 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 \p Q is a \c rosa::TypeList, a \c rosa::TypeList /// otherwise /// \tparam Q a type if \p P is a \c rosa::TypeList, a \c rosa::TypeList /// otherwise -template -struct TypeListPush; +template struct TypeListPush; /// Implementation for the case when pushing at the back of the /// \c rosa::TypeList. -template -struct TypeListPush, T> { - using Type = TypeList; +template struct TypeListPush, T> { + using Type = TypeList; }; /// Implementation for the case when pushing to the front of the /// \c rosa::TypeList. -template -struct TypeListPush> { +template struct TypeListPush> { using Type = TypeList; }; ///@} /// \defgroup TypeListDrop /// /// \brief Drops some elements from the beginning of a \c rosa::TypeList. /// /// The first \c N types of a \c rosa::TypeList \c List can be dropped as \code /// typename TypeListDrop::Type /// \endcode ///@{ /// Declaration of the template. /// /// \tparam N number of types to drop /// \tparam List \c rosa::TypeList to drop the first \p N element of template struct TypeListDrop; /// Specialization for \c rosa::EmptyTypeList. template struct TypeListDrop { using Type = EmptyTypeList; }; /// Implementation for a non-empty \c 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 \c rosa::TypeList. /// /// The size of a \c rosa::TypeList \c List can be obtained as \code /// TypeListSize::Value /// \endcode ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to get the size of -template -struct TypeListSize; +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 \c rosa::TypeList is empty. /// /// \tparam List \c rosa::TypeList to check template struct TypeListEmpty { /// Denotes whether \p List is an empty \c rosa::TypeList or not. static constexpr bool Value = std::is_same::value; }; /// \defgroup TypeListContains /// /// \brief Tells if a \c rosa::TypeList contains a given type. /// /// Whether a \c rosa::TypeList \c List contains the type \c T can be checked as /// \code /// TypeListContains::Value /// \endcode ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::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 \c rosa::TypeList is a subset of another one. /// /// Whether a \c rosa::TypeList \c ListA is a subset of another /// \c rosa::TypeList \c ListB can be checked as \code /// TypeListSubsetOf::Value /// \endcode ///@{ /// Declaration of the template. /// /// \tparam ListA \c rosa::TypeList to check if is a subset of \p ListB /// \tparam ListB \c rosa::TypeList to check if is a superset of \p ListA /// \tparam Fwd always use the default value! template struct TypeListSubsetOf; /// Specialization for the case when all the elements of the original \p ListA /// was found in \p ListB. -template -struct TypeListSubsetOf { +template struct TypeListSubsetOf { static constexpr bool Value = true; }; /// Specializaton for the case when an element of the original \p ListA cannot /// be found in \p 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 \c rosa::TypeListFind. ///@{ /// Declaration of the template. /// /// \tparam Pred the predicate to check types against /// \tparam Ts list of types to check template