diff --git a/examples/type-facilities/type-facilities.cpp b/examples/type-facilities/type-facilities.cpp index d714a73..b4a3ade 100644 --- a/examples/type-facilities/type-facilities.cpp +++ b/examples/type-facilities/type-facilities.cpp @@ -1,88 +1,88 @@ //===-- 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 +/// \date 2017-2019 /// /// \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" using namespace rosa; using namespace rosa::terminal; int main(void) { LOG_INFO_STREAM << library_string() << " -- " << Color::Red << "type facilities" << Color::Default << '\n'; auto &Log = LOG_TRACE_STREAM; Log << "\nNumberOfBuiltinTypes: " << NumberOfBuiltinTypes << "\nTokenBits: " << token::TokenBits << "\nRepresentationBits: " << token::RepresentationBits << "\nMaxTokenizableListSize: " << token::MaxTokenizableListSize << "\n\n"; Log << "Type number information on 'uint8_t':"; constexpr TypeNumber TN = TypeNumberOf::Value; Log << "\n type number: " << PRINTABLE_TN(TN) << "\n size: " << TypeForNumber::Size << "\n name: " << TypeForNumber::Name << "\n\n"; Log << "Type number information on 'std::string':"; constexpr TypeNumber TNS = TypeNumberOf::Value; Log << "\n type number: " << PRINTABLE_TN(TNS) << "\n size: " << TypeForNumber::Size << "\n name: " << TypeForNumber::Name << "\n\n"; Log << "Type number information of AtomConstants:"; using Atom1 = AtomConstant; using Atom2 = AtomConstant; Log << "\n std::is_same::value: " << std::is_same::value << "\n TypeNumberOf::Value: " << PRINTABLE_TN(TypeNumberOf::Value) << "\n TypeNumberOf::Value: " << PRINTABLE_TN(TypeNumberOf::Value) << "\n name: " << TypeForNumber::Value>::Name << "\n\n"; Log << "Type token information on 'TypeList':"; // \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 << "\n token: " << PRINTABLE_TOKEN(T_) << "\n valid: " << validToken(T_) << "\n empty: " << emptyToken(T_) - << "\n length: " << lengthOfToken(T_) + << "\n length: " << static_cast(lengthOfToken(T_)) << "\n full size: " << sizeOfValuesOfToken(T_) << "\n head type number: " << PRINTABLE_TN(headOfToken(T_)) << "\n size of head: " << sizeOfHeadOfToken(T_) << "\n name of head: " << nameOfHeadOfToken(T_) << "\n is head uint8_t: " << isHeadOfTokenTheSameType(T_) << "\n is head uint16_t: " << isHeadOfTokenTheSameType(T_) << "\n is head std::string: " << isHeadOfTokenTheSameType(T_) << "\nDropping head..."; dropHeadOfToken(T_); } // Here when Token became empty. Log << "\n token: " << PRINTABLE_TOKEN(T_) << "\n empty: " << emptyToken(T_) << '\n'; return 0; } diff --git a/include/rosa/core/Message.hpp b/include/rosa/core/Message.hpp index ce9e6ec..06b6c60 100644 --- a/include/rosa/core/Message.hpp +++ b/include/rosa/core/Message.hpp @@ -1,258 +1,259 @@ //===-- rosa/core/Message.hpp -----------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/core/Message.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \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/tokenized_storages.hpp" #include "rosa/core/forward_declarations.h" 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. /// /// \todo Some member functions of \c rosa::Message duplicate member functions /// of \c rosa::TokenizedStorage, which cannot be easily factored out into a /// common base class due to eventual diamond inheritance issues in derived /// classes. Could this duplication be avoided? 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; + const token_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; + template bool isTypeAt(const token_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; + template + const Type &valueAt(const token_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; + virtual const void *pointerTo(const token_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; /// 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 final : public Message, private TokenizedStorage { 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...), TokenizedStorage(T, Ts...) { ASSERT(this->T == this->ST && Size == this->size()); // Sanity check. } /// 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...), TokenizedStorage(std::move(T), std::move(Ts)...) { ASSERT(this->T == this->ST && Size == this->size()); // Sanity check. } /// 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 /// /// \pre \p Pos is a valid index:\code /// Pos < Size /// \endcode - const void *pointerTo(const size_t Pos) const noexcept override { + const void *pointerTo(const token_size_t Pos) const noexcept override { ASSERT(Pos < Size); return TokenizedStorage::pointerTo(Pos); } /// Aborts the program! /// /// Since \c rosa::Message instances are supposed to be immutable, the /// non-const inherited function is overridden so that it aborts execution. - void *pointerTo(const size_t) noexcept override { + void *pointerTo(const token_size_t) noexcept override { ROSA_CRITICAL("Unallowed operation of rosa::LocalMessage"); } }; } // 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 { +bool Message::isTypeAt(const token_size_t Pos) const noexcept { ASSERT(Pos < Size); Token TT = T; dropNOfToken(TT, Pos); return isHeadOfTokenTheSameType(TT); } template -const Type &Message::valueAt(const size_t Pos) const noexcept { +const Type &Message::valueAt(const token_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/MessageMatcher.hpp b/include/rosa/core/MessageMatcher.hpp index 71bd69f..0092cb9 100644 --- a/include/rosa/core/MessageMatcher.hpp +++ b/include/rosa/core/MessageMatcher.hpp @@ -1,201 +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 +/// \date 2017-2019 /// /// \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 using MsgMatcher = MessageMatcher>; /// Nested namespace with implementation for features of /// \c rosa::MessageMatcher, consider it private. namespace { /// \defgroup MessageMatcherImpl Implementation for rosa::MessageMatcher /// /// 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 bool doesStronglyMatchFrom(const Message &Msg, - const size_t Pos) noexcept { + const token_size_t Pos) noexcept { // Matching EmptyTypeList only if reached the end of the stored types. return Pos == Msg.Size; } static std::tuple<> extractedValuesFrom(const Message &Msg, - const size_t Pos) noexcept { + const token_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 bool doesHeadStronglyMatchAt(const Message &Msg, - const size_t Pos) noexcept { + const token_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 bool doesStronglyMatchFrom(const Message &Msg, - const size_t Pos) noexcept { + const token_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 std::tuple &, const Ts &...> - extractedValuesFrom(const Message &Msg, const size_t Pos) noexcept { + extractedValuesFrom(const Message &Msg, const token_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 bool doesHeadStronglyMatchAt(const Message &Msg, - const size_t Pos) noexcept { + const token_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 bool doesStronglyMatchFrom(const Message &Msg, - const size_t Pos) noexcept { + const token_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 std::tuple - extractedValuesFrom(const Message &Msg, const size_t Pos) noexcept { + extractedValuesFrom(const Message &Msg, const token_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/deluxe/DeluxeAgent.hpp b/include/rosa/deluxe/DeluxeAgent.hpp index c73afdc..5534660 100755 --- a/include/rosa/deluxe/DeluxeAgent.hpp +++ b/include/rosa/deluxe/DeluxeAgent.hpp @@ -1,1119 +1,1124 @@ //===-- rosa/deluxe/DeluxeAgent.hpp -----------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeAgent.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Specialization of \c rosa::Agent for *agent* role of the *deluxe /// interface*. /// /// \see \c rosa::deluxe::DeluxeContext /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXEAGENT_HPP #define ROSA_DELUXE_DELUXEAGENT_HPP #include "rosa/core/Agent.hpp" #include "rosa/deluxe/DeluxeAtoms.hpp" #include /// Local helper macros to deal with built-in types. /// ///@{ /// Creates function name for member functions in \c rosa::deluxe::DeluxeAgent. /// /// \param N name suffix to use #define DASLAVEHANDLERNAME(N) handleSlave_##N /// Creates function name for member functions in \c rosa::deluxe::DeluxeAgent. /// /// \param N name suffix to use #define DAMASTERHANDLERNAME(N) handleMaster_##N /// Defines member functions for handling messages from *slaves* in /// \c rosa::deluxe::DeluxeAgent. /// /// \see \c DeluxeAgentInputHandlers /// /// \note No pre- and post-conditions are validated directly by these functions, /// they rather rely on \c rosa::deluxe::DeluxeAgent::saveInput to do that. /// /// \param T the type of input to handle /// \param N name suffix for the function identifier #define DASLAVEHANDLERDEFN(T, N) \ void DASLAVEHANDLERNAME(N)(atoms::Slave, id_t SlaveId, T Value) noexcept { \ saveInput(SlaveId, Value); \ } /// Defines member functions for handling messages from *master* in /// \c rosa::deluxe::DeluxeAgent. /// /// \see \c DeluxeAgentMasterInputHandlers /// /// \note No pre- and post-conditions are validated directly by these functions, /// they rather rely on \c rosa::deluxe::DeluxeAgent::saveMasterInput to do /// that. /// /// \param T the type of input to handle /// \param N name suffix for the function identifier #define DAMASTERHANDLERDEFN(T, N) \ void DAMASTERHANDLERNAME(N)(atoms::Master, id_t MasterId, \ T Value) noexcept { \ saveMasterInput(MasterId, Value); \ } /// Convenience macro for \c DASLAVEHANDLERDEFN with identical arguments. /// /// \see \c DASLAVEHANDLERDEFN /// /// This macro can be used instead of \c DASLAVEHANDLERDEFN if the actual value /// of \p T can be used as a part of a valid identifier. /// /// \param T the type of input to handle #define DASLAVEHANDLERDEF(T) DASLAVEHANDLERDEFN(T, T) /// Convenience macro for \c DAMASTERHANDLERDEFN with identical arguments. /// /// \see \c DAMASTERHANDLERDEFN /// /// This macro can be used instead of \c DAMASTERHANDLERDEFN if the actual value /// of \p T can be used as a part of a valid identifier. /// /// \param T the type of input to handle #define DAMASTERHANDLERDEF(T) DAMASTERHANDLERDEFN(T, T) /// Results in a \c THISMEMBER reference to a member function defined by /// \c DASLAVEHANDLERDEFN. /// /// Used in the constructor of \c rosa::deluxe::DeluxeAgent to initialize super /// class \c rosa::Agent with member function defined by \c DASLAVEHANDLERDEFN. /// /// \see \c DASLAVEHANDLERDEFN, \c THISMEMBER /// /// \param N name suffix for the function identifier #define DASLAVEHANDLERREF(N) THISMEMBER(DASLAVEHANDLERNAME(N)) /// Results in a \c THISMEMBER reference to a member function defined by /// \c DAMASTERHANDLERDEFN. /// /// Used in the constructor of \c rosa::deluxe::DeluxeAgent to initialize super /// class \c rosa::Agent with member function defined by \c DAMASTERHANDLERDEFN. /// /// \see \c DAMASTERHANDLERDEFN, \c THISMEMBER /// /// \param N name suffix for the function identifier #define DAMASTERHANDLERREF(N) THISMEMBER(DAMASTERHANDLERNAME(N)) ///@} namespace rosa { namespace deluxe { /// Specialization of \c rosa::Agent for *agent* role of the *deluxe interface*. /// /// \see \c rosa::deluxe::DeluxeContext /// /// \invariant All input-related container objects have a size matching /// \c rosa::deluxe::DeluxeAgent::NumberOfInputs, thus having a corresponding /// entry for each input. \c rosa::deluxe::DeluxeAgent::NumberOfMasterOutputs /// matches \c rosa::deluxe::DeluxeAgent::NumberOfInputs. All /// master-output-related container objects have a size matching \c /// rosa::deluxe::DeluxeAgent::NumberOfMasterOutputs. Types of input and /// master-output values are consistent throughout all the input-related and /// master-output-related containers, respectively. No *slave* is registered at /// more than one input position. *Slave* registrations and corresponding /// reverse lookup information are consistent. /// /// \see Definition of \c rosa::deluxe::DeluxeAgent::inv on the class invariant /// /// \note All member functions validate the class invariant as part of their /// precondition. Moreover, non-const functions validate the invariant before /// return as their postcondition. class DeluxeAgent : public Agent { /// Checks whether \p this object holds the class invariant. /// /// \see Invariant of the class \c rosa::deluxe::DeluxeAgent /// /// \return if \p this object holds the class invariant bool inv(void) const noexcept; public: /// The type of values produced by \p this object. /// /// That is the type of values \p this object sends to its *master*. /// /// \see \c rosa::deluxe::DeluxeAgent::master const TypeNumber OutputType; /// Number of inputs processed by \p this object. const size_t NumberOfInputs; /// The type of values \p this object processes from its *master*. /// /// \see \c rosa::deluxe::DeluxeAgent::master const TypeNumber MasterInputType; /// Number of outputs produces by \p this object for its *slaves*. /// /// \note This values is equal to \c /// rosa::deluxe::DeluxeAgent::NumberOfInputs. /// /// \see \c rosa::deluxe::DeluxeAgent::slave. const size_t NumberOfMasterOutputs; private: /// Types of input values produced by *slaves* of \p this object. /// /// \note The \c rosa::TypeNumber values stored here match the corresponding /// values in \c rosa::deluxe::DeluxeAgent::InputValues. /// /// \note The position of a type in the \c std::vector indicates which /// argument of \p this object's processing function it belongs to. See also /// \c rosa::deluxe::DeluxeAgent::DeluxeAgent. const std::vector InputTypes; /// Indicates whether any particular input value has been changed since the /// last trigger received from the system. /// /// All the flags are reset to \c false upon handling a trigger and then set /// to \c true by \c rosa::deluxe::DeluxeAgent::saveInput when storing a new /// input value in \c rosa::deluxe::DeluxeAgent::InputValues. /// /// \note The position of a flag in the \c std::vector indicates which /// argument of \p this object's processing function it belongs to. See also /// \c rosa::deluxe::DeluxeAgent::DeluxeAgent. std::vector InputChanged; /// Stores the actual input values. /// /// \note The types of stored values match the corresponding /// \c rosa::TypeNumber values in \c rosa::deluxe::DeluxeAgent::InputTypes. /// /// \note The position of a value in the \c rosa::AbstractTokenizedStorage /// indicates which argument of \p this object's processing function it is. /// See also \c rosa::deluxe::DeluxeAgent::DeluxeAgent. const std::unique_ptr InputValues; /// Indicates whether the input value from the *master* has been changed since /// the last trigger received from the system. /// /// The flag is reset to \c false upon handling a trigger and then set to \c /// true by \c rosa::deluxe::DeluxeAgent::saveMasterInput when storig a new /// input value in \c rosa::deluxe::DeluxeAgent::MasterInputValue. bool MasterInputChanged; /// Stores the actual input value from *master*. /// /// \note The type of the stored value matches the type indicated by \c /// rosa::deluxe::DeluxeAgent::MasterInputType. const std::unique_ptr MasterInputValue; /// Types of output values produced by \p this object for its *slaves*. /// /// That is the type of values \p this object sends to its *slaves*. /// /// \note The position of a type in the \c std::vector indicates which /// *slave* of \p this object the type belongs to. See also /// \c rosa::deluxe::DeluxeAgent::DeluxeAgent. const std::vector MasterOutputTypes; /// Alias for function objects used as trigger handler for /// \c rosa::deluxe::DeluxeAgent. /// /// \note The function used for \c H is to be \c noexcept. /// /// \see \c rosa::deluxe::DeluxeAgent::FP using H = std::function; /// Handles trigger from the system. /// /// The actual functions processing *slave* and *master* inputs and generating /// optional output to *master* and *slaves* are captured in a lambda /// expression that is in turn wrapped in a \c std::function object. The /// lambda expression calls the master-input processing function with the /// actual master-input data and sends its result -- if any -- to *slaves* by /// calling \c rosa::deluxe::DeluxeAgent::handleMasterOutputs; then calls the /// input processing function with the actual input data and sends its result /// -- if any -- to *master* by calling \c /// rosa::deluxe::DeluxeAgent::sendToMaster and *slaves* by calling \c /// rosa::deluxe::DeluxeAgent::handleMasterOutputs. Also, all the flags stored /// in \c rosa::deluxe::DeluxeAgent::InputChanged and \c /// rosa::deluxe::DeluxeAgent::MasterInputChanged are reset when the current /// values are processed. The function \c /// rosa::deluxe::DeluxeAgent::handleTrigger needs only to call the /// function object. /// /// \see \c /// rosa::deluxe::DeluxeAgent::triggerHandlerFromProcessingFunctions const H FP; /// The *master* to send values to. /// /// \note *Masters* are set dynamically, hence it is possible that a /// \c rosa::deluxe::DeluxeAgent instance does not have any *master* at a /// given moment. Optional Master; /// The *slaves* sending input to \p this object. /// /// \note The position of a *slave* in the \c std::vector indicates which /// argument of \p this object's processing function it belongs to. See also /// \c rosa::deluxe::DeluxeAgent::DeluxeAgent. /// /// \note *Slaves* are set dynamically, hence it is possible that a /// \c rosa::deluxe::DeluxeAgent instance does have input positions without /// any *slave* associated to them. /// /// \note Reverse lookup information is maintained in /// \c rosa::deluxe::DeluxeAgent::SlaveIds, which is to be kept in sync with /// the *slaves* stored here. std::vector> Slaves; /// Associates \c rosa::id_t values to corresponding indices of registered /// *slaves*. /// /// \see \c rosa::deluxe::DeluxeAgent::Slaves std::map SlaveIds; /// Tells the unique identifier of the *master* of \p this object, if any /// registered. /// /// \return the unique identifier of the *master* /// /// \pre A *master* is registered for \p this object: \code /// Master /// \endcode id_t masterId(void) const noexcept; /// Tells whether types stored in \c rosa::TypeList \p As match the input /// types of \p this object. /// /// \tparam As \c rosa::TypeList containing types to match against values in /// \c rosa::deluxe::DeluxeAgent::InputTypes /// /// \note Instatiation of the template fails if \p As is not \c /// rosa::TypeList. /// /// \return if types in \p As match \c rosa::TypeNumber values stored in /// \c rosa::deluxe::DeluxeAgent::InputTypes template bool inputTypesMatch(void) const noexcept; /// Tells whether types stored in \c rosa::TypeList \p Ts match the /// master-output types of \p this object. /// /// \tparam Ts \c rosa::TypeList containing types to match against values in /// \c rosa::deluxe::DeluxeAgent::MasterOutputTypes /// /// \note Instatiation of the template fails if \p As is not \c /// rosa::TypeList. /// /// \return if types in \p Ts match \c rosa::TypeNumber values stored in /// \c rosa::deluxe::DeluxeAgent::MasterOutputTypes template bool masterOutputTypesMatch(void) const noexcept; /// Gives an \c std::tuple containing the current input values and their /// change flags so that they can be used for the processing function. /// /// \tparam As types of the input values /// \tparam S0 indices for accessing input values and their change flags /// /// \note The only argument provides indices statically as template arguments /// \p S0..., so its actual value is ignored. /// /// \return current input values and their change flags prepared for invoking /// the processing function with them /// /// \pre The type arguments \p As... match the input types of \p this object /// and the provided indices \p S0... constitute a proper sequence for /// accessing input values and their change flags: \code /// inputTypesMatch>() && sizeof...(As) == sizeof...(S0) /// \endcode template std::tuple...> prepareCurrentInputs(Seq) const noexcept; /// Invokes a processing function matching the input, output, and /// master-output input types of \p this object with actual arguments provided /// in a \c std::tuple. /// /// \note \p Args providing the actual arguments for \p F is to be created by /// \c rosa::deluxe::DeluxeAgent::prepareCurrentInputs. /// /// \tparam T output type of the processing function /// \tparam Ts types of master-output values of the processing function /// \tparam As types of inputs for the processing function /// \tparam S0 indices starting with `0` for extracting actual arguments from /// \p Args /// /// \param F the processing function to invoke /// \param Args the actual arguments to invoke \p F with /// /// \note The last argument provides indices statically as template arguments /// \p S0..., so its actual value is ignored. /// /// \return the result of \p F for actual arguments \p Args /// /// \pre The provided sequence of indices \p S0... constitutes a proper /// sequence for extracting all actual arguments for /// \p F from \p Args: \code /// sizeof...(As) == sizeof...(S0) /// \endcode template static std::tuple, Optional...> invokeWithTuple(std::function, Optional...>( std::pair...)> F, const std::tuple...> Args, Seq) noexcept; /// Handles the master-output value at position \p Pos of \p Output. /// /// \p Output is a \c std::tuple resulted by a processing function and /// contains master-output values starting at position \p Offset. The /// function takes the master-output value for *slave* position \p Pos and /// sends its actual value, if any, to the corresponding *slave*. /// /// \note A master-output of type \c rosa::unit_t indicates no actual output /// and hence no message is generated for a position whose corresponding /// master-output type is \c rosa::unit_t. /// /// \note The function provides position-based implementation for \c /// rosa::deluxe::DeluxeAgent::handleMasterOutputs. /// /// \tparam Offset index of the first master-output value in \p Output /// \tparam Pos the position of the master-output to handle /// \tparam Ts output types stored in \p Output /// /// \param Output \c std::tuple resulted by the processing function /// /// \pre \p Output matches the master-output types \p this object was created /// with and \p Pos is a valid master-output index: \code /// masterOutputTypesMatch>::Type>() && /// Pos < NumberOfMasterOutputs /// \endcode template void handleMasterOutputAtPos( const std::tuple...> &Output) noexcept; /// Handles master-output values from \p Output. /// /// \p Output is a \c std::tuple resulted by a processing function and /// contains master-output values starting at position \p Offset. The function /// takes master-output values and sends each actual value to the /// corresponding *slave*. /// /// \tparam Offset index of the first master-output value in \p Output /// \tparam Ts output types stored in \p Output /// \tparam S0 indices starting with `0` for extracting master-output values /// from \p Output /// /// \param Output \c std::tuple resulted by a processing function /// /// \pre \p Output matches the master-output types \p this object was created /// with and the provided sequence of indices \p S0... constitues a proper /// sequence for extraing all master-output values from \p Output: \code /// masterOutputTypesMatch>::Type>() && /// sizeof...(S0) == NumberOfMasterOutputs /// \endcode template void handleMasterOutputs(const std::tuple...> &Output, Seq) noexcept; /// Wraps processing functions into a trigger handler. /// /// \see \c rosa::deluxe::DeluxeAgent::FP /// /// \note The function cannot be const qualified because the lambda /// expression defined in it needs to capture \p this object by a non-const /// reference /// /// \tparam MT type of master-input /// \tparam T type of output /// \tparam Ts types of master-output values /// \tparam As types of input values /// /// \param MF function processing master-input and generating output /// \param F function processing inputs and generating output /// /// \note A master-input type of \c rosa::unit_t indicates that \p this object /// does not receive master-input, \p MF is never called if \p MT is \c /// rosa::unit_t. /// /// \return trigger handler function based on \p F and \p MF /// /// \pre Template arguments \p MT, \p T, \p Ts..., and \p As... match the /// corresponding types \p this object was created with: \code /// MasterInputType == TypeNumberOf::Value && /// OutputType == TypeNumberOf::Value && /// inputTypesMatch>() && /// masterOutputTypesMatch>() /// \endcode template H triggerHandlerFromProcessingFunctions( std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept; public: /// Creates a new instance. /// /// The constructor instantiates the base-class with functions to handle /// messages as defined for the *deluxe interface*. /// /// The function \p F generates a \c std::tuple of values: the first value is /// the output for the *master* and the rest is for the *slaves*. All output /// generated by the function is optional as an agent may decide not to output /// anything at some situation. /// /// \todo Enforce \p F and \p MF do not potentially throw exception. /// /// \tparam MT type of master-input handled by \p MF /// \tparam T type of output of \p F /// \tparam Ts type of master-output values of \p F and \p MF /// \tparam As types of input values of \p F /// /// \note Instantiation fails if any of the type arguments \p MT, \p T, \p /// Ts..., and \p As... is not a built-in type. /// /// \note If \p MT is \c rosa::unit_t, the constructed object does not receive /// master-input. Similarly, if any of \p Ts... is \c rosa::unit_t, the /// constructed object does not generated master-output for the corresponding /// *slave* position. /// /// \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 MF function to process master-input values and generate /// master-output with /// \param F function to process input values and generate output and /// master-output with /// /// \pre Statically, all of the type arguments \p MT, \p T, \p Ts..., and \p /// As... is a built-in type and the number of input and master-output types /// are equal: \code /// TypeListSubsetOf, /// BuiltinTypes>::Value && sizeof...(Ts) == sizeof...(As) ///\endcode /// Dynamically, the instance is created as of kind \c /// rosa::deluxe::atoms::AgentKind: \code /// Kind == rosa::deluxe::atoms::AgentKind /// \endcode template , BuiltinTypes>::Value && sizeof...(Ts) == sizeof...(As)>> DeluxeAgent( const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept; /// Destroys \p this object. ~DeluxeAgent(void) noexcept; /// The *master* of \p this object, if any is registered. /// /// \see \c rosa::deluxe::DeluxeAgent::registerMaster /// /// \return the *master* registered for \p this object Optional master(void) const noexcept; /// Registers a *master* for \p this object. /// /// The new *master* is registered by overwriting the reference to any /// already registered *master*. One can clear the registered reference by /// passing an *empty* \c rosa::Optional object as actual argument. /// /// \note The role of the referred *master* is validated by checking its /// *kind*. /// /// \note Any call to \c rosa::deluxe::DeluxeAgent::registerMaster should be /// paired with a corresponding call of \c /// rosa::deluxe::DeluxeAgent::registerSlave, which validates that /// input/output types of master and slave matches. /// /// \param _Master the *master* to register /// /// \pre \p _Master is empty or of kind \c rosa::deluxe::atoms::AgentKind: /// \code /// !_Master || unwrapAgent(*_Master).Kind == rosa::deluxe::atoms::AgentKind /// \endcode void registerMaster(const Optional _Master) noexcept; /// Tells the type of values consumed from the *slave* at a position. /// /// That is the type of values \p this object expect to be sent to it by its /// *slave* registered at position \p Pos. /// /// \see \c rosa::deluxe::DeluxeAgent::slave /// /// \param Pos position of *slave* /// /// \return \c rosa::TypeNumber representing the type of values consumed from /// the *slave* at position \p Pos /// /// \pre \p Pos is a valid index of input: \code /// Pos < NumberOfInputs /// \endcode TypeNumber inputType(const size_t Pos) const noexcept; /// Tells the type of values produced for the *slave* at a position. /// /// That is the type of values \p this object potentially sends to its /// *slave* registered at position \p Pos. /// /// \see \c rosa::deluxe::DeluxeAgent::slave /// /// \param Pos position of *slave* /// /// \return \c rosa::TypeNumber representing the type of values produced for /// the *slave* at position \p Pos /// /// \pre \p Pos is a valid index of input: \code /// Pos < NumberOfMasterOutputs /// \endcode TypeNumber masterOutputType(const size_t Pos) const noexcept; /// The *slave* of \p this object registered at a position, if any. /// /// \see \c rosa::deluxe::DeluxeAgent::registerSlave /// /// \param Pos position of *slave* /// /// \return the *slave* registered for \p this object at position \p Pos /// /// \pre \p Pos is a valid index of input: \code /// Pos < NumberOfInputs /// \endcode Optional slave(const size_t Pos) const noexcept; /// Registers a *slave* for \p this object at a position. /// /// The new *slave* is registered by overwriting the reference to any already /// registered *slave* at position \p Pos. One can clear the registered /// reference by passing an *empty* \c rosa::Optional object as actual /// argument. If \p Slave is already registered for another position, the /// other position gets cleared. /// /// \note The role of the referred *slave* is validated by checking its /// *kind*. /// /// \note The type of values produced by the referred *slave* is validated by /// matching its `OutputType` against the corresponding value in /// \c rosa::deluxe::DeluxeAgent::InputTypes. /// /// \note The type of master-input values processed by the referred *slave* is /// validated by matching its `MasterInputType` against the corresponding /// value in \c rosa::deluxe::DeluxeAgent::MasterOutputTypes. /// /// \param Pos position to register \p Slave at /// \param Slave the *slave* to register /// /// \pre \p Pos is a valid index of input, \p Slave is empty or of kind /// \c rosa::deluxe::atoms::AgentKind or \c rosa::deluxe::atoms::SensorKind, /// and \p Slave -- if not empty -- produces values of types matching the /// expected input type at position \p Pos and processes values of types /// matching the produced master-output type at position \p Pos: /// \code /// Pos < NumberOfInputs && /// (!Slave || /// (unwrapAgent(*Slave.)Kind == rosa::deluxe::atoms::SensorKind && /// static_cast(unwrapAgent(*Slave)).OutputType == /// InputTypes[Pos] && /// (MasterOutputTypes[Pos] == TypeNumberOf::Value || /// static_cast(unwrapAgent(*Slave)).MasterInputType /// == MasterOutputTypes[Pos])) || /// (unwrapAgent(*Slave).Kind == rosa::deluxe::atoms::AgentKind && /// static_cast(unwrapAgent(*Slave)).OutputType == /// InputTypes[Pos] && /// (MasterOutputTypes[Pos] == TypeNumberOf::Value || /// static_cast(unwrapAgent(*Slave)).MasterInputType == /// MasterOutputTypes[Pos]))) /// \endcode void registerSlave(const size_t Pos, const Optional Slave) noexcept; /// Tells the position of a registered *slave*. /// /// \param Slave \c rosa::AgentHandle for the *slave* to check /// /// \return position of \p Slave if it is registered and found, /// \c rosa::deluxe::DeluxeAgent::NumberOfInputs otherwise. size_t positionOfSlave(AgentHandle Slave) const noexcept; private: /// Sends a value to the *master* of \p this object. /// /// \p Value is getting sent to \c rosa::deluxe::DeluxeAgent::Master if it /// contains a valid handle for a \c rosa::deluxe::DeluxeAgent. The function /// does nothing otherwise. /// /// \tparam T type of the value to send /// /// \param Value value to send /// /// \pre \p T matches \c rosa::deluxe::DeluxeiAgent::OutputType: \code /// OutputType == TypeNumberOf::Value /// \endcode template void sendToMaster(const T &Value) noexcept; /// Sends a value to a *slave* of \p this object at position \p Pos. /// /// \p Value is getting sent to \c rosa::deluxe::DeluxeAgent::Slaves[Pos] if /// it contains a valid handle. The function does nothing otherwise. /// /// \tparam T type of the value to send /// /// \param Pos the position of the *slave* to send \p Value to /// \param Value value to send /// /// \pre \p Pos is a valid *slave* position and \p T matches \c /// rosa::deluxe::DeluxeiAgent::MasterOutputTypes[Pos]: \code /// Pos < NumberOfMasterOutputs && /// MasterOutputTypes[Pos] == TypeNumberOf::Value /// \endcode template void sendToSlave(const size_t Pos, const T &Value) noexcept; /// Generates the next output by processing current input values upon trigger /// from the system. /// /// Executes \c rosa::deluxe::DeluxeAgent::FP. /// /// \note The only argument is a \c rosa::AtomConstant, hence its actual /// value is ignored. void handleTrigger(atoms::Trigger) noexcept; /// Stores a new input value from a *slave*. /// /// The function stores \p Value in \c rosa::deluxe::DeluxeAgent::InputValues /// at the position associated to \p Id in /// \c rosa::deluxe::DeluxeAgent::SlaveIds and also sets the corresponding /// flag in \c rosa::deluxe::DeluxeAgent::InputChanged. /// /// \note Utilized by member functions of group \c DeluxeAgentInputHandlers. /// /// \tparam T type of input to store /// /// \param Id unique identifier of *slave* /// \param Value the input value to store /// /// \pre The *slave* with \p Id is registered and the input from it is /// expected to be of type \p T: \code /// SlaveIds.find(Id) != SlaveIds.end() && /// InputTypes[SlaveIds.find(Id)->second] == TypeNumberOf::Value /// \endcode template void saveInput(id_t Id, T Value) noexcept; /// Stores a new input value from the *master*. /// /// The function stores \p Value in \c /// rosa::deluxe::DeluxeAgent::MasterInputValue and also sets the /// corresponding flag \c rosa::deluxe::DeluxeAgent::MasterInputChanged. /// /// \note Utilized by member functions of group \c /// DeluxeAgentMasterInputHandlers. /// /// \tparam T type of input to store /// /// \param Id unique identifier of the *master* /// \param Value the input value to store /// /// \pre The *master* with \p Id is registered and the input from the *master* /// is expected to be of type \p T: \code /// Master && masterId() == Id && MasterInputType == TypeNumberOf::Value /// \endcode template void saveMasterInput(id_t Id, T Value) noexcept; /// \defgroup DeluxeAgentInputHandlers Input handlers of /// rosa::deluxe::DeluxeAgent /// /// Definition of member functions handling messages from *slaves* with /// different types of input /// /// A *master* generally needs to be prepared to deal with values of any /// built-in type to handle messages from its *slaves*. Each type requires a /// separate message handler, which are implemented by these functions. The /// functions instantiate \c rosa::deluxe::DeluxeAgent::saveInput with the /// proper template argument and pass the content of the message on for /// processing. /// /// \note The member functions in this group are defined by \c /// DASLAVEHANDLERDEF. /// /// \note Keep these definitions in sync with \c rosa::BuiltinTypes. /// ///@{ DASLAVEHANDLERDEF(AtomValue) DASLAVEHANDLERDEF(int16_t) DASLAVEHANDLERDEF(int32_t) DASLAVEHANDLERDEF(int64_t) DASLAVEHANDLERDEF(int8_t) DASLAVEHANDLERDEFN(long double, long_double) DASLAVEHANDLERDEFN(std::string, std__string) DASLAVEHANDLERDEF(uint16_t) DASLAVEHANDLERDEF(uint32_t) DASLAVEHANDLERDEF(uint64_t) DASLAVEHANDLERDEF(uint8_t) DASLAVEHANDLERDEF(unit_t) DASLAVEHANDLERDEF(bool) DASLAVEHANDLERDEF(double) DASLAVEHANDLERDEF(float) /// @} /// \defgroup DeluxeAgentMasterInputHandlers Master-input handlers of /// rosa::deluxe::DeluxeAgent /// /// Definition of member functions handling messages from the *master* with /// different types of input /// /// A *slave* generally needs to be prepared to deal with values of any /// built-in type, except for \c rosa::unit_t, to handle messages from its /// *master*. Each type requires a separate message handler, which are /// implemented by these functions. The functions instantiate /// \c rosa::deluxe::DeluxeAgent::saveMasterInput with the proper template /// argument and pass the content of the message on for processing. /// /// \note The member functions in this group are defined by \c /// DAMASTERHANDLERDEF. /// /// \note Keep these definitions in sync with \c rosa::BuiltinTypes; but do no /// include \c rosa::unit_t. /// ///@{ DAMASTERHANDLERDEF(AtomValue) DAMASTERHANDLERDEF(int16_t) DAMASTERHANDLERDEF(int32_t) DAMASTERHANDLERDEF(int64_t) DAMASTERHANDLERDEF(int8_t) DAMASTERHANDLERDEFN(long double, long_double) DAMASTERHANDLERDEFN(std::string, std__string) DAMASTERHANDLERDEF(uint16_t) DAMASTERHANDLERDEF(uint32_t) DAMASTERHANDLERDEF(uint64_t) DAMASTERHANDLERDEF(uint8_t) DAMASTERHANDLERDEF(bool) DAMASTERHANDLERDEF(double) DAMASTERHANDLERDEF(float) /// @} }; /// Anonymous namespace with implementation for /// \c rosa::deluxe::DeluxeAgent::inputTypesMatch and \c /// rosa::deluxe::DeluxeAgent::masterOutputTypesMatch, consider it private. namespace { /// Template \c struct whose specializations provide a recursive implementation /// for \c TypesMatchList. /// /// \tparam As types to match template struct TypesMatchImpl; /// Template specialization for the general case, when at least one type is to /// be matched. /// /// \tparam A first type to match /// \tparam As further types to match template struct TypesMatchImpl { /// Tells whether types \p A, \p As... match \c rosa::TypeNumber values /// stored in \p Types starting at position \p Pos. /// /// The function has got a recursive implementation: it matches the first /// type \p A against \c rosa::TypeNumber at position \p Pos of \p Types, then /// further types \p As.. are matched recursively starting at position /// \c (Pos + 1). /// /// \param Types container of \c rosa::TypeNumber values to match types /// against /// \param Pos position in \p Types to start matching at /// /// \return if types \p A, \p As... match \c rosa::TypeNumber values stored /// in \p Types starting at position \p Pos static bool f(const std::vector &Types, size_t Pos) noexcept { return Pos < Types.size() && TypeNumberOf::Value == Types[Pos] && TypesMatchImpl::f(Types, Pos + 1); } }; /// Template specialization for the terminal case, when no type remains to /// check. template <> struct TypesMatchImpl<> { /// Tells whether \p Pos is the number of values stored in \p Types. /// /// In this terminal case, there is no more types to match because all the /// types are supposed to be already matched successfully. The whole list of /// types already matched is a complete match if it covers all values in /// \p Types. That is true if \p Pos points exactly to the end of \p Types. /// /// \param Types container of \c rosa::TypeNumber values to match types /// against /// \param Pos position in \p Types to start matching at /// /// \return if \p Pos is the number of values stored in \p Types static bool f(const std::vector &Types, size_t Pos) noexcept { return Pos == Types.size(); } }; /// Template \c struct that provides an implementation for \c /// rosa::deluxe::DeluxeAgent::inputTypesMatch and \c /// rosa::deluxe::DeluxeAgent::masterOutputTypesMatch. /// /// \note Match a list of types \p As... against a \c std::vector of /// \c rosa::TypeNumber values, \c Types, like \code /// bool match = TypesMatchList>::f(Types); /// \endcode /// /// \tparam As \c rosa::TypeList that contains types to match template struct TypesMatchList; /// Template specialization implementing the feature. /// /// \tparam As \c rosa::TypeList that contains types to match template struct TypesMatchList> { /// Tells whether types \p As... match \c rosa::TypeNumber values /// stored in \p Types. /// /// The function unwraps the types from \c rosa::TypeList and utilizes \c /// TypesMatchImpl to do the check. /// /// \param Types container of \c rosa::TypeNumber values to match types /// against /// /// \return if types \p As... match \c rosa::TypeNumber values stored /// in \p Types static bool f(const std::vector &Types) noexcept { return TypesMatchImpl::f(Types, 0); } }; } // End namespace template bool DeluxeAgent::inputTypesMatch(void) const noexcept { return TypesMatchList::f(InputTypes); } template bool DeluxeAgent::masterOutputTypesMatch(void) const noexcept { return TypesMatchList::f(MasterOutputTypes); } template std::tuple...> DeluxeAgent::prepareCurrentInputs(Seq) const noexcept { // Need to indirectly reference \c rosa::deluxe::DeluxeAgent::inputTypesMatch // inside \c ASSERT because of the comma in its template argument list. auto MFP = &DeluxeAgent::inputTypesMatch>; ASSERT(inv() && (this->*MFP)() && sizeof...(As) == sizeof...(S0)); + // The below should hold because of the above, just leave it for sanity check. + ASSERT((true && ... && static_cast(static_cast(S0)))); return std::make_tuple( - std::make_pair(*static_cast(InputValues->pointerTo(S0)), + std::make_pair(*static_cast( + InputValues->pointerTo(static_cast(S0))), InputChanged[S0])...); } template std::tuple, Optional...> DeluxeAgent::invokeWithTuple( std::function< std::tuple, Optional...>(std::pair...)> F, const std::tuple...> Args, Seq) noexcept { ASSERT(sizeof...(As) == sizeof...(S0)); return F(std::get(Args)...); } template void DeluxeAgent::handleMasterOutputAtPos( const std::tuple...> &Output) noexcept { // Need to indirectly reference \c // rosa::deluxe::DeluxeAgent::masterOutputTypesMatch inside \c ASSERT because // of the comma in its template argument list. auto MOTMFP = &DeluxeAgent::masterOutputTypesMatch< typename TypeListDrop>::Type>; ASSERT(inv() && (this->*MOTMFP)() && Pos < NumberOfMasterOutputs); // Do not do anything for master-output type \c rosa::unit_t. if (!std::is_same, Offset + Pos>::Type, unit_t>::value) { const auto MasterOutput = std::get(Output); if (MasterOutput) { sendToSlave(Pos, *MasterOutput); } } } template void DeluxeAgent::handleMasterOutputs(const std::tuple...> &Output, Seq) noexcept { // Need to indirectly reference \c // rosa::deluxe::DeluxeAgent::masterOutputTypesMatch inside \c ASSERT because // of the comma in its template argument list. auto MOTMFP = &DeluxeAgent::masterOutputTypesMatch< typename TypeListDrop>::Type>; ASSERT(inv() && (this->*MOTMFP)() && sizeof...(S0) == NumberOfMasterOutputs); // Handle each master-output position in a fold expression. (handleMasterOutputAtPos(Output), ...); } template DeluxeAgent::H DeluxeAgent::triggerHandlerFromProcessingFunctions( std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept { // Need to indirectly reference \c rosa::deluxe::DeluxeAgent::inputTypesMatch // and \c rosa::deluxe::DeluxeAgent::masterOutputTypesMatch inside \c ASSERT // because of the comma in their respective template argument lists. auto ITMFP = &DeluxeAgent::inputTypesMatch>; auto MOTMFP = &DeluxeAgent::masterOutputTypesMatch>; ASSERT(MasterInputType == TypeNumberOf::Value && OutputType == TypeNumberOf::Value && (this->*ITMFP)() && (this->*MOTMFP)()); return [ this, MF, F ]() noexcept { // \note These indices work for both inputs and master-outputs. using Indices = typename GenSeq::Type; // Handle master-input. // Do not do anything for master-input type \c rosa::unit_t. if (MasterInputType != TypeNumberOf::Value) { LOG_TRACE_STREAM << "DeluxeAgent " << Name << " handles master-input." << std::endl; const auto MasterInputArg = std::make_pair( *static_cast(MasterInputValue->pointerTo(0)), MasterInputChanged); MasterInputChanged = false; const std::tuple...> MasterOutput = MF(MasterInputArg); handleMasterOutputs<0>(MasterOutput, Indices()); } // Handle inputs. LOG_TRACE_STREAM << "DeluxeAgent " << Name << " handles input." << std::endl; const auto InputArgs = prepareCurrentInputs(Indices()); std::fill(InputChanged.begin(), InputChanged.end(), false); const std::tuple, Optional...> Output = invokeWithTuple(F, InputArgs, Indices()); const auto OutputToMaster = std::get<0>(Output); if (OutputToMaster) { sendToMaster(*OutputToMaster); } handleMasterOutputs<1>(Output, Indices()); }; } template DeluxeAgent::DeluxeAgent( const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept : Agent(Kind, Id, Name, S, THISMEMBER(handleTrigger), DASLAVEHANDLERREF(AtomValue), DASLAVEHANDLERREF(int16_t), DASLAVEHANDLERREF(int32_t), DASLAVEHANDLERREF(int64_t), DASLAVEHANDLERREF(int8_t), DASLAVEHANDLERREF(long_double), DASLAVEHANDLERREF(std__string), DASLAVEHANDLERREF(uint16_t), DASLAVEHANDLERREF(uint32_t), DASLAVEHANDLERREF(uint64_t), DASLAVEHANDLERREF(uint8_t), DASLAVEHANDLERREF(unit_t), DASLAVEHANDLERREF(bool), DASLAVEHANDLERREF(double), DASLAVEHANDLERREF(float), DAMASTERHANDLERREF(AtomValue), DAMASTERHANDLERREF(int16_t), DAMASTERHANDLERREF(int32_t), DAMASTERHANDLERREF(int64_t), DAMASTERHANDLERREF(int8_t), DAMASTERHANDLERREF(long_double), DAMASTERHANDLERREF(std__string), DAMASTERHANDLERREF(uint16_t), DAMASTERHANDLERREF(uint32_t), DAMASTERHANDLERREF(uint64_t), DAMASTERHANDLERREF(uint8_t), DAMASTERHANDLERREF(bool), DAMASTERHANDLERREF(double), DAMASTERHANDLERREF(float)), OutputType(TypeNumberOf::Value), NumberOfInputs(sizeof...(As)), MasterInputType(TypeNumberOf::Value), NumberOfMasterOutputs(NumberOfInputs), InputTypes({TypeNumberOf::Value...}), InputChanged(NumberOfInputs, false), InputValues(new TokenizedStorage()), MasterInputChanged(false), MasterInputValue(new TokenizedStorage()), MasterOutputTypes({TypeNumberOf::Value...}), FP(triggerHandlerFromProcessingFunctions(std::move(MF), std::move(F))), Slaves(NumberOfInputs) { ASSERT(Kind == atoms::AgentKind); LOG_TRACE("DeluxeAgent is created."); ASSERT(inv()); } template void DeluxeAgent::sendToMaster(const T &Value) noexcept { ASSERT(inv() && OutputType == TypeNumberOf::Value); // There is a handle and the referred *master* is in a valid state. if (Master && *Master) { Master->sendMessage(Message::create(atoms::Slave::Value, Id, Value)); } } template void DeluxeAgent::sendToSlave(const size_t Pos, const T &Value) noexcept { ASSERT(inv() && Pos < NumberOfMasterOutputs && MasterOutputTypes[Pos] == TypeNumberOf::Value); // There is a handle and the referred *slave* is in a valid state. auto Slave = Slaves[Pos]; if (Slave && *Slave) { Slave->sendMessage(Message::create(atoms::Master::Value, Id, Value)); } } template void DeluxeAgent::saveInput(id_t Id, T Value) noexcept { ASSERT(inv() && SlaveIds.find(Id) != SlaveIds.end() && InputTypes[SlaveIds.find(Id)->second] == TypeNumberOf::Value); size_t Pos = SlaveIds.at(Id); - *static_cast(InputValues->pointerTo(Pos)) = Value; + ASSERT(static_cast(static_cast(Pos)) == Pos); + *static_cast(InputValues->pointerTo(static_cast(Pos))) = + Value; InputChanged[Pos] = true; ASSERT(inv()); } template void DeluxeAgent::saveMasterInput(id_t Id, T Value) noexcept { ASSERT(inv() && Master && masterId() == Id && MasterInputType == TypeNumberOf::Value); *static_cast(MasterInputValue->pointerTo(0)) = Value; MasterInputChanged = true; ASSERT(inv()); } } // End namespace deluxe } // End namespace rosa #undef DASLAVEHANDLEREF #undef DAMASTERHANDLEREF #undef DASLAVEHANDLEDEF #undef DAMASTERHANDLEDEF #undef DASLAVEHANDLEDEFN #undef DAMASTERHANDLEDEFN #undef DASLAVEHANDLENAME #undef DAMASTERHANDLENAME #endif // ROSA_DELUXE_DELUXEAGENT_HPP diff --git a/include/rosa/support/tokenized_storages.hpp b/include/rosa/support/tokenized_storages.hpp index 36d10fb..63cf40f 100755 --- a/include/rosa/support/tokenized_storages.hpp +++ b/include/rosa/support/tokenized_storages.hpp @@ -1,500 +1,501 @@ //===-- rosa/support/tokenized_storages.hpp ---------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/support/tokenized_storages.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Definition of storage helper template for storing values in a /// type-safe way based on type tokens. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_TOKENIZED_STORAGES_HPP #define ROSA_SUPPORT_TOKENIZED_STORAGES_HPP #include "rosa/support/type_token.hpp" #include #include namespace rosa { /// Defines a simple interface for storing and accessing values of different /// types. /// /// While the interface provides features to access values and know their /// types, it is the users responsibility to use particular values according to /// their actual types. No facilities for type-safe access of values is /// provided by the class. /// /// \see \c rosa::TokenizedStorage for a type-safe specialization of the /// interface. class AbstractTokenizedStorage { protected: /// Protected constructor restricts instantiation for derived classes. AbstractTokenizedStorage(void) noexcept = default; public: /// No copying and moving of \c rosa::AbstractTokenizedStorage instances. ///@{ AbstractTokenizedStorage(const AbstractTokenizedStorage&) = delete; AbstractTokenizedStorage &operator=(const AbstractTokenizedStorage&) = delete; AbstractTokenizedStorage(AbstractTokenizedStorage&& Other) = delete; AbstractTokenizedStorage &operator=(AbstractTokenizedStorage&&) = delete; ///@} /// Destroys \p this object. virtual ~AbstractTokenizedStorage(void) noexcept = default; /// Tells how many values are stored in \p this object. /// /// \return number of values stored in \p this object virtual size_t size(void) const noexcept = 0; /// Tells the type of the value stored at a position. /// /// \param Pos the index of the value whose type is to returned /// /// \return \c rosa::TypeNumber for the value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code /// Pos < size() /// \endcode - virtual TypeNumber typeAt(const size_t Pos) const noexcept = 0; + virtual TypeNumber typeAt(const token_size_t Pos) const noexcept = 0; /// Provides an untyped pointer for the value stored at a position. /// /// \param Pos the index of the value to return an untyped 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 void *pointerTo(const size_t Pos) noexcept = 0; + virtual void *pointerTo(const token_size_t Pos) noexcept = 0; /// Provides a constant untyped pointer for the value stored at a position. /// /// \param Pos the index of the value to return an untyped pointer for /// /// \return constant untyped pointer for the value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code /// Pos < Offsets.size() /// \endcode - virtual const void *pointerTo(const size_t Pos) const noexcept = 0; + virtual const void *pointerTo(const token_size_t Pos) const noexcept = 0; }; /// Template class storing values and providing dynamic type-safe access to /// them in a lightweight way based on type tokens. /// /// \see rosa/support/type_token.hpp /// /// \tparam Types types whose values are to be stored template class TokenizedStorage; /// Nested namespace with implementation for \c rosa::TokenizedStorage, consider /// it private. namespace { /// 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 createArenaElements(void *const Arena, const Types &... Ts) noexcept; /// \defgroup createLvalueArenaElement Implementation of creating lvalue arena elements /// /// Stores values from constant lvalue references into a pre-allocated memory /// area. /// /// \note To be used by the implementation of \c createArenaElements. /// /// \todo Document these functions. ///@{ /// \note This terminal case is used for both constant lvalue references and /// value references. template inline void createArenaElement(void *const, const std::vector &Offsets) { ASSERT(Pos == Offsets.size()); } template inline void createArenaElement(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); createArenaElement(Arena, Offsets, Ts...); } template inline void createArenaElement(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; createArenaElement(Arena, Offsets, Ts...); } ///@} /// Implementation of the template. /// /// \tparam Types types of values to store /// /// \param Arena pre-allocated memory area to store values to /// \param Ts values to store in \p Arena /// /// \pre \p Arena is not \p nullptr. template inline void createArenaElements(void *const Arena, const Types &... Ts) noexcept { ASSERT(Arena != nullptr); createArenaElement<0>(Arena, TokenizedStorage::Offsets, 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 createArenaElements(void *const Arena, Types &&... Ts) noexcept; /// \defgroup createRvalueArenaElement Implementation of creating rvalue arena elements /// /// Stores values from rvalue references into a pre-allocated memory area. /// /// \note To be used by the implementation of \c createArenaElements. /// /// \todo Document these functions. ///@{ template inline void createArenaElement(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)); createArenaElement(Arena, Offsets, std::move(Ts)...); } template inline void createArenaElement(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; createArenaElement(Arena, Offsets, std::move(Ts)...); } ///@} /// Implementation of the template. /// /// \tparam Types types of values to store /// /// \param Arena pre-allocated memory area to store values to /// \param Ts values to store in \p Arena /// /// \pre \p Arena is not \c nullptr. template inline void createArenaElements(void *const Arena, Types &&... Ts) noexcept { ASSERT(Arena != nullptr); createArenaElement<0>(Arena, TokenizedStorage::Offsets, std::move(Ts)...); } /// Destroys values allocated by \c createArenaElements. /// /// \tparam Types 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 destroyArenaElements(void *const Arena) noexcept; /// \defgroup destroyArenaElement Implementation of destroying arena elements /// /// Destroys values from a memory area. /// /// \note To be used by the implementation of \c destroyArenaElements. /// /// \todo Document these functions. ///@{ template inline void destroyArenaElement(void *const, const std::vector &Offsets) noexcept { ASSERT(Pos == Offsets.size()); } template inline void destroyArenaElement(void *const Arena, const std::vector &Offsets) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); static_cast( static_cast(static_cast(Arena) + Offsets[Pos])) ->~Type(); destroyArenaElement(Arena, Offsets); } ///@} /// Implementation of the template. /// /// \tparam Types types of values to destroy /// /// \param Arena the memory area to destroy values from /// /// \pre \p Arena is not \c nullptr. template inline void destroyArenaElements(void *const Arena) noexcept { ASSERT(Arena != nullptr); destroyArenaElement<0, Types...>(Arena, TokenizedStorage::Offsets); } } // End namespace /// Implementation of the template \c rosa::TokenizedStorage as a /// specialization of \c rosa::AbstractTokenizedStorage. /// /// The class provides facilities for storing values and providing type-safe /// access to them. /// /// \tparam Types types of values to store template class TokenizedStorage : public AbstractTokenizedStorage { public: /// \c rosa::Token for the stored values. static constexpr Token ST = TypeToken::type...>::Value; /// Byte offsets to access stored values in \c rosa::TokenizedStorage::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 rosa::TokenizedStorage::Arena. /// /// \return \c std::vector containing byte offsets for accessing values stored /// in \c rosa::TokenizedStorage::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. + const token_size_t N = lengthOfToken(T); // Number of types encoded in \c T. std::vector O(N); // Allocate vector of proper size. // Do nothing for 0 elements. if (N > 0) { - size_t I = 0; // Start indexing from position \c 0. + token_size_t I = 0; // Start indexing from position \c 0. 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 with default values. /// /// \note This constructor requires that all actual template arguments \p /// Types... are default constructible. TokenizedStorage(void) noexcept : Arena(::operator new(sizeOfValuesOfToken(ST))) { ASSERT(Arena != nullptr); // Sanity check. createArenaElements(Arena, Types()...); } /// Creates an instance from constant lvalue references. /// /// \param Ts values to store TokenizedStorage(const Types &... Ts) noexcept : Arena(::operator new(sizeOfValuesOfToken(ST))) { ASSERT(Arena != nullptr); // Sanity check. createArenaElements(Arena, Ts...); } /// Creates an instance from rvalue references. /// /// \param Ts values to store TokenizedStorage(Types &&... Ts) noexcept : Arena(::operator new(sizeOfValuesOfToken(ST))) { ASSERT(Arena != nullptr); // Sanity check. createArenaElements(Arena, std::move(Ts)...); } /// No copying and moving of \c rosa::TokenizedStorage instances. /// /// \note This restriction may be relaxed as moving should be easy to /// implement, only requires the possiblity to validate Arena pointer. ///@{ TokenizedStorage(const TokenizedStorage&) = delete; TokenizedStorage &operator=(const TokenizedStorage&) = delete; TokenizedStorage(TokenizedStorage&& Other) = delete; TokenizedStorage &operator=(TokenizedStorage&&) = delete; ///@} // Destroys \p this object. ~TokenizedStorage(void) { destroyArenaElements(Arena); ::operator delete(Arena); } /// Tells how many values are stored in \p this object. /// /// \return number of values stored in \p this object size_t size(void) const noexcept override { return Offsets.size(); } /// Tells the type of the value stored at a position. /// /// \param Pos the index of the value whose type is to returned /// /// \return \c rosa::TypeNumber for the value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code /// Pos < size() /// \endcode - TypeNumber typeAt(const size_t Pos) const noexcept override { + TypeNumber typeAt(const token_size_t Pos) const noexcept override { ASSERT(Pos < size()); Token TT = ST; dropNOfToken(TT, Pos); return headOfToken(TT); } /// Provides an untyped pointer for the value stored at a position. /// /// \param Pos the index of the value to return an untyped pointer for /// /// \return untyped pointer for the value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code /// Pos < size() /// \endcode - void *pointerTo(const size_t Pos) noexcept override { + void *pointerTo(const token_size_t Pos) noexcept override { ASSERT(Pos < size()); return static_cast(Arena) + Offsets[Pos]; } /// Provides a constant untyped pointer for the value stored at a position. /// /// \param Pos the index of the value to return an untyped pointer for /// /// \return constant untyped pointer for the value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code /// Pos < Offsets.size() /// \endcode - const void *pointerTo(const size_t Pos) const noexcept override { + const void *pointerTo(const token_size_t Pos) const noexcept override { ASSERT(Pos < size()); return static_cast(Arena) + Offsets[Pos]; } /// 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 T 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 T /// /// \pre \p Pos is a valid index:\code /// Pos < Offsets.size() /// \endcode template bool isTypeAt(const size_t Pos) const noexcept { ASSERT(Pos < size()); Token TT = ST; dropNOfToken(TT, Pos); return isHeadOfTokenTheSameType(TT); } /// Gives a reference of a value of a given type stored at a given index. /// /// \note The constant variant of the function relies on this implementation, /// the function may not modify \p this object! /// /// \tparam T type to give a reference of /// /// \param Pos index to set the reference for /// /// \return reference of \p T 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 T: /// \code /// Pos < Size && isTypeAt(Pos) /// \endcode - template T &valueAt(const size_t Pos) noexcept { + template T &valueAt(const token_size_t Pos) noexcept { ASSERT(Pos < size() && isTypeAt(Pos)); return *static_cast(pointerTo(Pos)); } /// Gives a constant reference of a value of a given type stored at a given /// index. /// /// \tparam T type to give a reference of /// /// \param Pos index to set the reference for /// /// \return constant reference of \p T 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 T: /// \code /// Pos < Size && isTypeAt(Pos) /// \endcode - template const T &valueAt(const size_t Pos) const noexcept { + template + const T &valueAt(const token_size_t Pos) const noexcept { // \note Just use the non-const implementation as that does not modify // \p this object. return const_cast(this)->valueAt(Pos); } }; // Implementation of the static member field \c rosa::TokenizedStorage::Offsets. template const std::vector TokenizedStorage::Offsets = TokenizedStorage::offsets(); } // End namespace rosa #endif // ROSA_SUPPORT_TOKENIZED_STORAGES_HPP diff --git a/include/rosa/support/type_token.hpp b/include/rosa/support/type_token.hpp index ed753bf..ee08886 100644 --- a/include/rosa/support/type_token.hpp +++ b/include/rosa/support/type_token.hpp @@ -1,279 +1,288 @@ //===-- rosa/support/type_token.hpp -----------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/support/type_token.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Facilities for encoding TypeLists as unsigned integer values. /// /// \note **On the compatibility between different versions of the type token /// implementation:** /// Different software versions produce compatible type tokens as long as /// *backward compatibility* of \c rosa::BuiltinTypes is maintained (denoted by /// \c rosa::TypeNumberVersion) and the type token implementation uses the same /// type as \c rosa::token_t (boiling down to the same \c rosa::token::TokenBits /// value) and the same \c rosa::token::RepresentationBits value. Thus, /// interacting software need to cross-validate the aforementioned values to /// check compatibility. Interoperation between compatible sofware is limited /// to backward compatiblity, that is builtin types defined in both software /// versions are handled correctly but a newer system may produce a type token /// which is invalid in an old one. Therefore, tokens obtained from a compatible /// remote system need to be validated. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_TYPE_TOKEN_HPP #define ROSA_SUPPORT_TYPE_TOKEN_HPP #include "rosa/support/type_numbers.hpp" namespace rosa { /// Integer type to store type tokens. /// /// \note The trade-off between the binary overhead of type encoding and the /// maximal size of encodable lists can be tuned by using unsigned integer types /// of different widths as \c rosa::token_t. using token_t = uint64_t; /// Sanity check in case someone would change \c rosa::token_t. STATIC_ASSERT(std::is_unsigned::value, "token_t is not an unsigned integer"); /// Turn \c rosa::token_t into a strongly typed enumeration. /// /// Values of \c rosa::token_t casted to \c rosa::Token can be used in a /// type-safe way. enum class Token : token_t {}; /// A type to cast tokens into in order to output them to streams as /// numbers and not ASCII-codes. /// /// \note Use it for safety, necessary for printing \c uint8_t values. using printable_token_t = printable_t; /// Casts a \c rosa::Token into \c rosa::printable_token_t. /// /// \param T \c rosa::Token to cast #define PRINTABLE_TOKEN(T) static_cast(T) /// Converts a \c rosa::Token into \c std::string. /// /// \param T \c rosa::Token to convert /// /// \return \c std::string representing \p T inline std::string to_string(const Token T) { return std::to_string(static_cast(T)); } /// Encloses constants related to the implementation of \c rosa::Token. namespace token { /// The number of bits in one \c rosa::Token. constexpr size_t TokenBits = sizeof(Token) * 8; /// The number of bits a builtin type can be uniquely encoded into, that is any /// valid \c rosa::TypeNumber can fit into. /// /// \note There is one extra bit position added for encoding so that providing a /// better chance to maintain backward comaptibility when \c rosa::BuiltinTypes /// is extended. constexpr size_t RepresentationBits = log2(NumberOfBuiltinTypes) + 1; /// Maximal size of uniquely tokenizable \c rosa::TypeList. constexpr size_t MaxTokenizableListSize = TokenBits / RepresentationBits; } // End namespace token +/// Integer type to store length of a \c rosa::Token. +/// +/// \note The narrowest unsigned integer type that is wide enough to +/// represent \c rosa::token::MaxTokenizableListSize different values. +using token_size_t = typename TypeListFind< + typename TypeListDrop::Type, + IsNotNoneType>::Type::Second; + /// \defgroup TypeListTokenImpl Implementation of rosa::TypeListToken /// /// \brief Generates a \c rosa::Token for a squashed \c rosa::TypeList. /// /// \note Only to be used by the implementation of \c rosa::TypeListToken. ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to generate \c rosa::Token for template struct TypeListTokenImpl; /// Specialization for \c rosa::EmptyTypeList. template <> struct TypeListTokenImpl { static constexpr Token Value = static_cast(0); }; /// Specialization for a non-empty \c rosa::TypeList. template struct TypeListTokenImpl> { static constexpr TypeNumber TN = TypeNumberOf::Value; // Check if the generated type number is valid. STATIC_ASSERT(validTypeNumber(TN), "non-builtin type"); static constexpr Token Value = static_cast( (static_cast(TypeListTokenImpl>::Value) << token::RepresentationBits) | static_cast(TN)); }; ///@} /// \name TypeListToken /// /// \brief Generates a \c rosa::Token for a \c rosa::TypeList. /// /// \c rosa::Token for a \c rosa::TypeList \c List can be obtained as \code /// TypeListToken::Value /// \endcode /// /// \note The \c rosa::TypeList cannot have more than /// \c rosa::token::MaxTokenizableListSize elements and must be a subset of /// \c rosa::BuiltinTypes with respect to squashed integers. /// /// \note A generated \c rosa::Token uniquely represents a list of types, except /// for \c rosa::AtomConstant types. Observe that any \c rosa::AtomConstant is /// encoded as the type \c rosa::AtomValue. The type information on all separate /// \c rosa::AtomConstant types are lost and replaced by the \c rosa::AtomValue /// type whose actual value needs to be used in order to obtain the original /// \c rosa::AtomConstant type and so the full type information on the encoded /// \c rosa::TypeList. /// ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to generate \c rosa::Token for template struct TypeListToken; /// Implementation using \c rosa::TypeListTokenImpl. template struct TypeListToken> { /// \note \c rosa::TypeNumber is computed against \c rosa::squased_t, so let's /// do the same here. using List = typename SquashedTypeList>::Type; /// Check the length of the list here. /// \note Type validation is done one-by-one in \c rosa::TypeListTokenImpl. STATIC_ASSERT((TypeListSize::Value <= token::MaxTokenizableListSize), "too long list of types"); /// The \c rosa::Token for \p List. static constexpr Token Value = TypeListTokenImpl::Value; }; ///@} /// Convenience template to generate \c rosa::Token for a list of types. template using TypeToken = TypeListToken>; /// Tells if a given \c rosa::Token is valid. /// /// A \c rosa::Token is considered valid if it can be decoded by the current /// software. /// /// \note Validation gives a correct result only when \p T was generated by a /// compatible software. /// /// \sa Note for type_token.hpp on compatibility. /// /// \param T \c rosa::Token to validate /// /// \return if \p T is valid bool validToken(const Token T); /// Tells if a \c rosa::Token does encode an empty list of types. /// /// \param T \c rosa::Token to check /// /// \return if \p T encodes an empty list of types bool emptyToken(const Token T); /// Tells how many types are encoded in a \c rosa::Token. /// /// \param T \c rosa::Token to check /// /// \return how many types are encoded in \p T -size_t lengthOfToken(const Token T); +token_size_t lengthOfToken(const Token T); /// Tells the full memory size of the types encoded in a \c rosa::Token. /// /// The full memory size of the types encoded in a \c rosa::Token is the sum of /// the separate memory sizes of all the types encoded in the \c rosa::Token. /// /// \param T \c rosa::Token to check /// /// \return full memory size of the types encoded in \p T /// /// \pre \p T is valid:\code /// validToken(T) /// \endcode size_t sizeOfValuesOfToken(const Token T); /// Extracts the \c rosa::TypeNumber of the first encoded type of a /// \c rosa::Token. /// /// \note The returned type number is not validated. /// /// \param T \c rosa::Token to take the first \c rosa::TypeNumber from /// /// \return the first \c rosa::TypeNumber encoded in \p T inline TypeNumber headOfToken(const Token T) { return static_cast(static_cast(T) & ((1 << token::RepresentationBits) - 1)); } /// Tells the memory size of the first type encoded in a \c rosa::Token. /// /// \param T \c rosa::Token to check the first encoded type of /// /// \return memory size of the first type encoded in \p T /// /// \pre \p T is not empty and valid:\code /// !empty(T) && validToken(T) /// \endcode size_t sizeOfHeadOfToken(const Token T); /// Gives the textual representation of the first type encoded in a /// \c rosa::Token. /// /// \param T \c rosa::Token to take the name of its first encoded type /// /// \return textual representation of the first type encoded in \p T /// /// \pre \p T is not empty and valid:\code /// !empty(T) && validToken(T) /// \endcode const char *nameOfHeadOfToken(const Token T); /// Updates a \c rosa::Token by dropping its first encoded type. /// /// \param [in,out] T \c rosa::Token to drop the first encoded type of void dropHeadOfToken(Token &T); /// Updates a \c rosa::Token by dropping a number of its first encoded types /// /// \param [in,out] T \c rosa::Token to drop the first \p N encoded types of /// \param N the number of types to drop from \p T void dropNOfToken(Token &T, const size_t N); /// Tells if the first encoded type of a \c rosa::Token is a given type. /// /// \tparam Type type to match the first encoded type of \p T against /// /// \param T \c rosa::Token whose first encoded type is to be matched against /// \p Type /// /// \return if the first encoded type of \p T is \p Type /// /// \pre \p T is not empty and valid:\code /// !empty(T) && validToken(T) /// \endcode template bool isHeadOfTokenTheSameType(const Token T) { ASSERT(!emptyToken(T) && validToken(T)); return TypeNumberOf::Value == headOfToken(T); } } // End namespace rosa #endif // ROSA_SUPPORT_TYPE_TOKEN_HPP diff --git a/lib/deluxe/DeluxeAgent.cpp b/lib/deluxe/DeluxeAgent.cpp index b32ac99..3a100fc 100755 --- a/lib/deluxe/DeluxeAgent.cpp +++ b/lib/deluxe/DeluxeAgent.cpp @@ -1,235 +1,237 @@ //===-- deluxe/DeluxeAgent.cpp ----------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file deluxe/DeluxeAgent.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Implementation of rosa/deluxe/DeluxeAgent.hpp. /// //===----------------------------------------------------------------------===// #include "rosa/deluxe/DeluxeAgent.hpp" #include "rosa/deluxe/DeluxeSensor.hpp" #include namespace rosa { namespace deluxe { bool DeluxeAgent::inv(void) const noexcept { // Check number of inputs and master-outputs. if (NumberOfInputs != NumberOfMasterOutputs) { return false; } // Check container sizes. if (!(InputTypes.size() == NumberOfInputs && InputChanged.size() == NumberOfInputs && InputValues->size() == NumberOfInputs && MasterOutputTypes.size() == NumberOfInputs // == NumberOfMasterOutputs && Slaves.size() == NumberOfInputs)) { return false; } // Check *slave* types and validate *slave* registrations and reverse lookup // information. std::map RefIds; // Build up a reference of SlaveIds in this. for (size_t I = 0; I < NumberOfInputs; ++I) { // First, validate input types at position \c I. const TypeNumber T = InputTypes[I]; - if (InputValues->typeAt(I) != T) { + // The assert must hold if \p this object was successfuuly constructed. + ASSERT(static_cast(static_cast(I)) == I); + if (InputValues->typeAt(static_cast(I)) != T) { return false; } // Check the registered *slave* at position \c I. const auto &Slave = Slaves[I]; // If \c Slave is empty, nothing to check. if (!Slave) continue; // Prepare master-output related info for the *slave*. const TypeNumber MT = MasterOutputTypes[I]; const bool hasMT = MT != TypeNumberOf::Value; // \c Slave is not empty here. // Check the `OutputType` and `MasterInputType` of the registered *slave*. const auto &A = unwrapAgent(*Slave); if (!((A.Kind == atoms::SensorKind && static_cast(A).OutputType == T && (!hasMT || static_cast(A).MasterInputType == MT)) || (A.Kind == atoms::AgentKind && static_cast(A).OutputType == T && (!hasMT || static_cast(A).MasterInputType == MT)))) { return false; } // Validate that the *slave* is not registered more than once. if (std::any_of( Slaves.begin() + I + 1, Slaves.end(), [&Slave](const Optional &O) { return O && *Slave == *O; })) { return false; } // Build the content of \c RefIds. RefIds.emplace(A.Id, I); } // Validate *slave* reverse lookup information against our reference. if (RefIds != SlaveIds) { return false; } // All checks were successful, the invariant is held. return true; } DeluxeAgent::~DeluxeAgent(void) noexcept { ASSERT(inv()); LOG_TRACE("Destroying DeluxeAgent..."); // Make sure \p this object is not a registered *slave*. if (Master) { ASSERT(unwrapAgent(*Master).Kind == atoms::AgentKind); // Sanity check. DeluxeAgent &M = static_cast(unwrapAgent(*Master)); ASSERT(M.positionOfSlave(self()) != M.NumberOfInputs); // Sanity check. M.registerSlave(M.positionOfSlave(self()), {}); Master = {}; } // Also, make sure \p this object is no acting *master*. for (size_t Pos = 0; Pos < NumberOfInputs; ++Pos) { registerSlave(Pos, {}); } // Now there is no connection with other entities, safe to destroy. } id_t DeluxeAgent::masterId(void) const noexcept { ASSERT(inv() && Master); return unwrapAgent(*Master).Id; } Optional DeluxeAgent::master(void) const noexcept { ASSERT(inv()); return Master; } void DeluxeAgent::registerMaster(const Optional _Master) noexcept { ASSERT(inv() && (!_Master || unwrapAgent(*_Master).Kind == atoms::AgentKind)); Master = _Master; ASSERT(inv()); } TypeNumber DeluxeAgent::inputType(const size_t Pos) const noexcept { ASSERT(inv() && Pos < NumberOfInputs); return InputTypes[Pos]; } TypeNumber DeluxeAgent::masterOutputType(const size_t Pos) const noexcept { ASSERT(inv() && Pos < NumberOfMasterOutputs); return MasterOutputTypes[Pos]; } Optional DeluxeAgent::slave(const size_t Pos) const noexcept { ASSERT(inv() && Pos < NumberOfInputs); return Slaves[Pos]; } void DeluxeAgent::registerSlave(const size_t Pos, const Optional Slave) noexcept { ASSERT(inv() && Pos < NumberOfInputs && (!Slave || (unwrapAgent(*Slave).Kind == atoms::SensorKind && static_cast(unwrapAgent(*Slave)).OutputType == InputTypes[Pos] && (MasterOutputTypes[Pos] == TypeNumberOf::Value || static_cast(unwrapAgent(*Slave)) .MasterInputType == MasterOutputTypes[Pos])) || (unwrapAgent(*Slave).Kind == atoms::AgentKind && static_cast(unwrapAgent(*Slave)).OutputType == InputTypes[Pos] && (MasterOutputTypes[Pos] == TypeNumberOf::Value || static_cast(unwrapAgent(*Slave)) .MasterInputType == MasterOutputTypes[Pos])))); // If registering an actual *slave*, not just clearing the slot, make sure // the same *slave* is not registered to another slot. if (Slave) { auto It = SlaveIds.find(unwrapAgent(*Slave).Id); if (It != SlaveIds.end()) { Slaves[It->second] = {};//Optional(); SlaveIds.erase(It); } } // Obtain the place whose content is to be replaced with \p Slave auto &OldSlave = Slaves[Pos]; // If there is already a *slave* registered at \p Pos, clear reverse lookup // information for it, and make sure it no longer has \p this object as // *master*. if (OldSlave) { auto &A = unwrapAgent(*OldSlave); ASSERT(SlaveIds.find(A.Id) != SlaveIds.end()); // Sanity check. SlaveIds.erase(A.Id); if (A.Kind == atoms::AgentKind) { static_cast(A).registerMaster({}); } else { ASSERT(A.Kind == atoms::SensorKind); // Sanity check. static_cast(A).registerMaster({}); } } // Register \p Slave at \p Pos. OldSlave = Slave; // If registering an actual *slave*, not just clearing the slot, register // reverse lookup information for the new *slave*. if (Slave) { SlaveIds.emplace(unwrapAgent(*Slave).Id, Pos); } ASSERT(inv()); } size_t DeluxeAgent::positionOfSlave(const AgentHandle Slave) const noexcept { ASSERT(inv()); bool Found = false; size_t Pos = 0; while (!Found && Pos < NumberOfInputs) { auto &ExistingSlave = Slaves[Pos]; if (ExistingSlave && *ExistingSlave == Slave) { Found = true; } else { ++Pos; } } ASSERT(Found || Pos == NumberOfInputs); // Sanity check. return Pos; } void DeluxeAgent::handleTrigger(atoms::Trigger) noexcept { ASSERT(inv()); FP(); ASSERT(inv()); } } // End namespace deluxe } // End namespace rosa diff --git a/lib/support/type_token.cpp b/lib/support/type_token.cpp index 30c37c7..508b471 100644 --- a/lib/support/type_token.cpp +++ b/lib/support/type_token.cpp @@ -1,131 +1,131 @@ //===-- support/type_token.cpp ----------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file support/type_token.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \brief Implementation for rosa/support/type_token.hpp. /// /// \todo Automatically generate proper cases for the switches with a big number /// of handcrafted similar cases in them. /// //===----------------------------------------------------------------------===// #include "rosa/support/type_token.hpp" namespace rosa { using namespace token; bool validToken(const Token T) { Token TT = T; bool Valid = true; while (Valid && !emptyToken(TT)) { Valid &= validTypeNumber(headOfToken(TT)); dropHeadOfToken(TT); } return Valid; } bool emptyToken(const Token T) { return static_cast(T) == 0; } -size_t lengthOfToken(const Token T) { +token_size_t lengthOfToken(const Token T) { Token TT = T; - size_t N = 0; + token_size_t N = 0; while (!emptyToken(TT)) { ++N; dropHeadOfToken(TT); } return N; } size_t sizeOfValuesOfToken(const Token T) { ASSERT(validToken(T)); Token TT = T; size_t S = 0; while (!emptyToken(TT)) { S += sizeOfHeadOfToken(TT); dropHeadOfToken(TT); } return S; } #define SIZECASE(N) \ { \ case N: \ return TypeForNumber(N)>::Size; \ } size_t sizeOfHeadOfToken(const Token T) { ASSERT(!emptyToken(T) && validToken(T)); switch (static_cast(headOfToken(T))) { default: { // Should never come here when \p T is valid and the case-list below covers // \c rosa::BuiltinTypes. ROSA_CRITICAL("unknown type number"); } SIZECASE(1); SIZECASE(2); SIZECASE(3); SIZECASE(4); SIZECASE(5); SIZECASE(6); SIZECASE(7); SIZECASE(8); SIZECASE(9); SIZECASE(10); SIZECASE(11); SIZECASE(12); SIZECASE(13); SIZECASE(14); SIZECASE(15); } } #define NAMECASE(N) \ { \ case N: \ return TypeForNumber(N)>::Name; \ } const char *nameOfHeadOfToken(const Token T) { ASSERT(!emptyToken(T) && validToken(T)); switch (static_cast(headOfToken(T))) { default: { // Should never come here when \p T is valid and the case-list below covers // \c rosa::BuiltinTypes. ROSA_CRITICAL("unknown type number"); } NAMECASE(1); NAMECASE(2); NAMECASE(3); NAMECASE(4); NAMECASE(5); NAMECASE(6); NAMECASE(7); NAMECASE(8); NAMECASE(9); NAMECASE(10); NAMECASE(11); NAMECASE(12); NAMECASE(13); NAMECASE(14); NAMECASE(15); } } void dropHeadOfToken(Token &T) { T = static_cast(static_cast(T) >> RepresentationBits); } void dropNOfToken(Token &T, const size_t N) { T = static_cast(static_cast(T) >> (N * RepresentationBits)); } } // End namespace rosa