diff --git a/include/rosa/deluxe/DeluxeAgent.hpp b/include/rosa/deluxe/DeluxeAgent.hpp index 8c0b17c..49923d7 100755 --- a/include/rosa/deluxe/DeluxeAgent.hpp +++ b/include/rosa/deluxe/DeluxeAgent.hpp @@ -1,1423 +1,1442 @@ //===-- 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, 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, token_size_t Pos, \ T Value) noexcept { \ 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 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 types of values \p this object sends to its *master* in a \c /// rosa::deluxe::DeluxeTUple. /// /// \see \c rosa::deluxe::DeluxeAgent::master 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 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::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 \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::Token in order) in \c /// rosa::deluxe::DeluxeAgent::InputTypes. /// /// \note The position of a value in the \c rosa::AbstractTokenizedStorage /// 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 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 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; /// 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 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::Token and in turn \c /// rosa::TypeNumber values stored in \c /// rosa::deluxe::DeluxeAgent::MasterOutputTypes template bool masterOutputTypesMatch(void) const noexcept; - /// TODO!!! + /// Gives the current input value for slave position \p Pos. + /// + /// \tparam Pos slave position to get input value for + /// \tparam Ts types of elements of the input value + /// \tparam S0 indices for accessing elements of the input value + /// + /// \note The arguments provide types and indices statically as template + /// arguments \p Ts... \p S0..., respectively, so their actual values are + /// ignored. + /// + /// \return current input value for slave position \p Pos + /// + /// \pre Statically, the provided indices \p S0... match the length of \p + /// Ts...: \code + /// sizeof...(Ts) == sizeof...(S0) + /// \endcode Dynamically, \p Pos is a valid slave position and type arguments + /// \p Ts... match the corresponding input value: \code + /// Pos < NumberOfInputs && DeluxeTuple::TT == InputTypes[Pos] + /// \endcode 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 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 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 a master-output value for a particular *slave* position. /// /// \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::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 Pos the position of the master-output to send \p Value for /// \tparam Ts types of elements in \p Value /// /// \param Value \c rosa::deluxe::DeluxeTuple resulted by the processing /// function for *slave* position \p Pos /// /// \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 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 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; /// 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 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 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 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 H triggerHandlerFromProcessingFunctions( 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 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::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 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 >::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 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 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::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 Token inputType(const size_t Pos) const noexcept; /// Tells the types of values produced for the *slave* at a position. /// /// 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::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 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] && /// (emptyToken(MasterOutputTypes[Pos]) || /// static_cast(unwrapAgent(*Slave)).MasterInputType /// == MasterOutputTypes[Pos])) || /// (unwrapAgent(*Slave).Kind == rosa::deluxe::atoms::AgentKind && /// static_cast(unwrapAgent(*Slave)).OutputType == /// InputTypes[Pos] && /// (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. /// /// 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 /// /// \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 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. /// /// 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 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] == TypeToken::Value /// \endcode 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 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, \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() && /// Pos == InputNextPos[SlaveIds.find(Id)->second] && /// typeAtPositionOfToken(InputTypes[SlaveIds.find(Id)->second], Pos) == /// TypeNumberOf::Value /// \endcode 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 at position \p Pos in \c /// rosa::deluxe::DeluxeAgent::MasterInputValue and also sets the /// 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, \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, 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 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. /// ///@{ 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::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 case, when at least one type is to /// be matched and that is an instance of \c rosa::deluxe::DeluxeTuple. /// /// \tparam Ts types of elements in the \c rosa::deluxe::DeluxeTuple to match /// \tparam As further types to match 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 \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 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 Tokens. That is true if \p Pos points exactly to the end of \p Tokens. /// /// \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 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 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 List \c rosa::TypeList that contains types to match template struct TypesMatchList; /// Template specialization implementing the feature. /// /// \tparam As types to match template struct TypesMatchList> { /// 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 Tokens container of \c rosa::Token values to match types against /// /// \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 DeluxeTuple DeluxeAgent::prepareInputValueAtPos(TypeList, Seq) const noexcept { using T = DeluxeTuple; + STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent type arguments"); 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)) == S0))); // Get all elements of the tuple in a fold expression. return T(*static_cast(InputValues->pointerTo( static_cast(StorageOffset + 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 void DeluxeAgent::handleMasterOutputAtPos( 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 { 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(std::get(Output)), ...); ASSERT(inv()); } template DeluxeAgent::H DeluxeAgent::triggerHandlerFromProcessingFunctions( 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 SlaveIndices = seq_t; // Handle master-input. // Do not do anything for master-input type \c // rosa::deluxe::EmptyDeluxeTuple. if (!std::is_same_v) { LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " 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( // 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, SlaveIndices()); } // Handle inputs. LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " handles input." << std::endl; const auto InputArgs = prepareCurrentInputs(SlaveIndices()); std::fill(InputChanged.begin(), InputChanged.end(), false); const std::tuple, Optional...> Output = invokeWithTuple(F, InputArgs, SlaveIndices()); const auto OutputToMaster = std::get<0>(Output); if (OutputToMaster) { sendToMaster(*OutputToMaster, seq_t()); } 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(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_STREAM << "DeluxeAgent " << FullName << " is created." << std::endl; ASSERT(inv()); } 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 " << FullName << "(" << 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) { // 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 DeluxeTuple &Value, Seq) noexcept { STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments"); ASSERT(inv() && Pos < NumberOfMasterOutputs && 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 " << FullName << "(" << Id << ") sends to slave (" << static_cast(Slave && *Slave) << ") at position " << Pos << ": " << Value << " (" << sizeof...(S0) << ")" << std::endl; if (Slave && *Slave) { // 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, token_size_t Pos, T Value) noexcept { ASSERT(inv() && SlaveIds.find(Id) != SlaveIds.end() && 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 " << FullName << "(" << 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; } // Set flag. InputChanged[SlavePos] = true; ASSERT(inv()); } template 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 " << FullName << "(" << 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; } // 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