diff --git a/include/rosa/deluxe/DeluxeAgent.hpp b/include/rosa/deluxe/DeluxeAgent.hpp index 5534660..099158e 100755 --- a/include/rosa/deluxe/DeluxeAgent.hpp +++ b/include/rosa/deluxe/DeluxeAgent.hpp @@ -1,1124 +1,1423 @@ //===-- 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 "rosa/deluxe/DeluxeTuple.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); \ + void DASLAVEHANDLERNAME(N)(atoms::Slave, id_t SlaveId, token_size_t Pos, \ + T Value) noexcept { \ + saveInput(SlaveId, Pos, 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, \ + void DAMASTERHANDLERNAME(N)(atoms::Master, id_t MasterId, token_size_t Pos, \ T Value) noexcept { \ - saveMasterInput(MasterId, Value); \ + saveMasterInput(MasterId, Pos, 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. +/// rosa::deluxe::DeluxeAgent::NumberOfMasterOutputs. Types and type-related +/// information of input and master-output values are consistent throughout all +/// the input-related and master-output-related containers, respectively. The +/// actual values in \c rosa::deluxe::DeluxeAgent::InputNextPos and \c +/// rosa::deluxe::DeluxeAgent::MasterInputNextPos are valid with respect to the +/// corresponding types. 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*. + /// That is the types of values \p this object sends to its *master* in a \c + /// rosa::deluxe::DeluxeTUple. /// /// \see \c rosa::deluxe::DeluxeAgent::master - const TypeNumber OutputType; + const Token OutputType; /// Number of inputs processed by \p this object. const size_t NumberOfInputs; /// The type of values \p this object processes from its *master*. /// + /// That is the types of values \p this object receives from its *master* in a + /// \c rosa::deluxe::DeluxeTuple. + /// /// \see \c rosa::deluxe::DeluxeAgent::master - const TypeNumber MasterInputType; + const Token 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 \c rosa::Token values stored correspond to \c + /// rosa::deluxe::DeluxeTuple instances at each argument position. The \c + /// rosa::TypeNumber values from the stored \c rosa::Token values match the + /// corresponding values in \c rosa::deluxe::DeluxeAgent::InputValues in + /// order. /// - /// \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; + /// \note The position of a \c rosa::Token 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 which element of an input is expected from any particular + /// *slave*. + /// + /// The *slave* is supposed to send one \c rosa::deluxe::DeluxeTuple value + /// element by element in their order of definition. This member field tells + /// the element at which position in the tuple should be received next from + /// the *slave* at a given position. + /// + /// \p this object is supposed to be triggered only when input values has been + /// received completely, that is all values in the field should hold the value + /// `0`. + /// + /// \see \c rosa::deluxe::DeluxeAgent::handleTrigger + /// \c rosa::deluxe::DeluxeAgent::saveInput + std::vector InputNextPos; /// 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; + /// Tells at which position in \c rosa::deluxe::DeluxeAgent::InputValues the + /// input from any particular *slave* starts. + /// + /// \note A value in the vector corresponds to the *slave* at the same + /// position and it is the sum of the elements of input values from *slaves* + /// at previous positions. + /// + /// \see \c rosa::deluxe::DeluxeAgent::saveInput + const std::vector InputStorageOffsets; + /// Stores the actual input values. /// /// \note The types of stored values match the corresponding - /// \c rosa::TypeNumber values in \c rosa::deluxe::DeluxeAgent::InputTypes. + /// \c rosa::TypeNumber values (in \c rosa::Token in order) 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. + /// indicates which element of the tuple of which argument of \p this object's + /// processing function it is. See also \c + /// rosa::deluxe::DeluxeAgent::DeluxeAgent. const std::unique_ptr InputValues; + /// Indicates which element of the master-input is expected from the *master*. + /// + /// The *master* is supposed to send one \c rosa::deluxe::DeluxeTuple value + /// element by element in their order of definition. This member field tells + /// the element at which position should be received next. + /// + /// \p this object is supposed to be triggered only when a complete + /// master-input has been received, that is the field should hold the value + /// `0`. + /// + /// \see \c rosa::deluxe::DeluxeAgent::handleTrigger + /// \c rosa::deluxe::DeluxeAgent::saveMasterInput + token_size_t MasterInputNextPos; + /// 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 + /// \note The type of the stored value matches the types 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*. + /// That is the types of values \p this object sends to its *slaves* in a \c + /// rosa::deluxe::DeluxeTuple. /// /// \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; + 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 + /// \return if types in \p As are instances of \c rosa::deluxe::DeluxeTuple + /// and their types match \c rosa::Token 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 + /// \return if types in \p Ts match \c rosa::Token and in turn \c + /// rosa::TypeNumber values stored in \c + /// rosa::deluxe::DeluxeAgent::MasterOutputTypes template bool masterOutputTypesMatch(void) const noexcept; + /// TODO!!! + template + DeluxeTuple prepareInputValueAtPos(TypeList, Seq) 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) + /// \pre Statically, all type arguments \p As... are instances of \c + /// rosa::deluxe::DeluxeTuple and the provided indices \p S0... match the + /// length of \p As...: \code + /// TypeListAllDeluxeTuple>::Value && + /// sizeof...(As) == sizeof...(S0) + /// \endcode Dynamically, type arguments \p As... match the input types of \p + /// this object: \code + /// inputTypesMatch>() /// \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. + /// master-output 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. + /// Handles a master-output value for a particular *slave* position. /// - /// \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*. + /// \p Value is a \c rosa::Optional resulted by a processing function and + /// contains a master-output value for the *slave* at position \p Pos. The + /// function takes the master-output value 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 A master-output of type \c rosa::deluxe::EmptyDeluxeTuple indicates + /// no actual output and hence no message is generated for a position whose + /// corresponding master-output type is \c rosa::deluxe::EmptyDeluxeTuple. /// /// \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 + /// \tparam Pos the position of the master-output to send \p Value for + /// \tparam Ts types of elements in \p Value /// - /// \param Output \c std::tuple resulted by the processing function + /// \param Value \c rosa::deluxe::DeluxeTuple resulted by the processing + /// function for *slave* position \p Pos /// - /// \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 + /// \pre \p Pos is a valid master-output position and \p Value matches the + /// master-output type of \p this object at position \p Pos: \code + /// Pos < NumberOfMasterOutputs && + /// DeluxeTuple::TT == MasterOutputTypes[Pos] /// \endcode - template - void handleMasterOutputAtPos( - const std::tuple...> &Output) noexcept; + template + void + handleMasterOutputAtPos(const Optional> &Value) 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 /// + /// \note Instantiation fails if any of the type arguments \p Ts... starting + /// at position \p Offset is not an instance of \c rosa::deluxe::DeluxeTuple + /// or the number of types \p Ts... is not consistent with the other template + /// arguments. + /// /// \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 + /// \pre Statically, type arguments \p Ts... starting at position \p Offset + /// are instances of \c rosa::deluxe::DeluxeTuple and the number of types \p + /// Ts... is consistent with the other template arguments: \code + /// TypeListAllDeluxeTuple< + /// typename TypeListDrop>::Type>::Value && + /// sizeof...(Ts) == Offset + sizeof...(S0) + /// \endcode Dynamically, \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 extracting all master-output values from + /// \p Output: \code /// masterOutputTypesMatch>::Type>() && /// sizeof...(S0) == NumberOfMasterOutputs /// \endcode template - void - handleMasterOutputs(const std::tuple...> &Output, - Seq) noexcept; + 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 MTs types of elements of master-input processed by \p MF /// \tparam T type of output /// \tparam Ts types of master-output values /// \tparam As types of input values + /// \tparam S0 indices for accessing master-input values + /// + /// \note Instantiation fails if any of the type arguments \p T, \p Ts..., + /// and \p As... is not an instance of \c rosa::deluxe::DeluxeTuple. /// /// \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. + /// \note The last argument provides indices statically as template + /// arguments \p S0..., so its actual value is ignored. + /// + /// \note A master-input type of \c rosa::deluxe::EmptyDeluxeTuple indicates + /// that \p this object does not receive master-input, \p MF is never called + /// if \p MTs is empty. /// /// \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 && + /// \pre Statically, type arguments \p T, \p Ts..., and \p As... are + /// instances of \c rosa::deluxe::DeluxeTuple and the indices match + /// master-input elements: \code + /// TypeListAllDeluxeTuple>::Value && + /// sizeof...(MTs) == sizeof...(S0) + /// \endcode Dynamically, template arguments \p MTs..., \p T, \p Ts..., and + /// \p As... match the corresponding types \p this object was created with: + /// \code + /// MasterInputType == DeluxeTuple::TT && OutputType == T::TT && /// inputTypesMatch>() && /// masterOutputTypesMatch>() /// \endcode - template + template H triggerHandlerFromProcessingFunctions( - std::function...>(std::pair)> &&MF, - std::function, Optional...>( - std::pair...)> &&F) noexcept; + std::function...>( + std::pair, bool>)> &&MF, + std::function< + std::tuple, Optional...>(std::pair...)> &&F, + Seq) 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. + /// Ts..., and \p As... is not an instance of \c rosa::deluxe::DeluxeTuple or + /// any of \p T and \p As... is \c rosa::deluxe::EmptyDeluxeTuple or the + /// number of inputs and master-outputs are not equal. /// - /// \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. + /// \note If \p MT is \c rosa::deluxe::EmptyDeluxeTuple, the constructed + /// object does not receive master-input. Similarly, if any of \p Ts... is \c + /// rosa::deluxe::EmptyDeluxeTuple, 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) + /// \pre Statically, all the type arguments \p MT, \p T, \p Ts..., and \p + /// As... are instances of \c rosa::deluxe::DeluxeTuple, with \p T and \p + /// As... containing at least one element, and the number of input and + /// master-output types are equal: \code + /// TypeListAllDeluxeTuple::Value && + /// T::Length > 0 && (true && ... && As::Length > 0) && + /// 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 + /// + /// \see \c rosa::deluxe::DeluxeTuple template , - BuiltinTypes>::Value && - sizeof...(Ts) == sizeof...(As)>> + typename = std::enable_if_t< + TypeListAllDeluxeTuple>::Value && + (T::Length > 0) && (true && ... && (As::Length > 0)) && + 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. + /// Tells the types 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. + /// That is the type of values \p this object expect to be sent to it in a \c + /// rosa::deluxe::DeluxeTuple 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 + /// \return \c rosa::Token representing the types 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; + Token inputType(const size_t Pos) const noexcept; - /// Tells the type of values produced for the *slave* at a position. + /// Tells the types 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. + /// That is the types of values \p this object potentially sends in a \c + /// rosa::deluxe::DeluxeTuple 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 + /// \return \c rosa::Token representing the types 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; + Token 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 || + /// (emptyToken(MasterOutputTypes[Pos]) || /// 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 || + /// (emptyToken(MasterOutputTypes[Pos]) || /// 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 + /// The elements from \p Value are sent one by one in separate messages to the + /// *master*. + /// + /// \tparam Ts types of the elements in \p Value + /// \tparam S0 indices for accessing elements of \p Value /// /// \param Value value to send /// - /// \pre \p T matches \c rosa::deluxe::DeluxeiAgent::OutputType: \code - /// OutputType == TypeNumberOf::Value + /// \note The second argument provides indices statically as template + /// arguments \p S0..., so its actual value is ignored. + /// + /// \pre Statically, the indices match the elements: \code + /// sizeof...(Ts) == sizeof...(S0) + /// \endcode Dynamically, \p Ts match \c + /// rosa::deluxe::DeluxeiAgent::OutputType: \code + /// OutputType == TypeToken::Value /// \endcode - template void sendToMaster(const T &Value) noexcept; + template + void sendToMaster(const DeluxeTuple &Value, Seq) 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 + /// The elements from \p Value are sent one by one in separate messages to the + /// *slave*. + /// + /// \tparam Ts types of the elements in \p Value + /// \tparam S0 indices for accessing elements of \p Value /// /// \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 + /// \pre Statically, the indices match the elements: \code + /// sizeof...(Ts) == sizeof...(S0) + /// \endcode Dynamically, \p Pos is a valid *slave* position and \p Ts match + /// \c rosa::deluxe::DeluxeiAgent::MasterOutputTypes[Pos]: \code /// Pos < NumberOfMasterOutputs && - /// MasterOutputTypes[Pos] == TypeNumberOf::Value + /// MasterOutputTypes[Pos] == TypeToken::Value /// \endcode - template - void sendToSlave(const size_t Pos, const T &Value) noexcept; + template + void sendToSlave(const size_t Pos, const DeluxeTuple &Value, + Seq) 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. + /// + /// \pre Master-input and all input from *slaves* are supposed to be + /// completely received upon triggering: \code + /// MasterInputNextPos == 0 && + /// std::all_of(InputNextPos.begin(), InputNextPos.end(), + /// [](const token_size_t &I){return I == 0;}) + /// \endcode 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. + /// The function stores \p Value at position \p Pos 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. The function also + /// takes care of checking and updating \c + /// rosa::deluxe::DeluxeSensor::MasterInputNextPos at the corresponding + /// position: increments the value and resets it to `0` when the last element + /// is received. /// /// \note Utilized by member functions of group \c DeluxeAgentInputHandlers. /// /// \tparam T type of input to store /// /// \param Id unique identifier of *slave* + /// \param Pos position of the value in the \c rosa::deluxe::DeluxeTuple /// \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 + /// \pre The *slave* with \p Id is registered, \p Pos is the expected + /// position of input from the *slave*, 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 + /// Pos == InputNextPos[SlaveIds.find(Id)->second] && + /// typeAtPositionOfToken(InputTypes[SlaveIds.find(Id)->second], Pos) == + /// TypeNumberOf::Value /// \endcode - template void saveInput(id_t Id, T Value) noexcept; + template + void saveInput(id_t Id, token_size_t Pos, T Value) noexcept; /// Stores a new input value from the *master*. /// - /// The function stores \p Value in \c + /// The function stores \p Value at position \p Pos in \c /// rosa::deluxe::DeluxeAgent::MasterInputValue and also sets the - /// corresponding flag \c rosa::deluxe::DeluxeAgent::MasterInputChanged. + /// flag \c rosa::deluxe::DeluxeAgent::MasterInputChanged. The function also + /// takes care of checking and updating \c + /// rosa::deluxe::DeluxeAgent::MasterInputNextPos: increments its value and + /// reset to `0` when the last element is received. /// /// \note Utilized by member functions of group \c /// DeluxeAgentMasterInputHandlers. /// /// \tparam T type of input to store /// /// \param Id unique identifier of the *master* + /// \param Pos position of the value in the \c rosa::deluxe::DeluxeTuple /// \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 + /// \pre The *master* with \p Id is registered, \p Pos is the expected + /// position of master-input, and the input from the *master* at position \p + /// Pos is expected to be of type \p T: \code + /// Master && masterId() == Id && Pos == MasterInputNextPos && + /// typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf::Value /// \endcode - template void saveMasterInput(id_t Id, T Value) noexcept; + template + void saveMasterInput(id_t Id, token_size_t Pos, 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. + /// built-in type 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. + /// \note Keep these definitions in sync with \c rosa::BuiltinTypes. /// ///@{ 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(unit_t) DAMASTERHANDLERDEF(bool) DAMASTERHANDLERDEF(double) DAMASTERHANDLERDEF(float) /// @} }; -/// Anonymous namespace with implementation for -/// \c rosa::deluxe::DeluxeAgent::inputTypesMatch and \c +/// Anonymous namespace with implementation for \c +/// rosa::deluxe::DeluxeAgent::DeluxeAgent, \c +/// rosa::deluxe::DeluxeAgent::inputTypesMatch, and \c /// rosa::deluxe::DeluxeAgent::masterOutputTypesMatch, consider it private. namespace { +/// Calculates storage offsets for values of \p Ts... stored in a \c +/// rosa::TokenizedStorage. +/// +/// \note Utilized by \c rosa::deluxe::DeluxeAgnet::DeluxeAgent to initialize \c +/// rosa::deluxe::DeluxeAgent::InputStorageOffsets. +/// +/// \tparam Ts types whose offsets to calculate +/// \tparam S0 indices for referring to positions in \p Ts... +/// +/// \note Instantiation fails if any of the type arguments \p Ts... is not an +/// instance of \c rosa::deluxe::DeluxeTuple. +/// +/// \note The only argument provides indices statically as template +/// arguments \p S0..., so its actual value is ignored. +/// +/// \return \c std::vector containing the calculated offsets +/// +/// \pre Statically, all the type arguments \p Ts... are instances of \c +/// rosa::deluxe::DeluxeTuple and the indices match the types: \code +/// TypeListAllDeluxeTuple>::Value && +/// sizeof...(Ts) == sizeof...(S0) +/// \endcode +template < + typename... Ts, size_t... S0, + typename = std::enable_if_t>::Value>> +static std::vector storageOffsets(Seq) noexcept { + STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments"); + std::vector Offsets(sizeof...(Ts)); + // Do nothing for no types. + if constexpr (sizeof...(Ts) != 0) { + Offsets[0] = 0; // The offset of the very first value is always `0`. + // Calculate further offsets... + (((S0 != sizeof...(Ts) - 1) && + (Offsets[S0 + 1] = Offsets[S0] + Ts::Length)), + ...); + } + return Offsets; +} + /// 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. +/// Template specialization for the case, when at least one type is to +/// be matched and that is an instance of \c rosa::deluxe::DeluxeTuple. /// -/// \tparam A first type to match +/// \tparam Ts types of elements in the \c rosa::deluxe::DeluxeTuple 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. +template +struct TypesMatchImpl, As...> { + /// Tells whether types \c rosa::deluxe::DeluxeTuple and \p As... match + /// \c rosa::Token values stored in \p Tokens 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); + /// type \c rosa::deluxe::DeluxeTuple against \c rosa::Token at + /// position \p Pos of \p Tokens, then further types \p As... are matched + /// recursively starting at position \c (Pos + 1). + /// + /// \param Tokens container of \c rosa::Token values to match types against + /// \param Pos position in \p Tokens to start matching at + /// + /// \return if types \c rosa::deluxe::DeluxeTuple and \p As... match \c + /// rosa::Token values stored in \p Tokens starting at position \p Pos + static bool f(const std::vector &Tokens, size_t Pos) noexcept { + return Pos < Tokens.size() && TypeToken::Value == Tokens[Pos] && + TypesMatchImpl::f(Tokens, Pos + 1); } }; +/// Template specialization for the case, when at least one type is to +/// be matched and that is *not* an instance of \c rosa::deluxe::DeluxeTuple. +/// +/// \tparam T first type to match +/// \tparam As further types to match +template +struct TypesMatchImpl { + /// Tells whether types \p T and \p As... match \c rosa::Token values stored + /// in \p Tokens starting at position \p Pos. + /// + /// This specialization is used only when \p T is not an instance of \c + /// rosa::deluxe::DeluxeTuple, in which case the match is not successful. + /// + /// \note The function takes two parameters to match the general signature but + /// the actual values are ignored. + /// + /// \return `false` + static bool f(const std::vector &, size_t) noexcept { return false; } +}; + /// 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. + /// Tells whether \p Pos is the number of values stored in \p Tokens. /// /// 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. + /// \p Tokens. That is true if \p Pos points exactly to the end of \p Tokens. /// - /// \param Types container of \c rosa::TypeNumber values to match types - /// against - /// \param Pos position in \p Types to start matching at + /// \param Tokens container of \c rosa::Token values to match types against + /// \param Pos position in \p Tokens 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(); + /// \return if \p Pos is the number of values stored in \p Tokens + static bool f(const std::vector &Tokens, size_t Pos) noexcept { + return Pos == Tokens.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); +/// \note Match a list of types \p List against a \c std::vector of +/// \c rosa::Token values, \c Tokens, like \code +/// bool match = TypesMatchList::f(Tokens); /// \endcode +/// If any type in \c rosa::TypeList \p Listis not an instance of \c +/// rosa::deluxe::DeluxeTuple, the match gives a negative result. /// -/// \tparam As \c rosa::TypeList that contains types to match -template struct TypesMatchList; +/// \tparam List \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 +/// \tparam As types to match template struct TypesMatchList> { - /// Tells whether types \p As... match \c rosa::TypeNumber values - /// stored in \p Types. + /// Tells whether types \p As... match \c rosa::Token values stored in \p + /// Tokens. /// /// 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 + /// \param Tokens container of \c rosa::Token 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); + /// \return if types \p As... match \c rosa::Token values stored in \p Tokens + static bool f(const std::vector &Tokens) noexcept { + return TypesMatchImpl::f(Tokens, 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)); +template +DeluxeTuple DeluxeAgent::prepareInputValueAtPos(TypeList, + Seq) const + noexcept { + using T = DeluxeTuple; + ASSERT(inv() && Pos < NumberOfInputs && T::TT == InputTypes[Pos]); + + const token_size_t StorageOffset = InputStorageOffsets[Pos]; + // The below should hold because of the above, just leave it for sanity check. - ASSERT((true && ... && static_cast(static_cast(S0)))); + ASSERT((true && ... && + (static_cast(static_cast(S0)) == S0))); + + // Get all elements of the tuple in a fold expression. + return T(*static_cast(InputValues->pointerTo( + static_cast(StorageOffset + S0)))...); +} - return std::make_tuple( - std::make_pair(*static_cast( - InputValues->pointerTo(static_cast(S0))), - InputChanged[S0])...); +template +std::tuple...> +DeluxeAgent::prepareCurrentInputs(Seq) const noexcept { + STATIC_ASSERT(TypeListAllDeluxeTuple>::Value, + "not tuple types"); + STATIC_ASSERT(sizeof...(As) == sizeof...(S0), "inconsistent type arguments"); + ASSERT(inv() && inputTypesMatch>()); + + return std::make_tuple(std::make_pair( + prepareInputValueAtPos(typename UnwrapDeluxeTuple::Type(), + seq_t()), + 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 +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); + const Optional> &Value) noexcept { + using MOT = DeluxeTuple; + ASSERT(inv() && Pos < NumberOfMasterOutputs && + MOT::TT == MasterOutputTypes[Pos]); + // Do not do anything for master-output of type \c + // rosa::deluxe::EmptyDeluxeTuple and when \p Value is empty. + if constexpr (!std::is_same_v) { + if (Value) { + sendToSlave(Pos, *Value, seq_t()); } + } else { + (void)Value; } + ASSERT(inv()); } 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); + using MOTs = typename TypeListDrop>::Type; + STATIC_ASSERT(TypeListAllDeluxeTuple::Value, + "not tuple type arguments"); + STATIC_ASSERT(sizeof...(Ts) == Offset + sizeof...(S0), + "inconsistent arguments"); + ASSERT(inv() && masterOutputTypesMatch() && + sizeof...(S0) == NumberOfMasterOutputs); // Handle each master-output position in a fold expression. - (handleMasterOutputAtPos(Output), ...); + (handleMasterOutputAtPos(std::get(Output)), ...); + ASSERT(inv()); } -template +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)()); + std::function< + std::tuple...>(std::pair, bool>)> &&MF, + std::function< + std::tuple, Optional...>(std::pair...)> &&F, + Seq) noexcept { + using MT = DeluxeTuple; + STATIC_ASSERT((TypeListAllDeluxeTuple>::Value), + "not tuple type arguments"); + STATIC_ASSERT(sizeof...(MTs) == sizeof...(S0), "inconsistent arguments"); + ASSERT(MasterInputType == MT::TT && OutputType == T::TT && + inputTypesMatch>() && + masterOutputTypesMatch>()); return [ this, MF, F ]() noexcept { // \note These indices work for both inputs and master-outputs. - using Indices = typename GenSeq::Type; + using SlaveIndices = seq_t; // Handle master-input. - // Do not do anything for master-input type \c rosa::unit_t. - if (MasterInputType != TypeNumberOf::Value) { + // Do not do anything for master-input type \c + // rosa::deluxe::EmptyDeluxeTuple. + if (!std::is_same_v) { LOG_TRACE_STREAM << "DeluxeAgent " << Name << " handles master-input." << std::endl; + // The assert must hold if \p this object was successfuuly constructed. + ASSERT((true && ... && + (static_cast(static_cast(S0)) == S0))); const auto MasterInputArg = std::make_pair( - *static_cast(MasterInputValue->pointerTo(0)), + // Get all elements of the tuple in a fold expression. + MT(*static_cast( + MasterInputValue->pointerTo(static_cast(S0)))...), MasterInputChanged); MasterInputChanged = false; const std::tuple...> MasterOutput = MF(MasterInputArg); - handleMasterOutputs<0>(MasterOutput, Indices()); + handleMasterOutputs<0>(MasterOutput, SlaveIndices()); } // Handle inputs. LOG_TRACE_STREAM << "DeluxeAgent " << Name << " handles input." << std::endl; - const auto InputArgs = prepareCurrentInputs(Indices()); + const auto InputArgs = prepareCurrentInputs(SlaveIndices()); std::fill(InputChanged.begin(), InputChanged.end(), false); const std::tuple, Optional...> Output = - invokeWithTuple(F, InputArgs, Indices()); + invokeWithTuple(F, InputArgs, SlaveIndices()); const auto OutputToMaster = std::get<0>(Output); if (OutputToMaster) { - sendToMaster(*OutputToMaster); + sendToMaster(*OutputToMaster, seq_t()); } - handleMasterOutputs<1>(Output, Indices()); + handleMasterOutputs<1>(Output, SlaveIndices()); }; } 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))), + DAMASTERHANDLERREF(unit_t), DAMASTERHANDLERREF(bool), + DAMASTERHANDLERREF(double), DAMASTERHANDLERREF(float)), + OutputType(T::TT), NumberOfInputs(sizeof...(As)), MasterInputType(MT::TT), + NumberOfMasterOutputs(NumberOfInputs), InputTypes({As::TT...}), + InputNextPos(NumberOfInputs, 0), InputChanged(NumberOfInputs, false), + InputStorageOffsets(storageOffsets(seq_t())), + InputValues(new typename TokenizedStorageForTypeList< + typename TypeListUnwrapDeluxeTuple>::Type>:: + Type()), + MasterInputNextPos(0), MasterInputChanged(false), + MasterInputValue(new typename TokenizedStorageForTypeList< + typename UnwrapDeluxeTuple::Type>::Type()), + MasterOutputTypes({Ts::TT...}), + FP(triggerHandlerFromProcessingFunctions(std::move(MF), std::move(F), + seq_t())), 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); +template +void DeluxeAgent::sendToMaster(const DeluxeTuple &Value, + Seq) noexcept { + STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments"); + ASSERT(inv() && OutputType == TypeToken::Value); + + // The assert must hold if \p this object was successfuuly constructed. + ASSERT((true && ... && + (static_cast(static_cast(S0)) == S0))); + // Create a static constant array for these indices to be available as lvalue + // references when creating messages below. \c S0... when used directly in a + // fold expression is a temporary value, which would result in \c + // rosa::Message instances being created with rvalue references. Further, all + // other values would to copied into a temporary variable for making them + /// available as rvalue references (they are constant lvalue references here). + static constexpr std::array Indices{{S0...}}; + + LOG_TRACE_STREAM << "DeluxeAgent " << Name << "(" << Id + << ") sends to master (" + << static_cast(Master && *Master) << "): " << Value + << " (" << sizeof...(S0) << ")" << std::endl; // 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)); + // Handle each element of the tuple in a fold expression. + (Master->sendMessage(Message::create(atoms::Slave::Value, Id, Indices[S0], + std::get(Value))), + ...); } } -template -void DeluxeAgent::sendToSlave(const size_t Pos, const T &Value) noexcept { +template +void DeluxeAgent::sendToSlave(const size_t Pos, const DeluxeTuple &Value, + Seq) noexcept { + STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments"); ASSERT(inv() && Pos < NumberOfMasterOutputs && - MasterOutputTypes[Pos] == TypeNumberOf::Value); + MasterOutputTypes[Pos] == TypeToken::Value); + + // The assert must hold if \p this object was successfuuly constructed. + ASSERT((true && ... && + (static_cast(static_cast(S0)) == S0))); + // Create a static constant array for these indices to be available as lvalue + // references when creating messages below. \c S0... when used directly in a + // fold expression is a temporary value, which would result in \c + // rosa::Message instances being created with rvalue references. Further, all + // other values would to copied into a temporary variable for making them + /// available as rvalue references (they are constant lvalue references here). + static constexpr std::array Indices{{S0...}}; // There is a handle and the referred *slave* is in a valid state. auto Slave = Slaves[Pos]; + + LOG_TRACE_STREAM << "DeluxeAgent " << Name << "(" << Id + << ") sends to slave (" << static_cast(Slave && *Slave) + << ") at position " << Pos << ": " << Value << " (" + << sizeof...(S0) << ")" << std::endl; + if (Slave && *Slave) { - Slave->sendMessage(Message::create(atoms::Master::Value, Id, Value)); + // Handle each element of the tuple in a fold expression. + (Slave->sendMessage(Message::create(atoms::Master::Value, Id, Indices[S0], + std::get(Value))), + ...); } } -template void DeluxeAgent::saveInput(id_t Id, T Value) noexcept { +template +void DeluxeAgent::saveInput(id_t Id, token_size_t Pos, T Value) noexcept { ASSERT(inv() && SlaveIds.find(Id) != SlaveIds.end() && - InputTypes[SlaveIds.find(Id)->second] == TypeNumberOf::Value); + Pos == InputNextPos[SlaveIds.find(Id)->second] && + typeAtPositionOfToken(InputTypes[SlaveIds.find(Id)->second], Pos) == + TypeNumberOf::Value); + + size_t SlavePos = SlaveIds.at(Id); + + LOG_TRACE_STREAM << "DeluxeAgent " << Name << "(" << Id + << ") saves value from slave at position " << SlavePos + << ": (" << static_cast(Pos) << ") " << Value + << std::endl; + + // Save value. + size_t StoragePos = (size_t)InputStorageOffsets[SlavePos] + Pos; + // This assert must hold if \p this object was successfully constructed. + ASSERT(static_cast(static_cast(StoragePos)) == + StoragePos); + *static_cast( + InputValues->pointerTo(static_cast(StoragePos))) = Value; + + // Update position of next value. + if (++InputNextPos[SlavePos] == lengthOfToken(InputTypes[SlavePos])) { + InputNextPos[SlavePos] = 0; + } - size_t Pos = SlaveIds.at(Id); - ASSERT(static_cast(static_cast(Pos)) == Pos); - *static_cast(InputValues->pointerTo(static_cast(Pos))) = - Value; - InputChanged[Pos] = true; + // Set flag. + InputChanged[SlavePos] = true; ASSERT(inv()); } template -void DeluxeAgent::saveMasterInput(id_t Id, T Value) noexcept { - ASSERT(inv() && Master && masterId() == Id && - MasterInputType == TypeNumberOf::Value); +void DeluxeAgent::saveMasterInput(id_t Id, token_size_t Pos, T Value) noexcept { + ASSERT(inv() && Master && masterId() == Id && Pos == MasterInputNextPos && + typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf::Value); + + LOG_TRACE_STREAM << "DeluxeAgent " << Name << "(" << Id + << ") saves value from master: (" << static_cast(Pos) + << ") " << Value << std::endl; + + // Save value. + *static_cast(MasterInputValue->pointerTo(Pos)) = Value; + + // Update position of next value. + if (++MasterInputNextPos == lengthOfToken(MasterInputType)) { + MasterInputNextPos = 0; + } - *static_cast(MasterInputValue->pointerTo(0)) = Value; + // Set flag. 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/deluxe/DeluxeContext.hpp b/include/rosa/deluxe/DeluxeContext.hpp index d5e2948..74da611 100755 --- a/include/rosa/deluxe/DeluxeContext.hpp +++ b/include/rosa/deluxe/DeluxeContext.hpp @@ -1,492 +1,887 @@ //===-- rosa/deluxe/DeluxeContext.hpp ---------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeContext.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Public interface for the *deluxe interface* for working with agent /// systems. /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXECONTEXT_HPP #define ROSA_DELUXE_DELUXECONTEXT_HPP #include "rosa/deluxe/DeluxeSystem.hpp" #include "rosa/support/types.hpp" #include #include #include /// Local helper macro to log and return a /// \c rosa::deluxe::DeluxeContext::ErrorCode value. /// /// Creates a debug message with the stringified value and returns the value. /// /// \param Err \c rosa::deluxe::DeluxeContext::ErrorCode value to log and /// return #define DCRETERROR(Err) \ { \ LOG_DEBUG(#Err); \ return Err; \ } namespace rosa { namespace deluxe { /// Defines the *deluxe interface*. /// /// \todo The classes \c rosa::deluxe::DeluxeSensor and \c /// rosa::deluxe::DeluxeAgent share some common features in relation to their /// *slave* role in the *deluxe interface*. But their definitions are completely /// independent. It could be investigated how to lift their common parts into a /// new *deluxe slave* class, which would serve as base for both, to avoid code /// duplication. -/// -/// \todo In the master-to-slave communication, the type \c rosa::unit_t -/// indicates no master-output in the *master* and no master-input in the -/// *slave*. That works fine, but does not allow \c rosa::unit_t to be used in -/// actual master-to-slave communication. It would make sense to use \c -/// rosa::none_t as the extreme type instead. That would need some adjustment of -/// code because \c rosa::none_t is not part of \c rosa::BuiltinTypes. class DeluxeContext { /// A system owned by \p this object. /// /// \note The reference is kept in a \c std::shared_ptr because of the member /// function \c rosa::deluxe::DeluxeContext::getSystem. std::shared_ptr System; /// References to all *sensors* and *agents* created by \p this object. std::set DeluxeUnits; public: /// Errors that may be resulted by some of the member functions of the class. enum struct ErrorCode { NoError, TypeMismatch, NotSensor, NotAgent, WrongPosition, AlreadyHasSlave, AlreadyHasMaster, AlreadyHasValueStream }; /// Returns a new instance of \c rosa::deluxe::DeluxeContext. /// /// \param Name name of the underlying \c rosa::DeluxeSystem /// /// \return \c std::unique_ptr for the new instance of /// \c rosa::deluxe::DeluxeContext with a new, empty \c rosa::DeluxeSystem static std::unique_ptr create(const std::string &Name) noexcept; private: /// Creates a new instance. /// /// \note Private constructor restricts instantiation to member functions of /// the class. /// /// \param Name name of the underlying \c rosa::MessagingSystem DeluxeContext(const std::string &Name) noexcept; public: /// Destroys \p this object. ~DeluxeContext(void) noexcept; /// Returns a reference for the underlying \c rosa::MessagingSystem. /// /// \note One cannot do much with a \c rosa::MessagingSystem currently, this /// is for future use. /// /// \return reference for the underlying \c rosa::MessagingSystem. std::weak_ptr getSystem(void) const noexcept; +private: + /// Creates a new *sensor* in the context of \p this object. + /// + /// The new *sensor* handles master-input by \p MF. + /// + /// \tparam MT type of master-input the new *sensor* handles + /// \tparam T type of data the new *sensor* operates on + /// + /// \note Instantiation fails if any of the type arguments \p MT and \p T + /// is not an instance of \c rosa::deluxe::DeluxeTuple or \p T is \c + /// rosa::deluxe::EmptyDeluxeTuple. + /// + /// \param Name name of the new *sensor* + /// \param MF function for the new *sensors* to process master-input + /// values with + /// \param F function for the new *sensor* to generate the next value with + /// during normal operation + /// + /// \note \p F is not used during simulation, in which case + /// \c rosa::deluxe::DeluxeContext::registerSensorValues is used to + /// register an alternative simulation data source with \c + /// rosa::deluxe::DeluxeSensor::registerSimulationDataSource. One may + /// safely keep relying on the default value of \p F as long as only + /// simulation of the system is to be done. + /// + /// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor. + /// + /// \return \c rosa::AgentHandle for the new *sensor* + template >::Value && + !std::is_same_v>> + AgentHandle createSensorImpl(const std::string &Name, + std::function)> &&MF, + std::function &&F) noexcept; + +public: /// Creates a new *sensor* in the context of \p this object. /// /// The new *sensor* does not receive master-input. /// /// \tparam T type of data the new *sensor* operates on /// + /// \note Instantiation fails if type argument \p T is neither a built-in type + /// nor an instance of \c rosa::deluxe::DeluxeTuple with at least one element. + /// /// \param Name name of the new *sensor* /// \param F function for the new *sensor* to generate the next value with /// during normal operation /// /// \note \p F is not used during simulation, in which case /// \c rosa::deluxe::DeluxeContext::registerSensorValues is used to register /// an alternative simulation data source with /// \c rosa::deluxe::DeluxeSensor::registerSimulationDataSource. One may /// safely keep relying on the default value of \p F as long as only /// simulation of the system is to be done. /// /// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor. /// /// \return \c rosa::AgentHandle for the new *sensor* - template + template ::Value || + (IsDeluxeTuple::Value && + !std::is_same_v)>> AgentHandle createSensor( const std::string &Name, std::function &&F = [](void) { return T(); }) noexcept; /// Creates a new *sensor* in the context of \p this object. /// /// The new *sensor* handles master-input by \p MF. /// /// \tparam MT type of master-input the new *sensor* handles /// \tparam T type of data the new *sensor* operates on /// - /// \note If \p MT is \c rosa::UnitType + /// \note The type arguments \p MT and \p T must be either all built-in types + /// or all instances of \c rosa::deluxe::DeluxeTuple. Moreover, \p T cannot be + /// \c rosa::deluxe::EmptyDeluxeTuple. Instantiation fails if these conditions + /// do not hold. /// /// \param Name name of the new *sensor* - /// \param MF function for the new *sensors* to process master-input values with - /// \param F function for the new *sensor* to generate the next value with - /// during normal operation + /// \param MF function for the new *sensors* to process master-input + /// values with \param F function for the new *sensor* to generate + /// the next value with during normal operation /// /// \note \p F is not used during simulation, in which case - /// \c rosa::deluxe::DeluxeContext::registerSensorValues is used to register - /// an alternative simulation data source with - /// \c rosa::deluxe::DeluxeSensor::registerSimulationDataSource. One may + /// \c rosa::deluxe::DeluxeContext::registerSensorValues is used to + /// register an alternative simulation data source with \c + /// rosa::deluxe::DeluxeSensor::registerSimulationDataSource. One may /// safely keep relying on the default value of \p F as long as only /// simulation of the system is to be done. /// /// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor. /// /// \return \c rosa::AgentHandle for the new *sensor* - template + template , BuiltinTypes>::Value || + (TypeListAllDeluxeTuple>::Value && + !std::is_same_v)>> AgentHandle createSensor( const std::string &Name, std::function)> &&MF, std::function &&F = [](void) { return T(); }) noexcept; +private: + /// Creates a new *agent* in the context of \p this object. + /// + /// The new *agent* receives master-input by \p MF and produces + /// master-output. + /// + /// \tparam MT type of master-input the new *agent* handles + /// \tparam T type of data the new *agent* outputs + /// \tparam Ts types of master-output the new *agent* produces + /// \tparam As types of inputs the new *agent* takes + /// + /// \note Instantiation fails if any of the type arguments \p MT, \p T, \p + /// Ts..., and \p As... is not an instance of \c rosa::deluxe::DeluxeTuple or + /// any of \p T and \p As... is \c rosa::deluxe::EmptyDeluxeTuple. + /// + /// \param Name name of the new *agent* + /// \param MF function for the new *agent* to process master-input + /// values with \param F function for the new *agent* to process + /// input values and generate output with + /// + /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. + /// + /// \return \c rosa::AgentHandle for the new *agent* + template >::Value && + !std::is_same_v && + (true && ... && (!std::is_same_v))>> + AgentHandle createAgentImpl( + const std::string &Name, + std::function...>(std::pair)> &&MF, + std::function, Optional...>( + std::pair...)> &&F) noexcept; + +public: /// Creates a new *agent* in the context of \p this object. /// - /// The new *agent* neither receives master-input nor produces master-output. + /// The new *agent* neither receives master-input nor produces + /// master-output. /// /// \tparam T type of data the new *agent* outputs /// \tparam As types of inputs the new *agent* takes /// + /// \note The type arguments \p T and \p As... must be either all built-in + /// types or all instances of \c rosa::deluxe::DeluxeTuple. Moreover, none of + /// them can be \c rosa::deluxe::EmptyDeluxeTuple. Instantiation fails if + /// these conditions do not hold. + /// /// \param Name name of the new *agent* /// \param F function for the new *agent* to process input values and /// generate output with /// /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. /// /// \return \c rosa::AgentHandle for the new *agent* - template + template , BuiltinTypes>::Value || + (TypeListAllDeluxeTuple>::Value && + !std::is_same_v && + (true && ... && (!std::is_same_v)))>> AgentHandle createAgent(const std::string &Name, std::function(std::pair...)> &&F) noexcept; /// Creates a new *agent* in the context of \p this object. /// - /// The new *agent* receives master-input by \p MF but does not produce - /// master-output. + /// The new *agent* receives master-input by \p MF but does not + /// produce master-output. /// /// \tparam MT type of master-input the new *agent* handles /// \tparam T type of data the new *agent* outputs /// \tparam As types of inputs the new *agent* takes /// + /// \note The type arguments \p MT, \p T, and \p As... must be either all + /// built-in types or all instances of \c rosa::deluxe::DeluxeTuple. Moreover, + /// none of \p T and \p As... can be \c rosa::deluxe::EmptyDeluxeTuple. + /// Instantiation fails if these conditions do not hold. + /// /// \param Name name of the new *agent* - /// \param MF function for the new *agent* to process master-input values - /// with + /// \param MF function for the new *agent* to process master-input + /// values with /// \param F function for the new *agent* to process input values and /// generate output with /// /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. /// /// \return \c rosa::AgentHandle for the new *agent* - template + template , BuiltinTypes>::Value || + (TypeListAllDeluxeTuple>::Value && + !std::is_same_v && + (true && ... && (!std::is_same_v)))>> AgentHandle createAgent(const std::string &Name, std::function)> &&MF, std::function(std::pair...)> &&F) noexcept; /// Creates a new *agent* in the context of \p this object. /// - /// The new *agent* does not receive master-input but produces master-output. + /// The new *agent* does not receive master-input but produces + /// master-output. /// /// \tparam T type of data the new *agent* outputs /// \tparam Ts types of master-output the new *agent* produces /// \tparam As types of inputs the new *agent* takes /// + /// \note The type arguments \p T, \p Ts, and \p As... must be either all + /// built-in types or all instances of \c rosa::deluxe::DeluxeTuple. Moreover, + /// none of \p T and \p As... can be \c rosa::deluxe::EmptyDeluxeTuple. + /// Instantiation fails if these conditions do not hold. + /// /// \param Name name of the new *agent* /// \param F function for the new *agent* to process input values and /// generate output with /// + /// \note \p F does not produce master-output for a given position if the + /// corresponding type is \c rosa::deluxe::EmptyDeluxeTuple. It is not + /// possible to disable master-output at any position by using built-in types. + /// /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. /// /// \return \c rosa::AgentHandle for the new *agent* - template + template < + typename T, typename... Ts, typename... As, + typename = std::enable_if_t< + TypeListSubsetOf, BuiltinTypes>::Value || + (TypeListAllDeluxeTuple>::Value && + !std::is_same_v && + (true && ... && (!std::is_same_v)))>> AgentHandle createAgent(const std::string &Name, std::function, Optional...>( std::pair...)> &&F) noexcept; /// Creates a new *agent* in the context of \p this object. /// - /// The new *agent* receives master-input by \p MF and produces master-output. + /// The new *agent* receives master-input by \p MF and produces + /// master-output. /// /// \tparam MT type of master-input the new *agent* handles /// \tparam T type of data the new *agent* outputs /// \tparam Ts types of master-output the new *agent* produces /// \tparam As types of inputs the new *agent* takes /// + /// \note The type arguments \p MT, \p T, \p Ts, and \p As... must be either + /// all built-in types or all instances of \c rosa::deluxe::DeluxeTuple. + /// Moreover, none of \p T and \p As... can be \c + /// rosa::deluxe::EmptyDeluxeTuple. Instantiation fails if these conditions + /// do not hold. + /// /// \param Name name of the new *agent* - /// \param MF function for the new *agent* to process master-input values - /// with + /// \param MF function for the new *agent* to process master-input + /// values with /// \param F function for the new *agent* to process input values and /// generate output with /// + /// \note \p F does not produce master-output for a given position if the + /// corresponding type is \c rosa::deluxe::EmptyDeluxeTuple. It is not + /// possible to disable master-output at any position by using built-in types. + /// /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. /// /// \return \c rosa::AgentHandle for the new *agent* - template + template , + BuiltinTypes>::Value || + (TypeListAllDeluxeTuple>::Value && + !std::is_same_v && + (true && ... && (!std::is_same_v)))>> AgentHandle createAgent( const std::string &Name, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept; - /// Connectes a *sensor* to an *agent* in the context of \p this object. + /// Connects a *sensor* to an *agent* in the context of \p this object. /// /// \param Agent the *agent* to connect to /// \param Pos the index of slot of \p Agent to connect \p Sensor to /// \param Sensor the *sensor* to connect /// \param Description optional textual description of the connection /// - /// \return how successfull connecting \p Sensor to \p Agent at slot index - /// \p Pos was + /// \return how successfull connecting \p Sensor to \p Agent at slot + /// index \p Pos was /// /// \note The function may return the following /// \c rosa::deluxe::DeluxeContext::ErrorCode values: /// `ErrorCode` | Comment /// ----------- | ------- /// `NoError` | Success /// `NotAgent` | Referred \p Agent is not \c rosa::deluxe::DeluxeAgent /// `NotSensor` | Referred \p Sensor is not \c rosa::deluxe::DeluxeSensor /// `WrongPosition` | \p Pos is not a valid input position of \p Agent - /// `TypeMismatch` | Expected input type at position \p Pos of \p Agent is other than the output type of \p Sensor or expected master-input of \p Sensor is other than master-output at position \p Pos of \p Agent if any + /// `TypeMismatch` | Expected input type at position \p Pos of \p Agent is other thanthe output type of \p Sensor or expected master-input of \p Sensor is other than master-output at position \p Pos of \p Agent if any /// `AlreadyHasSlave` | \p Agent at position \p Pos already has a *slave* registered /// `AlreadyHasMaster` | \p Sensor already has a *master* registered ErrorCode connectSensor(AgentHandle Agent, const size_t Pos, AgentHandle Sensor, const std::string &Description = "") noexcept; /// Connectes two *agents* in the context of \p this object. /// /// \param Master the *agent* to connect to /// \param Pos the index of slot of \p Master to connect \p Slave to /// \param Slave the *agent* to connect /// \param Description optional textual description of the connection /// - /// \return how succesfull connecting \p Slave to \p Master at slot index - /// \p Pos was + /// \return how succesfull connecting \p Slave to \p Master at slot + /// index \p Pos was /// /// \note The function may return the following /// \c rosa::deluxe::DeluxeContext::ErrorCode values: /// `ErrorCode` | Comment /// ----------- | ------- /// `NoError` | Success /// `NotAgent` | Referred \p Master or \p Slave is not \c rosa::deluxe::DeluxeAgent /// `WrongPosition` | \p Pos is not a valid input position of \p Master /// `TypeMismatch` | Expected input type at position \p Pos of \p Master is other than the output type of \p Slave or expected master-input of \p Slave is other than master-output at position \p Pos of \p Master if any /// `AlreadyHasSlave` | \p Master at position \p Pos already has a *slave* registered /// `AlreadyHasMaster` | \p Slave already has a *master* registered ErrorCode connectAgents(AgentHandle Master, const size_t Pos, AgentHandle Slave, const std::string &Description = "") noexcept; - /// Initializes \c this object and others managed by \p this object for - /// setting up and performing simulation. + /// Initializes \c this object and others managed by \p this object + /// for setting up and performing simulation. /// /// \see \c rosa::deluxe::DeluxeContext::registerSensorValues, /// \c rosa::deluxe::DeluxeContext::simulate /// /// Need to clear simulation data sources from all the *sensors*. void initializeSimulation(void) noexcept; - /// Registers a stream providing values for a *sensor* during simulation. +public: + /// Registers a stream providing values for a *sensor* during + /// simulation. /// /// \tparam Iterator type of iterator providing values for \p Sensor - /// \tparam T type of values \p Sensor is operating on, always use default! + /// \tparam T type of values \p Sensor is operating on, always use + /// default! + /// + /// \note Instantiation fails if type argument \p T is neither a built-in type + /// nor an instance of \c rosa::deluxe::DeluxeTuple with at least one element. /// /// \param Sensor the *sensor* to register values for /// \param Start provides values for \p Sensor /// \param End denotes the end of stream of values - /// \param Default value to be used when input stream is depleted during - /// simulation + /// \param Default value to be used when input stream is depleted + /// during simulation /// /// \return how successful registering \p Source for \p Sensor /// /// \note The function may return the following /// \c rosa::deluxe::DeluxeContext::ErrorCode values: /// `ErrorCode` | Comment /// ----------- | ------- /// `NoError` | Success - /// `TypeMismatch` | \p Sensor generates values of a type other than \p T - /// `NotSensor` | Referred \p Sensor is not \c rosa::deluxe::DeluxeSensor - /// `AlreadyHasValueStream` | \p Sensor already has simulation data source set - template + /// `TypeMismatch` | \p Sensor generates values of a type other than + /// \p T `NotSensor` | Referred \p Sensor is not \c + /// rosa::deluxe::DeluxeSensor `AlreadyHasValueStream` | \p Sensor already has + /// simulation data source set + template < + typename Iterator, typename T = typename Iterator::value_type, + typename = std::enable_if_t::Value || + (IsDeluxeTuple::Value && + !std::is_same_v)>> ErrorCode registerSensorValues(AgentHandle Sensor, Iterator &&Start, const Iterator &End, T Default = {}) noexcept; /// Performs the system contained by \p this object. /// - /// The function performs \p NumCycles cycle of simulation. In each cycle, - /// all the *agents* and *sensors* registered in - /// \c rosa::deluxe::DeluxeContext::DeluxeUnits are trigged for execution. + /// The function performs \p NumCycles cycle of simulation. In each + /// cycle, all the *agents* and *sensors* registered in \c + /// rosa::deluxe::DeluxeContext::DeluxeUnits are trigged for + /// execution. /// /// \param NumCycles number of cycles to perform /// - /// \pre All the *sensors* in the system contained by \p this object generate - /// their output from simulation data sources. + /// \pre All the *sensors* in the system contained by \p this object + /// generate their output from simulation data sources. void simulate(const size_t NumCycles) const noexcept; }; /// Anonymous namespace with helper features for implementing /// \c rosa::deluxe::DeluxeContext, consider it private. namespace { -/// Maps any type \p T to \c rosa::unit_t. -template struct MapToUnit { using Type = unit_t; }; +/// Maps any type \p T to \c rosa::deluxe::EmptyDeluxeTuple. +template struct MapToEmptyDeluxeTuple { + using Type = EmptyDeluxeTuple; +}; + +/// Convenience template alias for \c MapToEmptyDeluxeTuple. +template +using empty_deluxe_t = typename MapToEmptyDeluxeTuple::Type; + +/// Converts a \c std::tuple of \c rosa::Optional built-in types into a +/// corresponding \c std::tuple of \c rosa::Optional with each actual value +/// wrapped in \c rosa::deluxe::DeluxeTuple. +/// +/// \tparam Ts types of the values +/// \tparam S0 indices for accessing values in \p Values +/// +/// \param Values the \c std::tuple of \c rosa::Optional with built-in values +/// +/// \note The second argument provides indices statically as template arguments +/// \p S0..., so its actual value is ignored. +/// +/// \return a \c std::tuple of \c rosa::Optional corresponding to \p Values +/// with each actual value wrapped in \c rosa::deluxe::DeluxeTuple +/// +/// \pre Statically, all type arguments \p Ts... are built-in types and the +/// provided indices \p S0... match the length of \p Ts...: \code +/// TypeListSubsetOf, BuiltinTypes>::Value && +/// sizeof...(Ts) == sizeof...(S0) +/// \endcode +template +std::tuple>...> +wrapBuiltinInDeluxeTuple(const std::tuple...> &Values, + Seq) noexcept { + STATIC_ASSERT((TypeListSubsetOf, BuiltinTypes>::Value), + "not built-in types"); + STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent type arguments"); + + return std::make_tuple(std::get(Values) + ? Optional>( + make_deluxe_tuple(*std::get(Values))) + : Optional>()...); +} } // End namespace -template +template +AgentHandle +DeluxeContext::createSensorImpl(const std::string &Name, + std::function)> &&MF, + std::function &&F) noexcept { + AgentHandle H = System->createSensor(Name, std::move(MF), std::move(F)); + DeluxeUnits.emplace(H); + return H; +} + +template AgentHandle DeluxeContext::createSensor(const std::string &Name, std::function &&F) noexcept { - return createSensor(Name, - std::function)>( - [](std::pair) {}), - std::move(F)); + auto EmptyMF = std::function)>( + [](std::pair) {}); + + if constexpr (TypeListContains::Value) { + using OutputType = DeluxeTuple; + return createSensorImpl( + Name, std::move(EmptyMF), + std::function( + [F{std::move(F)}](void) { return OutputType(F()); })); + + } else if constexpr (IsDeluxeTuple::Value && + !std::is_same_v) { + return createSensorImpl(Name, std::move(EmptyMF), std::move(F)); + + } else { + ASSERT(false && "Unexpected type argument"); + } } -template +template AgentHandle DeluxeContext::createSensor(const std::string &Name, std::function)> &&MF, std::function &&F) noexcept { - AgentHandle H = System->createSensor(Name, std::move(MF), std::move(F)); + + if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { + using MasterInputType = DeluxeTuple; + using OutputType = DeluxeTuple; + return createSensorImpl( + Name, + std::function)>( + [MF{std::move(MF)}](std::pair Arg) { + MF({std::get<0>(Arg.first), Arg.second}); + }), + std::function( + [F{std::move(F)}](void) { return OutputType(F()); })); + + } else if constexpr (TypeListAllDeluxeTuple>::Value && + !std::is_same_v) { + return createSensorImpl(Name, std::move(MF), std::move(F)); + + } else { + ASSERT(false && "Unexpected type arguments"); + } +} + +template +AgentHandle DeluxeContext::createAgentImpl( + const std::string &Name, + std::function...>(std::pair)> &&MF, + std::function, Optional...>( + std::pair...)> &&F) noexcept { + AgentHandle H = System->createAgent(Name, std::move(MF), std::move(F)); DeluxeUnits.emplace(H); return H; } -template +template AgentHandle DeluxeContext::createAgent( const std::string &Name, std::function(std::pair...)> &&F) noexcept { - using NoMasterOutputType = - std::tuple::Type>...>; - return createAgent( - Name, - std::function)>( - [](std::pair) { return NoMasterOutputType(); }), - std::function< - std::tuple, Optional::Type>...>( - std::pair...)>( - [F{std::move(F)}](std::pair... Args) { - return std::tuple_cat(std::make_tuple(F(Args...)), - NoMasterOutputType()); - })); + + using NoMasterOutputType = std::tuple>...>; + auto EmptyMF = + std::function)>( + [](std::pair) { + return NoMasterOutputType(); + }); + + if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { + using OutputType = DeluxeTuple; + return createAgentImpl( + Name, std::move(EmptyMF), + std::function< + std::tuple, Optional>...>( + std::pair, bool>...)>( + [F{std::move(F)}](std::pair, bool>... Args) { + const auto Result = F({std::get<0>(Args.first), Args.second}...); + return std::tuple_cat( + wrapBuiltinInDeluxeTuple(std::tuple(Result), seq_t<1>()), + NoMasterOutputType()); + })); + + } else if constexpr (TypeListAllDeluxeTuple>::Value && + !std::is_same_v && + (true && ... && + (!std::is_same_v))) { + return createAgentImpl( + Name, std::move(EmptyMF), + std::function, Optional>...>( + std::pair...)>( + [F{std::move(F)}](std::pair... Args) { + const auto Result = F(Args...); + return std::tuple_cat(std::tuple(Result), NoMasterOutputType()); + })); + + } else { + ASSERT(false && "Unexpected type arguments"); + } } -template +template AgentHandle DeluxeContext::createAgent( const std::string &Name, std::function)> &&MF, std::function(std::pair...)> &&F) noexcept { - using NoMasterOutputType = - std::tuple::Type>...>; - return createAgent( - Name, - std::function)>( - [MF{std::move(MF)}](std::pair Arg) { - MF(Arg); - return NoMasterOutputType(); - }), - std::function< - std::tuple, Optional::Type>...>( - std::pair...)>( - [F{std::move(F)}](std::pair... Args) { - return std::tuple_cat(std::make_tuple(F(Args...)), - NoMasterOutputType()); - })); + + using NoMasterOutputType = std::tuple>...>; + + if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { + using MasterInputType = DeluxeTuple; + using OutputType = DeluxeTuple; + return createAgentImpl( + Name, + std::function)>( + [MF{std::move(MF)}](std::pair Arg) { + MF({std::get<0>(Arg.first), Arg.second}); + return NoMasterOutputType(); + }), + std::function< + std::tuple, Optional>...>( + std::pair, bool>...)>( + [F{std::move(F)}](std::pair, bool>... Args) { + const auto Result = F({std::get<0>(Args.first), Args.second}...); + return std::tuple_cat( + wrapBuiltinInDeluxeTuple(std::tuple(Result), seq_t<1>()), + NoMasterOutputType()); + })); + + } else if constexpr (TypeListAllDeluxeTuple>::Value && + !std::is_same_v && + (true && ... && + (!std::is_same_v))) { + return createAgentImpl( + Name, + std::function)>( + [MF{std::move(MF)}](std::pair Arg) { + MF(Arg); + return NoMasterOutputType(); + }), + std::function, Optional>...>( + std::pair...)>( + [F{std::move(F)}](std::pair... Args) { + const auto Result = F(Args...); + return std::tuple_cat(std::tuple(Result), NoMasterOutputType()); + })); + + } else { + ASSERT(false && "Unexpected type arguments"); + } } -template +template AgentHandle DeluxeContext::createAgent( const std::string &Name, std::function, Optional...>( std::pair...)> &&F) noexcept { - using MasterOutputType = std::tuple...>; - return createAgent( - Name, - std::function)>( - [](std::pair) { return MasterOutputType(); }), - std::move(F)); + + if constexpr (TypeListSubsetOf, + BuiltinTypes>::Value) { + using MasterOutputType = std::tuple>...>; + using OutputType = DeluxeTuple; + return createAgentImpl( + Name, + std::function)>( + [](std::pair) { + return MasterOutputType(); + }), + std::function< + std::tuple, Optional>...>( + std::pair, bool>...)>( + [F{std::move(F)}](std::pair, bool>... Args) { + const auto Result = F({std::get<0>(Args.first), Args.second}...); + return wrapBuiltinInDeluxeTuple(Result, + seq_t<1 + sizeof...(Ts)>()); + })); + + } else if constexpr (TypeListAllDeluxeTuple< + TypeList>::Value && + !std::is_same_v && + (true && ... && + (!std::is_same_v))) { + using MasterOutputType = std::tuple...>; + return createAgentImpl( + Name, + std::function)>( + [](std::pair) { + return MasterOutputType(); + }), + std::function, Optional...>( + std::pair...)>( + [F{std::move(F)}](std::pair... Args) { + const auto Output = F(Args...); + return Output; + })); + + } else { + ASSERT(false && "Unexpected type arguments"); + } } -template +template AgentHandle DeluxeContext::createAgent( const std::string &Name, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept { - AgentHandle H = System->createAgent(Name, std::move(MF), std::move(F)); - DeluxeUnits.emplace(H); - return H; + + if constexpr (TypeListSubsetOf, + BuiltinTypes>::Value) { + using MasterInputType = DeluxeTuple; + using MasterOutputType = std::tuple>...>; + using OutputType = DeluxeTuple; + return createAgentImpl( + Name, + std::function)>( + [MF{std::move(MF)}](std::pair Arg) { + const auto Result = MF({std::get<0>(Arg.first), Arg.second}); + return wrapBuiltinInDeluxeTuple(Result, seq_t()); + }), + std::function< + std::tuple, Optional>...>( + std::pair, bool>...)>( + [F{std::move(F)}](std::pair, bool>... Args) { + const auto Result = F({std::get<0>(Args.first), Args.second}...); + return wrapBuiltinInDeluxeTuple(Result, + seq_t<1 + sizeof...(Ts)>()); + })); + + } else if constexpr (TypeListAllDeluxeTuple< + TypeList>::Value && + !std::is_same_v && + (true && ... && + (!std::is_same_v))) { + using MasterOutputType = std::tuple...>; + return createAgentImpl( + Name, + std::function)>( + [MF{std::move(MF)}](std::pair Arg) { + const auto Output = MF(Arg); + return Output; + }), + std::function, Optional...>( + std::pair...)>( + [F{std::move(F)}](std::pair... Args) { + const auto Output = F(Args...); + return Output; + })); + + } else { + ASSERT(false && "Unexpected type arguments"); + } } -template +template DeluxeContext::ErrorCode DeluxeContext::registerSensorValues(AgentHandle Sensor, Iterator &&Start, const Iterator &End, T Default) noexcept { // Get the type of values provided by \p Iterator. STATIC_ASSERT((std::is_same::value), "type mismatch"); // Make sure preconditions are met. if (!System->isDeluxeSensor(Sensor)) { DCRETERROR(ErrorCode::NotSensor); } auto S = System->getDeluxeSensor(Sensor); ASSERT(S); // Sanity check. - if (S->OutputType != TypeNumberOf::Value) { - DCRETERROR(ErrorCode::TypeMismatch); - } else if (S->simulationDataSourceIsSet()) { + if (S->simulationDataSourceIsSet()) { DCRETERROR(ErrorCode::AlreadyHasValueStream); } - // Register input stream. - // \note Need to capture parameters by value so having local copies. - S->registerSimulationDataSource( - std::function([=](void) mutable noexcept->T { - if (Start != End) { - LOG_TRACE_STREAM << "Reading next value for sensor '" << S->FullName - << "': " << *Start << '\n'; - return *Start++; - } else { - LOG_TRACE_STREAM << "Providing default value for sensor '" - << S->FullName << "': " << Default << '\n'; - return Default; - } - })); + if constexpr (TypeListContains::Value) { + if (S->OutputType != TypeToken::Value) { + DCRETERROR(ErrorCode::TypeMismatch); + } + + // Register input stream. + // \note Need to capture parameters by value so having local copies. + S->registerSimulationDataSource(std::function(void)>([= + ](void) mutable noexcept->DeluxeTuple { + if (Start != End) { + LOG_TRACE_STREAM << "Reading next value for sensor '" << S->FullName + << "': " << *Start << '\n'; + return make_deluxe_tuple(*Start++); + } else { + LOG_TRACE_STREAM << "Providing default value for sensor '" + << S->FullName << "': " << Default << '\n'; + return make_deluxe_tuple(Default); + } + })); + + } else if constexpr (IsDeluxeTuple::Value && + !std::is_same_v) { + if (S->OutputType != T::TT) { + DCRETERROR(ErrorCode::TypeMismatch); + } + + // Register input stream. + // \note Need to capture parameters by value so having local copies. + S->registerSimulationDataSource( + std::function([=](void) mutable noexcept->T { + if (Start != End) { + LOG_TRACE_STREAM << "Reading next value for sensor '" << S->FullName + << "': " << *Start << '\n'; + return *Start++; + } else { + LOG_TRACE_STREAM << "Providing default value for sensor '" + << S->FullName << "': " << Default << '\n'; + return Default; + } + })); + + } else { + ASSERT(false && "Unexpected type argument"); + } + return ErrorCode::NoError; } } // End namespace deluxe } // End namespace rosa // Undef local macro if not used in the corresponding implementation. #ifndef ROSA_LIB_DELUXE_DELUXECONTEXT_CPP #undef DCRETERROR #endif #endif // ROSA_DELUXE_DELUXECONTEXT_HPP diff --git a/include/rosa/deluxe/DeluxeSensor.hpp b/include/rosa/deluxe/DeluxeSensor.hpp index 1760116..89dcf24 100755 --- a/include/rosa/deluxe/DeluxeSensor.hpp +++ b/include/rosa/deluxe/DeluxeSensor.hpp @@ -1,489 +1,599 @@ //===-- rosa/deluxe/DeluxeSensor.hpp ----------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeSensor.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Specialization of \c rosa::Agent for *sensor* role of the the *deluxe /// interface*. /// /// \see \c rosa::deluxe::DeluxeContext /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXESENSOR_HPP #define ROSA_DELUXE_DELUXESENSOR_HPP #include "rosa/core/Agent.hpp" #include "rosa/deluxe/DeluxeAtoms.hpp" +#include "rosa/deluxe/DeluxeTuple.hpp" /// Local helper macros to deal with built-in types. /// ///@{ /// Creates function name for member functions in \c rosa::deluxe::DeluxeSensor. /// /// \param N name suffix to use #define DSMASTERHANDLERNAME(N) handleMaster_##N /// Defines member functions for handling messages from *master* in /// \c rosa::deluxe::DeluxeSensor. /// /// \see \c DeluxeSensorMasterInputHandlers /// /// \note No pre- and post-conditions are validated directly by these functions, /// they rather rely on \c rosa::deluxe::DeluxeSensor::saveMasterInput to do /// that. /// /// \param T the type of input to handle /// \param N name suffix for the function identifier #define DSMASTERHANDLERDEFN(T, N) \ - void DSMASTERHANDLERNAME(N)(atoms::Master, id_t MasterId, \ + void DSMASTERHANDLERNAME(N)(atoms::Master, id_t MasterId, token_size_t Pos, \ T Value) noexcept { \ - saveMasterInput(MasterId, Value); \ + saveMasterInput(MasterId, Pos, Value); \ } /// Convenience macro for \c DSMASTERHANDLERDEFN with identical arguments. /// /// \see \c DSMASTERHANDLERDEFN /// /// This macro can be used instead of \c DSMASTERHANDLERDEFN 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 DSMASTERHANDLERDEF(T) DSMASTERHANDLERDEFN(T, T) /// Results in a \c THISMEMBER reference to a member function defined by /// \c DSMASTERHANDLERDEFN. /// /// Used in the constructor of \c rosa::deluxe::DeluxeSensor to initialize super /// class \c rosa::Agent with member function defined by \c DSMASTERHANDLERDEFN. /// /// \see \c DSMASTERHANDLERDEFN, \c THISMEMBER /// /// \param N name suffix for the function identifier #define DSMASTERHANDLERREF(N) THISMEMBER(DSMASTERHANDLERNAME(N)) ///@} namespace rosa { namespace deluxe { /// Specialization of \c rosa::Agent for *sensor* role of the *deluxe /// interface*. /// /// \see \c rosa::deluxe::DeluxeContext +/// +/// \todo Add \c rosa::deluxe::DeluxeSensor::MasterInputNextPos to the class +/// invariant once having one. class DeluxeSensor : public Agent { public: /// The type of values produced by \p this object. /// - /// That is the type of values \p this object sends to its *master*. + /// That is the types of values \p this object sends to its *master* in a + /// \c rosa::deluxe::DeluxeTuple. /// /// \see \c rosa::deluxe::DeluxeSensor::master - const TypeNumber OutputType; + const Token OutputType; /// The type of values \p this object processes from its *master*. /// + /// That is the types of values \p this object receives from its *master* in a + /// \c rosa::deluxe::DeluxeTuple. + /// /// \see \c rosa::deluxe::DeluxeSensor::master - const TypeNumber MasterInputType; + const Token MasterInputType; private: + /// Indicates which element of the master-input is expected from the *master*. + /// + /// The *master* is supposed to send one \c rosa::deluxe::DeluxeTuple value + /// element by element in their order of definition. This member field tells + /// the element at which position should be received next. + /// + /// \p this object is supposed to be triggered only when a complete + /// master-input has been received, that is the field should hold the value + /// `0`. + /// + /// \see \c rosa::deluxe::DeluxeSensor::handleTrigger + /// \c rosa::deluxe::DeluxeSensor::saveMasterInput + token_size_t MasterInputNextPos; + /// 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::DeluxeSensor::saveMasterInput when storig a new /// input value in \c rosa::deluxe::DeluxeSensor::MasterInputValue. bool MasterInputChanged; /// Stores the actual input value from *master*. /// - /// \note The type of the stored value matches the type indicated by \c + /// \note The type of the stored value matches the types indicated by \c /// rosa::deluxe::DeluxeSensor::MasterInputType. const std::unique_ptr MasterInputValue; /// Alias for function objects used as trigger handler for /// \c rosa::deluxe::DeluxeSensor. /// /// \note The function used for \c H is to be \c noexcept. /// /// \see \c DeluxeSensorTriggerHandlers using H = std::function; /// \defgroup DeluxeSensorTriggerHandlers Trigger handlers of /// rosa::deluxe::DeluxeSensor /// /// \brief Trigger handler functions of \c rosa::deluxe::DeluxeSensor /// /// The actual data source functions and master-input processing function are /// captured in lambda expressions that are in turn wrapped in \c /// std::function objects. The lambda expression calls a processing function, /// either to handle master-input or obtain the next sensory value from data /// source. The next sensory value is sent it to *master* by calling \c /// rosa::deluxe::DeluxeSensor::sendToMaster. Also, the flag \c /// rosa::deluxe::DeluxeSensor::MasterInputChanged is reset when the current /// value is passed to the master-input processing function. The function \c /// rosa::deluxe::DeluxeSensor::handleTrigger needs only to call the proper /// function object. /// Processes master-input. /// /// \ingroup DeluxeSensorTriggerHandlers /// /// The function is called upon the sensor is trigged by the system. const H MFP; /// Produces the next sensory value during normal execution. /// /// \ingroup DeluxeSensorTriggerHandlers /// /// The function is used during normal execution. During simulation, the /// simulation environment sets \c rosa::deluxe::DeluxeSensor::SFP, which is /// used instead of \c rosa::deluxe::DeluxeSensor::FP. const H FP; /// Produces the next sensory value during simulation. /// /// \ingroup DeluxeSensorTriggerHandlers /// /// The function is empty by default. The simulation environment sets it to be /// used during simulation. H SFP; /// The *master* to send values to. /// /// \note *Masters* are set dynamically, hence it is possible that a /// \c rosa::deluxe::DeluxeSensor instance does not have any *master* at a /// given moment. Optional Master; /// 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; /// Wraps a master-input processing function into a trigger handler. /// /// \see \c rosa::deluxe::DeluxeSensor::MFP and \c DeluxeSensorTriggerHandlers /// - /// \tparam MT type of master-input processed by \p MF + /// \tparam Ts types of elements of master-input processed by \p MF + /// \tparam S0 indices for accessing master-input values /// /// \param MF function that processes master-input /// - /// \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. + /// \note The second argument provides indices statically as template + /// arguments \p S0..., so its actual value is ignored. + /// + /// \note A master-input type of \c rosa::deluxe::EmptyDeluxeTuple indicates + /// that \p this object does not receive master-input, \p MF is never called + /// if \p Ts is empty. /// /// \return trigger handler function based on \p MF /// - /// \pre \p MT matches \c rosa::deluxe::DeluxeSensor::MasterInputType: \code - /// MasterInputType == TypeNumberOf::Value + /// \pre Statically, the indices match the elements: \code + /// sizeof...(Ts) == sizeof...(S0) + /// \endcode Dynamically, \p Ts... match \c + /// rosa::deluxe::DeluxeSensor::MasterInputType: \code + /// MasterInputType == DeluxeTuple::TT /// \endcode - template + template H triggerHandlerFromProcessingFunction( - std::function)> &&MF) noexcept; + std::function, bool>)> &&MF, + Seq) noexcept; /// Wraps a data source function into a trigger handler. /// /// \see \c rosa::deluxe::DeluxeSensor::FP, \c /// rosa::deluxe::DeluxeSensor::SFP, and \c DeluxeSensorTriggerHandlers /// /// \tparam T type of data provided by \p F /// /// \param F function to generate value with /// /// \return trigger handler function based on \p F /// - /// \pre \p T matches \c rosa::deluxe::DeluxeSensor::OutputType: \code - /// OutputType == TypeNumberOf::Value + /// \pre Statically, the type agument \p T is an instance of \c + /// rosa::deluxe::DeluxeTuple: \code + /// IsDeluxeTuple::Value + /// \endcode Dynamically, \p T matches \c + /// rosa::deluxe::DeluxeSensor::OutputType: \code + /// OutputType == T::TT /// \endcode template H triggerHandlerFromDataSource(std::function &&F) noexcept; public: /// Creates a new instance. /// /// The constructor instantiates the base-class with functions to handle /// messages as defined for the *deluxe interface*. /// /// \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 data to operate on /// - /// \note Instantiation fails if any of the type arguments \p T and \p MT is - /// not a built-in type. + /// \note Instantiation fails if any of the type arguments \p MT and \p T is + /// not an instance of \c rosa::deluxe::DeluxeTuple or \p T is \c + /// rosa::deluxe::EmptyDeluxeTuple. /// - /// \note If \p MT is \c rosa::unit_t, the constructed object does not receive - /// master-input. + /// \note If \p MT is \c rosa::deluxe::EmptyDeluxeTuple, the constructed + /// object does not receive master-input. /// /// \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 with /// \param F function to generate the next value with during normal operation /// - /// \pre Statically, \p MT and \p T are built-in types:\code - /// TypeListSubsetOf, BuiltinTypes>::Value + /// \pre Statically, \p MT and \p T are instances of \c + /// rosa::deluxe::DeluxeTuple and \p T contains at least one element:\code + /// TypeListAllDeluxeTuple>::Value && T::Length > 0 /// \endcode /// Dynamically, the instance is created as of kind /// \c rosa::deluxe::atoms::SensorKind: /// \code /// Kind == rosa::deluxe::atoms::SensorKind /// \endcode - template , BuiltinTypes>::Value>> + /// + /// \see \c rosa::deluxe::DeluxeTuple + template < + typename MT, typename T, + typename = std::enable_if_t< + TypeListAllDeluxeTuple>::Value && (T::Length > 0)>> DeluxeSensor(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, std::function)> &&MF, std::function &&F) noexcept; /// Destroys \p this object. ~DeluxeSensor(void) noexcept; /// The *master* of \p this object, if any. /// /// \see \c rosa::deluxe::DeluxeSensor::registerMaster /// /// \return the *master* registered for \p this object Optional master(void) const noexcept; /// Registers a *master* for \p this object. /// /// The new *master* is registered by overwriting the reference to any /// already registered *master*. One can clear the registered reference by /// passing an *empty* \c rosa::Optional object as actual argument. /// /// \note The role of the referred *master* is validated by checking its /// *kind*. /// /// \note Any call to \c rosa::deluxe::DeluxeSensor::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; /// Clears the simulation trigger handler of \p this object. /// /// The function assigns \c rosa::deluxe::DeluxeSensor::SFP with \c nullptr. void clearSimulationDataSource(void) noexcept; /// Tells whether a simulation trigger handler is set for \p this object. /// /// The function returns whether \c rosa::deluxe::DeluxeSensor::SFP is not /// \c nullptr. /// /// \return if a simulation trigger handler is set for \p this object. bool simulationDataSourceIsSet(void) const noexcept; /// Registers a simulation data source for \p this object. /// /// A new simulation trigger handler wrapping \p SF is stored in /// \c rosa::deluxe::DeluxeSensor::SFP by overwriting any already registered /// simulation data source. /// /// \todo Enforce SF does not potentially throw exception. /// - /// \tparam T type of data provided by \p SF + /// \tparam Ts types of elements of values provided by \p SF /// /// \param SF function to generate value with /// - /// \pre \p T matches \c rosa::deluxe::DeluxeSensor::OutputType: \code - /// OutputType == TypeNumberOf::Value + /// \pre \p Ts... match \c rosa::deluxe::DeluxeSensor::OutputType: \code + /// OutputType == TypeToken::Value /// \endcode - template - void registerSimulationDataSource(std::function &&SF) noexcept; + template + void registerSimulationDataSource( + std::function(void)> &&SF) noexcept; private: /// Sends a value to the *master* of \p this object. /// /// \p Value is getting sent to \c rosa::deluxe::DeluxeSensor::Master if it /// contains a valid handle for a \c rosa::deluxe::DeluxeAgent. The function /// does nothing otherwise. /// - /// \tparam T type of the value to send + /// The elements from \p Value are sent one by one in separate messages to the + /// *master*. + /// + /// \tparam Ts types of the elements in \p Value + /// \tparam S0 indices for accessing elements of \p Value /// /// \param Value value to send /// - /// \pre \p T matches \c rosa::deluxe::DeluxeSensor::OutputType: \code - /// OutputType == TypeNumberOf::Value + /// \note The second argument provides indices statically as template + /// arguments \p S0..., so its actual value is ignored. + /// + /// \pre Statically, the indices match the elements: \code + /// sizeof...(Ts) == sizeof...(S0) + /// \endcode Dynamically, \p Ts match \c + /// rosa::deluxe::DeluxeSensor::OutputType: \code + /// OutputType == TypeToken::Value /// \endcode - template void sendToMaster(const T &Value) noexcept; + template + void sendToMaster(const DeluxeTuple &Value, Seq) noexcept; /// Handles master-input and generates the next sensory value upon trigger /// from the system. /// /// Executes \c rosa::deluxe::DeluxeSensor::MFP for processing master-input /// and data generating function \c rosa::deluxe::DeluxeSensor::FP or \c /// rosa::deluxe::DeluxeSensor::SFP if set. /// /// \note The only argument is a \c rosa::AtomConstant, hence its actual /// value is ignored. + /// + /// \pre Master-input is supposed to be completely received upon triggering: + /// \code + /// MasterInputNextPos == 0 + /// \endcode void handleTrigger(atoms::Trigger) noexcept; /// Stores a new input value from the *master*. /// - /// The function stores \p Value in \c + /// The function stores \p Value at position \p Pos in \c /// rosa::deluxe::DeluxeSensor::MasterInputValue and also sets the - /// corresponding flag \c rosa::deluxe::DeluxeSensor::MasterInputChanged. + /// flag \c rosa::deluxe::DeluxeSensor::MasterInputChanged. The function also + /// takes care of checking and updating \c + /// rosa::deluxe::DeluxeSensor::MasterInputNextPos: increments its value and + /// resets it to `0` when the last element is received. /// /// \note Utilized by member functions of group \c /// DeluxeSensorMasterInputHandlers. /// /// \tparam T type of input to store /// /// \param Id unique identifier of the *master* + /// \param Pos position of the value in the \c rosa::deluxe::DeluxeTuple /// \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 + /// \pre The *master* with \p Id is registered, \p Pos is the expected + /// position of master-input, and the input from the *master* at position \p + /// Pos is expected to be of type \p T: \code + /// Master && masterId() == Id && Pos == MasterInputNextPos && + /// typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf::Value /// \endcode - template void saveMasterInput(id_t Id, T Value) noexcept; + template + void saveMasterInput(id_t Id, token_size_t Pos, T Value) noexcept; /// \defgroup DeluxeSensorMasterInputHandlers Master-input handlers of /// rosa::deluxe::DeluxeSensor /// /// 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::DeluxeSensor::saveMasterInput with the proper template - /// argument and pass the content of the message on for processing. + /// built-in type 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::DeluxeSensor::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 /// DSMASTERHANDLERDEF. /// - /// \note Keep these definitions in sync with \c rosa::BuiltinTypes; but do no - /// include \c rosa::unit_t. + /// \note Keep these definitions in sync with \c rosa::BuiltinTypes. /// ///@{ DSMASTERHANDLERDEF(AtomValue) DSMASTERHANDLERDEF(int16_t) DSMASTERHANDLERDEF(int32_t) DSMASTERHANDLERDEF(int64_t) DSMASTERHANDLERDEF(int8_t) DSMASTERHANDLERDEFN(long double, long_double) DSMASTERHANDLERDEFN(std::string, std__string) DSMASTERHANDLERDEF(uint16_t) DSMASTERHANDLERDEF(uint32_t) DSMASTERHANDLERDEF(uint64_t) DSMASTERHANDLERDEF(uint8_t) + DSMASTERHANDLERDEF(unit_t) DSMASTERHANDLERDEF(bool) DSMASTERHANDLERDEF(double) DSMASTERHANDLERDEF(float) /// @} }; -template +template DeluxeSensor::H DeluxeSensor::triggerHandlerFromProcessingFunction( - std::function)> &&MF) noexcept { - ASSERT(MasterInputType == TypeNumberOf::Value); + std::function, bool>)> &&MF, + Seq) noexcept { + using MT = DeluxeTuple; + STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments"); + ASSERT(MasterInputType == MT::TT); + return [ this, MF ](void) noexcept { - // Do not do anything for master-input type \c rosa::unit_t. - if (MasterInputType != TypeNumberOf::Value) { + // Do not do anything for master-input type \c + // rosa::deluxe::EmptyDeluxeTuple. + if (!std::is_same_v) { LOG_TRACE_STREAM << "DeluxeSensor " << Name << " handles master-input." << std::endl; + // The assert must hold if \p this object was successfuuly constructed. + ASSERT((true && ... && + (static_cast(static_cast(S0)) == S0))); const auto MasterInputArg = std::make_pair( - *static_cast(MasterInputValue->pointerTo(0)), + // Get all elements of the tuple in a fold expression. + DeluxeTuple(*static_cast( + MasterInputValue->pointerTo(static_cast(S0)))...), MasterInputChanged); MasterInputChanged = false; MF(MasterInputArg); } }; } template DeluxeSensor::H DeluxeSensor::triggerHandlerFromDataSource( std::function &&F) noexcept { - ASSERT(OutputType == TypeNumberOf::Value); + STATIC_ASSERT(IsDeluxeTuple::Value, "not tuple type argument"); + ASSERT(OutputType == T::TT); return [ this, F ](void) noexcept { LOG_TRACE_STREAM << "DeluxeSensor " << Name << " obtains next value." << std::endl; - sendToMaster(F()); + sendToMaster(F(), seq_t()); }; } template DeluxeSensor::DeluxeSensor(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, std::function)> &&MF, std::function &&F) noexcept : Agent(Kind, Id, Name, S, THISMEMBER(handleTrigger), DSMASTERHANDLERREF(AtomValue), DSMASTERHANDLERREF(int16_t), DSMASTERHANDLERREF(int32_t), DSMASTERHANDLERREF(int64_t), DSMASTERHANDLERREF(int8_t), DSMASTERHANDLERREF(long_double), DSMASTERHANDLERREF(std__string), DSMASTERHANDLERREF(uint16_t), DSMASTERHANDLERREF(uint32_t), DSMASTERHANDLERREF(uint64_t), - DSMASTERHANDLERREF(uint8_t), DSMASTERHANDLERREF(bool), - DSMASTERHANDLERREF(double), DSMASTERHANDLERREF(float)), - OutputType(TypeNumberOf::Value), - MasterInputType(TypeNumberOf::Value), MasterInputChanged(false), - MasterInputValue(new TokenizedStorage()), - MFP(triggerHandlerFromProcessingFunction(std::move(MF))), + DSMASTERHANDLERREF(uint8_t), DSMASTERHANDLERREF(unit_t), + DSMASTERHANDLERREF(bool), DSMASTERHANDLERREF(double), + DSMASTERHANDLERREF(float)), + OutputType(T::TT), MasterInputType(MT::TT), MasterInputChanged(false), + MasterInputValue(new typename TokenizedStorageForTypeList< + typename UnwrapDeluxeTuple::Type>::Type()), + MFP(triggerHandlerFromProcessingFunction(std::move(MF), + seq_t())), FP(triggerHandlerFromDataSource(std::move(F))), SFP(nullptr) { ASSERT(Kind == atoms::SensorKind); LOG_TRACE("DeluxeSensor is created."); } -template +template void DeluxeSensor::registerSimulationDataSource( - std::function &&SF) noexcept { - ASSERT(OutputType == TypeNumberOf::Value); + std::function(void)> &&SF) noexcept { + ASSERT(OutputType == TypeToken::Value); SFP = triggerHandlerFromDataSource(std::move(SF)); } -template -void DeluxeSensor::sendToMaster(const T &Value) noexcept { - ASSERT(OutputType == TypeNumberOf::Value); +template +void DeluxeSensor::sendToMaster(const DeluxeTuple &Value, + Seq) noexcept { + STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments"); + ASSERT(OutputType == TypeToken::Value); + + // The assert must hold if \p this object was successfuuly constructed. + ASSERT((true && ... && + (static_cast(static_cast(S0)) == S0))); + // Create a static constant array for these indices to be available as lvalue + // references when creating messages below. \c S0... when used directly in a + // fold expression is a temporary value, which would result in \c + // rosa::Message instances being created with rvalue references. Further, all + // other values would to copied into a temporary variable for making them + /// available as rvalue references (they are constant lvalue references here). + static constexpr std::array Indices{{S0...}}; + + LOG_TRACE_STREAM << "DeluxeSensor " << Name << "(" << Id + << ") sends to master(" + << static_cast(Master && *Master) << "): " << Value + << std::endl; // 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)); + // Handle each element of the tuple in a fold expression. + (Master->sendMessage(Message::create(atoms::Slave::Value, Id, Indices[S0], + std::get(Value))), + ...); } } template -void DeluxeSensor::saveMasterInput(id_t Id, T Value) noexcept { - ASSERT(Master && masterId() == Id && - MasterInputType == TypeNumberOf::Value); +void DeluxeSensor::saveMasterInput(id_t Id, token_size_t Pos, + T Value) noexcept { + ASSERT(Master && masterId() == Id && Pos == MasterInputNextPos && + typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf::Value); + + LOG_TRACE_STREAM << "DeluxeSensor " << Name << "(" << Id + << ") saves value from master: (" << static_cast(Pos) + << ") " << Value << std::endl; + + // Save value. + *static_cast(MasterInputValue->pointerTo(Pos)) = Value; + + // Update position of next value. + if (++MasterInputNextPos == lengthOfToken(MasterInputType)) { + MasterInputNextPos = 0; + } - *static_cast(MasterInputValue->pointerTo(0)) = Value; + // Set flag. MasterInputChanged = true; } } // End namespace deluxe } // End namespace rosa #undef DSMASTERHANDLEREF #undef DSMASTERHANDLEDEF #undef DSMASTERHANDLEDEFN #undef DSMASTERHANDLENAME #endif // ROSA_DELUXE_DELUXESENSOR_HPP diff --git a/include/rosa/deluxe/DeluxeSystem.hpp b/include/rosa/deluxe/DeluxeSystem.hpp index ed8071a..14b9464 100755 --- a/include/rosa/deluxe/DeluxeSystem.hpp +++ b/include/rosa/deluxe/DeluxeSystem.hpp @@ -1,233 +1,239 @@ //===-- rosa/deluxe/DeluxeSystem.hpp ----------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeSystem.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Specialization of \c rosa::MessagingSystem for the *deluxe /// interface*. /// /// \see \c rosa::deluxe::DeluxeContext /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXESYSTEM_HPP #define ROSA_DELUXE_DELUXESYSTEM_HPP #include "rosa/core/MessagingSystem.hpp" #include "rosa/deluxe/DeluxeAgent.hpp" #include "rosa/deluxe/DeluxeSensor.hpp" namespace rosa { namespace deluxe { /// Implements and extends the \c rosa::MessagingSystem interface to be /// used by \c rosa::deluxe::DeluxeContext. /// /// The class is a specialization of \c rosa::MessagingSystem, where objects /// of two specialized subtypes of \c rosa::Agent, \c rosa::deluxe::DeluxeSensor /// and \c rosa::deluxe::DeluxeAgent, constitute a system. The class extends the /// \c rosa::MessagingSystem interface with features required to implement the /// *deluxe interface*. /// /// \see rosa::deluxe::DeluxeContext class DeluxeSystem : public MessagingSystem { friend class DeluxeContext; public: /// Returns an object implementing the \c rosa::deluxe::DeluxeSystem /// interface. /// /// \param Name name of the new instance /// /// \return \c std::unique_ptr for the new instance of /// \c rosa::DeluxeSystem static std::unique_ptr createSystem(const std::string &Name) noexcept; protected: /// Creates a new instance. /// /// \note Protected constructor restricts instantiation for subclasses. DeluxeSystem(void) noexcept = default; public: /// Creates a \c rosa::deluxe::DeluxeSensor instance owned by \p this object /// and returns a \p rosa::AgentHandle for it. /// /// \tparam MT type of master-input the new \c rosa::deluxe::DeluxeSensor /// receives /// \tparam T type of data the new \c rosa::deluxe::DeluxeSensor operates on /// + /// \note Type arguments \p MT and \p T must be instances of \c + /// rosa::deluxe::DeluxeTuple. + /// /// \param Name name of the new \c rosa::deluxe::DeluxeSensor /// \param MF function to process master-input values /// \param F function to generate the next value with during normal operation /// /// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor. /// /// \return \c rosa::AgentHandle for new \c rosa::deluxe::DeluxeSensor template AgentHandle createSensor(const std::string &Name, std::function)> &&MF, std::function &&F) noexcept; /// Creates a \c rosa::deluxe::DeluxeAgent instance owned by \p this object /// and returns a \c rosa::AgentHandle for it. /// /// \tparam MT type of master-input the new \c rosa::deluxe::DeluxeAgent /// receives /// \tparam T type of data the new \c rosa::deluxe::DeluxeAgent outputs /// \tparam Ts types of master-output the new \c rosa::deluxe::DeluxeAgent /// produces /// \tparam As types of inputs the new \c rosa::deluxe::DeluxeAgent takes /// + /// \note Type arguments \p MT, \p T, \p Ts..., and \p As... must be + /// instances of \c rosa::deluxe::DeluxeTuple. + /// /// \param Name name of the new \c rosa::deluxe::DeluxeAgent /// \param MF function for the new \c rosa::deluxe::DeluxeAgent to process /// master-input values and generate master-output with /// \param F function for the new \c rosa::deluxe::DeluxeAgent to process /// input values and generate output and master-output with /// /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. /// /// \return \c rosa::AgentHandle for new \c rosa::deluxe::DeluxeAgent template AgentHandle createAgent( const std::string &Name, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept; protected: /// Tells whether a \c rosa::AgentHandle refers to a /// \c rosa::deluxe::DeluxeSensor owned by \p this object. /// /// \param H \c rosa::AgentHandle to check /// /// \return whether \p H refers to a \c rosa::deluxe::DeluxeSensor owned by /// \p this object virtual bool isDeluxeSensor(const AgentHandle &H) const noexcept = 0; /// Extracts a const qualified \c rosa::deluxe::DeluxeSensor reference from a /// const qualified \c rosa::AgentHandle if possible. /// /// The function returns a \c rosa::Optional object containing a const /// qualified reference to a \c rosa::deluxe::DeluxeSensor object extracted /// from a const qualified \c rosa::AgentHandle instance if the referred /// object is of type \c rosa::deluxeDeluxeSensor and owned by \p this object. /// The returned \c rosa::Optional object is empty otherwise. /// /// \see rosa::deluxe::DeluxeSystem::isDeluxeSensor /// /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeSensor /// from /// /// \return const qualified reference to \c rosa::deluxe::DeluxeSensor if /// \p H refers to an object which is of that type and is owned by \p this /// object Optional getDeluxeSensor(const AgentHandle &H) const noexcept; /// Extracts a \c rosa::deluxe::DeluxeSensor reference from a /// \c rosa::AgentHandle if possible. /// /// The function returns a \c rosa::Optional object containing a reference to /// a \c rosa::deluxe::DeluxeSensor object extracted from a /// \c rosa::AgentHandle instance if the referred object is of type /// \c rosa::deluxeDeluxeSensor and owned by \p this object. The returned /// \c rosa::Optional object is empty otherwise. /// /// \see rosa::deluxe::DeluxeSystem::isDeluxeSensor /// /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeSensor /// from /// /// \return reference to \c rosa::deluxe::DeluxeSensor if \p H refers to an /// object which is of that type and is owned by \p this object Optional getDeluxeSensor(AgentHandle &H) const noexcept; /// Tells whether a \c rosa::AgentHandle refers to a /// \c rosa::deluxe::DeluxeAgent owned by \p this object. /// /// \param H \c rosa::AgentHandle to check /// /// \return whether \p H refers to a \c rosa::deluxe::DeluxeAgent owned by /// \p this object virtual bool isDeluxeAgent(const AgentHandle &H) const noexcept = 0; /// Extracts a const qualified \c rosa::deluxe::DeluxeAgent reference from a /// const qualified \c rosa::AgentHandle if possible. /// /// The function returns a \c rosa::Optional object containing a const /// qualified reference to a \c rosa::deluxe::DeluxeAgent object extracted /// from a const qualified \c rosa::AgentHandle instance if the referred /// object is of type \c rosa::deluxeDeluxeAgent and owned by \p this object. /// The returned \c rosa::Optional object is empty otherwise. /// /// \see rosa::deluxe::DeluxeSystem::isDeluxeAgent /// /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeAgent /// from /// /// \return const qualified reference to \c rosa::deluxe::DeluxeAgent if \p H /// refers to an object which is of that type and is owned by \p this object Optional getDeluxeAgent(const AgentHandle &H) const noexcept; /// Extracts a \c rosa::deluxe::DeluxeAgent reference from a /// \c rosa::AgentHandle if possible. /// /// The function returns a \c rosa::Optional object containing a reference to /// a \c rosa::deluxe::DeluxeAgent object extracted from a /// \c rosa::AgentHandle instance if the referred object is of type /// \c rosa::deluxeDeluxeAgent and owned by \p this object. The returned /// \c rosa::Optional object is empty otherwise. /// /// \see rosa::deluxe::DeluxeSystem::isDeluxeAgent /// /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeAgent /// from /// /// \return reference to \c rosa::deluxe::DeluxeAgent if \p H refers to an /// object which is of that type and is owned by \p this object Optional getDeluxeAgent(AgentHandle &H) const noexcept; }; template AgentHandle DeluxeSystem::createSensor(const std::string &Name, std::function)> &&MF, std::function &&F) noexcept { Agent &DS = createUnit( [&](const id_t Id, MessagingSystem &S) { return new DeluxeSensor(atoms::SensorKind, Id, Name, S, std::move(MF), std::move(F)); }); return {DS}; } template AgentHandle DeluxeSystem::createAgent( const std::string &Name, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept { Agent &DA = createUnit( [&](const id_t Id, DeluxeSystem &S) { return new DeluxeAgent(atoms::AgentKind, Id, Name, S, std::move(MF), std::move(F)); }); return {DA}; } } // End namespace deluxe } // End namespace rosa #endif // ROSA_LIB_DELUXE_DELUXESYSTEM_HPP diff --git a/include/rosa/deluxe/DeluxeTuple.hpp b/include/rosa/deluxe/DeluxeTuple.hpp new file mode 100644 index 0000000..9a00e25 --- /dev/null +++ b/include/rosa/deluxe/DeluxeTuple.hpp @@ -0,0 +1,349 @@ +//===-- rosa/deluxe/DeluxeTuple.hpp -----------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/deluxe/DeluxeTuple.hpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Facilities for handling multiple input/output values for connections +/// in the *deluxe interface*. +/// +/// \see \c rosa::deluxe::DeluxeContext +/// +//===----------------------------------------------------------------------===// + +#ifndef ROSA_DELUXE_DELUXETUPLE_HPP +#define ROSA_DELUXE_DELUXETUPLE_HPP + +#include "rosa/support/sequence.hpp" +#include "rosa/support/type_token.hpp" +#include +#include + +namespace rosa { +namespace deluxe { + +/// A tuple to manage multiple input/output values in the *deluxe interface*. +/// +/// \tparam Ts types of elements of the tuple +/// +/// \note The template may be instantiated only with built-in types and the +/// number of those type may not exceed the capacity of a \c rosa::Token. +template +struct DeluxeTuple : public std::tuple { + // Statically enforce that the class template is instantiated only with + // built-in types. + STATIC_ASSERT((TypeListSubsetOf, BuiltinTypes>::Value), + "not built-in types"); + // Statically enforce that the class template is instantiated with not too + // many types. + // \note Instantiation would fail on \c rosa::deluxe::DeluxeTuple::TT if there + // are too any types; this assertion is for more readable error reporting. + STATIC_ASSERT(sizeof...(Ts) <= token::MaxTokenizableListSize, + "Too many types"); + + /// How many elements the instance has. + static constexpr token_size_t Length = sizeof...(Ts); + + /// What types the class contains. + /// + /// Type information encoded as \c rosa::Token. + static constexpr Token TT = TypeToken::Value; + + /// Default constructor, zero-initializes elements. + DeluxeTuple(void) = default; + + /// Constructor, initializes the underlying \c std::tuple with lvalue + /// references. + /// + /// \param Args value references to the values to store + DeluxeTuple(const std::decay_t &... Args) : std::tuple(Args...) {} + + /// Constructor, initializes the underlying \c std::tuple with rvalue + /// references. + /// + /// \param Args rvalue references to the values to store + DeluxeTuple(std::decay_t &&... Args) + : std::tuple(std::move(Args)...) {} + + /// Default copy-constructor. + DeluxeTuple(const DeluxeTuple &) = default; + + /// Default move-constructor. + DeluxeTuple(DeluxeTuple &&) = default; + + /// Default copy-assignment. + DeluxeTuple &operator=(const DeluxeTuple &) = default; + + /// Default move-assignment. + DeluxeTuple &operator=(DeluxeTuple &&) = default; + +private: + /// Dumps \p this object to a given \c std::ostream. + /// + /// \note Provides implementation for \c rosa::deluxe::DeluxeTuple::dump. + /// + /// \tparam S0 Indices for accessing elements. + /// + /// \param [in,out] OS output stream to dump to + /// + /// \note The second argument provides indices statically as template + /// arguments \p S0..., so its actual value is ignored. + /// + /// \pre Statically, \p S0... matches number of types \p this object was + /// created: \code + /// sizeof...(S0) == sizeof...(Ts) + /// \endcode + template + void dump(std::ostream &OS, Seq) const noexcept; + +public: + /// Dumps \p this object to a given \c std::ostream. + /// + /// \param [in,out] OS output stream to dump to + void dump(std::ostream &OS) const noexcept; +}; + +template +template +void DeluxeTuple::dump(std::ostream &OS, Seq) const noexcept { + STATIC_ASSERT(sizeof...(S0) == sizeof...(Ts), "inconsistent type arguments"); + OS << "{"; + (OS << ... << (" " + std::to_string(std::get(*this)))); + OS << " }"; +} + +template +void DeluxeTuple::dump(std::ostream &OS) const noexcept { + dump(OS, seq_t()); +} + +/// Type alias for a \c rosa::deluxe::DeluxeTuple that contains no elements. +using EmptyDeluxeTuple = DeluxeTuple<>; + +/// Template specialization for \c rosa::deluxe::EmptyDeluxeTuple. +template <> struct DeluxeTuple<> : public std::tuple<> { + /// How many elements the instance has. + static constexpr token_size_t Length = 0; + + /// What types the class contains. + /// + /// Type information encoded as \c rosa::Token. + static constexpr Token TT = TypeToken<>::Value; + + /// Constructor, initializes the underlying \c std::tuple. + DeluxeTuple(void) : std::tuple<>() {} + + /// Default copy-constructor. + DeluxeTuple(const DeluxeTuple &) = default; + + // Default move-constructor. + DeluxeTuple(DeluxeTuple &&) = default; + + /// Default copy-assignment. + DeluxeTuple &operator=(const DeluxeTuple &) = default; + + // Default move-assignment, + DeluxeTuple &operator=(DeluxeTuple &&) = default; + + /// Dumps \p this object to a given \c std::ostream. + /// + /// \param [in,out] OS output stream to dump to + static void dump(std::ostream &OS) noexcept; +}; + +/// Creates a \c rosa::deluxe::DeluxeTuple instance from the given lvalues +/// references. +/// +/// \tparam Ts types of elements of the tuple +/// +/// \see \c rosa::deluxe::DeluxeTuple +/// +/// \param Args values to store in the tuple +/// +/// \return an instance of \c rosa::deluxe::DeluxeTuple with \p Args as +/// elements +template +inline DeluxeTuple make_deluxe_tuple(const Ts &... Args) noexcept { + return DeluxeTuple(Args...); +} + +/// Creates a \c rosa::deluxe::DeluxeTuple instance from the given rvalue +/// references. +/// +/// \tparam Ts types of elements of the tuple +/// +/// \see \c rosa::deluxe::DeluxeTuple +/// +/// \param Args values to store in the tuple +/// +/// \return an instance of \c rosa::deluxe::DeluxeTuple with \p Args as +/// elements +template +inline DeluxeTuple make_deluxe_tuple(Ts&&... Args) noexcept { + return DeluxeTuple(std::move(Args)...); +} + +/// \defgroup UnwrapDeluxeTuple Implementation of +/// rosa::deluxe::UnwrapDeluxeTuple +/// +/// \brief Unwraps element types from an instance of \c +/// rosa::deluxe::DeluxeTuple into a \c rosa::TypeList +/// +/// Types can be unwrapped from a \c rosa::deluxe::DeluxeTuple instance as \code +/// typename UnwrapDeluxeTuple::Type +/// \endcode +/// +/// For example, the following expression evaluates to `true`: \code +/// std::is_same_v>::Type, +/// TypeList> +/// \endcode +///@{ + +/// Declaration of the template. +/// +/// \tparam Tuple \c rosa::deluxe::DeluxeTuple to unwrap +template struct UnwrapDeluxeTuple; + +/// Implementation of the template for \c rosa::deluxe::DeluxeTuple instances. +template struct UnwrapDeluxeTuple> { + using Type = TypeList; +}; + +///@} + +/// \defgroup TypeListUnwrapDeluxeTuple Implementation of +/// \c rosa::deluxe::TypeListUnwrapDeluxeTuple +/// +/// \brief Unwraps element types from instances of \c +/// rosa::deluxe::DeluxeTuple in a \c rosa::TypeList. +/// +/// Types can be unwrapped from \c rosa::deluxe::DeluxeTuple instances as \code +/// typename TypeListUnwrapDeluxeTuple::Type +/// \endcode +/// +/// For example, the following expression evaluates to `true`: \code +/// std::is_same_v< +/// typename TypeListUnwrapDeluxeTuple, +/// T3>>::Type, +/// TypeList> +/// \endcode +///@{ + +/// Declaration of the template. +/// +/// \tparam List \c rosa::TypeList to check +template struct TypeListUnwrapDeluxeTuple; + +/// Specialization for \c rosa::EmptyTypeList. +template <> struct TypeListUnwrapDeluxeTuple { + using Type = EmptyTypeList; +}; + +/// Specialization for the case when the first type in \p List is an instance of +/// \c rosa::deluxe::DeluxeTuple. +template +struct TypeListUnwrapDeluxeTuple, Ts...>> { + using Type = typename TypeListConcat< + typename UnwrapDeluxeTuple>::Type, + typename TypeListUnwrapDeluxeTuple>::Type>::Type; +}; + +/// Implementation for a general first type in \p List. +template +struct TypeListUnwrapDeluxeTuple> { + using Type = typename TypeListPush< + T, typename TypeListUnwrapDeluxeTuple>::Type>::Type; +}; + +///@} + +/// \defgroup IsDeluxeTuple Implementation of \c rosa::deluxe::IsDeluxeTuple +/// +/// \brief Tells if a type is an instance of \c rosa::deluxe::DeluxeTuple. +/// +/// Whether a type \c T is an instance of \c rosa::deluxe::DeluxeTuple can be +/// checked as \code +/// IsDeluxeTuple::Value +/// \endcode +///@{ + +/// Declaration of the template. +/// +/// \tparam T type to check +template struct IsDeluxeTuple; + +/// Specialization for the case when the type is an instance of \c +/// rosa::deluxe::DeluxeTuple. +template +struct IsDeluxeTuple> { + static constexpr bool Value = true; +}; + +/// Implementation for a general case of type \p T. +template +struct IsDeluxeTuple { + static constexpr bool Value = false; +}; + +///@} + +/// \defgroup TypeListAllDeluxeTuple Implementation of +/// \c rosa::deluxe::TypeListAllDeluxeTuple +/// +/// \brief Tells if all types in a \c rosa::TypeList is an instance of \c +/// rosa::deluxe::DeluxeTuple. +/// +/// Whether a \c rosa::TypeList \c List contains instances of \c +/// rosa::deluxe::DeluxeTuple only can be checked as \code +/// TypeListAllDeluxeTuple::Value +/// \endcode +///@{ + +/// Declaration of the template. +/// +/// \tparam List \c rosa::TypeList to check +template struct TypeListAllDeluxeTuple; + +/// Specialization for \c rosa::EmptyTypeList. +template <> struct TypeListAllDeluxeTuple { + static constexpr bool Value = true; +}; + +/// Implementation for the general case when there is at leasst one element in +/// the list. +template +struct TypeListAllDeluxeTuple> { + static constexpr bool Value = + IsDeluxeTuple::Value && TypeListAllDeluxeTuple>::Value; +}; + +///@} + +} // End namespace deluxe +} // End namespace rosa + +namespace std { + +/// Dumps a \c rosa::deluxe::Deluxe instance to a given \c std::ostream. +/// +/// \param [in,out] OS output stream to dump to +/// \param Tuple \c rosa::deluxe::Deluxe to dump +/// +/// \return \p OS after dumping \p Tuple to it +template +ostream &operator<<(ostream &OS, + const rosa::deluxe::DeluxeTuple &Tuple) { + Tuple.dump(OS); + return OS; +} + +} // End namespace std + +#endif // ROSA_DELUXE_DELUXETUPLE_HPP diff --git a/lib/deluxe/CMakeLists.txt b/lib/deluxe/CMakeLists.txt index 41a22b3..78e0ee8 100755 --- a/lib/deluxe/CMakeLists.txt +++ b/lib/deluxe/CMakeLists.txt @@ -1,20 +1,22 @@ set(LIB_INCLUDE_DIR ${ROSA_MAIN_INCLUDE_DIR}/rosa/deluxe) add_library(ROSADeluxe ${LIB_INCLUDE_DIR}/namespace.h namespace.cpp ${LIB_INCLUDE_DIR}/DeluxeAtoms.hpp DeluxeAtoms.cpp + ${LIB_INCLUDE_DIR}/DeluxeTuple.hpp + DeluxeTuple.cpp ${LIB_INCLUDE_DIR}/DeluxeSensor.hpp DeluxeSensor.cpp ${LIB_INCLUDE_DIR}/DeluxeAgent.hpp DeluxeAgent.cpp ${LIB_INCLUDE_DIR}/DeluxeSystem.hpp DeluxeSystem.cpp DeluxeSystemImpl.hpp DeluxeSystemImpl.cpp ${LIB_INCLUDE_DIR}/DeluxeContext.hpp DeluxeContext.cpp ) ROSA_add_library_dependencies(ROSADeluxe ROSACore) diff --git a/lib/deluxe/DeluxeAgent.cpp b/lib/deluxe/DeluxeAgent.cpp index 3a100fc..d6e973c 100755 --- a/lib/deluxe/DeluxeAgent.cpp +++ b/lib/deluxe/DeluxeAgent.cpp @@ -1,237 +1,279 @@ //===-- 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 && + InputNextPos.size() == NumberOfInputs && InputChanged.size() == NumberOfInputs && - InputValues->size() == NumberOfInputs && + InputStorageOffsets.size() == NumberOfInputs && + InputValues->size() == + InputStorageOffsets[NumberOfInputs - 1] + + lengthOfToken(InputTypes[NumberOfInputs - 1]) && MasterOutputTypes.size() == NumberOfInputs // == NumberOfMasterOutputs && Slaves.size() == NumberOfInputs)) { return false; } + // Stores storage offset for the next slave position checked in the following + // loop. + token_size_t InputStorageOffset = 0; + // 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]; - // 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) { + // First, validate the corresponding storage offset value. + if (InputStorageOffsets[I] != InputStorageOffset) { + return false; + } + + // Fetch type-related information for the input position. + const Token T = InputTypes[I]; + const token_size_t TL = lengthOfToken(T); + const size_t StorageOffset = InputStorageOffsets[I]; + + // Update storage offset for the next position. + InputStorageOffset += TL; + + // Validate input types at position \c I. + for (token_size_t TI = 0; TI < TL; ++TI) { + const size_t ElemOffset = StorageOffset + TI; + // The assert must hold if \p this object was successfuuly constructed. + ASSERT(static_cast(static_cast(ElemOffset)) == + ElemOffset); + if (InputValues->typeAt(static_cast(ElemOffset)) != + typeAtPositionOfToken(T, TI)) { + return false; + } + } + + // Check the index of next expected element for position \c I. + if (InputNextPos[I] >= TL) { 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; + const Token MT = MasterOutputTypes[I]; + const bool hasMT = !emptyToken(MT); // \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; } + // Check the size of the master-input storage. + if (MasterInputValue->size() != lengthOfToken(MasterInputType)) { + return false; + } + + // Check the index of next expected element from the *master*. + const token_size_t MITL = lengthOfToken(MasterInputType); + if ((MITL != 0 && MasterInputNextPos >= MITL) || + (MITL == 0 && MasterInputNextPos != 0)) { + 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 { +Token DeluxeAgent::inputType(const size_t Pos) const noexcept { ASSERT(inv() && Pos < NumberOfInputs); return InputTypes[Pos]; } -TypeNumber DeluxeAgent::masterOutputType(const size_t Pos) const noexcept { +Token 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 || + (emptyToken(MasterOutputTypes[Pos]) || static_cast(unwrapAgent(*Slave)) .MasterInputType == MasterOutputTypes[Pos])) || (unwrapAgent(*Slave).Kind == atoms::AgentKind && static_cast(unwrapAgent(*Slave)).OutputType == InputTypes[Pos] && - (MasterOutputTypes[Pos] == TypeNumberOf::Value || + (emptyToken(MasterOutputTypes[Pos]) || 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/deluxe/DeluxeContext.cpp b/lib/deluxe/DeluxeContext.cpp index 10d9d5c..175e321 100755 --- a/lib/deluxe/DeluxeContext.cpp +++ b/lib/deluxe/DeluxeContext.cpp @@ -1,155 +1,155 @@ //===-- deluxe/DeluxeContext.cpp --------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file deluxe/DeluxeContext.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Implementation for rosa/deluxe/DeluxeContext.hpp. /// //===----------------------------------------------------------------------===// #define ROSA_LIB_DELUXE_DELUXECONTEXT_CPP // For including helper macros. #include "rosa/deluxe/DeluxeContext.hpp" #include namespace rosa { namespace deluxe { std::unique_ptr DeluxeContext::create(const std::string &Name) noexcept { return std::unique_ptr(new DeluxeContext(Name)); } DeluxeContext::DeluxeContext(const std::string &Name) noexcept : System(DeluxeSystem::createSystem(Name)) { LOG_TRACE("DeluxeContext for '" + System->name() + "' is created."); } DeluxeContext::~DeluxeContext(void) noexcept { // \c rosa::deluxe::DeluxeContext::System is not used outside, just clean it. for(auto U : DeluxeUnits) { System->destroyAgent(U); } // \note \c System will be marked clean by SystemImpl::~SystemImpl. LOG_TRACE("DeluxeContext for '" + System->name() + "' prepared for destruction."); } DeluxeContext::ErrorCode DeluxeContext::connectSensor(AgentHandle Agent, const size_t Pos, AgentHandle Sensor, const std::string &Description) noexcept { // Generate trace log. auto &Trace = LOG_TRACE_STREAM; Trace << "Establishing connection"; if (!Description.empty()) { Trace << " '" << Description << "'"; } Trace << " between '" << System->unwrapAgent(Sensor).FullName << "' and '" << System->unwrapAgent(Agent).FullName << "'\n"; // Make sure preconditions are met. if (!System->isDeluxeAgent(Agent)) { DCRETERROR(ErrorCode::NotAgent); } else if (!System->isDeluxeSensor(Sensor)) { DCRETERROR(ErrorCode::NotSensor); } auto A = System->getDeluxeAgent(Agent); auto S = System->getDeluxeSensor(Sensor); ASSERT(A && S); // Sanity check. if (Pos >= A->NumberOfInputs) { DCRETERROR(ErrorCode::WrongPosition); } else if (A->inputType(Pos) != S->OutputType || - (A->masterOutputType(Pos) != TypeNumberOf::Value && + (!emptyToken(A->masterOutputType(Pos)) && A->masterOutputType(Pos) != S->MasterInputType)) { DCRETERROR(ErrorCode::TypeMismatch); } else if (A->slave(Pos)) { DCRETERROR(ErrorCode::AlreadyHasSlave); } else if (S->master()) { DCRETERROR(ErrorCode::AlreadyHasMaster); } // Do register. A->registerSlave(Pos, {Sensor}); S->registerMaster({Agent}); return ErrorCode::NoError; } DeluxeContext::ErrorCode DeluxeContext::connectAgents(AgentHandle Master, const size_t Pos, AgentHandle Slave, const std::string &Description) noexcept { // Generate trace log. auto &Trace = LOG_TRACE_STREAM; Trace << "Establishing connection"; if (!Description.empty()) { Trace << " '" << Description << "'"; } Trace << " between '" << System->unwrapAgent(Slave).FullName << "' and '" << System->unwrapAgent(Master).FullName << "'\n"; // Make sure preconditions are met. if (!(System->isDeluxeAgent(Master) && System->isDeluxeAgent(Slave))) { DCRETERROR(ErrorCode::NotAgent); } auto M = System->getDeluxeAgent(Master); auto S = System->getDeluxeAgent(Slave); ASSERT(M && S); // Sanity check. if (Pos >= M->NumberOfInputs) { DCRETERROR(ErrorCode::WrongPosition); } else if (M->inputType(Pos) != S->OutputType || - (M->masterOutputType(Pos) != TypeNumberOf::Value && + (!emptyToken(M->masterOutputType(Pos)) && M->masterOutputType(Pos) != S->MasterInputType)) { DCRETERROR(ErrorCode::TypeMismatch); } else if (M->slave(Pos)) { DCRETERROR(ErrorCode::AlreadyHasSlave); } else if (S->master()) { DCRETERROR(ErrorCode::AlreadyHasMaster); } // Do register. M->registerSlave(Pos, {Slave}); S->registerMaster({Master}); return ErrorCode::NoError; } std::weak_ptr DeluxeContext::getSystem(void) const noexcept { return std::weak_ptr(System); } void DeluxeContext::initializeSimulation(void) noexcept { // Clear simulation data sources from sensors. for (auto U : DeluxeUnits) { if (auto S = System->getDeluxeSensor(U)) { S->clearSimulationDataSource(); } } } void DeluxeContext::simulate(const size_t NumCycles) const noexcept { ASSERT(std::all_of( DeluxeUnits.begin(), DeluxeUnits.end(), [&](const AgentHandle &H) { return System->isDeluxeAgent(H) || System->isDeluxeSensor(H) && System->getDeluxeSensor(H)->simulationDataSourceIsSet(); })); for (size_t I = 1; I <= NumCycles; ++I) { LOG_TRACE("Simulation cycle: " + std::to_string(I)); for (auto U : DeluxeUnits) { U.sendMessage(Message::create(atoms::Trigger::Value)); } } } } // End namespace deluxe } // End namespace rosa diff --git a/lib/deluxe/DeluxeSensor.cpp b/lib/deluxe/DeluxeSensor.cpp index 1e9968e..632352e 100755 --- a/lib/deluxe/DeluxeSensor.cpp +++ b/lib/deluxe/DeluxeSensor.cpp @@ -1,72 +1,74 @@ //===-- deluxe/DeluxeSensor.cpp ---------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file deluxe/DeluxeSensor.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Implementation of rosa/deluxe/DeluxeSensor.hpp. /// //===----------------------------------------------------------------------===// #include "rosa/deluxe/DeluxeSensor.hpp" #include "rosa/deluxe/DeluxeAgent.hpp" namespace rosa { namespace deluxe { id_t DeluxeSensor::masterId(void) const noexcept { ASSERT(Master); return unwrapAgent(*Master).Id; } DeluxeSensor::~DeluxeSensor(void) noexcept { LOG_TRACE("Destroying DeluxeSensor..."); // Make sure \p this object is not a registered *slave*. if (Master) { ASSERT(unwrapAgent(*Master).Kind == atoms::AgentKind); // Sanity check. DeluxeAgent &M = static_cast(unwrapAgent(*Master)); ASSERT(M.positionOfSlave(self()) != M.NumberOfInputs); // Sanity check. M.registerSlave(M.positionOfSlave(self()), {}); Master = {}; } } Optional DeluxeSensor::master(void) const noexcept { return Master; } void DeluxeSensor::registerMaster(const Optional _Master) noexcept { ASSERT(!_Master || unwrapAgent(*_Master).Kind == atoms::AgentKind); Master = _Master; } void DeluxeSensor::clearSimulationDataSource(void) noexcept { SFP = nullptr; } bool DeluxeSensor::simulationDataSourceIsSet(void) const noexcept { return SFP != nullptr; } void DeluxeSensor::handleTrigger(atoms::Trigger) noexcept { + ASSERT(MasterInputNextPos == 0); + // Process master-input. MFP(); // Obtain the next sensory value. // Use \c rosa::deluxe::DeluxeSensor::SFP if set, otherwise // \c rosa::deluxe::DeluxeSensor::FP. const H &F = SFP ? SFP : FP; F(); } } // End namespace deluxe } // End namespace rosa diff --git a/lib/deluxe/DeluxeTuple.cpp b/lib/deluxe/DeluxeTuple.cpp new file mode 100644 index 0000000..41895fb --- /dev/null +++ b/lib/deluxe/DeluxeTuple.cpp @@ -0,0 +1,28 @@ +//===-- deluxe/DeluxeTuple.cpp ----------------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file deluxe/DeluxeTuple.cpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Implementation of rosa/deluxe/DeluxeTuple.hpp. +/// +/// +//===----------------------------------------------------------------------===// + +#include "rosa/deluxe/DeluxeTuple.hpp" + +namespace rosa { +namespace deluxe { + +void DeluxeTuple<>::dump(std::ostream &OS) noexcept { + OS << "{ }"; +} + +} // End namespace deluxe +} // End namespace rosa