Page MenuHomePhorge

No OneTemporary

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

File Metadata

Mime Type
text/x-diff
Expires
Thu, Jul 3, 12:12 PM (1 d, 17 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
157243
Default Alt Text
(186 KB)

Event Timeline