Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F376191
AppAgent.hpp
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Size
58 KB
Referenced Files
None
Subscribers
None
AppAgent.hpp
View Options
//===-- rosa/app/AppAgent.hpp -----------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
// Distributed under the terms and conditions of the Boost Software License 1.0.
// See accompanying file LICENSE.
//
// If you did not receive a copy of the license file, see
// http://www.boost.org/LICENSE_1_0.txt.
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/app/AppAgent.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2020
///
/// \brief Specialization of \c rosa::Agent for *agent* role of the *application
/// interface*.
///
/// \see \c rosa::app::Application
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_APP_APPAGENT_HPP
#define ROSA_APP_APPAGENT_HPP
#include
"rosa/core/Agent.hpp"
#include
"rosa/app/AppAtoms.hpp"
#include
"rosa/app/AppExecutionPolicy.h"
#include
"rosa/app/AppTuple.hpp"
#include
<map>
/// Local helper macros to deal with built-in types.
///
///@{
/// Creates function name for member functions in \c rosa::app::AppAgent.
///
/// \param N name suffix to use
#define AASLAVEHANDLERNAME(N) handleSlave_##N
/// Creates function name for member functions in \c rosa::app::AppAgent.
///
/// \param N name suffix to use
#define AAMASTERHANDLERNAME(N) handleMaster_##N
/// Defines member functions for handling messages from *slaves* in
/// \c rosa::app::AppAgent.
///
/// \see \c AppAgentInputHandlers
///
/// \note No pre- and post-conditions are validated directly by these functions,
/// they rather rely on \c rosa::app::AppAgent::saveInput to do that.
///
/// \param T the type of input to handle
/// \param N name suffix for the function identifier
#define AASLAVEHANDLERDEFN(T, N) \
void AASLAVEHANDLERNAME(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::app::AppAgent.
///
/// \see \c AppAgentMasterInputHandlers
///
/// \note No pre- and post-conditions are validated directly by these functions,
/// they rather rely on \c rosa::app::AppAgent::saveMasterInput to do
/// that.
///
/// \param T the type of input to handle
/// \param N name suffix for the function identifier
#define AAMASTERHANDLERDEFN(T, N) \
void AAMASTERHANDLERNAME(N)(atoms::Master, id_t MasterId, token_size_t Pos, \
T Value) noexcept { \
saveMasterInput(MasterId, Pos, Value); \
}
/// Convenience macro for \c AASLAVEHANDLERDEFN with identical arguments.
///
/// \see \c AASLAVEHANDLERDEFN
///
/// This macro can be used instead of \c AASLAVEHANDLERDEFN 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 AASLAVEHANDLERDEF(T) AASLAVEHANDLERDEFN(T, T)
/// Convenience macro for \c AAMASTERHANDLERDEFN with identical arguments.
///
/// \see \c AAMASTERHANDLERDEFN
///
/// This macro can be used instead of \c AAMASTERHANDLERDEFN 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 AAMASTERHANDLERDEF(T) AAMASTERHANDLERDEFN(T, T)
/// Results in a \c THISMEMBER reference to a member function defined by
/// \c AASLAVEHANDLERDEFN.
///
/// Used in the constructor of \c rosa::app::AppAgent to initialize super
/// class \c rosa::Agent with member function defined by \c AASLAVEHANDLERDEFN.
///
/// \see \c AASLAVEHANDLERDEFN, \c THISMEMBER
///
/// \param N name suffix for the function identifier
#define AASLAVEHANDLERREF(N) THISMEMBER(AASLAVEHANDLERNAME(N))
/// Results in a \c THISMEMBER reference to a member function defined by
/// \c AAMASTERHANDLERDEFN.
///
/// Used in the constructor of \c rosa::app::AppAgent to initialize super
/// class \c rosa::Agent with member function defined by \c AAMASTERHANDLERDEFN.
///
/// \see \c AAMASTERHANDLERDEFN, \c THISMEMBER
///
/// \param N name suffix for the function identifier
#define AAMASTERHANDLERREF(N) THISMEMBER(AAMASTERHANDLERNAME(N))
///@}
namespace
rosa
{
namespace
app
{
/// Specialization of \c rosa::Agent for *agent* role of the *application
/// interface*.
///
/// \see \c rosa::app::Application
///
/// \invariant There is a compatible *execution policy* set, all input-related
/// container objects have a size matching \c
/// rosa::app::AppAgent::NumberOfInputs, thus having a corresponding entry
/// for each input. \c rosa::app::AppAgent::NumberOfMasterOutputs matches
/// \c rosa::app::AppAgent::NumberOfInputs. All master-output-related
/// container objects have a size matching \c
/// rosa::app::AppAgent::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::app::AppAgent::InputNextPos and \c
/// rosa::app::AppAgent::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::app::AppAgent::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
AppAgent
:
public
Agent
{
/// Checks whether \p this object holds the class invariant.
///
/// \see Invariant of the class \c rosa::app::AppAgent
///
/// \return if \p this object holds the class invariant
bool
inv
(
void
)
const
noexcept
;
/// The \c rosa::app::AppExecutionPolicy that controls the execution of
/// \c this object.
std
::
unique_ptr
<
AppExecutionPolicy
>
ExecutionPolicy
;
public
:
/// The type of values produced by \p this object.
///
/// That is the types of values \p this object sends to its *master* in a \c
/// rosa::app::AppTUple.
///
/// \see \c rosa::app::AppAgent::master
const
Token
OutputType
;
/// Number of inputs processed by \p this object.
const
size_t
NumberOfInputs
;
/// The type of values \p this object processes from its *master*.
///
/// That is the types of values \p this object receives from its *master* in a
/// \c rosa::app::AppTuple.
///
/// \see \c rosa::app::AppAgent::master
const
Token
MasterInputType
;
/// Number of outputs produces by \p this object for its *slaves*.
///
/// \note This values is equal to \c
/// rosa::app::AppAgent::NumberOfInputs.
///
/// \see \c rosa::app::AppAgent::slave.
const
size_t
NumberOfMasterOutputs
;
private
:
/// Types of input values produced by *slaves* of \p this object.
///
/// \note The \c rosa::Token values stored correspond to \c
/// rosa::app::AppTuple instances at each argument position. The \c
/// rosa::TypeNumber values from the stored \c rosa::Token values match the
/// corresponding values in \c rosa::app::AppAgent::InputValues in
/// order.
///
/// \note The position of a \c rosa::Token in the \c std::vector indicates
/// which argument of \p this object's processing function it belongs to. See
/// also \c rosa::app::AppAgent::AppAgent.
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::app::AppTuple 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::app::AppAgent::handleTrigger
/// \c rosa::app::AppAgent::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::app::AppAgent::saveInput when storing a new
/// input value in \c rosa::app::AppAgent::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::app::AppAgent::AppAgent.
std
::
vector
<
bool
>
InputChanged
;
/// Stores the actual input values.
///
/// \note The types of stored values match the corresponding
/// \c rosa::TypeNumber values (in \c rosa::Token in order) in \c
/// rosa::app::AppAgent::InputTypes.
///
/// \note The position of a \c rosa::AbstractTokenizedStorage in the \c
/// std::vector indicates which argument of \p this object's processing
/// function the tuple is; and the position of the value in the \c
/// rosa::AbstractTokenizedStorage indicates which element of that tuple the
/// value is. See also \c rosa::app::AppAgent::AppAgent.
const
std
::
vector
<
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::app::AppTuple 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::app::AppAgent::handleTrigger
/// \c rosa::app::AppAgent::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::app::AppAgent::saveMasterInput when storig a new
/// input value in \c rosa::app::AppAgent::MasterInputValue.
bool
MasterInputChanged
;
/// Stores the actual input value from *master*.
///
/// \note The type of the stored value matches the types indicated by \c
/// rosa::app::AppAgent::MasterInputType.
const
std
::
unique_ptr
<
AbstractTokenizedStorage
>
MasterInputValue
;
/// Types of output values produced by \p this object for its *slaves*.
///
/// That is the types of values \p this object sends to its *slaves* in a \c
/// rosa::app::AppTuple.
///
/// \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::app::AppAgent::AppAgent.
const
std
::
vector
<
Token
>
MasterOutputTypes
;
/// Alias for function objects used as trigger handler for
/// \c rosa::app::AppAgent.
///
/// \note The function used for \c H is to be \c noexcept.
///
/// \see \c rosa::app::AppAgent::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::app::AppAgent::handleMasterOutputs; then calls the
/// input processing function with the actual input data and sends its result
/// -- if any -- to *master* by calling \c
/// rosa::app::AppAgent::sendToMaster and *slaves* by calling \c
/// rosa::app::AppAgent::handleMasterOutputs. Also, all the flags stored
/// in \c rosa::app::AppAgent::InputChanged and \c
/// rosa::app::AppAgent::MasterInputChanged are reset when the current
/// values are processed. The function \c
/// rosa::app::AppAgent::handleTrigger needs only to call the
/// function object.
///
/// \see \c
/// rosa::app::AppAgent::triggerHandlerFromProcessingFunctions
const
H
FP
;
/// The *master* to send values to.
///
/// \note *Masters* are set dynamically, hence it is possible that a
/// \c rosa::app::AppAgent 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::app::AppAgent::AppAgent.
///
/// \note *Slaves* are set dynamically, hence it is possible that a
/// \c rosa::app::AppAgent instance does have input positions without
/// any *slave* associated to them.
///
/// \note Reverse lookup information is maintained in
/// \c rosa::app::AppAgent::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::app::AppAgent::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::app::AppAgent::InputTypes
///
/// \note Instatiation of the template fails if \p As is not \c
/// rosa::TypeList.
///
/// \return if types in \p As are instances of \c rosa::app::AppTuple
/// and their types match \c rosa::Token values stored in \c
/// rosa::app::AppAgent::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::app::AppAgent::MasterOutputTypes
///
/// \note Instatiation of the template fails if \p As is not \c
/// rosa::TypeList.
///
/// \return if types in \p Ts match \c rosa::Token and in turn \c
/// rosa::TypeNumber values stored in \c
/// rosa::app::AppAgent::MasterOutputTypes
template
<
typename
Ts
>
bool
masterOutputTypesMatch
(
void
)
const
noexcept
;
/// Gives the current input value for slave position \p Pos.
///
/// \tparam Pos slave position to get input value for
/// \tparam Ts types of elements of the input value
/// \tparam S0 indices for accessing elements of the input value
///
/// \note The arguments provide types and indices statically as template
/// arguments \p Ts... \p S0..., respectively, so their actual values are
/// ignored.
///
/// \return current input value for slave position \p Pos
///
/// \pre Statically, the provided indices \p S0... match the length of \p
/// Ts...: \code
/// sizeof...(Ts) == sizeof...(S0)
/// \endcode Dynamically, \p Pos is a valid slave position and type arguments
/// \p Ts... match the corresponding input value: \code
/// Pos < NumberOfInputs && AppTuple<Ts...>::TT == InputTypes[Pos]
/// \endcode
template
<
size_t
Pos
,
typename
...
Ts
,
size_t
...
S0
>
AppTuple
<
Ts
...
>
prepareInputValueAtPos
(
TypeList
<
Ts
...
>
,
Seq
<
S0
...
>
)
const
noexcept
;
/// Gives an \c std::tuple containing the current input values and their
/// change flags so that they can be used for the processing function.
///
/// \tparam As types of the input values
/// \tparam S0 indices for accessing input values and their change flags
///
/// \note The only argument provides indices statically as template arguments
/// \p S0..., so its actual value is ignored.
///
/// \return current input values and their change flags prepared for invoking
/// the processing function with them
///
/// \pre Statically, all type arguments \p As... are instances of \c
/// rosa::app::AppTuple and the provided indices \p S0... match the
/// length of \p As...: \code
/// TypeListAllAppTuple<TypeList<As...>>::Value &&
/// sizeof...(As) == sizeof...(S0)
/// \endcode Dynamically, type arguments \p As... match the input types of \p
/// this object: \code
/// inputTypesMatch<TypeList<As...>>()
/// \endcode
template
<
typename
...
As
,
size_t
...
S0
>
std
::
tuple
<
std
::
pair
<
As
,
bool
>
...
>
prepareCurrentInputs
(
Seq
<
S0
...
>
)
const
noexcept
;
/// Invokes a processing function matching the input, output, and
/// master-output types of \p this object with actual arguments provided in a
/// \c std::tuple.
///
/// \note \p Args providing the actual arguments for \p F is to be created by
/// \c rosa::app::AppAgent::prepareCurrentInputs.
///
/// \tparam T output type of the processing function
/// \tparam Ts types of master-output values of the processing function
/// \tparam As types of inputs for the processing function
/// \tparam S0 indices starting with `0` for extracting actual arguments from
/// \p Args
///
/// \param F the processing function to invoke
/// \param Args the actual arguments to invoke \p F with
///
/// \note The last argument provides indices statically as template arguments
/// \p S0..., so its actual value is ignored.
///
/// \return the result of \p F for actual arguments \p Args
///
/// \pre The provided sequence of indices \p S0... constitutes a proper
/// sequence for extracting all actual arguments for
/// \p F from \p Args: \code
/// sizeof...(As) == sizeof...(S0)
/// \endcode
template
<
typename
T
,
typename
...
Ts
,
typename
...
As
,
size_t
...
S0
>
static
std
::
tuple
<
Optional
<
T
>
,
Optional
<
Ts
>
...
>
invokeWithTuple
(
std
::
function
<
std
::
tuple
<
Optional
<
T
>
,
Optional
<
Ts
>
...
>
(
std
::
pair
<
As
,
bool
>
...)
>
F
,
const
std
::
tuple
<
std
::
pair
<
As
,
bool
>
...
>
Args
,
Seq
<
S0
...
>
)
noexcept
;
/// Handles a master-output value for a particular *slave* position.
///
/// \p Value is a \c rosa::Optional resulted by a processing function and
/// contains a master-output value for the *slave* at position \p Pos. The
/// function takes the master-output value and sends its actual value, if any,
/// to the corresponding *slave*.
///
/// \note A master-output of type \c rosa::app::EmptyAppTuple indicates
/// no actual output and hence no message is generated for a position whose
/// corresponding master-output type is \c rosa::app::EmptyAppTuple.
///
/// \note The function provides position-based implementation for \c
/// rosa::app::AppAgent::handleMasterOutputs.
///
/// \tparam Pos the position of the master-output to send \p Value for
/// \tparam Ts types of elements in \p Value
///
/// \param Value \c rosa::app::AppTuple resulted by the processing
/// function for *slave* position \p Pos
///
/// \pre \p Pos is a valid master-output position and \p Value matches the
/// master-output type of \p this object at position \p Pos: \code
/// Pos < NumberOfMasterOutputs &&
/// AppTuple<Ts...>::TT == MasterOutputTypes[Pos]
/// \endcode
template
<
size_t
Pos
,
typename
...
Ts
>
void
handleMasterOutputAtPos
(
const
Optional
<
AppTuple
<
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::app::AppTuple
/// or the number of types \p Ts... is not consistent with the other template
/// arguments.
///
/// \param Output \c std::tuple resulted by a processing function
///
/// \pre Statically, type arguments \p Ts... starting at position \p Offset
/// are instances of \c rosa::app::AppTuple and the number of types \p
/// Ts... is consistent with the other template arguments: \code
/// TypeListAllAppTuple<
/// typename TypeListDrop<Offset, TypeList<Ts...>>::Type>::Value &&
/// sizeof...(Ts) == Offset + sizeof...(S0)
/// \endcode Dynamically, \p Output matches the master-output types \p this
/// object was created with and the provided sequence of indices \p S0...
/// constitues a proper sequence for extracting all master-output values from
/// \p Output: \code
/// masterOutputTypesMatch<typename TypeListDrop<Offset,
/// TypeList<Ts...>>::Type>() &&
/// sizeof...(S0) == NumberOfMasterOutputs
/// \endcode
template
<
size_t
Offset
,
typename
...
Ts
,
size_t
...
S0
>
void
handleMasterOutputs
(
const
std
::
tuple
<
Optional
<
Ts
>
...
>
&
Output
,
Seq
<
S0
...
>
)
noexcept
;
/// Wraps processing functions into a trigger handler.
///
/// \see \c rosa::app::AppAgent::FP
///
/// \note The function cannot be const qualified because the lambda
/// expression defined in it needs to capture \p this object by a non-const
/// reference
///
/// \tparam MTs types of elements of master-input processed by \p MF
/// \tparam T type of output
/// \tparam Ts types of master-output values
/// \tparam As types of input values
/// \tparam S0 indices for accessing master-input values
///
/// \note Instantiation fails if any of the type arguments \p T, \p Ts...,
/// and \p As... is not an instance of \c rosa::app::AppTuple.
///
/// \param MF function processing master-input and generating output
/// \param F function processing inputs and generating output
///
/// \note The last argument provides indices statically as template
/// arguments \p S0..., so its actual value is ignored.
///
/// \note A master-input type of \c rosa::app::EmptyAppTuple indicates
/// that \p this object does not receive master-input, \p MF is never called
/// if \p MTs is empty.
///
/// \return trigger handler function based on \p F and \p MF
///
/// \pre Statically, type arguments \p T, \p Ts..., and \p As... are
/// instances of \c rosa::app::AppTuple and the indices match
/// master-input elements: \code
/// TypeListAllAppTuple<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 == AppTuple<MTs...>::TT && OutputType == T::TT &&
/// inputTypesMatch<TypeList<As...>>() &&
/// masterOutputTypesMatch<TypeList<Ts...>>()
/// \endcode
template
<
typename
...
MTs
,
typename
T
,
typename
...
Ts
,
typename
...
As
,
size_t
...
S0
>
H
triggerHandlerFromProcessingFunctions
(
std
::
function
<
std
::
tuple
<
Optional
<
Ts
>
...
>
(
std
::
pair
<
AppTuple
<
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 *application interface*.
///
/// The function \p F generates a \c std::tuple of values: the first value is
/// the output for the *master* and the rest is for the *slaves*. All output
/// generated by the function is optional as an agent may decide not to output
/// anything at some situation.
///
/// \todo Enforce \p F and \p MF do not potentially throw exception.
///
/// \tparam MT type of master-input handled by \p MF
/// \tparam T type of output of \p F
/// \tparam Ts type of master-output values of \p F and \p MF
/// \tparam As types of input values of \p F
///
/// \note Instantiation fails if any of the type arguments \p MT, \p T, \p
/// Ts..., and \p As... is not an instance of \c rosa::app::AppTuple or
/// any of \p T and \p As... is \c rosa::app::EmptyAppTuple or the
/// number of inputs and master-outputs are not equal.
///
/// \note If \p MT is \c rosa::app::EmptyAppTuple, the constructed
/// object does not receive master-input. Similarly, if any of \p Ts... is \c
/// rosa::app::EmptyAppTuple, the constructed object does not generated
/// master-output for the corresponding *slave* position.
///
/// \param Kind kind of the new \c rosa::Unit instance
/// \param Id unique identifier of the new \c rosa::Unit instance
/// \param Name name of the new \c rosa::Unit instance
/// \param S \c rosa::MessagingSystem owning the new instance
/// \param MF function to process master-input values and generate
/// master-output with
/// \param F function to process input values and generate output and
/// master-output with
///
/// \pre Statically, all the type arguments \p MT, \p T, \p Ts..., and \p
/// As... are instances of \c rosa::app::AppTuple, with \p T and \p
/// As... containing at least one element, and the number of input and
/// master-output types are equal: \code
/// TypeListAllAppTuple<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::app::atoms::AgentKind: \code
/// Kind == rosa::app::atoms::AgentKind
/// \endcode
///
/// \see \c rosa::app::AppTuple
template
<
typename
MT
,
typename
T
,
typename
...
Ts
,
typename
...
As
,
typename
=
std
::
enable_if_t
<
TypeListAllAppTuple
<
TypeList
<
MT
,
T
,
Ts
...,
As
...
>>::
Value
&&
(
T
::
Length
>
0
)
&&
(
true
&&
...
&&
(
As
::
Length
>
0
))
&&
sizeof
...(
Ts
)
==
sizeof
...(
As
)
>>
AppAgent
(
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.
~
AppAgent
(
void
)
noexcept
;
/// Returns the current execution policy of \p this object.
///
/// \see \c rosa::app::AppExecutionPolicy
///
/// \note The returned reference is valid only as long as \c
/// rosa::app::AppAgent::setExecutionPolicy() is not called and \p this
/// object is not destroyed.
///
/// \return \c rosa::app::AppAgent::ExecutionPolicy
const
AppExecutionPolicy
&
executionPolicy
(
void
)
const
noexcept
;
/// Sets the current execution policy of \p this object to \p EP.
///
/// \see \c rosa::app::AppExecutionPolicy
///
/// \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
<
AppExecutionPolicy
>
&&
EP
)
noexcept
;
/// The *master* of \p this object, if any is registered.
///
/// \see \c rosa::app::AppAgent::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::app::AppAgent::registerMaster should be
/// paired with a corresponding call of \c
/// rosa::app::AppAgent::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::app::atoms::AgentKind:
/// \code
/// !_Master || unwrapAgent(*_Master).Kind == rosa::app::atoms::AgentKind
/// \endcode
void
registerMaster
(
const
Optional
<
AgentHandle
>
_Master
)
noexcept
;
/// Tells the types of values consumed from the *slave* at a position.
///
/// That is the type of values \p this object expect to be sent to it in a \c
/// rosa::app::AppTuple by its *slave* registered at position \p Pos.
///
/// \see \c rosa::app::AppAgent::slave
///
/// \param Pos position of *slave*
///
/// \return \c rosa::Token representing the types of values consumed from
/// the *slave* at position \p Pos
///
/// \pre \p Pos is a valid index of input: \code
/// Pos < NumberOfInputs
/// \endcode
Token
inputType
(
const
size_t
Pos
)
const
noexcept
;
/// Tells the types of values produced for the *slave* at a position.
///
/// That is the types of values \p this object potentially sends in a \c
/// rosa::app::AppTuple to its *slave* registered at position \p Pos.
///
/// \see \c rosa::app::AppAgent::slave
///
/// \param Pos position of *slave*
///
/// \return \c rosa::Token representing the types of values produced for
/// the *slave* at position \p Pos
///
/// \pre \p Pos is a valid index of input: \code
/// Pos < NumberOfMasterOutputs
/// \endcode
Token
masterOutputType
(
const
size_t
Pos
)
const
noexcept
;
/// The *slave* of \p this object registered at a position, if any.
///
/// \see \c rosa::app::AppAgent::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::app::AppAgent::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::app::AppAgent::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::app::atoms::AgentKind or \c rosa::app::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::app::atoms::SensorKind &&
/// static_cast<const AppSensor &>(unwrapAgent(*Slave)).OutputType ==
/// InputTypes[Pos] &&
/// (emptyToken(MasterOutputTypes[Pos]) ||
/// static_cast<const AppSensor &>(unwrapAgent(*Slave)).MasterInputType
/// == MasterOutputTypes[Pos])) ||
/// (unwrapAgent(*Slave).Kind == rosa::app::atoms::AgentKind &&
/// static_cast<const AppAgent &>(unwrapAgent(*Slave)).OutputType ==
/// InputTypes[Pos] &&
/// (emptyToken(MasterOutputTypes[Pos]) ||
/// static_cast<const AppAgent &>(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::app::AppAgent::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::app::AppAgent::Master if it
/// contains a valid handle for a \c rosa::app::AppAgent. The function
/// does nothing otherwise.
///
/// The elements from \p Value are sent one by one in separate messages to the
/// *master*.
///
/// \tparam Ts types of the elements in \p Value
/// \tparam S0 indices for accessing elements of \p Value
///
/// \param Value value to send
///
/// \note The second argument provides indices statically as template
/// arguments \p S0..., so its actual value is ignored.
///
/// \pre Statically, the indices match the elements: \code
/// sizeof...(Ts) == sizeof...(S0)
/// \endcode Dynamically, \p Ts match \c
/// rosa::app::AppiAgent::OutputType: \code
/// OutputType == TypeToken<Ts...>::Value
/// \endcode
template
<
typename
...
Ts
,
size_t
...
S0
>
void
sendToMaster
(
const
AppTuple
<
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::app::AppAgent::Slaves[Pos] if
/// it contains a valid handle. The function does nothing otherwise.
///
/// The elements from \p Value are sent one by one in separate messages to the
/// *slave*.
///
/// \tparam Ts types of the elements in \p Value
/// \tparam S0 indices for accessing elements of \p Value
///
/// \param Pos the position of the *slave* to send \p Value to
/// \param Value value to send
///
/// \pre Statically, the indices match the elements: \code
/// sizeof...(Ts) == sizeof...(S0)
/// \endcode Dynamically, \p Pos is a valid *slave* position and \p Ts match
/// \c rosa::app::AppiAgent::MasterOutputTypes[Pos]: \code
/// Pos < NumberOfMasterOutputs &&
/// MasterOutputTypes[Pos] == TypeToken<Ts...>::Value
/// \endcode
template
<
typename
...
Ts
,
size_t
...
S0
>
void
sendToSlave
(
const
size_t
Pos
,
const
AppTuple
<
Ts
...
>
&
Value
,
Seq
<
S0
...
>
)
noexcept
;
/// Generates the next output by processing current input values upon trigger
/// from the system.
///
/// Executes \c rosa::app::AppAgent::FP.
///
/// \note The only argument is a \c rosa::AtomConstant, hence its actual
/// value is ignored.
///
/// \pre Master-input and all input from *slaves* are supposed to be
/// completely received upon triggering: \code
/// MasterInputNextPos == 0 &&
/// std::all_of(InputNextPos.begin(), InputNextPos.end(),
/// [](const token_size_t &I){return I == 0;})
/// \endcode
void
handleTrigger
(
atoms
::
Trigger
)
noexcept
;
/// Stores a new input value from a *slave*.
///
/// The function stores \p Value at position \p Pos in \c
/// rosa::app::AppAgent::InputValues at the position associated to \p Id
/// in \c rosa::app::AppAgent::SlaveIds and also sets the corresponding
/// flag in \c rosa::app::AppAgent::InputChanged. The function also
/// takes care of checking and updating \c
/// rosa::app::AppSensor::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 AppAgentInputHandlers.
///
/// \tparam T type of input to store
///
/// \param Id unique identifier of *slave*
/// \param Pos position of the value in the \c rosa::app::AppTuple
/// \param Value the input value to store
///
/// \pre The *slave* with \p Id is registered, \p Pos is the expected
/// position of input from the *slave*, and the input from it is expected to
/// be of type \p T: \code
/// SlaveIds.find(Id) != SlaveIds.end() &&
/// Pos == InputNextPos[SlaveIds.find(Id)->second] &&
/// typeAtPositionOfToken(InputTypes[SlaveIds.find(Id)->second], Pos) ==
/// TypeNumberOf<T>::Value
/// \endcode
template
<
typename
T
>
void
saveInput
(
id_t
Id
,
token_size_t
Pos
,
T
Value
)
noexcept
;
/// Stores a new input value from the *master*.
///
/// The function stores \p Value at position \p Pos in \c
/// rosa::app::AppAgent::MasterInputValue and also sets the
/// flag \c rosa::app::AppAgent::MasterInputChanged. The function also
/// takes care of checking and updating \c
/// rosa::app::AppAgent::MasterInputNextPos: increments its value and
/// reset to `0` when the last element is received.
///
/// \note Utilized by member functions of group \c
/// AppAgentMasterInputHandlers.
///
/// \tparam T type of input to store
///
/// \param Id unique identifier of the *master*
/// \param Pos position of the value in the \c rosa::app::AppTuple
/// \param Value the input value to store
///
/// \pre The *master* with \p Id is registered, \p Pos is the expected
/// position of master-input, and the input from the *master* at position \p
/// Pos is expected to be of type \p T: \code
/// Master && masterId() == Id && Pos == MasterInputNextPos &&
/// typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf<T>::Value
/// \endcode
template
<
typename
T
>
void
saveMasterInput
(
id_t
Id
,
token_size_t
Pos
,
T
Value
)
noexcept
;
/// \defgroup AppAgentInputHandlers Input handlers of
/// rosa::app::AppAgent
///
/// 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::app::AppAgent::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
/// AASLAVEHANDLERDEF.
///
/// \note Keep these definitions in sync with \c rosa::BuiltinTypes.
///
///@{
AASLAVEHANDLERDEF
(
AtomValue
)
AASLAVEHANDLERDEF
(
int16_t
)
AASLAVEHANDLERDEF
(
int32_t
)
AASLAVEHANDLERDEF
(
int64_t
)
AASLAVEHANDLERDEF
(
int8_t
)
AASLAVEHANDLERDEFN
(
long
double
,
long_double
)
AASLAVEHANDLERDEFN
(
std
::
string
,
std__string
)
AASLAVEHANDLERDEF
(
uint16_t
)
AASLAVEHANDLERDEF
(
uint32_t
)
AASLAVEHANDLERDEF
(
uint64_t
)
AASLAVEHANDLERDEF
(
uint8_t
)
AASLAVEHANDLERDEF
(
unit_t
)
AASLAVEHANDLERDEF
(
bool
)
AASLAVEHANDLERDEF
(
double
)
AASLAVEHANDLERDEF
(
float
)
/// @}
/// \defgroup AppAgentMasterInputHandlers Master-input handlers of
/// rosa::app::AppAgent
///
/// Definition of member functions handling messages from the *master* with
/// different types of input
///
/// A *slave* generally needs to be prepared to deal with values of any
/// built-in type to handle messages from its *master*. Each type requires a
/// separate message handler, which are implemented by these functions. The
/// functions instantiate \c rosa::app::AppAgent::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
/// AAMASTERHANDLERDEF.
///
/// \note Keep these definitions in sync with \c rosa::BuiltinTypes.
///
///@{
AAMASTERHANDLERDEF
(
AtomValue
)
AAMASTERHANDLERDEF
(
int16_t
)
AAMASTERHANDLERDEF
(
int32_t
)
AAMASTERHANDLERDEF
(
int64_t
)
AAMASTERHANDLERDEF
(
int8_t
)
AAMASTERHANDLERDEFN
(
long
double
,
long_double
)
AAMASTERHANDLERDEFN
(
std
::
string
,
std__string
)
AAMASTERHANDLERDEF
(
uint16_t
)
AAMASTERHANDLERDEF
(
uint32_t
)
AAMASTERHANDLERDEF
(
uint64_t
)
AAMASTERHANDLERDEF
(
uint8_t
)
AAMASTERHANDLERDEF
(
unit_t
)
AAMASTERHANDLERDEF
(
bool
)
AAMASTERHANDLERDEF
(
double
)
AAMASTERHANDLERDEF
(
float
)
/// @}
};
/// Anonymous namespace with implementation for \c
/// rosa::app::AppAgent::AppAgent, \c
/// rosa::app::AppAgent::inputTypesMatch, and \c
/// rosa::app::AppAgent::masterOutputTypesMatch, consider it private.
namespace
{
/// Creates storages for data of types \p Ts... in a \c std::vector of \c
/// rosa::TokenizedStorage.
///
/// \note Utilized by \c rosa::app::AppAgnet::AppAgent to initialize \c
/// rosa::app::AppAgent::InputValues. That is due to not being able to use
/// an initializer list directly; the initializer list always copies but \c
/// std::unique_ptr is not copyable.
///
/// \tparam Ts types to create storages for
///
/// \note Instantiation fails if any of the type arguments \p Ts... is not an
/// instance of \c rosa::app::AppTuple.
///
/// \return \c std::vector with pointers for the created storage objects
///
/// \pre Statically, all the type arguments \p Ts... are instances of \c
/// rosa::app::AppTuple: \code
/// TypeListAllAppTuple<TypeList<Ts...>>::Value
/// \endcode
template
<
typename
...
Ts
>
std
::
vector
<
std
::
unique_ptr
<
AbstractTokenizedStorage
>>
makeInputStorages
(
void
)
noexcept
{
std
::
vector
<
std
::
unique_ptr
<
AbstractTokenizedStorage
>>
InputStorages
;
(
InputStorages
.
push_back
(
std
::
make_unique
<
typename
TokenizedStorageForTypeList
<
typename
UnwrapAppTuple
<
Ts
>::
Type
>::
Type
>
()),
...);
return
InputStorages
;
}
/// Template \c struct whose specializations provide a recursive implementation
/// for \c TypesMatchList.
///
/// \tparam As types to match
template
<
typename
...
As
>
struct
TypesMatchImpl
;
/// Template specialization for the case, when at least one type is to
/// be matched and that is an instance of \c rosa::app::AppTuple.
///
/// \tparam Ts types of elements in the \c rosa::app::AppTuple to match
/// \tparam As further types to match
template
<
typename
...
Ts
,
typename
...
As
>
struct
TypesMatchImpl
<
AppTuple
<
Ts
...
>
,
As
...
>
{
/// Tells whether types \c rosa::app::AppTuple<Ts...> and \p As... match
/// \c rosa::Token values stored in \p Tokens starting at position \p Pos.
///
/// The function has got a recursive implementation: it matches the first
/// type \c rosa::app::AppTuple<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::app::AppTuple<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::app::AppTuple.
///
/// \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::app::AppTuple, in which case the match is not successful.
///
/// \note The function takes two parameters to match the general signature but
/// the actual values are ignored.
///
/// \return `false`
static
bool
f
(
const
std
::
vector
<
Token
>
&
,
size_t
)
noexcept
{
return
false
;
}
};
/// Template specialization for the terminal case, when no type remains to
/// check.
template
<>
struct
TypesMatchImpl
<>
{
/// Tells whether \p Pos is the number of values stored in \p Tokens.
///
/// In this terminal case, there is no more types to match because all the
/// types are supposed to be already matched successfully. The whole list of
/// types already matched is a complete match if it covers all values in
/// \p Tokens. That is true if \p Pos points exactly to the end of \p Tokens.
///
/// \param Tokens container of \c rosa::Token values to match types against
/// \param Pos position in \p Tokens to start matching at
///
/// \return if \p Pos is the number of values stored in \p Tokens
static
bool
f
(
const
std
::
vector
<
Token
>
&
Tokens
,
size_t
Pos
)
noexcept
{
return
Pos
==
Tokens
.
size
();
}
};
/// Template \c struct that provides an implementation for \c
/// rosa::app::AppAgent::inputTypesMatch and \c
/// rosa::app::AppAgent::masterOutputTypesMatch.
///
/// \note Match a list of types \p List against a \c std::vector of
/// \c rosa::Token values, \c Tokens, like \code
/// bool match = TypesMatchList<List>::f(Tokens);
/// \endcode
/// If any type in \c rosa::TypeList \p Listis not an instance of \c
/// rosa::app::AppTuple, the match gives a negative result.
///
/// \tparam List \c rosa::TypeList that contains types to match
template
<
typename
List
>
struct
TypesMatchList
;
/// Template specialization implementing the feature.
///
/// \tparam As types to match
template
<
typename
...
As
>
struct
TypesMatchList
<
TypeList
<
As
...
>>
{
/// Tells whether types \p As... match \c rosa::Token values stored in \p
/// Tokens.
///
/// The function unwraps the types from \c rosa::TypeList and utilizes \c
/// TypesMatchImpl to do the check.
///
/// \param Tokens container of \c rosa::Token values to match types against
///
/// \return if types \p As... match \c rosa::Token values stored in \p Tokens
static
bool
f
(
const
std
::
vector
<
Token
>
&
Tokens
)
noexcept
{
return
TypesMatchImpl
<
As
...
>::
f
(
Tokens
,
0
);
}
};
}
// End namespace
template
<
typename
As
>
bool
AppAgent
::
inputTypesMatch
(
void
)
const
noexcept
{
return
TypesMatchList
<
As
>::
f
(
InputTypes
);
}
template
<
typename
Ts
>
bool
AppAgent
::
masterOutputTypesMatch
(
void
)
const
noexcept
{
return
TypesMatchList
<
Ts
>::
f
(
MasterOutputTypes
);
}
template
<
size_t
Pos
,
typename
...
Ts
,
size_t
...
S0
>
AppTuple
<
Ts
...
>
AppAgent
::
prepareInputValueAtPos
(
TypeList
<
Ts
...
>
,
Seq
<
S0
...
>
)
const
noexcept
{
using
T
=
AppTuple
<
Ts
...
>
;
STATIC_ASSERT
(
sizeof
...(
Ts
)
==
sizeof
...(
S0
),
"inconsistent type arguments"
);
ASSERT
(
inv
()
&&
Pos
<
NumberOfInputs
&&
T
::
TT
==
InputTypes
[
Pos
]);
// The below should hold because of the above, just leave it for sanity check.
STATIC_ASSERT
((
true
&&
...
&&
(
static_cast
<
size_t
>
(
static_cast
<
token_size_t
>
(
S0
))
==
S0
)),
"Should not happen"
);
const
auto
&
SlaveInput
=
InputValues
[
Pos
];
// Get all elements of the tuple in a fold expression.
return
T
(
*
static_cast
<
const
Ts
*>
(
SlaveInput
->
pointerTo
(
static_cast
<
token_size_t
>
(
S0
)))...);
}
template
<
typename
...
As
,
size_t
...
S0
>
std
::
tuple
<
std
::
pair
<
As
,
bool
>
...
>
AppAgent
::
prepareCurrentInputs
(
Seq
<
S0
...
>
)
const
noexcept
{
STATIC_ASSERT
(
TypeListAllAppTuple
<
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
UnwrapAppTuple
<
As
>::
Type
(),
seq_t
<
As
::
Length
>
()),
InputChanged
[
S0
])...);
}
template
<
typename
T
,
typename
...
Ts
,
typename
...
As
,
size_t
...
S0
>
std
::
tuple
<
Optional
<
T
>
,
Optional
<
Ts
>
...
>
AppAgent
::
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
{
STATIC_ASSERT
(
sizeof
...(
As
)
==
sizeof
...(
S0
),
"wrong number of type parameters"
);
return
F
(
std
::
get
<
S0
>
(
Args
)...);
}
template
<
size_t
Pos
,
typename
...
Ts
>
void
AppAgent
::
handleMasterOutputAtPos
(
const
Optional
<
AppTuple
<
Ts
...
>>
&
Value
)
noexcept
{
using
MOT
=
AppTuple
<
Ts
...
>
;
ASSERT
(
inv
()
&&
Pos
<
NumberOfMasterOutputs
&&
MOT
::
TT
==
MasterOutputTypes
[
Pos
]);
// Do not do anything for master-output of type \c
// rosa::app::EmptyAppTuple and when \p Value is empty.
if
constexpr
(
!
std
::
is_same
<
MOT
,
EmptyAppTuple
>::
value
)
{
if
(
Value
)
{
sendToSlave
(
Pos
,
*
Value
,
seq_t
<
MOT
::
Length
>
());
}
}
else
{
(
void
)
Value
;
}
ASSERT
(
inv
());
}
template
<
size_t
Offset
,
typename
...
Ts
,
size_t
...
S0
>
void
AppAgent
::
handleMasterOutputs
(
const
std
::
tuple
<
Optional
<
Ts
>
...
>
&
Output
,
Seq
<
S0
...
>
)
noexcept
{
using
MOTs
=
typename
TypeListDrop
<
Offset
,
TypeList
<
Ts
...
>>::
Type
;
STATIC_ASSERT
(
TypeListAllAppTuple
<
MOTs
>::
Value
,
"not tuple type arguments"
);
STATIC_ASSERT
(
sizeof
...(
Ts
)
==
Offset
+
sizeof
...(
S0
),
"inconsistent arguments"
);
ASSERT
(
inv
()
&&
masterOutputTypesMatch
<
MOTs
>
()
&&
sizeof
...(
S0
)
==
NumberOfMasterOutputs
);
// Handle each master-output position in a fold expression.
(
handleMasterOutputAtPos
<
S0
>
(
std
::
get
<
Offset
+
S0
>
(
Output
)),
...);
ASSERT
(
inv
());
}
template
<
typename
...
MTs
,
typename
T
,
typename
...
Ts
,
typename
...
As
,
size_t
...
S0
>
AppAgent
::
H
AppAgent
::
triggerHandlerFromProcessingFunctions
(
std
::
function
<
std
::
tuple
<
Optional
<
Ts
>
...
>
(
std
::
pair
<
AppTuple
<
MTs
...
>
,
bool
>
)
>
&&
MF
,
std
::
function
<
std
::
tuple
<
Optional
<
T
>
,
Optional
<
Ts
>
...
>
(
std
::
pair
<
As
,
bool
>
...)
>
&&
F
,
Seq
<
S0
...
>
)
noexcept
{
using
MT
=
AppTuple
<
MTs
...
>
;
STATIC_ASSERT
((
TypeListAllAppTuple
<
TypeList
<
T
,
Ts
...,
As
...
>>::
Value
),
"not tuple type arguments"
);
STATIC_ASSERT
(
sizeof
...(
MTs
)
==
sizeof
...(
S0
),
"inconsistent arguments"
);
ASSERT
(
MasterInputType
==
MT
::
TT
&&
OutputType
==
T
::
TT
&&
inputTypesMatch
<
TypeList
<
As
...
>>
()
&&
masterOutputTypesMatch
<
TypeList
<
Ts
...
>>
());
return
[
this
,
MF
,
F
]()
noexcept
{
// \note These indices work for both inputs and master-outputs.
using
SlaveIndices
=
seq_t
<
sizeof
...(
As
)
>
;
// Handle master-input.
// Do not do anything for master-input type \c
// rosa::app::EmptyAppTuple.
if
(
!
std
::
is_same
<
MT
,
EmptyAppTuple
>::
value
)
{
LOG_TRACE_STREAM
<<
"AppAgent "
<<
FullName
<<
" handles master-input."
<<
std
::
endl
;
// The assert must hold if \p this object was successfully constructed.
STATIC_ASSERT
(
(
true
&&
...
&&
(
static_cast
<
size_t
>
(
static_cast
<
token_size_t
>
(
S0
))
==
S0
)),
"Unexpected error"
);
const
auto
MasterInputArg
=
std
::
make_pair
(
// Get all elements of the tuple in a fold expression.
MT
(
*
static_cast
<
const
MTs
*>
(
MasterInputValue
->
pointerTo
(
static_cast
<
token_size_t
>
(
S0
)))...),
MasterInputChanged
);
MasterInputChanged
=
false
;
const
std
::
tuple
<
Optional
<
Ts
>
...
>
MasterOutput
=
MF
(
MasterInputArg
);
handleMasterOutputs
<
0
>
(
MasterOutput
,
SlaveIndices
());
}
// Handle inputs.
// Call the processing function only if \p ExecutionPolicy allows.
if
(
ExecutionPolicy
->
shouldProcess
(
InputChanged
))
{
LOG_TRACE_STREAM
<<
"AppAgent "
<<
FullName
<<
" handles input."
<<
std
::
endl
;
const
auto
InputArgs
=
prepareCurrentInputs
<
As
...
>
(
SlaveIndices
());
std
::
fill
(
InputChanged
.
begin
(),
InputChanged
.
end
(),
false
);
const
std
::
tuple
<
Optional
<
T
>
,
Optional
<
Ts
>
...
>
Output
=
invokeWithTuple
(
F
,
InputArgs
,
SlaveIndices
());
const
auto
OutputToMaster
=
std
::
get
<
0
>
(
Output
);
if
(
OutputToMaster
)
{
sendToMaster
(
*
OutputToMaster
,
seq_t
<
T
::
Length
>
());
}
handleMasterOutputs
<
1
>
(
Output
,
SlaveIndices
());
}
else
{
LOG_TRACE_STREAM
<<
"AppAgent "
<<
Name
<<
" skips input."
<<
std
::
endl
;
}
};
}
template
<
typename
MT
,
typename
T
,
typename
...
Ts
,
typename
...
As
,
typename
>
AppAgent
::
AppAgent
(
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
),
AASLAVEHANDLERREF
(
AtomValue
),
AASLAVEHANDLERREF
(
int16_t
),
AASLAVEHANDLERREF
(
int32_t
),
AASLAVEHANDLERREF
(
int64_t
),
AASLAVEHANDLERREF
(
int8_t
),
AASLAVEHANDLERREF
(
long_double
),
AASLAVEHANDLERREF
(
std__string
),
AASLAVEHANDLERREF
(
uint16_t
),
AASLAVEHANDLERREF
(
uint32_t
),
AASLAVEHANDLERREF
(
uint64_t
),
AASLAVEHANDLERREF
(
uint8_t
),
AASLAVEHANDLERREF
(
unit_t
),
AASLAVEHANDLERREF
(
bool
),
AASLAVEHANDLERREF
(
double
),
AASLAVEHANDLERREF
(
float
),
AAMASTERHANDLERREF
(
AtomValue
),
AAMASTERHANDLERREF
(
int16_t
),
AAMASTERHANDLERREF
(
int32_t
),
AAMASTERHANDLERREF
(
int64_t
),
AAMASTERHANDLERREF
(
int8_t
),
AAMASTERHANDLERREF
(
long_double
),
AAMASTERHANDLERREF
(
std__string
),
AAMASTERHANDLERREF
(
uint16_t
),
AAMASTERHANDLERREF
(
uint32_t
),
AAMASTERHANDLERREF
(
uint64_t
),
AAMASTERHANDLERREF
(
uint8_t
),
AAMASTERHANDLERREF
(
unit_t
),
AAMASTERHANDLERREF
(
bool
),
AAMASTERHANDLERREF
(
double
),
AAMASTERHANDLERREF
(
float
)),
ExecutionPolicy
(
AppExecutionPolicy
::
decimation
(
1
)),
OutputType
(
T
::
TT
),
NumberOfInputs
(
sizeof
...(
As
)),
MasterInputType
(
MT
::
TT
),
NumberOfMasterOutputs
(
NumberOfInputs
),
InputTypes
({
As
::
TT
...}),
InputNextPos
(
NumberOfInputs
,
0
),
InputChanged
(
NumberOfInputs
,
false
),
InputValues
(
makeInputStorages
<
As
...
>
()),
MasterInputNextPos
(
0
),
MasterInputChanged
(
false
),
MasterInputValue
(
new
typename
TokenizedStorageForTypeList
<
typename
UnwrapAppTuple
<
MT
>::
Type
>::
Type
()),
MasterOutputTypes
({
Ts
::
TT
...}),
FP
(
triggerHandlerFromProcessingFunctions
(
std
::
move
(
MF
),
std
::
move
(
F
),
seq_t
<
MT
::
Length
>
())),
Slaves
(
NumberOfInputs
)
{
ASSERT
(
Kind
==
atoms
::
AgentKind
);
LOG_TRACE_STREAM
<<
"AppAgent "
<<
FullName
<<
" is created."
<<
std
::
endl
;
ASSERT
(
inv
());
}
template
<
typename
...
Ts
,
size_t
...
S0
>
void
AppAgent
::
sendToMaster
(
const
AppTuple
<
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 successfully constructed.
STATIC_ASSERT
((
true
&&
...
&&
(
static_cast
<
size_t
>
(
static_cast
<
token_size_t
>
(
S0
))
==
S0
)),
"Unexpected error"
);
// 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
<<
"AppAgent "
<<
FullName
<<
"("
<<
Id
<<
") sends to master ("
<<
static_cast
<
bool
>
(
Master
&&
*
Master
)
<<
"): "
<<
Value
<<
" ("
<<
sizeof
...(
S0
)
<<
")"
<<
std
::
endl
;
// There is a handle and the referred *master* is in a valid state.
if
(
Master
&&
*
Master
)
{
// Handle each element of the tuple in a fold expression.
(
Master
->
sendMessage
(
Message
::
create
(
atoms
::
Slave
::
Value
,
Id
,
Indices
[
S0
],
std
::
get
<
S0
>
(
Value
))),
...);
}
ASSERT
(
inv
());
}
template
<
typename
...
Ts
,
size_t
...
S0
>
void
AppAgent
::
sendToSlave
(
const
size_t
Pos
,
const
AppTuple
<
Ts
...
>
&
Value
,
Seq
<
S0
...
>
)
noexcept
{
STATIC_ASSERT
(
sizeof
...(
Ts
)
==
sizeof
...(
S0
),
"inconsistent arguments"
);
ASSERT
(
inv
()
&&
Pos
<
NumberOfMasterOutputs
&&
MasterOutputTypes
[
Pos
]
==
TypeToken
<
Ts
...
>::
Value
);
// The assert must hold if \p this object was successfully constructed.
STATIC_ASSERT
((
true
&&
...
&&
(
static_cast
<
size_t
>
(
static_cast
<
token_size_t
>
(
S0
))
==
S0
)),
"Unexpected error"
);
// 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
<<
"AppAgent "
<<
FullName
<<
"("
<<
Id
<<
") sends to slave ("
<<
static_cast
<
bool
>
(
Slave
&&
*
Slave
)
<<
") at position "
<<
Pos
<<
": "
<<
Value
<<
" ("
<<
sizeof
...(
S0
)
<<
")"
<<
std
::
endl
;
if
(
Slave
&&
*
Slave
)
{
// Handle each element of the tuple in a fold expression.
(
Slave
->
sendMessage
(
Message
::
create
(
atoms
::
Master
::
Value
,
Id
,
Indices
[
S0
],
std
::
get
<
S0
>
(
Value
))),
...);
}
}
template
<
typename
T
>
void
AppAgent
::
saveInput
(
id_t
Id
,
token_size_t
Pos
,
T
Value
)
noexcept
{
ASSERT
(
inv
()
&&
SlaveIds
.
find
(
Id
)
!=
SlaveIds
.
end
()
&&
Pos
==
InputNextPos
[
SlaveIds
.
find
(
Id
)
->
second
]
&&
typeAtPositionOfToken
(
InputTypes
[
SlaveIds
.
find
(
Id
)
->
second
],
Pos
)
==
TypeNumberOf
<
T
>::
Value
);
const
size_t
SlavePos
=
SlaveIds
.
at
(
Id
);
LOG_TRACE_STREAM
<<
"AppAgent "
<<
FullName
<<
"("
<<
Id
<<
") saves value from slave at position "
<<
SlavePos
<<
": ("
<<
static_cast
<
size_t
>
(
Pos
)
<<
") "
<<
Value
<<
std
::
endl
;
// Save value.
*
static_cast
<
T
*>
(
InputValues
[
SlavePos
]
->
pointerTo
(
Pos
))
=
Value
;
// Update position of next value.
if
(
++
InputNextPos
[
SlavePos
]
==
lengthOfToken
(
InputTypes
[
SlavePos
]))
{
InputNextPos
[
SlavePos
]
=
0
;
}
// Set flag.
InputChanged
[
SlavePos
]
=
true
;
ASSERT
(
inv
());
}
template
<
typename
T
>
void
AppAgent
::
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
<<
"AppAgent "
<<
FullName
<<
"("
<<
Id
<<
") saves value from master: ("
<<
static_cast
<
size_t
>
(
Pos
)
<<
") "
<<
Value
<<
std
::
endl
;
// Save value.
*
static_cast
<
T
*>
(
MasterInputValue
->
pointerTo
(
Pos
))
=
Value
;
// Update position of next value.
if
(
++
MasterInputNextPos
==
lengthOfToken
(
MasterInputType
))
{
MasterInputNextPos
=
0
;
}
// Set flag.
MasterInputChanged
=
true
;
ASSERT
(
inv
());
}
}
// End namespace app
}
// End namespace rosa
#undef AASLAVEHANDLEREF
#undef AAMASTERHANDLEREF
#undef AASLAVEHANDLEDEF
#undef AAMASTERHANDLEDEF
#undef AASLAVEHANDLEDEFN
#undef AAMASTERHANDLEDEFN
#undef AASLAVEHANDLENAME
#undef AAMASTERHANDLENAME
#endif
// ROSA_APP_APPAGENT_HPP
File Metadata
Details
Attached
Mime Type
text/x-c++
Expires
Sun, Jun 8, 8:45 PM (21 h, 13 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
149390
Default Alt Text
AppAgent.hpp (58 KB)
Attached To
Mode
R20 SoC_Rosa_repo
Attached
Detach File
Event Timeline
Log In to Comment