diff --git a/include/rosa/deluxe/DeluxeContext.hpp b/include/rosa/deluxe/DeluxeContext.hpp index bb91eeb..9363c41 100644 --- a/include/rosa/deluxe/DeluxeContext.hpp +++ b/include/rosa/deluxe/DeluxeContext.hpp @@ -1,985 +1,935 @@ //===-- rosa/deluxe/DeluxeContext.hpp ---------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeContext.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Public interface for the *deluxe interface* for working with agent /// systems. /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXECONTEXT_HPP #define ROSA_DELUXE_DELUXECONTEXT_HPP #include "rosa/deluxe/DeluxeSystem.hpp" #include "rosa/support/types.hpp" #include #include #include /// Local helper macro to log and return a /// \c rosa::deluxe::DeluxeContext::ErrorCode value. /// /// Creates a debug message with the stringified value and returns the value. /// /// \param Err \c rosa::deluxe::DeluxeContext::ErrorCode value to log and /// return #define DCRETERROR(Err) \ { \ LOG_DEBUG(#Err); \ return Err; \ } namespace rosa { namespace deluxe { /// Defines the *deluxe interface*. /// /// \todo The classes \c rosa::deluxe::DeluxeSensor and \c /// rosa::deluxe::DeluxeAgent share some common features in relation to their /// *slave* role in the *deluxe interface*. But their definitions are completely /// independent. It could be investigated how to lift their common parts into a /// new *deluxe slave* class, which would serve as base for both, to avoid code /// duplication. class DeluxeContext { /// A system owned by \p this object. /// /// \note The reference is kept in a \c std::shared_ptr because of the member /// function \c rosa::deluxe::DeluxeContext::getSystem. std::shared_ptr System; /// References to all *sensors* and *agents* created by \p this object. std::set DeluxeUnits; public: /// Errors that may be resulted by some of the member functions of the class. enum struct ErrorCode { NoError, TypeMismatch, NotSensor, NotAgent, NotUnit, WrongPosition, AlreadyHasSlave, AlreadyHasMaster, AlreadyHasValueStream, UnsuitableExecutionPolicy }; /// Returns a new instance of \c rosa::deluxe::DeluxeContext. /// /// \param Name name of the underlying \c rosa::DeluxeSystem /// /// \return \c std::unique_ptr for the new instance of /// \c rosa::deluxe::DeluxeContext with a new, empty \c rosa::DeluxeSystem static std::unique_ptr create(const std::string &Name) noexcept; private: /// Creates a new instance. /// /// \note Private constructor restricts instantiation to member functions of /// the class. /// /// \param Name name of the underlying \c rosa::MessagingSystem DeluxeContext(const std::string &Name) noexcept; public: /// Destroys \p this object. ~DeluxeContext(void) noexcept; /// Returns a reference for the underlying \c rosa::MessagingSystem. /// /// \note One cannot do much with a \c rosa::MessagingSystem currently, this /// is for future use. /// /// \return reference for the underlying \c rosa::MessagingSystem. std::weak_ptr getSystem(void) const noexcept; private: /// Creates a new *sensor* in the context of \p this object. /// /// The new *sensor* handles master-input by \p MF. /// /// \tparam MT type of master-input the new *sensor* handles /// \tparam T type of data the new *sensor* operates on /// /// \note Instantiation fails if any of the type arguments \p MT and \p T /// is not an instance of \c rosa::deluxe::DeluxeTuple or \p T is \c /// rosa::deluxe::EmptyDeluxeTuple. /// /// \param Name name of the new *sensor* /// \param MF function for the new *sensors* to process master-input /// values with /// \param F function for the new *sensor* to generate the next value with /// during normal operation /// /// \note \p F is not used during simulation, in which case /// \c rosa::deluxe::DeluxeContext::registerSensorValues is used to /// register an alternative simulation data source with \c /// rosa::deluxe::DeluxeSensor::registerSimulationDataSource. One may /// safely keep relying on the default value of \p F as long as only /// simulation of the system is to be done. /// /// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor. /// /// \return \c rosa::AgentHandle for the new *sensor* template >::Value && !std::is_same::value>> AgentHandle createSensorImpl(const std::string &Name, std::function)> &&MF, std::function &&F) noexcept; public: /// Creates a new *sensor* in the context of \p this object. /// /// The new *sensor* does not receive master-input. /// /// \tparam T type of data the new *sensor* operates on /// /// \note Instantiation fails if type argument \p T is neither a built-in type /// nor an instance of \c rosa::deluxe::DeluxeTuple with at least one element. /// /// \param Name name of the new *sensor* /// \param F function for the new *sensor* to generate the next value with /// during normal operation /// /// \note \p F is not used during simulation, in which case /// \c rosa::deluxe::DeluxeContext::registerSensorValues is used to register /// an alternative simulation data source with /// \c rosa::deluxe::DeluxeSensor::registerSimulationDataSource. One may /// safely keep relying on the default value of \p F as long as only /// simulation of the system is to be done. /// /// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor. /// /// \return \c rosa::AgentHandle for the new *sensor* template ::Value || (IsDeluxeTuple::Value && !std::is_same::value)>> AgentHandle createSensor( const std::string &Name, std::function &&F = [](void) { return T(); }) noexcept; /// Creates a new *sensor* in the context of \p this object. /// /// The new *sensor* handles master-input by \p MF. /// /// \tparam MT type of master-input the new *sensor* handles /// \tparam T type of data the new *sensor* operates on /// /// \note 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 /// /// \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 , BuiltinTypes>::Value || (TypeListAllDeluxeTuple>::Value && !std::is_same::value)>> AgentHandle createSensor( const std::string &Name, std::function)> &&MF, std::function &&F = [](void) { return T(); }) noexcept; private: /// Creates a new *agent* in the context of \p this object. /// /// The new *agent* receives master-input by \p MF and produces /// master-output. /// /// \tparam MT type of master-input the new *agent* handles /// \tparam T type of data the new *agent* outputs /// \tparam Ts types of master-output the new *agent* produces /// \tparam As types of inputs the new *agent* takes /// /// \note Instantiation fails if any of the type arguments \p MT, \p T, \p /// Ts..., and \p As... is not an instance of \c rosa::deluxe::DeluxeTuple or /// any of \p T and \p As... is \c rosa::deluxe::EmptyDeluxeTuple. /// /// \param Name name of the new *agent* /// \param MF function for the new *agent* to process master-input /// values with \param F function for the new *agent* to process /// input values and generate output with /// /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. /// /// \return \c rosa::AgentHandle for the new *agent* template >::Value && !std::is_same::value && (true && ... && (!std::is_same::value))>> AgentHandle createAgentImpl( const std::string &Name, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept; public: /// Creates a new *agent* in the context of \p this object. /// /// The new *agent* neither receives master-input nor produces /// master-output. /// /// \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, typename = std::enable_if_t< TypeListSubsetOf, BuiltinTypes>::Value || (TypeListAllDeluxeTuple>::Value && !std::is_same::value && (true && ... && (!std::is_same::value)))>> AgentHandle createAgent(const std::string &Name, std::function(std::pair...)> &&F) noexcept; /// Creates a new *agent* in the context of \p this object. /// /// The new *agent* receives master-input by \p MF but does not /// produce master-output. /// /// \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 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, typename = std::enable_if_t< TypeListSubsetOf, BuiltinTypes>::Value || (TypeListAllDeluxeTuple>::Value && !std::is_same::value && (true && ... && (!std::is_same::value)))>> AgentHandle createAgent(const std::string &Name, std::function)> &&MF, std::function(std::pair...)> &&F) noexcept; /// Creates a new *agent* in the context of \p this object. /// /// The new *agent* does not receive master-input but produces /// master-output. /// /// \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, typename = std::enable_if_t< TypeListSubsetOf, BuiltinTypes>::Value || (TypeListAllDeluxeTuple>::Value && !std::is_same::value && (true && ... && (!std::is_same::value)))>> AgentHandle createAgent(const std::string &Name, std::function, Optional...>( std::pair...)> &&F) noexcept; /// Creates a new *agent* in the context of \p this object. /// /// The new *agent* receives master-input by \p MF and produces /// master-output. /// /// \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 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, typename = std::enable_if_t< TypeListSubsetOf, BuiltinTypes>::Value || (TypeListAllDeluxeTuple>::Value && !std::is_same::value && (true && ... && (!std::is_same::value)))>> AgentHandle createAgent( const std::string &Name, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept; /// Returns the current execution policy of the referred \p Unit /// /// \see \c rosa::deluxe::DeluxeExecutionPolicy /// /// \note The referred \p Unit is either *sensor* or *agent*. /// /// \note The returned reference is valid only as long as \c /// rosa::deluxe::DeluxeContext::setExecutionPolicy() is not called with the /// *unit* referred by \p Unit and the *unit* is not destroyed. /// /// \param Unit the *unit* whose execution policy is to be obtained /// /// \return the \c rosa::deluxe::DeluxeExecutionPolicy from \p Unit if \p Unit /// is valid Optional getExecutionPolicy(AgentHandle Unit) const noexcept; /// Sets the current execution policy of the referred \p Unit to \p /// ExecutionPolicy. /// /// \see \c rosa::deluxe::DeluxeExecutionPolicy /// /// \note The referred \p Unit is either *sensor* or *agent*. /// /// \param Unit the *unit* whose execution policy is to be set /// \param ExecutionPolicy the new execution policy for \p Unit /// /// \return how successful setting \p ExecutionPolicy for \p Unit was /// /// \note The function may return the following /// \c rosa::deluxe::DeluxeContext::ErrorCode values: /// `ErrorCode` | Comment /// ----------- | ------- /// `NoError` | Success /// `NotUnit` | Referred \p Unit is not valid /// `UnsuitableExecutionPolicy` | \p ExecutionPolicy cannot handle \p Unit ErrorCode setExecutionPolicy( AgentHandle Unit, std::unique_ptr &&ExecutionPolicy) noexcept; /// 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 /// /// \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 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 /// /// \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. /// /// \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; 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 that can be matched to 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. + /// nor a tuple (i.e., can be converted to \c rosa::deluxe::DeluxeTuple). /// /// \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 /// /// \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, - typename = std::enable_if_t::Value || - (IsDeluxeTuple::Value && - !std::is_same::value)>> + /// `TypeMismatch` | \p T does not match the type of values + /// generated by \p Sensor + /// `NotSensor` | Referred \p Sensor is not \c + /// rosa::deluxe::DeluxeSensor + /// `AlreadyHasValueStream` | \p Sensor already has simulation data source set + template ::Value || IsTuple::Value>> 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. /// /// \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. 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::deluxe::EmptyDeluxeTuple. template struct MapToEmptyDeluxeTuple { using Type = EmptyDeluxeTuple; }; /// Convenience template alias for \c MapToEmptyDeluxeTuple. template using empty_deluxe_t = typename MapToEmptyDeluxeTuple::Type; /// Converts a \c std::tuple of \c rosa::Optional built-in types into a /// corresponding \c std::tuple of \c rosa::Optional with each actual value /// wrapped in \c rosa::deluxe::DeluxeTuple. /// /// \tparam Ts types of the values /// \tparam S0 indices for accessing values in \p Values /// /// \param Values the \c std::tuple of \c rosa::Optional with built-in values /// /// \note The second argument provides indices statically as template arguments /// \p S0..., so its actual value is ignored. /// /// \return a \c std::tuple of \c rosa::Optional corresponding to \p Values /// with each actual value wrapped in \c rosa::deluxe::DeluxeTuple /// /// \pre Statically, all type arguments \p Ts... are built-in types and the /// provided indices \p S0... match the length of \p Ts...: \code /// TypeListSubsetOf, BuiltinTypes>::Value && /// sizeof...(Ts) == sizeof...(S0) /// \endcode template std::tuple>...> wrapBuiltinInDeluxeTuple(const std::tuple...> &Values, Seq) noexcept { STATIC_ASSERT((TypeListSubsetOf, BuiltinTypes>::Value), "not built-in types"); STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent type arguments"); return std::make_tuple(std::get(Values) ? Optional>( make_deluxe_tuple(*std::get(Values))) : Optional>()...); } } // End namespace template AgentHandle DeluxeContext::createSensorImpl(const std::string &Name, std::function)> &&MF, std::function &&F) noexcept { AgentHandle H = System->createSensor(Name, std::move(MF), std::move(F)); DeluxeUnits.emplace(H); return H; } template AgentHandle DeluxeContext::createSensor(const std::string &Name, std::function &&F) noexcept { auto EmptyMF = std::function)>( [](std::pair) {}); if constexpr (TypeListContains::Value) { using OutputType = DeluxeTuple; return createSensorImpl( Name, std::move(EmptyMF), std::function( [F{std::move(F)}](void) { return OutputType(F()); })); } else if constexpr (IsDeluxeTuple::Value && !std::is_same::value) { return createSensorImpl(Name, std::move(EmptyMF), std::move(F)); } else { ASSERT(false && "Unexpected type argument"); } } template AgentHandle DeluxeContext::createSensor(const std::string &Name, std::function)> &&MF, std::function &&F) noexcept { if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { using MasterInputType = DeluxeTuple; using OutputType = DeluxeTuple; return createSensorImpl( Name, std::function)>( [MF{std::move(MF)}](std::pair Arg) { MF({std::get<0>(Arg.first), Arg.second}); }), std::function( [F{std::move(F)}](void) { return OutputType(F()); })); } else if constexpr (TypeListAllDeluxeTuple>::Value && !std::is_same::value) { return createSensorImpl(Name, std::move(MF), std::move(F)); } else { ASSERT(false && "Unexpected type arguments"); } } template AgentHandle DeluxeContext::createAgentImpl( const std::string &Name, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept { AgentHandle H = System->createAgent(Name, std::move(MF), std::move(F)); DeluxeUnits.emplace(H); return H; } template AgentHandle DeluxeContext::createAgent( const std::string &Name, std::function(std::pair...)> &&F) noexcept { using NoMasterOutputType = std::tuple>...>; auto EmptyMF = std::function)>( [](std::pair) { return NoMasterOutputType(); }); if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { using OutputType = DeluxeTuple; return createAgentImpl( Name, std::move(EmptyMF), std::function< std::tuple, Optional>...>( std::pair, bool>...)>( [F{std::move(F)}](std::pair, bool>... Args) { const auto Result = F({std::get<0>(Args.first), Args.second}...); return std::tuple_cat( wrapBuiltinInDeluxeTuple(std::tuple(Result), seq_t<1>()), NoMasterOutputType()); })); } else if constexpr (TypeListAllDeluxeTuple>::Value && !std::is_same::value && (true && ... && (!std::is_same::value))) { return createAgentImpl( Name, std::move(EmptyMF), std::function, Optional>...>( std::pair...)>( [F{std::move(F)}](std::pair... Args) { const auto Result = F(Args...); return std::tuple_cat(std::tuple(Result), NoMasterOutputType()); })); } else { ASSERT(false && "Unexpected type arguments"); } } template AgentHandle DeluxeContext::createAgent( const std::string &Name, std::function)> &&MF, std::function(std::pair...)> &&F) noexcept { using NoMasterOutputType = std::tuple>...>; if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { using MasterInputType = DeluxeTuple; using OutputType = DeluxeTuple; return createAgentImpl( Name, std::function)>( [MF{std::move(MF)}](std::pair Arg) { MF({std::get<0>(Arg.first), Arg.second}); return NoMasterOutputType(); }), std::function< std::tuple, Optional>...>( std::pair, bool>...)>( [F{std::move(F)}](std::pair, bool>... Args) { const auto Result = F({std::get<0>(Args.first), Args.second}...); return std::tuple_cat( wrapBuiltinInDeluxeTuple(std::tuple(Result), seq_t<1>()), NoMasterOutputType()); })); } else if constexpr (TypeListAllDeluxeTuple>::Value && !std::is_same::value && (true && ... && (!std::is_same::value))) { return createAgentImpl( Name, std::function)>( [MF{std::move(MF)}](std::pair Arg) { MF(Arg); return NoMasterOutputType(); }), std::function, Optional>...>( std::pair...)>( [F{std::move(F)}](std::pair... Args) { const auto Result = F(Args...); return std::tuple_cat(std::tuple(Result), NoMasterOutputType()); })); } else { ASSERT(false && "Unexpected type arguments"); } } template AgentHandle DeluxeContext::createAgent( const std::string &Name, std::function, Optional...>( std::pair...)> &&F) noexcept { if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { using MasterOutputType = std::tuple>...>; using OutputType = DeluxeTuple; return createAgentImpl( Name, std::function)>( [](std::pair) { return MasterOutputType(); }), std::function< std::tuple, Optional>...>( std::pair, bool>...)>( [F{std::move(F)}](std::pair, bool>... Args) { const auto Result = F({std::get<0>(Args.first), Args.second}...); return wrapBuiltinInDeluxeTuple(Result, seq_t<1 + sizeof...(Ts)>()); })); } else if constexpr (TypeListAllDeluxeTuple< TypeList>::Value && !std::is_same::value && (true && ... && (!std::is_same::value))) { using MasterOutputType = std::tuple...>; return createAgentImpl( Name, std::function)>( [](std::pair) { return MasterOutputType(); }), std::function, Optional...>( std::pair...)>( [F{std::move(F)}](std::pair... Args) { const auto Output = F(Args...); return Output; })); } else { ASSERT(false && "Unexpected type arguments"); } } template AgentHandle DeluxeContext::createAgent( const std::string &Name, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept { if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { using MasterInputType = DeluxeTuple; using MasterOutputType = std::tuple>...>; using OutputType = DeluxeTuple; return createAgentImpl( Name, std::function)>( [MF{std::move(MF)}](std::pair Arg) { const auto Result = MF({std::get<0>(Arg.first), Arg.second}); return wrapBuiltinInDeluxeTuple(Result, seq_t()); }), std::function< std::tuple, Optional>...>( std::pair, bool>...)>( [F{std::move(F)}](std::pair, bool>... Args) { const auto Result = F({std::get<0>(Args.first), Args.second}...); return wrapBuiltinInDeluxeTuple(Result, seq_t<1 + sizeof...(Ts)>()); })); } else if constexpr (TypeListAllDeluxeTuple< TypeList>::Value && !std::is_same::value && (true && ... && (!std::is_same::value))) { using MasterOutputType = std::tuple...>; return createAgentImpl( Name, std::function)>( [MF{std::move(MF)}](std::pair Arg) { const auto Output = MF(Arg); return Output; }), std::function, Optional...>( std::pair...)>( [F{std::move(F)}](std::pair... Args) { const auto Output = F(Args...); return Output; })); } else { ASSERT(false && "Unexpected type arguments"); } } template DeluxeContext::ErrorCode DeluxeContext::registerSensorValues(AgentHandle Sensor, Iterator &&Start, const Iterator &End, T Default) noexcept { // Get the type of values provided by \p Iterator. STATIC_ASSERT((std::is_same::value), "type mismatch"); - // \note This constexpr variable is defined in the lambda below for MSVC. - // Keep that definition in sync with this one. - constexpr bool isBuiltin = TypeListContains::Value; - if constexpr (!isBuiltin) { - // T must be a std::tuple. - STATIC_ASSERT(std::tuple_size::value == 1, "Wrong tuple type"); - STATIC_ASSERT( - (TypeListContains::type>::Value), - "Wrong element type in tuple"); - } - using TT = sensor_t; // Make sure preconditions are met. if (!System->isDeluxeSensor(Sensor)) { DCRETERROR(ErrorCode::NotSensor); } auto S = System->getDeluxeSensor(Sensor); ASSERT(S); // Sanity check. -<<<<<<< HEAD - if (S->OutputType != TypeNumberOf::Value) { - DCRETERROR(ErrorCode::TypeMismatch); - } else if (S->simulationDataSourceIsSet()) { - DCRETERROR(ErrorCode::AlreadyHasValueStream); - } - // Register input stream. - // \note Need to capture parameters by value so having local copies. - S->registerSimulationDataSource( - DeluxeSensor::D([=](void) mutable noexcept { -#ifdef ROSA_WINDOWS - // MSVC has problem propagating constexpr into the lambda; repeat it. - // Keep this definition in sync with the original above. - constexpr bool isBuiltin = TypeListContains::Value; -#endif // defined ROSA_WINDOWS - if (Start != End) { - TT Value; - if constexpr (isBuiltin) { - Value = *Start; - } else { - Value = std::get<0>(*Start); - } - ++Start; - LOG_TRACE_STREAM << "Reading next value for sensor '" << S->FullName - << "': " << Value << '\n'; - return Value; - } else { - TT Value; - if constexpr (isBuiltin) { - Value = Default; - } else { - Value = std::get<0>(Default); - } - LOG_TRACE_STREAM << "Providing default value for sensor '" - << S->FullName << "': " << Value << '\n'; - return Value; - } - })); -======= if (S->simulationDataSourceIsSet()) { DCRETERROR(ErrorCode::AlreadyHasValueStream); } if constexpr (TypeListContains::Value) { if (S->OutputType != TypeToken::Value) { DCRETERROR(ErrorCode::TypeMismatch); } // Register input stream. // \note Need to capture parameters by value so having local copies. S->registerSimulationDataSource(std::function(void)>([= ](void) mutable noexcept->DeluxeTuple { if (Start != End) { LOG_TRACE_STREAM << "Reading next value for sensor '" << S->FullName << "': " << *Start << '\n'; return make_deluxe_tuple(*Start++); } else { LOG_TRACE_STREAM << "Providing default value for sensor '" << S->FullName << "': " << Default << '\n'; return make_deluxe_tuple(Default); } })); - } else if constexpr (IsDeluxeTuple::Value && - !std::is_same::value) { - if (S->OutputType != T::TT) { + } else if constexpr (IsTuple::Value) { + + using TT = matching_deluxe_tuple_t; + if (std::is_same::value || S->OutputType != TT::TT) { DCRETERROR(ErrorCode::TypeMismatch); } // Register input stream. // \note Need to capture parameters by value so having local copies. S->registerSimulationDataSource( - std::function([=](void) mutable noexcept->T { + std::function([=](void) mutable noexcept->TT { if (Start != End) { + const TT DV(*Start++); LOG_TRACE_STREAM << "Reading next value for sensor '" << S->FullName - << "': " << *Start << '\n'; - return *Start++; + << "': " << DV << '\n'; + return DV; } else { + static const TT DD(Default); LOG_TRACE_STREAM << "Providing default value for sensor '" - << S->FullName << "': " << Default << '\n'; - return Default; + << S->FullName << "': " << DD << '\n'; + return DD; } })); } else { ASSERT(false && "Unexpected type argument"); } ->>>>>>> master 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/DeluxeTuple.hpp b/include/rosa/deluxe/DeluxeTuple.hpp index e57d29b..9e26950 100644 --- a/include/rosa/deluxe/DeluxeTuple.hpp +++ b/include/rosa/deluxe/DeluxeTuple.hpp @@ -1,359 +1,465 @@ //===-- rosa/deluxe/DeluxeTuple.hpp -----------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeTuple.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2019 /// /// \brief Facilities for handling multiple input/output values for connections /// in the *deluxe interface*. /// /// \see \c rosa::deluxe::DeluxeContext /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXETUPLE_HPP #define ROSA_DELUXE_DELUXETUPLE_HPP #include "rosa/support/sequence.hpp" #include "rosa/support/type_token.hpp" #include #include namespace rosa { namespace deluxe { /// A tuple to manage multiple input/output values in the *deluxe interface*. /// /// \tparam Ts types of elements of the tuple /// /// \note The template may be instantiated only with built-in types and the /// number of those type may not exceed the capacity of a \c rosa::Token. template struct DeluxeTuple : public std::tuple { // Statically enforce that the class template is instantiated only with // built-in types. STATIC_ASSERT((TypeListSubsetOf, BuiltinTypes>::Value), "not built-in types"); // Statically enforce that the class template is instantiated with not too // many types. // \note Instantiation would fail on \c rosa::deluxe::DeluxeTuple::TT if there // are too any types; this assertion is for more readable error reporting. STATIC_ASSERT(sizeof...(Ts) <= token::MaxTokenizableListSize, "Too many types"); /// How many elements the instance has. static constexpr token_size_t Length = sizeof...(Ts); /// What types the class contains. /// /// Type information encoded as \c rosa::Token. static constexpr Token TT = TypeToken::Value; /// Default constructor, zero-initializes elements. DeluxeTuple(void) = default; /// Constructor, initializes the underlying \c std::tuple with lvalue /// references. /// /// \param Args value references to the values to store DeluxeTuple(const std::decay_t &... Args) : std::tuple(Args...) {} /// Constructor, initializes the underlying \c std::tuple with rvalue /// references. /// /// \param Args rvalue references to the values to store DeluxeTuple(std::decay_t &&... Args) : std::tuple(std::move(Args)...) {} + /// Contructor, initializes the underlying \c std::tuple from another matching + /// \c std::tuple. + DeluxeTuple(const std::tuple &Args) : std::tuple(Args) {} + /// Default copy-constructor. DeluxeTuple(const DeluxeTuple &) = default; /// Default move-constructor. DeluxeTuple(DeluxeTuple &&) = default; /// Default copy-assignment. DeluxeTuple &operator=(const DeluxeTuple &) = default; /// Default move-assignment. DeluxeTuple &operator=(DeluxeTuple &&) = default; private: /// Dumps \p this object to a given \c std::ostream. /// /// \note Provides implementation for \c rosa::deluxe::DeluxeTuple::dump. /// /// \tparam S0 Indices for accessing elements. /// /// \param [in,out] OS output stream to dump to /// /// \note The second argument provides indices statically as template /// arguments \p S0..., so its actual value is ignored. /// /// \pre Statically, \p S0... matches number of types \p this object was /// created: \code /// sizeof...(S0) == sizeof...(Ts) /// \endcode template void dump(std::ostream &OS, Seq) const noexcept; public: /// Dumps \p this object to a given \c std::ostream. /// /// \param [in,out] OS output stream to dump to void dump(std::ostream &OS) const noexcept; }; template template void DeluxeTuple::dump(std::ostream &OS, Seq) const noexcept { STATIC_ASSERT(sizeof...(S0) == sizeof...(Ts), "inconsistent type arguments"); // Convert value to std::string with std::to_string except for a value of // std::string that does not need conversion. auto dump_to_string = [](const auto &V) { if constexpr (std::is_same, std::string>::value) { return V; } else { return std::to_string(V); } }; OS << "{"; (OS << ... << (" " + dump_to_string(std::get(*this)))); OS << " }"; } template void DeluxeTuple::dump(std::ostream &OS) const noexcept { dump(OS, seq_t()); } /// Type alias for a \c rosa::deluxe::DeluxeTuple that contains no elements. using EmptyDeluxeTuple = DeluxeTuple<>; /// Template specialization for \c rosa::deluxe::EmptyDeluxeTuple. template <> struct DeluxeTuple<> : public std::tuple<> { /// How many elements the instance has. static constexpr token_size_t Length = 0; /// What types the class contains. /// /// Type information encoded as \c rosa::Token. static constexpr Token TT = TypeToken<>::Value; /// Constructor, initializes the underlying \c std::tuple. DeluxeTuple(void) : std::tuple<>() {} /// Default copy-constructor. DeluxeTuple(const DeluxeTuple &) = default; // Default move-constructor. DeluxeTuple(DeluxeTuple &&) = default; /// Default copy-assignment. DeluxeTuple &operator=(const DeluxeTuple &) = default; // Default move-assignment, DeluxeTuple &operator=(DeluxeTuple &&) = default; /// Dumps \p this object to a given \c std::ostream. /// /// \param [in,out] OS output stream to dump to static void dump(std::ostream &OS) noexcept; }; /// Creates a \c rosa::deluxe::DeluxeTuple instance from the given lvalues /// references. /// /// \tparam Ts types of elements of the tuple /// /// \see \c rosa::deluxe::DeluxeTuple /// /// \param Args values to store in the tuple /// /// \return an instance of \c rosa::deluxe::DeluxeTuple with \p Args as /// elements template inline DeluxeTuple make_deluxe_tuple(const Ts &... Args) noexcept { return DeluxeTuple(Args...); } /// Creates a \c rosa::deluxe::DeluxeTuple instance from the given rvalue /// references. /// /// \tparam Ts types of elements of the tuple /// /// \see \c rosa::deluxe::DeluxeTuple /// /// \param Args values to store in the tuple /// /// \return an instance of \c rosa::deluxe::DeluxeTuple with \p Args as /// elements template inline DeluxeTuple make_deluxe_tuple(Ts&&... Args) noexcept { return DeluxeTuple(std::move(Args)...); } +/// Creates a \c rosa::deluxe::DeluxeTuple instance from the given \c std::tuple +/// reference. +/// +/// \tparam Ts types of elements of the tuple +/// +/// \see \c rosa::deluxe::DeluxeTuple +/// +/// \param Args values to store in the tuple +/// +/// \return an instance of \c rosa::deluxe::DeluxeTuple with \p Args as +/// elements +template +inline DeluxeTuple +make_deluxe_tuple(const std::tuple &Args) noexcept { + return DeluxeTuple(Args); +} + /// \defgroup UnwrapDeluxeTuple Implementation of /// rosa::deluxe::UnwrapDeluxeTuple /// /// \brief Unwraps element types from an instance of \c /// rosa::deluxe::DeluxeTuple into a \c rosa::TypeList /// /// Types can be unwrapped from a \c rosa::deluxe::DeluxeTuple instance as \code /// typename UnwrapDeluxeTuple::Type /// \endcode /// /// For example, the following expression evaluates to `true`: \code /// std::is_same>::Type, /// TypeList>::value /// \endcode ///@{ /// Declaration of the template. /// /// \tparam Tuple \c rosa::deluxe::DeluxeTuple to unwrap template struct UnwrapDeluxeTuple; /// Implementation of the template for \c rosa::deluxe::DeluxeTuple instances. template struct UnwrapDeluxeTuple> { using Type = TypeList; }; ///@} /// \defgroup TypeListUnwrapDeluxeTuple Implementation of /// \c rosa::deluxe::TypeListUnwrapDeluxeTuple /// /// \brief Unwraps element types from instances of \c /// rosa::deluxe::DeluxeTuple in a \c rosa::TypeList. /// /// Types can be unwrapped from \c rosa::deluxe::DeluxeTuple instances as \code /// typename TypeListUnwrapDeluxeTuple::Type /// \endcode /// /// For example, the following expression evaluates to `true`: \code /// std::is_same< /// typename TypeListUnwrapDeluxeTuple, /// T3>>::Type, /// TypeList /// >::value /// \endcode ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to check template struct TypeListUnwrapDeluxeTuple; /// Specialization for \c rosa::EmptyTypeList. template <> struct TypeListUnwrapDeluxeTuple { using Type = EmptyTypeList; }; /// Specialization for the case when the first type in \p List is an instance of /// \c rosa::deluxe::DeluxeTuple. template struct TypeListUnwrapDeluxeTuple, Ts...>> { using Type = typename TypeListConcat< typename UnwrapDeluxeTuple>::Type, typename TypeListUnwrapDeluxeTuple>::Type>::Type; }; /// Implementation for a general first type in \p List. template struct TypeListUnwrapDeluxeTuple> { using Type = typename TypeListPush< T, typename TypeListUnwrapDeluxeTuple>::Type>::Type; }; ///@} +/// \defgroup IsTuple Implementation of \c rosa::deluxe::IsTuple +/// +/// \brief Tells if a type is a tuple as in it can be converted to \c +/// rosa::deluxe::DeluxeTuple. +/// +/// \see \c rosa::deluxe::MatchingDeluxeTuple +/// +/// Whether a type \c T is a tuple can be checked as \code +/// IsTuple::Value +/// \endcode +///@{ + +/// Declaration of the template. +/// +/// \tparam T type to check +template struct IsTuple; + +/// Specialization for the case when the type is an instance of \c std::tuple. +template struct IsTuple> { + static constexpr bool Value = true; +}; + +/// Specialization for the case when the type is an instance of \c std::tuple. +template struct IsTuple> { + static constexpr bool Value = true; +}; + +/// Implementation for a general case of type \p T. +template struct IsTuple { static constexpr bool Value = false; }; + +///@} + /// \defgroup IsDeluxeTuple Implementation of \c rosa::deluxe::IsDeluxeTuple /// /// \brief Tells if a type is an instance of \c rosa::deluxe::DeluxeTuple. /// /// Whether a type \c T is an instance of \c rosa::deluxe::DeluxeTuple can be /// checked as \code /// IsDeluxeTuple::Value /// \endcode +/// +/// \note `!IsDeluxeTuple::Value || IsTuple::Value` ///@{ /// Declaration of the template. /// /// \tparam T type to check template struct IsDeluxeTuple; /// Specialization for the case when the type is an instance of \c /// rosa::deluxe::DeluxeTuple. template struct IsDeluxeTuple> { static constexpr bool Value = true; }; /// Implementation for a general case of type \p T. template struct IsDeluxeTuple { static constexpr bool Value = false; }; ///@} +/// \defgroup MatchingDeluxeTuple Implementation of \c +/// rosa::deluxe::MatchingDeluxeTuple +/// +/// \brief Gives the \c rosa::deluxe::DeluxeTuple type that matches a given +/// tuple type. +/// +/// The matching \c rosa::deluxe::DeluxeTuple type for a tuple type \p T can be +/// obtained as \code +/// typename MatchingDeluxeTuple::Type +/// \endcode +/// If \p T is \c rosa::deluxe::DeluxeTuple, the matching type is \p T itself. +/// If \p T is \c std::tuple, the matching type if \c rosa::deluxe::DeluxeTuple +/// with the same type parameters as \p T. +/// \c rosa::deluxe::MatchingDeluxeTuple is not defined for other type +/// parameters. +/// +/// \note The template is defined for type \p T only if +/// `rosa::deluxe::IsTuple::Value`. Values of such types can be used to +/// construct a new instance of the class \c rosa::deluxe::DeluxeTuple. +/// +///\see \c rosa::deluxe::IsTuple +///@{ + +/// Declaration of the template. +/// +/// \tparam T type to check +template struct MatchingDeluxeTuple; + +/// Specialization for the case when the type is an instance of \c +/// rosa::deluxe::DeluxeTuple. +template struct MatchingDeluxeTuple> { + using Type = DeluxeTuple; +}; + +/// Specialization for the case when the type is an instance of \c +/// std::tuple. +template struct MatchingDeluxeTuple> { + using Type = DeluxeTuple; +}; + +///@} + +/// Convenience template type alias for easy use of \c +/// rosa::deluxe::MatchingDeluxeTuple. +/// +/// Converts a tuple type to the matching \c rosa::deluxe::DeluxeTuple type. +/// +/// \tparam Tuple type to convert +template +using matching_deluxe_tuple_t = typename MatchingDeluxeTuple::Type; + /// \defgroup TypeListAllDeluxeTuple Implementation of /// \c rosa::deluxe::TypeListAllDeluxeTuple /// /// \brief Tells if all types in a \c rosa::TypeList is an instance of \c /// rosa::deluxe::DeluxeTuple. /// /// Whether a \c rosa::TypeList \c List contains instances of \c /// rosa::deluxe::DeluxeTuple only can be checked as \code /// TypeListAllDeluxeTuple::Value /// \endcode ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to check template struct TypeListAllDeluxeTuple; /// Specialization for \c rosa::EmptyTypeList. template <> struct TypeListAllDeluxeTuple { static constexpr bool Value = true; }; /// Implementation for the general case when there is at leasst one element in /// the list. template struct TypeListAllDeluxeTuple> { static constexpr bool Value = IsDeluxeTuple::Value && TypeListAllDeluxeTuple>::Value; }; ///@} } // End namespace deluxe } // End namespace rosa namespace std { /// Dumps a \c rosa::deluxe::Deluxe instance to a given \c std::ostream. /// /// \param [in,out] OS output stream to dump to /// \param Tuple \c rosa::deluxe::Deluxe to dump /// /// \return \p OS after dumping \p Tuple to it template ostream &operator<<(ostream &OS, const rosa::deluxe::DeluxeTuple &Tuple) { Tuple.dump(OS); return OS; } } // End namespace std #endif // ROSA_DELUXE_DELUXETUPLE_HPP