Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F386522
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Size
60 KB
Referenced Files
None
Subscribers
None
View Options
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 <map>
/// 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<Token> 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<token_size_t> 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<bool> 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<token_size_t> 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<AbstractTokenizedStorage> 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<AbstractTokenizedStorage> 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<Token> 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<void(void)>;
/// 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<AgentHandle> 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<Optional<AgentHandle>> Slaves;
/// Associates \c rosa::id_t values to corresponding indices of registered
/// *slaves*.
///
/// \see \c rosa::deluxe::DeluxeAgent::Slaves
std::map<id_t, size_t> 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 <typename As> 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 <typename Ts> 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<Ts...>::TT == InputTypes[Pos]
+ /// \endcode
template <size_t Pos, typename... Ts, size_t... S0>
DeluxeTuple<Ts...> prepareInputValueAtPos(TypeList<Ts...>, Seq<S0...>) 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<TypeList<As...>>::Value &&
/// sizeof...(As) == sizeof...(S0)
/// \endcode Dynamically, type arguments \p As... match the input types of \p
/// this object: \code
/// inputTypesMatch<TypeList<As...>>()
/// \endcode
template <typename... As, size_t... S0>
std::tuple<std::pair<As, bool>...> prepareCurrentInputs(Seq<S0...>) 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 <typename T, typename... Ts, typename... As, size_t... S0>
static std::tuple<Optional<T>, Optional<Ts>...>
invokeWithTuple(std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)>
F,
const std::tuple<std::pair<As, bool>...> Args,
Seq<S0...>) 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<Ts...>::TT == MasterOutputTypes[Pos]
/// \endcode
template <size_t Pos, typename... Ts>
void
handleMasterOutputAtPos(const Optional<DeluxeTuple<Ts...>> &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<Offset, TypeList<Ts...>>::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<typename TypeListDrop<Offset,
/// TypeList<Ts...>>::Type>() &&
/// sizeof...(S0) == NumberOfMasterOutputs
/// \endcode
template <size_t Offset, typename... Ts, size_t... S0>
void handleMasterOutputs(const std::tuple<Optional<Ts>...> &Output,
Seq<S0...>) 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<TypeList<T, Ts..., As...>>::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<MTs...>::TT && OutputType == T::TT &&
/// inputTypesMatch<TypeList<As...>>() &&
/// masterOutputTypesMatch<TypeList<Ts...>>()
/// \endcode
template <typename... MTs, typename T, typename... Ts, typename... As,
size_t... S0>
H triggerHandlerFromProcessingFunctions(
std::function<std::tuple<Optional<Ts>...>(
std::pair<DeluxeTuple<MTs...>, bool>)> &&MF,
std::function<
std::tuple<Optional<T>, Optional<Ts>...>(std::pair<As, bool>...)> &&F,
Seq<S0...>) 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<TypeList<MT, T, Ts..., As...>::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 <typename MT, typename T, typename... Ts, typename... As,
typename = std::enable_if_t<
TypeListAllDeluxeTuple<TypeList<MT, T, Ts..., As...>>::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::tuple<Optional<Ts>...>(std::pair<MT, bool>)> &&MF,
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)> &&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<AgentHandle> 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<AgentHandle> _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<AgentHandle> 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<const DeluxeSensor &>(unwrapAgent(*Slave)).OutputType ==
/// InputTypes[Pos] &&
/// (emptyToken(MasterOutputTypes[Pos]) ||
/// static_cast<const DeluxeSensor &>(unwrapAgent(*Slave)).MasterInputType
/// == MasterOutputTypes[Pos])) ||
/// (unwrapAgent(*Slave).Kind == rosa::deluxe::atoms::AgentKind &&
/// static_cast<const DeluxeAgent &>(unwrapAgent(*Slave)).OutputType ==
/// InputTypes[Pos] &&
/// (emptyToken(MasterOutputTypes[Pos]) ||
/// static_cast<const DeluxeAgent &>(unwrapAgent(*Slave)).MasterInputType ==
/// MasterOutputTypes[Pos])))
/// \endcode
void registerSlave(const size_t Pos,
const Optional<AgentHandle> 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<Ts...>::Value
/// \endcode
template <typename... Ts, size_t... S0>
void sendToMaster(const DeluxeTuple<Ts...> &Value, Seq<S0...>) 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<Ts...>::Value
/// \endcode
template <typename... Ts, size_t... S0>
void sendToSlave(const size_t Pos, const DeluxeTuple<Ts...> &Value,
Seq<S0...>) 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<T>::Value
/// \endcode
template <typename T>
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<T>::Value
/// \endcode
template <typename T>
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<TypeList<Ts...>>::Value &&
/// sizeof...(Ts) == sizeof...(S0)
/// \endcode
template <
typename... Ts, size_t... S0,
typename = std::enable_if_t<TypeListAllDeluxeTuple<TypeList<Ts...>>::Value>>
static std::vector<token_size_t> storageOffsets(Seq<S0...>) noexcept {
STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments");
std::vector<token_size_t> 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 <typename... As> 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 <typename... Ts, typename... As>
struct TypesMatchImpl<DeluxeTuple<Ts...>, As...> {
/// Tells whether types \c rosa::deluxe::DeluxeTuple<Ts...> 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<Ts...> 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<Ts...> and \p As... match \c
/// rosa::Token values stored in \p Tokens starting at position \p Pos
static bool f(const std::vector<Token> &Tokens, size_t Pos) noexcept {
return Pos < Tokens.size() && TypeToken<Ts...>::Value == Tokens[Pos] &&
TypesMatchImpl<As...>::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 <typename T, typename... As>
struct TypesMatchImpl<T, As...> {
/// 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<Token> &, 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<Token> &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<List>::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 <typename List> struct TypesMatchList;
/// Template specialization implementing the feature.
///
/// \tparam As types to match
template <typename... As> struct TypesMatchList<TypeList<As...>> {
/// 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<Token> &Tokens) noexcept {
return TypesMatchImpl<As...>::f(Tokens, 0);
}
};
} // End namespace
template <typename As>
bool DeluxeAgent::inputTypesMatch(void) const noexcept {
return TypesMatchList<As>::f(InputTypes);
}
template <typename Ts>
bool DeluxeAgent::masterOutputTypesMatch(void) const noexcept {
return TypesMatchList<Ts>::f(MasterOutputTypes);
}
template <size_t Pos, typename... Ts, size_t... S0>
DeluxeTuple<Ts...> DeluxeAgent::prepareInputValueAtPos(TypeList<Ts...>,
Seq<S0...>) const
noexcept {
using T = DeluxeTuple<Ts...>;
+ 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<size_t>(static_cast<token_size_t>(S0)) == S0)));
// Get all elements of the tuple in a fold expression.
return T(*static_cast<const Ts *>(InputValues->pointerTo(
static_cast<token_size_t>(StorageOffset + S0)))...);
}
template <typename... As, size_t... S0>
std::tuple<std::pair<As, bool>...>
DeluxeAgent::prepareCurrentInputs(Seq<S0...>) const noexcept {
STATIC_ASSERT(TypeListAllDeluxeTuple<TypeList<As...>>::Value,
"not tuple types");
STATIC_ASSERT(sizeof...(As) == sizeof...(S0), "inconsistent type arguments");
ASSERT(inv() && inputTypesMatch<TypeList<As...>>());
return std::make_tuple(std::make_pair(
prepareInputValueAtPos<S0>(typename UnwrapDeluxeTuple<As>::Type(),
seq_t<As::Length>()),
InputChanged[S0])...);
}
template <typename T, typename... Ts, typename... As, size_t... S0>
std::tuple<Optional<T>, Optional<Ts>...> DeluxeAgent::invokeWithTuple(
std::function<
std::tuple<Optional<T>, Optional<Ts>...>(std::pair<As, bool>...)>
F,
const std::tuple<std::pair<As, bool>...> Args, Seq<S0...>) noexcept {
ASSERT(sizeof...(As) == sizeof...(S0));
return F(std::get<S0>(Args)...);
}
template <size_t Pos, typename... Ts>
void DeluxeAgent::handleMasterOutputAtPos(
const Optional<DeluxeTuple<Ts...>> &Value) noexcept {
using MOT = DeluxeTuple<Ts...>;
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<MOT, EmptyDeluxeTuple>) {
if (Value) {
sendToSlave(Pos, *Value, seq_t<MOT::Length>());
}
} else {
(void)Value;
}
ASSERT(inv());
}
template <size_t Offset, typename... Ts, size_t... S0>
void DeluxeAgent::handleMasterOutputs(const std::tuple<Optional<Ts>...> &Output,
Seq<S0...>) noexcept {
using MOTs = typename TypeListDrop<Offset, TypeList<Ts...>>::Type;
STATIC_ASSERT(TypeListAllDeluxeTuple<MOTs>::Value,
"not tuple type arguments");
STATIC_ASSERT(sizeof...(Ts) == Offset + sizeof...(S0),
"inconsistent arguments");
ASSERT(inv() && masterOutputTypesMatch<MOTs>() &&
sizeof...(S0) == NumberOfMasterOutputs);
// Handle each master-output position in a fold expression.
(handleMasterOutputAtPos<S0>(std::get<Offset + S0>(Output)), ...);
ASSERT(inv());
}
template <typename... MTs, typename T, typename... Ts, typename... As,
size_t... S0>
DeluxeAgent::H DeluxeAgent::triggerHandlerFromProcessingFunctions(
std::function<
std::tuple<Optional<Ts>...>(std::pair<DeluxeTuple<MTs...>, bool>)> &&MF,
std::function<
std::tuple<Optional<T>, Optional<Ts>...>(std::pair<As, bool>...)> &&F,
Seq<S0...>) noexcept {
using MT = DeluxeTuple<MTs...>;
STATIC_ASSERT((TypeListAllDeluxeTuple<TypeList<T, Ts..., As...>>::Value),
"not tuple type arguments");
STATIC_ASSERT(sizeof...(MTs) == sizeof...(S0), "inconsistent arguments");
ASSERT(MasterInputType == MT::TT && OutputType == T::TT &&
inputTypesMatch<TypeList<As...>>() &&
masterOutputTypesMatch<TypeList<Ts...>>());
return [ this, MF, F ]() noexcept {
// \note These indices work for both inputs and master-outputs.
using SlaveIndices = seq_t<sizeof...(As)>;
// Handle master-input.
// Do not do anything for master-input type \c
// rosa::deluxe::EmptyDeluxeTuple.
if (!std::is_same_v<MT, EmptyDeluxeTuple>) {
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<size_t>(static_cast<token_size_t>(S0)) == S0)));
const auto MasterInputArg = std::make_pair(
// Get all elements of the tuple in a fold expression.
MT(*static_cast<const MTs *>(
MasterInputValue->pointerTo(static_cast<token_size_t>(S0)))...),
MasterInputChanged);
MasterInputChanged = false;
const std::tuple<Optional<Ts>...> MasterOutput = MF(MasterInputArg);
handleMasterOutputs<0>(MasterOutput, SlaveIndices());
}
// Handle inputs.
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " handles input."
<< std::endl;
const auto InputArgs = prepareCurrentInputs<As...>(SlaveIndices());
std::fill(InputChanged.begin(), InputChanged.end(), false);
const std::tuple<Optional<T>, Optional<Ts>...> Output =
invokeWithTuple(F, InputArgs, SlaveIndices());
const auto OutputToMaster = std::get<0>(Output);
if (OutputToMaster) {
sendToMaster(*OutputToMaster, seq_t<T::Length>());
}
handleMasterOutputs<1>(Output, SlaveIndices());
};
}
template <typename MT, typename T, typename... Ts, typename... As, typename>
DeluxeAgent::DeluxeAgent(
const AtomValue Kind, const id_t Id, const std::string &Name,
MessagingSystem &S,
std::function<std::tuple<Optional<Ts>...>(std::pair<MT, bool>)> &&MF,
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)> &&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<As...>(seq_t<sizeof...(As)>())),
InputValues(new typename TokenizedStorageForTypeList<
typename TypeListUnwrapDeluxeTuple<TypeList<As...>>::Type>::
Type()),
MasterInputNextPos(0), MasterInputChanged(false),
MasterInputValue(new typename TokenizedStorageForTypeList<
typename UnwrapDeluxeTuple<MT>::Type>::Type()),
MasterOutputTypes({Ts::TT...}),
FP(triggerHandlerFromProcessingFunctions(std::move(MF), std::move(F),
seq_t<MT::Length>())),
Slaves(NumberOfInputs) {
ASSERT(Kind == atoms::AgentKind);
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " is created." << std::endl;
ASSERT(inv());
}
template <typename... Ts, size_t... S0>
void DeluxeAgent::sendToMaster(const DeluxeTuple<Ts...> &Value,
Seq<S0...>) noexcept {
STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments");
ASSERT(inv() && OutputType == TypeToken<Ts...>::Value);
// The assert must hold if \p this object was successfuuly constructed.
ASSERT((true && ... &&
(static_cast<size_t>(static_cast<token_size_t>(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<token_size_t, sizeof...(S0)> Indices{{S0...}};
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id
<< ") sends to master ("
<< static_cast<bool>(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<S0>(Value))),
...);
}
}
template <typename... Ts, size_t... S0>
void DeluxeAgent::sendToSlave(const size_t Pos, const DeluxeTuple<Ts...> &Value,
Seq<S0...>) noexcept {
STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments");
ASSERT(inv() && Pos < NumberOfMasterOutputs &&
MasterOutputTypes[Pos] == TypeToken<Ts...>::Value);
// The assert must hold if \p this object was successfuuly constructed.
ASSERT((true && ... &&
(static_cast<size_t>(static_cast<token_size_t>(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<token_size_t, sizeof...(S0)> 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<bool>(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<S0>(Value))),
...);
}
}
template <typename T>
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<T>::Value);
size_t SlavePos = SlaveIds.at(Id);
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id
<< ") saves value from slave at position " << SlavePos
<< ": (" << static_cast<size_t>(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<size_t>(static_cast<token_size_t>(StoragePos)) ==
StoragePos);
*static_cast<T *>(
InputValues->pointerTo(static_cast<token_size_t>(StoragePos))) = Value;
// Update position of next value.
if (++InputNextPos[SlavePos] == lengthOfToken(InputTypes[SlavePos])) {
InputNextPos[SlavePos] = 0;
}
// Set flag.
InputChanged[SlavePos] = true;
ASSERT(inv());
}
template <typename T>
void DeluxeAgent::saveMasterInput(id_t Id, token_size_t Pos, T Value) noexcept {
ASSERT(inv() && Master && masterId() == Id && Pos == MasterInputNextPos &&
typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf<T>::Value);
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id
<< ") saves value from master: (" << static_cast<size_t>(Pos)
<< ") " << Value << std::endl;
// Save value.
*static_cast<T *>(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
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Jul 3, 7:39 PM (6 h, 31 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
157329
Default Alt Text
(60 KB)
Attached To
Mode
R20 SoC_Rosa_repo
Attached
Detach File
Event Timeline
Log In to Comment