diff --git a/include/rosa/core/Invoker.hpp b/include/rosa/core/Invoker.hpp index 9f35ca3..9fa294f 100644 --- a/include/rosa/core/Invoker.hpp +++ b/include/rosa/core/Invoker.hpp @@ -1,258 +1,259 @@ //===-- rosa/core/Invoker.hpp -----------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/core/Invoker.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Facilities for providing actual arguments for functions as /// \c rosa::Messageobjects. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_INVOKER_HPP #define ROSA_CORE_INVOKER_HPP #include "rosa/core/MessageMatcher.hpp" #include "rosa/support/log.h" #include "rosa/support/sequence.hpp" #include #include namespace rosa { /// Wraps a function and provides a simple interface to invoke the stored /// function by passing actual arguments as a \c rosa::Message object. /// /// \note A \c rosa::Invoker instance is supposed to be owned by a /// \c rosa::MessageHandler instance, and not being used directly from user /// code. class Invoker { protected: /// Creates an instance. /// /// \note Protected constructor restricts instantiation to derived classes. Invoker(void) noexcept; public: /// Destroys \p this object. virtual ~Invoker(void); /// Possible results of an invocation. enum class Result { NoMatch, ///< The wrapped function could not be invoked Invoked ///< The wrapped function has been invoked }; /// Type alias for a smart-pointer for \c rosa::Invoker. using invoker_t = std::unique_ptr; /// Type alias for \c rosa::Invoker::Result. using result_t = Result; /// Tells if a \c rosa::Message object can be used to invoke the function /// wrapped in \p this object. /// /// \param Msg \c rosa::Message to check /// /// \return whether \p Msg can be used to invoke the wrapped function virtual bool match(const Message &Msg) const noexcept = 0; /// Tries to invoke the wrapped function with a \c rosa::Message object. /// /// The wrapped function is invoked if the actual \c rosa::Message object can /// be used to invoke it. /// /// \param Msg \c rosa::Message to try to invoke the wrapped function with /// /// \return whether the wrapped function could be invoked with \p Msg virtual result_t operator()(const Message &Msg) const noexcept = 0; /// Instantiates an implementation of \c rosa::Invoker with the given /// function. /// /// \note As there is no empty \c rosa::Message, no \c rosa::Invoker wraps a /// function without any argument. /// /// \todo Enforce F does not potentially throw exception. /// /// \tparam T type of the first mandatory argument /// \tparam Ts types of any further arguments /// /// \param F function to wrap /// /// \return new \c rosa::Invoker::invoker_t object created from the given /// function template static invoker_t wrap(std::function &&F) noexcept; /// Convenience template alias for casting callable stuff to function objects /// for wrapping. /// /// \tparam Ts types of arguments /// /// \todo Should make it possible to avoid using an explicit conversion for /// the arguments of wrap. template using F = std::function; /// Convenience template for preparing non-static member functions into /// function objects for wrapping. /// /// \tparam C type whose non-static member the function is /// \tparam Ts types of arguments /// /// \see \c THISMEMBER template static inline F M(C *O, void (C::*Fun)(Ts...) noexcept) noexcept; }; /// Convenience preprocessor macro for the typical use of \c rosa::Invoker::M. /// It can be used inside a class to turn a non-static member function into a /// function object capturing this pointer, so using the actual object when /// handling a \c rosa::Message. /// /// \param FUN the non-static member function to wrap /// /// \note Inside the class \c MyClass, use\code /// THISMEMBER(fun) /// \endcode instead of\code /// Invoker::M(this, &MyClass::fun) /// \endcode #define THISMEMBER(FUN) \ Invoker::M(this, &std::decay::type::FUN) /// Nested namespace with implementation of \c rosa::Invoker and helper /// templates, consider it private. namespace { /// \defgroup InvokerImpl Implementation for rosa::Invoker /// /// Implements the \c rosa::Invoker interface for functions with different /// signatures. /// ///@{ /// Declaration of \c rosa::InvokerImpl implementing \c rosa::Invoker. /// /// \tparam Fun function to wrap template class InvokerImpl; /// Implementation of \c rosa::InvokerImpl for \c std::function. /// /// \tparam T type of the first mandatory argument /// \tparam Ts types of further arguments /// /// \note As there is no empty \c rosa::Message, no \c rosa::Invoker wraps a /// function without any argument, i.e., no /// \c std::function. template class InvokerImpl> final : public Invoker { /// Type alias for the stored function. using function_t = std::function; /// Type alias for correctly typed argument-tuples as obtained from /// \c rosa::Message. using args_t = std::tuple; /// Alias for \c rosa::MessageMatcher for the arguments of the stored /// function. using Matcher = MsgMatcher; /// The wrapped function. const function_t F; /// Invokes \c InvokerImpl::F by unpacking arguments from a \c std::tuple with /// the help of the actual template arguments. /// /// \tparam S sequence of numbers indexing \c std::tuple for arguments /// /// \param Args arguments to invoke \c InvokerImpl::F with /// /// \pre the length of \p S and size of \p Args are matching:\code /// sizeof...(S) == std::tuple_size::value /// \endcode template inline void invokeFunction(Seq, const args_t &Args) const noexcept; public: /// Creates an instance. /// /// \param F function to wrap /// /// \pre \p F is valid:\code /// bool(F) /// \endcode InvokerImpl(function_t &&F) noexcept : F(F) { ASSERT(bool(F)); // Sanity check. } /// Destroys \p this object. ~InvokerImpl(void) = default; /// Tells if a \c rosa::Message object can be used to invoke the function /// wrapped in \p this object. /// /// \param Msg \c rosa::Message to check /// /// \return whether \p Msg can be used to invoke the wrapped function bool match(const Message &Msg) const noexcept override { return Matcher::doesStronglyMatch(Msg); }; /// Tries to invoke the wrapped function with a \c rosa::Message object. /// /// The wrapped function is invoked if the actual \c rosa::Message object can /// be used to invoke it. /// /// \param Msg \c rosa::Message to try to invoke the wrapped function with /// /// \return whether the wrapped function could be invoked with \p Msg result_t operator()(const Message &Msg) const noexcept override { if (match(Msg)) { LOG_TRACE("Invoking with matching arguments"); invokeFunction(typename GenSeq::Type(), Matcher::extractedValues(Msg)); return result_t::Invoked; } else { LOG_TRACE("Tried to invoke with non-matching arguments"); return result_t::NoMatch; } } }; template template void InvokerImpl>::invokeFunction( Seq, const args_t &Args) const noexcept { - ASSERT(sizeof...(S) == std::tuple_size::value); // Sanity check. + STATIC_ASSERT(sizeof...(S) == std::tuple_size::value, + "wrong number of type parameters"); F(std::get(Args)...); } ///@} } // End namespace template Invoker::invoker_t Invoker::wrap(std::function &&F) noexcept { return std::unique_ptr( new InvokerImpl>(std::move(F))); } template Invoker::F Invoker::M(C *O, void (C::*Fun)(Ts...) noexcept) noexcept { return [ O, Fun ](Ts... Vs) noexcept->void { (O->*Fun)(Vs...); }; } } // End namespace rosa #endif // ROSA_CORE_INVOKER_HPP diff --git a/include/rosa/deluxe/DeluxeAgent.hpp b/include/rosa/deluxe/DeluxeAgent.hpp index 48555ab..809290a 100755 --- a/include/rosa/deluxe/DeluxeAgent.hpp +++ b/include/rosa/deluxe/DeluxeAgent.hpp @@ -1,705 +1,706 @@ //===-- 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/DeluxeExecutionPolicy.h" #include /// Local helper macros to deal with built-in types. /// ///@{ /// Creates function name for member functions in \c rosa::deluxe::DeluxeAgent. /// /// \param N name suffix to use #define DAHANDLERNAME(N) handleSlave_##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 DAHANDLERDEFN(T, N) \ void DAHANDLERNAME(N)(atoms::Slave, id_t SlaveId, T Value) noexcept { \ saveInput(SlaveId, Value); \ } /// Convenience macro for \c DAHANDLERDEFN with identical arguments. /// /// \see \c DAHANDLERDEFN /// /// This macro can be used instead of \c DAHANDLERDEFN 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 DAHANDLERDEF(T) DAHANDLERDEFN(T, T) /// Results in a \c THISMEMBER reference to a member function defined by /// \c DAHANDLERDEFN. /// /// Used in the constructor of \c rosa::deluxe::DeluxeAgent to initialize super /// class \c rosa::Agent with member function defined by \c DAHANDLERDEFN. /// /// \see \c DAHANDLERDEFN, \c THISMEMBER /// /// \param N name suffix for the function identifier #define DAHANDLERREF(N) THISMEMBER(DAHANDLERNAME(N)) ///@} namespace rosa { namespace deluxe { /// Specialization of \c rosa::Agent for *agent* role of the *deluxe interface*. /// /// \see \c rosa::deluxe::DeluxeContext /// /// \invariant There is a compatible *execution policy* set, all input-related /// container objects have a size matching \c /// rosa::deluxe::DeluxeAgent::NumberOfInputs, thus having a corresponding entry /// for each input. Types of input values are consistent throughout all the /// input-related containers. 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; /// The \c rosa::deluxe::DeluxeExecutionPolicy that controls the execution of /// \c this object. std::unique_ptr ExecutionPolicy; public: /// Template alias for function objects used to process input and generate /// output for \c rosa::deluxe::DeluxeAgent. /// /// The output generated by the function is optional as an agent may decide /// not to output anything at some situation. /// /// \note The function used for \c D is to be \c noexcept. /// /// \tparam T type of output /// \tparam As types of input values template using D = std::function(std::pair...)>; /// The type of values produced by \p this object. /// /// That is the type of values \p this object sends to its *master*. /// /// \see \c rosa::deluxe::DeluxeAgent::master const TypeNumber OutputType; /// Number of inputs processed by \p this object. const size_t NumberOfInputs; 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 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::D. const std::vector InputTypes; /// 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::D. std::vector InputChanged; /// Stores the actual input values. /// /// \note The types of stored values match the corresponding /// \c rosa::TypeNumber values 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::D. const std::unique_ptr InputValues; /// Alias for function objects used as trigger handler for /// \c rosa::deluxe::DeluxeAgent. /// /// \note The function used for \c H is to be \c noexcept. /// /// \see \c rosa::deluxe::DeluxeAgent::FP using H = std::function; /// Handles trigger from the system. /// /// The actual function processing *slave* inputs and generating optional /// output to *master* is captured in a lambda expression that is in turn /// wrapped in a \c std::function object. The lambda expression calls the /// processing function with the actual input data and sends its result -- if /// any -- to *master* by calling \c rosa::deluxe::DeluxeAgent::sendToMaster. /// Also, all the flags stored in \c rose::deluxe::DeluxeAgent::InputChanged /// are reset when the current input values are processed. The function /// \c rosa::deluxe::DeluxeAgent::handleTrigger needs only to call the /// function object. /// /// \see \c rosa::deluxe::DeluxeAgent::triggerHandlerFromProcessingFunction const H FP; /// The *master* to send values to. /// /// \note *Masters* are set dynamically, hence it is possible that a /// \c rosa::deluxe::DeluxeAgent instance does not have any *master* at a /// given moment. Optional Master; /// The *slaves* sending input to \p this object. /// /// \note The position of a *slave* in the \c std::vector indicates which /// argument of \p this object's processing function it belongs to. See also /// \c rosa::deluxe::DeluxeAgent::D. /// /// \note *Slaves* are set dynamically, hence it is possible that a /// \c rosa::deluxe::DeluxeAgent instance does have input positions without /// any *slave* associated to them. /// /// \note Reverse lookup information is maintained in /// \c rosa::deluxe::DeluxeAgent::SlaveIds, which is to be kept in sync with /// the *slaves* stored here. std::vector> Slaves; /// Associates \c rosa::id_t values to corresponding indices of registered /// *slaves*. /// /// \see \c rosa::deluxe::DeluxeAgent::Slaves std::map SlaveIds; /// Tells whether types \p As... match the input types of \p this object. /// /// \tparam As types to match against values in /// \c rosa::deluxe::DeluxeAgent::InputTypes /// /// \return if types \p As... match \c rosa::TypeNumber values stored in /// \c rosa::deluxe::DeluxeAgent::InputTypes template bool inputTypesMatch(void) const noexcept; /// Gives an \c std::tuple containing the current input values and their /// change flags so that they can be used for the processing function. /// /// \tparam As types of the input values /// \tparam S0 indices for accessing input values and their change flags /// /// \note The only argument provides indices statically as template arguments /// \p S0..., so its actual value is ignored. /// /// \return current input values and their change flags prepared for invoking /// the processing function with them /// /// \pre The type arguments \p As... match the input types of \p this object /// and the provided indices \p S0... constitute a proper sequence for /// accessing input values and their change flags: \code /// inputTypesMatch() && sizeof...(As) == sizeof...(S0) /// \endcode template std::tuple...> prepareCurrentInputs(Seq) const noexcept; /// Invokes a processing function matching the output and input 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 As types of inputs for the processing function /// \tparam S0 indices starting with `0` for extracting actual arguments from /// \p Args /// /// \param F the processing function to invoke /// \param Args the actual arguments to invoke \p F with /// /// \note The last argument provides indices statically as template arguments /// \p S0..., so its actual value is ignored. /// /// \return the result of \p F for actual arguments \p Args /// /// \pre The provided sequence of indices \p S0... constitutes a proper /// sequence for extracting all actual arguments for /// \p F from \p Args: \code /// sizeof...(As) == sizeof...(S0) /// \endcode template static Optional invokeWithTuple(D F, std::tuple...> Args, Seq) noexcept; /// Wraps a processing function 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 T type of output /// \tparam As types of input values /// /// \param F function processing inputs and generating output /// /// \pre Template arguments \p T and \p As... match the corresponding /// types \p this object was created with: \code /// OutputType == TypeNumberOf::Value && inputTypesMatch() /// \endcode template H triggerHandlerFromProcessingFunction(D &&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 F does not potentially throw exception. /// /// \tparam T type of output of \p F /// \tparam As types of input values of \p F /// /// \note Instantiation fails if any of the type arguments \p T and \p As... /// is not a built-in type. /// /// \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 F function to process input values and generate output with /// /// \pre Statically, all of the type arguments \p T and \p As... is a /// built-in type: \code /// TypeListSubsetOf, BuiltinTypes>::Value /// \endcode Dynamically, the instance is created as of kind /// \c rosa::deluxe::atoms::AgentKind: \code /// Kind == rosa::deluxe::atoms::AgentKind /// \endcode template , BuiltinTypes>::Value>> DeluxeAgent(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, D &&F) noexcept; /// Destroys \p this object. ~DeluxeAgent(void) noexcept; /// Returns the current execution policy of \p this object. /// /// \see \c rosa::deluxe::DeluxeExecutionPolicy /// /// \note The returned reference is valid only as long as \c /// rosa::deluxe::DeluxeAgent::setExecutionPolicy() is not called and \p this /// object is not destroyed. /// /// \return \c rosa::deluxe::DeluxeAgent::ExecutionPolicy const DeluxeExecutionPolicy &executionPolicy(void) const noexcept; /// Sets the current execution policy of \p this object to \p EP. /// /// \see \c rosa::deluxe::DeluxeExecutionPolicy /// /// \note \p EP is set only if it can handle \p this object. /// /// \param EP the new execution policy for \p this object /// /// \return if \p EP was successfully set for \p this object. bool setExecutionPolicy(std::unique_ptr &&EP) noexcept; /// The *master* of \p this object, if any is registered. /// /// \see \c rosa::deluxe::DeluxeAgent::registerMaster /// /// \return the *master* registered for \p this object Optional master(void) const noexcept; /// Registers a *master* for \p this object. /// /// The new *master* is registered by overwriting the reference to any /// already registered *master*. One can clear the registered reference by /// passing an *empty* \c rosa::Optional object as actual argument. /// /// \note The role of the referred *master* is validated by checking its /// *kind*. /// /// \param _Master the *master* to register /// /// \pre \p _Master is empty or of kind \c rosa::deluxe::atoms::AgentKind: /// \code /// !_Master || unwrapAgent(*_Master).Kind == rosa::deluxe::atoms::AgentKind /// \endcode void registerMaster(const Optional _Master) noexcept; /// Tells the type of values consumed from the *slave* at a position. /// /// That is the type of values \p this object expect to be sent to it 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 /// 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; /// The *slave* of \p this object registered at a position, if any. /// /// \see \c rosa::deluxe::DeluxeAgent::registerSlave /// /// \param Pos position of *slave* /// /// \return the *slave* registered for \p this object at position \p Pos /// /// \pre \p Pos is a valid index of input: \code /// Pos < NumberOfInputs /// \endcode Optional slave(const size_t Pos) const noexcept; /// Registers a *slave* for \p this object at a position. /// /// The new *slave* is registered by overwriting the reference to any already /// registered *slave* at position \p Pos. One can clear the registered /// reference by passing an *empty* \c rosa::Optional object as actual /// argument. If \p Slave is already registered for another position, the /// other position gets cleared. /// /// \note The role of the referred *slave* is validated by checking its /// *kind*. /// /// \note The type of values produced by the referred *slave* is validated by /// matching its `OutputType` against the corresponding value in /// \c rosa::deluxe::DeluxeAgent::InputTypes. /// /// \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: /// \code /// Pos < NumberOfInputs && /// (!Slave || /// (unwrapAgent(*Slave.)Kind == rosa::deluxe::atoms::SensorKind && /// static_cast(unwrapAgent(*Slave)).OutputType == /// InputTypes[Pos]) || /// (unwrapAgent(*Slave).Kind == rosa::deluxe::atoms::AgentKind && /// static_cast(unwrapAgent(*Slave)).OutputType == /// InputTypes[Pos])) /// \endcode void registerSlave(const size_t Pos, const Optional Slave) noexcept; /// Tells the position of a registered *slave*. /// /// \param Slave \c rosa::AgentHandle for the *slave* to check /// /// \return position of \p Slave if it is registered and found, /// \c rosa::deluxe::DeluxeAgent::NumberOfInputs otherwise. size_t positionOfSlave(AgentHandle Slave) const noexcept; private: /// Sends a value to the *master* of \p this object. /// /// \p Value is getting sent to \c rosa::deluxe::DeluxeAgent::Master if it /// contains a valid handle for a \c rosa::deluxe::DeluxeAgent. The function /// does nothing otherwise. /// /// \tparam T type of the value to send /// /// \param Value value to send /// /// \pre \p T matches \c rosa::deluxe::DeluxeiAgent::OutputType: \code /// OutputType == TypeNumberOf::Value /// \endcode template void sendToMaster(const T &Value) 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. 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. /// /// \note Utilized by member functions of group \c DeluxeAgentInputHandlers. /// /// \tparam T type of input to store /// /// \param Id unique identifier of *slave* /// \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 /// SlaveIds.find(Id) != SlaveIds.end() && /// InputTypes[SlaveIds.find(Id)->second] == TypeNumberOf::Value /// \endcode template void saveInput(id_t Id, 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. 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 DAHANDLERDEF. /// /// \note Keep these definitions in sync with \c rosa::BuiltinTypes. /// ///@{ DAHANDLERDEF(AtomValue) DAHANDLERDEF(int16_t) DAHANDLERDEF(int32_t) DAHANDLERDEF(int64_t) DAHANDLERDEF(int8_t) DAHANDLERDEFN(long double, long_double) DAHANDLERDEFN(std::string, std__string) DAHANDLERDEF(uint16_t) DAHANDLERDEF(uint32_t) DAHANDLERDEF(uint64_t) DAHANDLERDEF(uint8_t) DAHANDLERDEF(unit_t) DAHANDLERDEF(bool) DAHANDLERDEF(double) DAHANDLERDEF(float) /// @} }; /// Anonymous namespace with implementation for /// \c rosa::deluxe::DeluxeAgent::inputTypesMatch, consider it private. namespace { /// Template \c struct whose specializations provide a recursive implementation /// for \c rosa::deluxe::DeluxeAgent::inputTypesMatch. /// /// \note Matching a list of types \p As... against a \c std::vector of /// \c rosa::TypeNumber values, \c InputTypes, like \code /// bool match = InputTypesMatchImpl::f(InputTypes, 0); /// \endcode /// /// \tparam As types to match template struct InputTypesMatchImpl; /// Template specialization for the general case, when at least one type is to /// be matched. /// /// \tparam A first type to match /// \tparam As further types to match template struct InputTypesMatchImpl { /// Tells whether types \p A, \p As... match \c rosa::TypeNumber values /// stored in \p InputTypes 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 /// InputTypes, then further types \p As.. are matched recursively starting /// at position \c (Pos + 1). /// /// \param InputTypes container of \c rosa::TypeNumber values to match /// types against /// \param Pos position in \p InputTypes to start matching at /// /// \return if types \p A, \p As... match \c rosa::TypeNumber values stored /// in \p InputTypes starting at position \p Pos static bool f(const std::vector &InputTypes, size_t Pos) noexcept { return Pos < InputTypes.size() && TypeNumberOf::Value == InputTypes[Pos] && InputTypesMatchImpl::f(InputTypes, Pos + 1); } }; /// Template specialization for the terminal case, when no type remains to /// check. template <> struct InputTypesMatchImpl<> { /// Tells whether \p Pos is the number of values stored in \p InputTypes. /// /// In this terminal case, there is no more types to matchi 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 InputTypes. That is true if \p Pos points exactly to the end of /// \p InputTypes. /// /// \param InputTypes container of \c rosa::TypeNumber values to match /// types against /// \param Pos position in \p InputTypes to start matching at /// /// \return if \p Pos is the number of values stored in \p InputTypes static bool f(const std::vector &InputTypes, size_t Pos) noexcept { return Pos == InputTypes.size(); } }; } // End namespace template bool DeluxeAgent::inputTypesMatch(void) const noexcept { return InputTypesMatchImpl::f(InputTypes, 0); } template std::tuple...> DeluxeAgent::prepareCurrentInputs(Seq) const noexcept { // Need to indirectly reference \c rosa::deluxe::DeluxeAgent::inputTypesMatch // inside \c ASSERT because of the comma in its template argument list. auto MFP = &DeluxeAgent::inputTypesMatch; ASSERT(inv() && (this->*MFP)() && sizeof...(As) == sizeof...(S0)); return std::make_tuple( std::make_pair(*static_cast(InputValues->pointerTo(S0)), InputChanged[S0])...); } template Optional DeluxeAgent::invokeWithTuple( D F, std::tuple...> Args, Seq) noexcept { - ASSERT(sizeof...(As) == sizeof...(S0)); + STATIC_ASSERT(sizeof...(As) == sizeof...(S0), + "wrong number of type parameters"); return F(std::get(Args)...); } template DeluxeAgent::H DeluxeAgent::triggerHandlerFromProcessingFunction(D &&F) noexcept { // Need to indirectly reference \c rosa::deluxe::DeluxeAgent::inputTypesMatch // inside \c ASSERT because of the comma in its template argument list. auto MFP = &DeluxeAgent::inputTypesMatch; ASSERT(OutputType == TypeNumberOf::Value && (this->*MFP)()); return [ this, F ]() noexcept { // Call the processing function only if \p ExecutionPolicy allows. if (ExecutionPolicy->shouldProcess(InputChanged)) { using Indices = typename GenSeq::Type; auto Args = prepareCurrentInputs(Indices()); std::fill(InputChanged.begin(), InputChanged.end(), false); Optional R = invokeWithTuple(F, Args, Indices()); if (R) { sendToMaster(*R); } } }; } template DeluxeAgent::DeluxeAgent(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, D &&F) noexcept : Agent(Kind, Id, Name, S, THISMEMBER(handleTrigger), DAHANDLERREF(AtomValue), DAHANDLERREF(int16_t), DAHANDLERREF(int32_t), DAHANDLERREF(int64_t), DAHANDLERREF(int8_t), DAHANDLERREF(long_double), DAHANDLERREF(std__string), DAHANDLERREF(uint16_t), DAHANDLERREF(uint32_t), DAHANDLERREF(uint64_t), DAHANDLERREF(uint8_t), DAHANDLERREF(unit_t), DAHANDLERREF(bool), DAHANDLERREF(double), DAHANDLERREF(float)), ExecutionPolicy(DeluxeExecutionPolicy::decimation(1)), OutputType(TypeNumberOf::Value), NumberOfInputs(sizeof...(As)), InputTypes({TypeNumberOf::Value...}), InputChanged(NumberOfInputs, false), InputValues(new TokenizedStorage()), FP(triggerHandlerFromProcessingFunction(std::move(F))), Slaves(NumberOfInputs) { ASSERT(Kind == atoms::AgentKind); LOG_TRACE("DeluxeAgent is created."); ASSERT(inv()); } template void DeluxeAgent::sendToMaster(const T &Value) noexcept { ASSERT(inv() && OutputType == TypeNumberOf::Value); // 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)); } ASSERT(inv()); } template void DeluxeAgent::saveInput(id_t Id, T Value) noexcept { ASSERT(inv() && SlaveIds.find(Id) != SlaveIds.end() && InputTypes[SlaveIds.find(Id)->second] == TypeNumberOf::Value); size_t Pos = SlaveIds.at(Id); *static_cast(InputValues->pointerTo(Pos)) = Value; InputChanged[Pos] = true; ASSERT(inv()); } } // End namespace deluxe } // End namespace rosa #undef DAHANDLEREF #undef DAHANDLEDEF #undef DAHANDLEDEFN #undef DAHANDLENAME #endif // ROSA_DELUXE_DELUXEAGENT_HPP