Page MenuHomePhorge

No OneTemporary

Size
139 KB
Referenced Files
None
Subscribers
None
diff --git a/.gitignore b/.gitignore
index 722d5e7..48bf31e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,3 @@
.vscode
+build*
+CMakeLists.txt.user
\ No newline at end of file
diff --git a/examples/agent-functionalities/agent-functionalities.cpp b/examples/agent-functionalities/agent-functionalities.cpp
index 0266565..0766f28 100644
--- a/examples/agent-functionalities/agent-functionalities.cpp
+++ b/examples/agent-functionalities/agent-functionalities.cpp
@@ -1,190 +1,188 @@
//===-- examples/agent-functionalities/agent-functionalities.cpp *-- C++-*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file examples/agent-functionalities/agent-functionalities.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief A simple example on defining \c rosa::Agent instances using
/// \c rosa::agent::Functionality object as components.
///
//===----------------------------------------------------------------------===//
// Make sure M_PI is available, needed for _WIN32
#define _USE_MATH_DEFINES
#include <cmath>
#include "rosa/agent/Abstraction.hpp"
+#include "rosa/agent/Confidence.hpp"
#include "rosa/agent/FunctionAbstractions.hpp"
#include "rosa/agent/RangeConfidence.hpp"
-#include "rosa/agent/Confidence.hpp"
#include "rosa/config/version.h"
#include "rosa/core/Agent.hpp"
#include "rosa/core/MessagingSystem.hpp"
#include "rosa/support/log.h"
#include "rosa/support/terminal_colors.h"
#include <vector>
using namespace rosa;
using namespace rosa::agent;
using namespace rosa::terminal;
// We use pi as float rather than double, which M_PI is.
constexpr float Pi = (float) M_PI;
/// A dummy wrapper for testing \c rosa::MessagingSystem.
///
/// \note Since we test \c rosa::MessagingSystem directly here, we need to get
/// access to its protected members. That we do by imitating to be a decent
/// subclass of \c rosa::MessagingSystem, while calling protected member
/// functions on an object of a type from which we actually don't inherit.
struct SystemTester : protected MessagingSystem {
template <typename T, typename... Funs>
static AgentHandle createMyAgent(MessagingSystem *S, const std::string &Name,
Funs &&... Fs) {
return ((SystemTester *)S)->createAgent<T>(Name, std::move(Fs)...);
}
static void destroyMyAgent(MessagingSystem *S, const AgentHandle &H) {
((SystemTester *)S)->destroyUnit(unwrapAgent(H));
}
};
/// A special \c rosa::Agent with its own state.
class MyAgent : public Agent {
public:
using Tick = AtomConstant<atom("tick")>;
private:
enum class Categories { Bad, Normal, Good };
static const std::map<Categories, const char *> CategoryNames;
- History<uint8_t, 10, HistoryPolicy::FIFO> H;
+ StaticLengthHistory<uint8_t, 10, HistoryPolicy::FIFO> H;
Confidence<uint8_t> C;
RangeAbstraction<uint8_t, Categories> A;
PartialFunction<int, int> L;
RangeConfidence<float, Categories, float> RCL;
RangeConfidence<float, Categories, float> RCS;
public:
void handler(Tick, uint8_t V) noexcept {
// Record \p V to the \c rosa::agent::History, then print state info.
H << V;
ASSERT(H.entry() == V); // Sanity check.
LOG_INFO_STREAM << "\nNext value: " << PRINTABLE(V)
<< ", confidence: " << C(H)
<< ", category: " << CategoryNames.at(A(H.entry()))
<< ", partial: " << int(L(H.entry()))
<< ", range-confidence-linear: ";
- std::map<Categories, float> res_lin = RCL(H.entry());
- for (auto i : res_lin){
- LOG_INFO_STREAM << " " << CategoryNames.at(i.first)
- << " " << i.second << "," ;
+ std::map<Categories, float> ResLin = RCL(H.entry());
+ for (auto Con : ResLin) {
+ LOG_INFO_STREAM << " " << CategoryNames.at(Con.first) << " " << Con.second
+ << ",";
}
LOG_INFO_STREAM << " range-confidence-sine: ";
- std::map<Categories, float> res_sine = RCS(H.entry());
- for (auto i : res_sine){
- LOG_INFO_STREAM << " " << CategoryNames.at(i.first)
- << " " << i.second << "," ;
+ std::map<Categories, float> ResSine = RCS(H.entry());
+ for (auto Con : ResSine) {
+ LOG_INFO_STREAM << " " << CategoryNames.at(Con.first) << " " << Con.second
+ << ",";
}
LOG_INFO_STREAM << '\n';
-
}
MyAgent(const AtomValue Kind, const rosa::id_t Id, const std::string &Name,
MessagingSystem &S)
: Agent(Kind, Id, Name, S, THISMEMBER(handler)), H(), C(5, 20, 1),
A({{{(uint8_t)10, (uint8_t)14}, Categories::Normal},
{{(uint8_t)15, (uint8_t)17}, Categories::Good},
{{(uint8_t)18, (uint8_t)19}, Categories::Normal}},
Categories::Bad),
L({{{0, 2}, std::make_shared<LinearFunction<int, int>>(0, 1)},
{{2, 4}, std::make_shared<LinearFunction<int, int>>(2, 0)},
{{4, 6}, std::make_shared<LinearFunction<int, int>>(6, -1)}},
0),
RCL({
{Categories::Bad, PartialFunction<float, float>({
{{0.f, 3.f}, std::make_shared<LinearFunction<float, float>>
(0.f, 1.f/3)},
{{3.f, 6.f}, std::make_shared<LinearFunction<float, float>>
(1.f, 0.f)},
{{6.f, 9.f}, std::make_shared<LinearFunction<float, float>>
(3.f, -1.f/3)},
},0)},
{Categories::Normal, PartialFunction<float, float>({
{{6.f, 9.f}, std::make_shared<LinearFunction<float, float>>
(-2.f, 1.f/3)},
{{9.f, 12.f}, std::make_shared<LinearFunction<float, float>>
(1.f, 0.f)},
{{12.f, 15.f}, std::make_shared<LinearFunction<float, float>>
(5.f, -1.f/3)},
},0)},
{Categories::Good, PartialFunction<float, float>({
{{12.f, 15.f}, std::make_shared<LinearFunction<float, float>>
(-4.f, 1.f/3)},
{{15.f, 18.f}, std::make_shared<LinearFunction<float, float>>
(1.f, 0.f)},
{{18.f, 21.f}, std::make_shared<LinearFunction<float, float>>
(7.f, -1.f/3)},
},0)}
}),
RCS({
{Categories::Bad, PartialFunction<float, float>({
{{0.f, 3.f}, std::make_shared<SineFunction<float, float>>
(Pi/3, 0.5f, -Pi/2, 0.5f)},
{{3.f, 6.f}, std::make_shared<LinearFunction<float, float>>(1.f, 0.f)},
{{6.f, 9.f}, std::make_shared<SineFunction<float, float>>
(Pi/3, 0.5f, -Pi/2 + 3, 0.5f)},
},0)},
{Categories::Normal, PartialFunction<float, float>({
{{6.f, 9.f}, std::make_shared<SineFunction<float, float>>
(Pi/3, 0.5f, -Pi/2, 0.5f)},
{{9.f, 12.f}, std::make_shared<LinearFunction<float, float>>(1.f, 0.f)},
{{12.f, 15.f}, std::make_shared<SineFunction<float, float>>
(Pi/3, 0.5f, -Pi/2 + 3, 0.5f)},
},0)},
{Categories::Good, PartialFunction<float, float>({
{{12.f, 15.f}, std::make_shared<SineFunction<float, float>>
(Pi/3, 0.5f, -Pi/2, 0.5f)},
{{15.f, 18.f}, std::make_shared<LinearFunction<float, float>>(1.f, 0.f)},
{{18.f, 21.f}, std::make_shared<SineFunction<float, float>>
(Pi/3, 0.5f, -Pi/2 + 3, 0.5f)},
},0)}
}, true){}
};
const std::map<MyAgent::Categories, const char *> MyAgent::CategoryNames{
{Categories::Bad, "Bad"},
{Categories::Normal, "Normal"},
{Categories::Good, "Good"}};
int main(void) {
- LOG_INFO_STREAM << library_string() << " -- " << Color::Red
- << "agent-functionalities example" << Color::Default
- << '\n';
+ LOG_INFO_STREAM << library_string() << " -- " << Color::Red
+ << "agent-functionalities example" << Color::Default << '\n';
std::unique_ptr<MessagingSystem> S = MessagingSystem::createSystem("Sys");
MessagingSystem *SP = S.get();
AgentHandle A = SystemTester::createMyAgent<MyAgent>(SP, "MyAgent");
- std::vector<uint8_t> Vs{0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 13,
- 15, 14, 15, 16, 19, 20, 21};
+ std::vector<uint8_t> Vs{0, 1, 2, 3, 4, 5, 6, 7, 9, 10,
+ 11, 13, 15, 14, 15, 16, 19, 20, 21};
for (auto I = Vs.begin(); I != Vs.end(); ++I) {
A.send<MyAgent::Tick, uint8_t>(MyAgent::Tick::Value, *I);
}
SystemTester::destroyMyAgent(SP, A);
return 0;
}
diff --git a/include/rosa/agent/Confidence.hpp b/include/rosa/agent/Confidence.hpp
index 3d07606..35b7496 100644
--- a/include/rosa/agent/Confidence.hpp
+++ b/include/rosa/agent/Confidence.hpp
@@ -1,204 +1,202 @@
//===-- rosa/agent/Confidence.hpp -------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/agent/Confidence.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Definition of *confidence* *functionality*.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_AGENT_CONFIDENCE_HPP
#define ROSA_AGENT_CONFIDENCE_HPP
#include "rosa/agent/History.hpp"
#include "rosa/support/debug.hpp"
#include <limits>
namespace rosa {
namespace agent {
/// Confidence validator.
///
/// Checks the plausibility of given values by validating if a valid region
/// contains them. It also capable of checking consistency by validating the
/// rate of change recorded by a \c rosa::agent::History object against a
/// maximal absolute valid rate of change.
///
/// \tparam T type of values to validate
///
/// \note The template is defined only for arithmetic types.
///
/// \note The lower bound is inclusive and the upper bound is exclusive.
///
/// \invariant The bounds are defined in a meaningful way:\code
/// LowerBound <= UpperBound
/// \endcode
template <typename T> class Confidence : public Functionality {
// Make sure the actual type argument is an arithmetic type.
STATIC_ASSERT(std::is_arithmetic<T>::value, "not arithmetic Confidence");
public:
/// Unsigned type corresponding to \p T.
using UT = unsigned_t<T>;
/// The minimal value of type \p T.
/// \note Not exist \c V of type \p T such that `V < Min`.
static constexpr T Min = std::is_integral<T>::value
? std::numeric_limits<T>::min()
: std::numeric_limits<T>::lowest();
/// The maximal value of type \p T.
/// \note Not exist \c V of type \p T such that `V > Max`.
static constexpr T Max =
(std::is_integral<T>::value || !std::numeric_limits<T>::has_infinity)
? std::numeric_limits<T>::max()
: std::numeric_limits<T>::infinity();
/// The maximal value of type \c UT.
/// \note Not exist \c V of type \c UT such that `V > UnsignedMax`.
static constexpr UT UnsignedMax =
(std::is_integral<UT>::value || !std::numeric_limits<UT>::has_infinity)
? std::numeric_limits<UT>::max()
: std::numeric_limits<UT>::infinity();
private:
/// The inclusive lower bound for plausibility check.
T LowerBound;
/// The exclusive upper bound for plausibility check.
T UpperBound;
/// The maximal absolute rate of change for consistency check.
UT ChangeRate;
public:
/// Creates an instance by setting the validator variables.
///
/// \param LowerBound the lower bound for plausability check
/// \param UpperBound the upper bound for plausability check
/// \param ChangeRate maximal absolute rate of change for consistency check
///
/// \pre The bounds are defined in a meaningful way:\code
/// LowerBound <= UpperBound
/// \endcode
Confidence(const T LowerBound = Min, const T UpperBound = Max,
const UT ChangeRate = UnsignedMax) noexcept
- : LowerBound(LowerBound),
- UpperBound(UpperBound),
- ChangeRate(ChangeRate) {
+ : LowerBound(LowerBound), UpperBound(UpperBound), ChangeRate(ChangeRate) {
// Make sure Confidence is created in a valid state.
if (LowerBound > UpperBound) {
ROSA_CRITICAL("Confidence with LowerBound higher than UpperBound");
}
}
/// Destroys \p this object.
~Confidence(void) = default;
/// Gives a snapshot of the current state of the validator variables.
///
/// \param [out] LowerBound to copy \c rosa::agent::Confidence::LowerBound
/// into
/// \param [out] UpperBound to copy \c rosa::agent::Confidence::UpperBound
/// into
/// \param [out] ChangeRate to copy \c rosa::agent::Confidence::ChangeRate
/// into
void getParameters(T &LowerBound, T &UpperBound, UT &ChangeRate) const
noexcept {
// Copy members to the given references.
LowerBound = this->LowerBound;
UpperBound = this->UpperBound;
ChangeRate = this->ChangeRate;
}
/// Sets the lower bound for plausability check.
///
/// Beyond setting the lower bound, the function also adjusts the upper bound
/// to the given lower bound if the new lower bound would be higher than the
/// upper bound.
///
/// \param LowerBound the new lower bound to set
void setLowerBound(const T LowerBound) noexcept {
// Adjust UpperBound if necessary, then set LowerBound.
if (UpperBound < LowerBound) {
UpperBound = LowerBound;
}
this->LowerBound = LowerBound;
}
/// Sets the upper bound for plausability check.
///
/// Beyond setting the upper bound, the function also adjusts the lower bound
/// to the given upper bound if the new upper bound would be lower than the
/// lower bound.
///
/// \param UpperBound the new upper bound to set
void setUpperBound(const T UpperBound) noexcept {
// Adjust LowerBound if necessary, then set UpperBound.
if (UpperBound < LowerBound) {
LowerBound = UpperBound;
}
this->UpperBound = UpperBound;
}
/// Sets the maximal rate of change for consistency check.
///
/// \param ChangeRate the new rate of change to set
void setChangeRate(const UT ChangeRate) noexcept {
// Set ChangeRate.
this->ChangeRate = ChangeRate;
}
/// Tells the binary confidence on the plausibility of a value.
///
/// \param V value to check
///
/// \return whether \c V is within the range defined by
/// \c rosa::agent::Confidence::LowerBound and
/// \c rosa::agent::Confidence::UpperBound.
bool operator()(const T V) const noexcept {
// Return if \c V is plausible.
return LowerBound <= V && V < UpperBound;
}
/// Tells the binary confidence on the plausibility and consistency of the
/// last value recorded by a \c rosa::agent::History instance.
///
/// Consistency of the last value is checked by validating the difference with
/// its preceding entry.
///
/// \note The \c rosa::agent::History instance needs to store values of type
/// \p T.
///
/// \note An empty \c rosa::agent::History instance results in full
/// confidence.
///
/// \tparam N number of values \p H is able to store
/// \tparam P retention policy followed by \p H when capacity is reached
///
/// \param H *history* whose last entry to check
template <size_t N, HistoryPolicy P>
- bool operator()(const History<T, N, P> &H) const noexcept {
+ bool operator()(const StaticLengthHistory<T, N, P> &H) const noexcept {
if (H.empty()) {
// No entry to validate.
return true;
} else {
// Validate the last entry and the one step average absolute difference.
return (*this)(H.entry()) && H.averageAbsDiff(1) <= ChangeRate;
}
}
};
} // End namespace agent
} // End namespace rosa
#endif // ROSA_AGENT_CONFIDENCE_HPP
diff --git a/include/rosa/agent/FunctionAbstractions.hpp b/include/rosa/agent/FunctionAbstractions.hpp
index 9a7127a..59200de 100644
--- a/include/rosa/agent/FunctionAbstractions.hpp
+++ b/include/rosa/agent/FunctionAbstractions.hpp
@@ -1,224 +1,222 @@
//===-- rosa/agent/FunctionAbstractions.hpp ---------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/agent/FunctionAbstractions.hpp
///
/// \author Benedikt Tutzer (benedikt.tutzer@tuwien.ac.at)
///
/// \date 2019
///
/// \brief Definition of *FunctionAbstractions* *functionality*.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_AGENT_FUNCTIONABSTRACTIONS_HPP
#define ROSA_AGENT_FUNCTIONABSTRACTIONS_HPP
-#include "rosa/agent/Functionality.h"
#include "rosa/agent/Abstraction.hpp"
+#include "rosa/agent/Functionality.h"
#include "rosa/support/debug.hpp"
#include <algorithm>
-#include <vector>
#include <cmath>
#include <memory>
+#include <vector>
namespace rosa {
namespace agent {
/// Implements \c rosa::agent::Abstraction as a linear function,
/// y = Coefficient * X + Intercept.
///
/// \note This implementation is supposed to be used to represent a linear
/// function from an arithmetic domain to an arithmetic range. This is enforced
/// statically.
///
/// \tparam D type of the functions domain
/// \tparam R type of the functions range
-template <typename D, typename R> class LinearFunction :
- public Abstraction<D, R>{
+template <typename D, typename R>
+class LinearFunction : public Abstraction<D, R> {
// Make sure the actual type arguments are matching our expectations.
STATIC_ASSERT((std::is_arithmetic<D>::value),
- "LinearFunction not arithmetic T");
+ "LinearFunction not arithmetic T");
STATIC_ASSERT((std::is_arithmetic<R>::value),
- "LinearFunction not to arithmetic");
+ "LinearFunction not to arithmetic");
+
protected:
/// The Intercept of the linear function
const D Intercept;
/// The Coefficient of the linear function
const D Coefficient;
public:
/// Creates an instance.
///
/// \param Intercept the intercept of the linear function
/// \param Coefficient the coefficient of the linear function
LinearFunction(D Intercept, D Coefficient) noexcept
- : Abstraction<D, R>(Intercept),
- Intercept(Intercept),
+ : Abstraction<D, R>(Intercept), Intercept(Intercept),
Coefficient(Coefficient) {}
/// Destroys \p this object.
~LinearFunction(void) = default;
/// Checks wether the Abstraction evaluates to default at the given position
/// As LinearFunctions can be evaluated everythwere, this is always false
///
/// \param V the value at which to check if the function falls back to it's
/// default value.
///
/// \return false
bool isDefaultAt(const D &V) const noexcept override {
(void)V;
return false;
}
/// Evaluates the linear function
///
/// \param X the value at which to evaluate the function
///
/// \return Coefficient*X + Intercept
virtual R operator()(const D &X) const noexcept override {
- return Intercept + X*Coefficient;
+ return Intercept + X * Coefficient;
}
};
/// Implements \c rosa::agent::Abstraction as a sine function,
/// y = Amplitude * sin(Frequency * X + Phase) + Average.
///
/// \note This implementation is supposed to be used to represent a sine
/// function from an arithmetic domain to an arithmetic range. This is enforced
/// statically.
///
/// \tparam D type of the functions domain
/// \tparam R type of the functions range
-template <typename D, typename R> class SineFunction :
- public Abstraction<D, R>{
+template <typename D, typename R>
+class SineFunction : public Abstraction<D, R> {
// Make sure the actual type arguments are matching our expectations.
STATIC_ASSERT((std::is_arithmetic<D>::value),
- "SineFunction not arithmetic T");
+ "SineFunction not arithmetic T");
STATIC_ASSERT((std::is_arithmetic<R>::value),
- "SineFunction not to arithmetic");
+ "SineFunction not to arithmetic");
+
protected:
/// The frequency of the sine wave
const D Frequency;
/// The Ampiltude of the sine wave
const D Amplitude;
/// The Phase-shift of the sine wave
const D Phase;
/// The y-shift of the sine wave
const D Average;
public:
/// Creates an instance.
///
/// \param Frequency the frequency of the sine wave
/// \param Amplitude the amplitude of the sine wave
/// \param Phase the phase of the sine wave
/// \param Average the average of the sine wave
SineFunction(D Frequency, D Amplitude, D Phase, D Average) noexcept
- : Abstraction<D, R>(Average),
- Frequency(Frequency),
- Amplitude(Amplitude),
- Phase(Phase),
- Average(Average) {}
+ : Abstraction<D, R>(Average), Frequency(Frequency), Amplitude(Amplitude),
+ Phase(Phase), Average(Average) {}
/// Destroys \p this object.
~SineFunction(void) = default;
/// Checks wether the Abstraction evaluates to default at the given position
/// As SineFunctions can be evaluated everythwere, this is always false
///
/// \param V the value at which to check if the function falls back to it's
/// default value.
///
/// \return false
bool isDefaultAt(const D &V) const noexcept override {
(void)V;
return false;
}
/// Evaluates the sine function
///
/// \param X the value at which to evaluate the function
/// \return the value of the sine-function at X
virtual R operator()(const D &X) const noexcept override {
- return Amplitude*sin(Frequency * X + Phase) + Average;
+ return Amplitude * sin(Frequency * X + Phase) + Average;
}
};
/// Implements \c rosa::agent::Abstraction as a partial function from a domain
// /to a range.
///
/// \note This implementation is supposed to be used to represent a partial
/// function from an arithmetic domain to an arithmetic range. This is enforced
/// statically.
///
/// A partial function is defined as a list of abstractions, where each
/// abstraction is associated a range in which it is defined. These ranges must
/// be mutually exclusive.
///
/// \tparam D type of the functions domain
/// \tparam R type of the functions range
template <typename D, typename R>
class PartialFunction : public Abstraction<D, R> {
// Make sure the actual type arguments are matching our expectations.
STATIC_ASSERT((std::is_arithmetic<D>::value), "abstracting not arithmetic");
STATIC_ASSERT((std::is_arithmetic<R>::value),
- "abstracting not to arithmetic");
+ "abstracting not to arithmetic");
private:
/// A \c rosa::agent::RangeAbstraction RA is used to represent the association
/// from ranges to Abstractions.
/// This returns the Abstraction that is defined for any given value, or
/// a default Abstraction if no Abstraction is defined for that value.
RangeAbstraction<D, std::shared_ptr<Abstraction<D, R>>> RA;
public:
/// Creates an instance by Initializing the underlying \c Abstraction.
///
/// \param Map the mapping to do abstraction according to
/// \param Default abstraction to abstract to by default
///
/// \pre Each key defines a valid range such that `first <= second` and
/// there are no overlapping ranges defined by the keys.
- PartialFunction(const std::map<std::pair<D, D>,
- std::shared_ptr<Abstraction<D, R>>> &Map,
- const R Default)
+ PartialFunction(
+ const std::map<std::pair<D, D>, std::shared_ptr<Abstraction<D, R>>> &Map,
+ const R Default)
: Abstraction<D, R>(Default),
- RA(Map, std::shared_ptr<Abstraction<D, R>>
- (new Abstraction<D, R>(Default))) {
+ RA(Map,
+ std::shared_ptr<Abstraction<D, R>>(new Abstraction<D, R>(Default))) {
}
/// Destroys \p this object.
~PartialFunction(void) = default;
/// Checks wether the Abstraction evaluates to default at the given position
///
/// \param V the value at which to check if the function falls back to it's
/// default value.
///
/// \return false if the value falls into a defined range and the Abstraction
/// defined for that range does not fall back to it's default value.
bool isDefaultAt(const D &V) const noexcept override {
return RA.isDefaultAt(V) ? true : RA(V)->isDefaultAt(V);
}
/// Searches for an Abstraction for the given value and executes it for that
/// value, if such an Abstraction is found. The default Abstraction is
/// evaluated otherwise.
///
/// \param V value to abstract
///
/// \return the abstracted value based on the set mapping
R operator()(const D &V) const noexcept override {
return RA(V)->operator()(V);
}
};
} // End namespace agent
} // End namespace rosa
#endif // ROSA_AGENT_FUNCTIONABSTRACTIONS_HPP
diff --git a/include/rosa/agent/History.hpp b/include/rosa/agent/History.hpp
index 1097ad0..5593cb7 100644
--- a/include/rosa/agent/History.hpp
+++ b/include/rosa/agent/History.hpp
@@ -1,292 +1,528 @@
//===-- rosa/agent/History.hpp ----------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/agent/History.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Definition of *history* *functionality*.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_AGENT_HISTORY_HPP
#define ROSA_AGENT_HISTORY_HPP
#include "rosa/agent/Functionality.h"
#include "rosa/config/config.h"
#include "rosa/support/debug.hpp"
#include "rosa/support/type_helper.hpp"
#include <array>
+#include <vector>
namespace rosa {
namespace agent {
/// Retention policies defining what a \c rosa::agent::History instance should
/// do when the number of recorded entries reached its capacity.
enum class HistoryPolicy {
SRWF, ///< Stop Recording When Full -- no new entry is recorded when full
- FIFO ///< First In First Out -- overwrite the earliest entry with a new one
+ FIFO, ///< First In First Out -- overwrite the earliest entry with a new one
+ LIFO ///< Last In First Out -- overwrite the latest entry with a new one
};
-/// Implements *history* by recording and storing values.
-///
-/// \note Not thread-safe implementation, which should not be a problem as any
-/// instance of \c rosa::agent::Functionality is an internal component of a
-/// \c rosa::Agent, which is the basic unit of concurrency.
-///
-/// \tparam T type of values to store
-/// \tparam N number of values to store at most
-/// \tparam P retention policy to follow when capacity is reached
-///
-/// \invariant The size of the underlying \c std::array is `N + 1`:\code
-/// max_size() == N + 1 && N == max_size() - 1
-/// \endcode
-template <typename T, size_t N, HistoryPolicy P>
-class History : public Functionality, private std::array<T, N + 1> {
-
- // Bring into scope inherited functions that are used.
- using std::array<T, N + 1>::max_size;
- using std::array<T, N + 1>::operator[];
-
- /// The index of the first data element in the circular buffer.
- size_t Data;
-
- /// The index of the first empty slot in the circular buffer.
- size_t Space;
+template <typename T, HistoryPolicy P> class History : public Functionality {
public:
- /// Creates an instances by initializing the indices for the circular buffer.
- History(void) noexcept : Data(0), Space(0) {}
+ History(void) noexcept {}
/// Destroys \p this object.
- ~History(void) = default;
+ virtual ~History(void) = default;
/// Tells the retention policy applied to \p this object.
///
/// \return \c rosa::agent::History::P
- static constexpr HistoryPolicy policyOfHistory(void) noexcept { return P; }
+ static constexpr HistoryPolicy policy(void) noexcept { return P; }
/// Tells how many entries may be recorded by \c this object.
///
/// \note The number of entries that are actually recorded may be smaller.
///
- /// \return \c rosa::agent::History::N
- static constexpr size_t lengthOfHistory(void) noexcept { return N; }
+ /// \return The max number of entries that may be recorded
+ virtual size_t maxLength(void) const noexcept = 0;
/// Tells how many entries are currently recorded by \p this object.
///
/// \return number of entries currently recorded by \p this object.
///
/// \post The returned value cannot be larger than the capacity of \p this
/// object:\code
/// 0 <= numberOfEntries() && numberOfEntries <= lengthOfHistory()
/// \endcode
- size_t numberOfEntries(void) const noexcept {
- return Data <= Space ? Space - Data : max_size() - Data + Space;
- }
+ virtual size_t numberOfEntries(void) const noexcept = 0;
/// Tells if \p this object has not recorded anything yet.
///
/// \return if \p this object has no entries recorded
bool empty(void) const noexcept { return numberOfEntries() == 0; }
+ /// Tells if the history reached it's maximum length
+ ///
+ /// \return if the history reached it's maximum length.
+ bool full(void) const noexcept { return numberOfEntries() == maxLength(); }
+
/// Gives a constant lvalue reference to an entry stored in \p this object.
///
/// \note The recorded entries are indexed starting from the latest one.
///
/// \param I the index at which the stored entry to take from
///
/// \pre \p I is a valid index:\code
- /// 0 <= I && I <= numberOfEntries()
+ /// 0 <= I && I < numberOfEntries()
/// \endcode
- const T &entry(const size_t I = 0) const noexcept {
- ASSERT(0 <= I && I < numberOfEntries()); // Boundary check.
- // Position counted back from the last recorded entry.
- typename std::make_signed<const size_t>::type Pos = Space - (1 + I);
- // Actual index wrapped around to the end of the buffer if negative.
- return (*this)[Pos >= 0 ? Pos : max_size() + Pos];
- }
+ virtual const T &entry(const size_t I = 0) const noexcept = 0;
+
+ /// Removes all entries recorded in \p this object.
+ virtual void clear() noexcept = 0;
private:
- /// Tells if the circular buffer is full.
+ /// Pushes a new entry into the history.
///
- /// \return if the circular buffer is full.
- bool full(void) const noexcept { return numberOfEntries() == N; }
-
- /// Pushes a new entry into the circular buffer.
+ /// \note The earliest entry gets overwritten if the history is full.
///
- /// \note The earliest entry gets overwritten if the buffer is full.
+ /// \param V value to push into the history
+ virtual void pushBack(const T &V) noexcept = 0;
+
+ /// Replaces the most recent entry in the history.
///
- /// \param V value to push into the buffer
- void pushBack(const T &V) noexcept {
- // Store value to the first empty slot and step Space index.
- (*this)[Space] = V;
- Space = (Space + 1) % max_size();
- if (Data == Space) {
- // Buffer was full, step Data index.
- Data = (Data + 1) % max_size();
- }
- }
+ /// \param V value to replace the most current value with
+ virtual void replaceFront(const T &V) noexcept = 0;
public:
/// Adds a new entry to \p this object and tells if the operation was
/// successful.
///
/// \note Success of the operation depends on the actual policy.
///
/// \param V value to store
///
/// \return if \p V was successfully stored
bool addEntry(const T &V) noexcept {
switch (P) {
default:
ROSA_CRITICAL("unkown HistoryPolicy");
+ case HistoryPolicy::LIFO:
+ if (full()) {
+ replaceFront(V);
+ return true;
+ }
case HistoryPolicy::SRWF:
if (full()) {
return false;
}
// \note Fall through to FIFO which unconditionally pushes the new entry.
case HistoryPolicy::FIFO:
// FIFO and SRWF not full.
pushBack(V);
return true;
}
}
/// Tells the trend set by the entries recorded by \p this object.
///
/// The number of steps to go back when calculating the trend is defined as
/// argument to the function.
///
/// \note The number of steps that can be made is limited by the number of
/// entries recorded by \p this object.
///
/// \note The function is made a template only to be able to use
/// \c std::enable_if.
///
/// \tparam X always use the default!
///
/// \param D number of steps to go back in *history*
///
/// \return trend set by analyzed entries
///
/// \pre Statically, \p this object stores signed arithmetic values:\code
/// std::is_arithmetic<T>::value && std::is_signed<T>::value
/// \endcode Dynamically, \p D is a valid number of steps to take:\code
- /// 0 <= D && D < N
+ /// 0 <= D && D < lengthOfHistory()
/// \endcode
template <typename X = T>
typename std::enable_if<
std::is_arithmetic<X>::value && std::is_signed<X>::value, X>::type
- trend(const size_t D = N - 1) const noexcept {
+ trend(const size_t D) const noexcept {
STATIC_ASSERT((std::is_same<X, T>::value), "not default template arg");
- ASSERT(0 <= D && D < N); // Boundary check.
+ ASSERT(0 <= D && D < maxLength()); // Boundary check.
if (numberOfEntries() < 2 || D < 1) {
// No entries for computing trend.
return {}; // Zero element of \p T
} else {
// Here at least two entries.
// \c S is the number of steps that can be done.
const size_t S = std::min(numberOfEntries() - 1, D);
size_t I = S;
// Compute trend with linear regression.
size_t SumIndices = 0;
T SumEntries = {};
T SumSquareEntries = {};
T SumProduct = {};
while (I > 0) {
// \note Indexing for the regression starts in the past.
const size_t Index = S - I;
const T Entry = entry(--I);
SumIndices += Index;
SumEntries += Entry;
SumSquareEntries += Entry * Entry;
SumProduct += Entry * Index;
}
return (SumProduct * S - SumEntries * SumIndices) /
(SumSquareEntries * S - SumEntries * SumEntries);
}
}
/// Tells the average absolute difference between consecutive entries recorded
/// by \p this object
/// The number of steps to go back when calculating the average is defined as
/// argument to the function.
///
/// \note The number of steps that can be made is limited by the number of
/// entries recorded by \p this object.
///
/// \note The function is made a template only to be able to use
/// \c std::enable_if.
///
/// \tparam X always use the default!
///
/// \param D number of steps to go back in *history*
///
/// \pre Statically, \p this object stores arithmetic values:\code
/// std::is_arithmetic<T>::value
/// \endcode Dynamically, \p D is a valid number of steps to take:\code
- /// 0 <= D && D < N
+ /// 0 <= D && D < lengthOfHistory()
/// \endcode
template <typename X = T>
typename std::enable_if<std::is_arithmetic<X>::value, size_t>::type
- averageAbsDiff(const size_t D = N - 1) const noexcept {
+ averageAbsDiff(const size_t D) const noexcept {
STATIC_ASSERT((std::is_same<X, T>::value), "not default template arg");
- ASSERT(0 <= D && D < N); // Boundary check.
+ ASSERT(0 <= D && D < maxLength()); // Boundary check.
if (numberOfEntries() < 2 || D < 1) {
// No difference to average.
return {}; // Zero element of \p T
} else {
// Here at least two entries.
// \c S is the number of steps that can be done.
const size_t S = std::min(numberOfEntries() - 1, D);
// Sum up differences as non-negative values only, hence using an
// unsigned variable for that.
size_t Diffs = {}; // Init to zero.
// Count down entry indices and sum up all the absolute differences.
size_t I = S;
T Last = entry(I);
while (I > 0) {
T Next = entry(--I);
Diffs += Last < Next ? Next - Last : Last - Next;
Last = Next;
}
// Return the average of the summed differences.
return Diffs / S;
}
}
+
+ /// Tells the average of all entries recorded by \p this object
+ ///
+ /// \tparam R type of the result
+ template <typename R> R average() const noexcept {
+ R Average = 0;
+ for (size_t I = 0; I < numberOfEntries(); I++) {
+ Average += entry(I);
+ }
+ Average /= numberOfEntries();
+ return Average;
+ }
+};
+
+/// Implements *history* by recording and storing values.
+/// The length of the underlying std::array is static and must be set at
+/// compile-time
+///
+/// \note Not thread-safe implementation, which should not be a problem as any
+/// instance of \c rosa::agent::Functionality is an internal component of a
+/// \c rosa::Agent, which is the basic unit of concurrency.
+///
+/// \tparam T type of values to store
+/// \tparam N number of values to store at most
+/// \tparam P retention policy to follow when capacity is reached
+///
+/// \invariant The size of the underlying \c std::array is `N + 1`:\code
+/// max_size() == N + 1 && N == max_size() - 1
+/// \endcode
+template <typename T, size_t N, HistoryPolicy P>
+class StaticLengthHistory : public History<T, P>, private std::array<T, N + 1> {
+
+ // Bring into scope inherited functions that are used.
+ using std::array<T, N + 1>::max_size;
+ using std::array<T, N + 1>::operator[];
+
+ /// The index of the first data element in the circular buffer.
+ size_t Data;
+
+ /// The index of the first empty slot in the circular buffer.
+ size_t Space;
+
+public:
+ using History<T, P>::policy;
+ using History<T, P>::empty;
+ using History<T, P>::full;
+ using History<T, P>::addEntry;
+ using History<T, P>::trend;
+ using History<T, P>::averageAbsDiff;
+
+ /// Creates an instances by initializing the indices for the circular buffer.
+ StaticLengthHistory(void) noexcept : Data(0), Space(0) {}
+
+ /// Destroys \p this object.
+ ~StaticLengthHistory(void) override = default;
+
+ /// Tells how many entries may be recorded by \c this object.
+ ///
+ /// \note The number of entries that are actually recorded may be smaller.
+ ///
+ /// \return \c rosa::agent::History::N
+ size_t maxLength(void) const noexcept override { return N; }
+
+ /// Tells how many entries are currently recorded by \p this object.
+ ///
+ /// \return number of entries currently recorded by \p this object.
+ ///
+ /// \post The returned value cannot be larger than the capacity of \p this
+ /// object:\code
+ /// 0 <= numberOfEntries() && numberOfEntries <= lengthOfHistory()
+ /// \endcode
+ size_t numberOfEntries(void) const noexcept override {
+ return Data <= Space ? Space - Data : max_size() - Data + Space;
+ }
+
+ /// Gives a constant lvalue reference to an entry stored in \p this object.
+ ///
+ /// \note The recorded entries are indexed starting from the latest one.
+ ///
+ /// \param I the index at which the stored entry to take from
+ ///
+ /// \pre \p I is a valid index:\code
+ /// 0 <= I && I < numberOfEntries()
+ /// \endcode
+ const T &entry(const size_t I = 0) const noexcept override {
+ ASSERT(0 <= I && I < numberOfEntries()); // Boundary check.
+ // Position counted back from the last recorded entry.
+ typename std::make_signed<const size_t>::type Pos = Space - (1 + I);
+ // Actual index wrapped around to the end of the buffer if negative.
+ return (*this)[Pos >= 0 ? Pos : max_size() + Pos];
+ }
+
+ /// Removes all entries recorded in \p this object.
+ void clear() noexcept override {
+ Data = 0;
+ Space = 0;
+ }
+
+private:
+ /// Pushes a new entry into the circular buffer.
+ ///
+ /// \note The earliest entry gets overwritten if the buffer is full.
+ ///
+ /// \param V value to push into the buffer
+ void pushBack(const T &V) noexcept override {
+ // Store value to the first empty slot and step Space index.
+ (*this)[Space] = V;
+ Space = (Space + 1) % max_size();
+ if (Data == Space) {
+ // Buffer was full, step Data index.
+ Data = (Data + 1) % max_size();
+ }
+ }
+
+ /// Replaces the most recent entry in the history.
+ ///
+ /// \param V value to replace the most current value with
+ void replaceFront(const T &V) noexcept override {
+ (*this)[(Space - 1) % max_size()] = V;
+ }
};
/// Adds a new entry to a \c rosa::agent::History instance.
///
/// \note The result of \c rosa::agent::History::addEntry is ignored.
///
/// \tparam T type of values stored in \p H
/// \tparam N number of values \p H is able to store
/// \tparam P retention policy followed by \p H when capacity is reached
///
/// \param H to add a new entry to
/// \param V value to add to \p H
///
/// \return \p H after adding \p V to it
template <typename T, size_t N, HistoryPolicy P>
-History<T, N, P> &operator<<(History<T, N, P> &H, const T &V) noexcept {
+StaticLengthHistory<T, N, P> &operator<<(StaticLengthHistory<T, N, P> &H,
+ const T &V) noexcept {
H.addEntry(V);
return H;
}
+/// Implements *DynamicLengthHistory* by recording and storing values.
+///
+/// \note Not thread-safe implementation, which should not be a problem as any
+/// instance of \c rosa::agent::Functionality is an internal component of a
+/// \c rosa::Agent, which is the basic unit of concurrency.
+///
+/// \tparam T type of values to store
+/// \tparam P retention policy to follow when capacity is reached
+template <typename T, HistoryPolicy P>
+class DynamicLengthHistory : public History<T, P>, private std::vector<T> {
+
+ // Bring into scope inherited functions that are used.
+ using std::vector<T>::erase;
+ using std::vector<T>::begin;
+ using std::vector<T>::end;
+ using std::vector<T>::size;
+ using std::vector<T>::max_size;
+ using std::vector<T>::resize;
+ using std::vector<T>::push_back;
+ using std::vector<T>::pop_back;
+ using std::vector<T>::operator[];
+
+ /// The current length of the DynamicLengthHistory.
+ size_t Length;
+
+public:
+ using History<T, P>::policy;
+ using History<T, P>::empty;
+ using History<T, P>::full;
+ using History<T, P>::addEntry;
+ using History<T, P>::trend;
+ using History<T, P>::averageAbsDiff;
+
+ /// Creates an instances by setting an initial length
+ DynamicLengthHistory(size_t Length) noexcept : Length(Length) {
+ this->resize(Length);
+ }
+
+ /// Destroys \p this object.
+ ~DynamicLengthHistory(void) override = default;
+
+ /// Tells how many entries may be recorded by \c this object.
+ ///
+ /// \note The number of entries that are actually recorded may be smaller.
+ ///
+ /// \return \c rosa::agent::DynamicLengthHistory::N
+ size_t maxLength(void) const noexcept override { return Length; }
+
+ /// Tells how many entries are currently recorded by \p this object.
+ ///
+ /// \return number of entries currently recorded by \p this object.
+ ///
+ /// \post The returned value cannot be larger than the capacity of \p this
+ /// object:\code
+ /// 0 <= numberOfEntries() && numberOfEntries <=
+ /// lengthOfHistory() \endcode
+ size_t numberOfEntries(void) const noexcept { return size(); }
+
+ /// Gives a constant lvalue reference to an entry stored in \p this object.
+ ///
+ /// \note The recorded entries are indexed starting from the latest one.
+ ///
+ /// \param I the index at which the stored entry to take from
+ ///
+ /// \pre \p I is a valid index:\code
+ /// 0 <= I && I < numberOfEntries()
+ /// \endcode
+ const T &entry(const size_t I = 0) const noexcept override {
+ ASSERT(0 <= I && I < numberOfEntries()); // Boundary check.
+ return this->operator[](size() - I - 1);
+ }
+
+ /// Removes all entries recorded in \p this object.
+ void clear() noexcept override { erase(begin(), end()); }
+
+private:
+ /// Pushes a new entry into the circular buffer.
+ ///
+ /// \note The earliest entry gets overwritten if the buffer is full.
+ ///
+ /// \param V value to push into the buffer
+ void pushBack(const T &V) noexcept override {
+ if (full()) {
+ erase(begin());
+ }
+ push_back(V);
+ }
+
+ /// Replaces the most recent entry in the history.
+ ///
+ /// \param V value to replace the most current value with
+ void replaceFront(const T &V) noexcept override {
+ (void)pop_back();
+ push_back(V);
+ }
+
+public:
+ /// Resizes the History length. If the new length is smaller than the number
+ /// of currently stored values, values are deleted according to the
+ /// HistoryPolicy.
+ ///
+ /// @param NewLength The new Length of the History.
+ void setLength(size_t NewLength) noexcept {
+ Length = NewLength;
+ if (NewLength < numberOfEntries()) {
+ switch (P) {
+ default:
+ ROSA_CRITICAL("unkown HistoryPolicy");
+ case HistoryPolicy::LIFO:
+ case HistoryPolicy::SRWF:
+ // Delete last numberOfEntries() - NewLength items from the back
+ erase(begin() + NewLength, end());
+ break;
+ case HistoryPolicy::FIFO:
+ // Delete last numberOfEntries() - NewLength items from the front
+ erase(begin(), begin() + (numberOfEntries() - NewLength));
+ break;
+ }
+ }
+ this->resize(Length);
+ }
+};
+
+/// Adds a new entry to a \c rosa::agent::DynamicLengthHistory instance.
+///
+/// \note The result of \c rosa::agent::DynamicLengthHistory::addEntry is
+/// ignored.
+///
+/// \tparam T type of values stored in \p H
+/// \tparam P retention policy followed by \p H when capacity is reached
+///
+/// \param H to add a new entry to
+/// \param V value to add to \p H
+///
+/// \return \p H after adding \p V to it
+template <typename T, HistoryPolicy P>
+DynamicLengthHistory<T, P> &operator<<(DynamicLengthHistory<T, P> &H,
+ const T &V) noexcept {
+ H.addEntry(V);
+ return H;
+}
} // End namespace agent
} // End namespace rosa
#endif // ROSA_AGENT_HISTORY_HPP
diff --git a/include/rosa/agent/RangeConfidence.hpp b/include/rosa/agent/RangeConfidence.hpp
index 0d8732e..60a53c1 100644
--- a/include/rosa/agent/RangeConfidence.hpp
+++ b/include/rosa/agent/RangeConfidence.hpp
@@ -1,109 +1,108 @@
//===-- rosa/agent/RangeConfidence.hpp --------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/agent/RangeConfidence.hpp
///
/// \author Benedikt Tutzer (benedikt.tutzer@tuwien.ac.at)
///
/// \date 2019
///
/// \brief Definition of *RangeConfidence* *functionality*.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_AGENT_RANGECONFIDENCE_HPP
#define ROSA_AGENT_RANGECONFIDENCE_HPP
-#include "rosa/agent/Functionality.h"
#include "rosa/agent/Abstraction.hpp"
#include "rosa/agent/FunctionAbstractions.hpp"
+#include "rosa/agent/Functionality.h"
#include "rosa/support/debug.hpp"
#include <algorithm>
-#include <vector>
#include <cmath>
#include <memory>
+#include <vector>
namespace rosa {
namespace agent {
/// Evaluates a map of ID's to Abstractions at a given value and returns the
/// results as a map from ID's to results of the corresponding Abstraction
///
/// \note This implementation is supposed to be used to abstract ranges of
/// arithmetic types into maps whose values are of another arithmetic type,
/// which is statically enforced.
///
/// \tparam D type to abstract from
/// \tparam I type the type of the ID's
/// \tparam R type of the range
template <typename D, typename I, typename R>
class RangeConfidence : protected Abstraction<D, std::map<I, R>>,
- private std::map<I, PartialFunction<D, R>>{
+ private std::map<I, PartialFunction<D, R>> {
// Make sure the actual type arguments are matching our expectations.
STATIC_ASSERT((std::is_arithmetic<D>::value), "abstracting not arithmetic");
STATIC_ASSERT((std::is_arithmetic<R>::value),
- "abstracting not to arithmetic");
+ "abstracting not to arithmetic");
private:
/// Wether to include default results in the result-map or not
bool IgnoreDefaults;
public:
/// Creates an instance by Initializing the underlying \c Abstraction and
/// \c std::map.
///
/// \param Abstractions the Abstractions to be evaluated
/// \param IgnoreDefaults wether to include default results in the result-map
/// or not (defaults to false).
RangeConfidence(const std::map<I, PartialFunction<D, R>> &Abstractions,
- bool IgnoreDefaults = false)
- : Abstraction<D, std::map<I, R>>({}),
- std::map<I, PartialFunction<D, R>>(Abstractions),
- IgnoreDefaults(IgnoreDefaults){
- }
+ bool IgnoreDefaults = false)
+ : Abstraction<D, std::map<I, R>>({}), std::map<I, PartialFunction<D, R>>(
+ Abstractions),
+ IgnoreDefaults(IgnoreDefaults) {}
/// Destroys \p this object.
~RangeConfidence(void) = default;
/// Checks wether all Abstractions evaluate to default at the given position
///
/// \param V the value at which to check if the functions falls back to it's
/// default value.
///
/// \return true, if all Abstractions evaluate to default
bool isDefaultAt(const D &V) const noexcept override {
- for (auto const& p : ((std::map<I, PartialFunction<D, R>>)*this)){
- if(!p.second.isDefaultAt(V))
- return false;
- }
+ for (auto const &P : ((std::map<I, PartialFunction<D, R>>)*this)) {
+ if (!P.second.isDefaultAt(V))
+ return false;
+ }
return true;
}
/// All Abstractions stored in the underlying \c std::map are evaluated for
/// the given value. Their results are stored in another map, with
/// corresponding keys.
/// If IgnoreDefaults is set, Abstractions that default for that value are not
/// evaluated and inserted into the resulting \c std::map
///
/// \param V value to abstract
///
/// \return a \c std::map containing the results of the stored Abstractions,
/// indexable by the key's the Abstractions are associated with
std::map<I, R> operator()(const D &V) const noexcept override {
- std::map<I, R> ret;
- for (auto const& p : ((std::map<I, PartialFunction<D, R>>)*this)){
- if(!IgnoreDefaults || !p.second.isDefaultAt(V))
- ret.insert(std::pair<I, R>(p.first, p.second(V)));
+ std::map<I, R> Ret;
+ for (auto const &P : ((std::map<I, PartialFunction<D, R>>)*this)) {
+ if (!IgnoreDefaults || !P.second.isDefaultAt(V))
+ Ret.insert(std::pair<I, R>(P.first, P.second(V)));
}
- return ret;
+ return Ret;
}
};
} // End namespace agent
} // End namespace rosa
#endif // ROSA_AGENT_RANGECONFIDENCE_HPP
diff --git a/include/rosa/core/Invoker.hpp b/include/rosa/core/Invoker.hpp
index 365e133..10786aa 100644
--- a/include/rosa/core/Invoker.hpp
+++ b/include/rosa/core/Invoker.hpp
@@ -1,257 +1,258 @@
//===-- 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 <functional>
#include <memory>
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<const Invoker>;
/// 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 <typename T, typename... Ts>
static invoker_t wrap(std::function<void(T, Ts...)> &&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 <typename... Ts> using F = std::function<void(Ts...)>;
/// 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 <typename C, typename... Ts>
static inline F<Ts...> 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<decltype(*this)>::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 <typename Fun> 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<void(void)>.
template <typename T, typename... Ts>
class InvokerImpl<std::function<void(T, Ts...)>> final
: public Invoker {
/// Type alias for the stored function.
using function_t = std::function<void(T, Ts...)>;
/// Type alias for correctly typed argument-tuples as obtained from
/// \c rosa::Message.
using args_t = std::tuple<const T &, const Ts &...>;
/// Alias for \c rosa::MessageMatcher for the arguments of the stored
/// function.
using Matcher = MsgMatcher<T, Ts...>;
/// 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<args_t>::value
/// \endcode
template <size_t... S>
inline void invokeFunction(Seq<S...>, 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(seq_t<sizeof...(Ts) + 1>(), Matcher::extractedValues(Msg));
return result_t::Invoked;
} else {
LOG_TRACE("Tried to invoke with non-matching arguments");
return result_t::NoMatch;
}
}
};
template <typename T, typename... Ts>
template <size_t... S>
void InvokerImpl<std::function<void(T, Ts...)>>::invokeFunction(
Seq<S...>, const args_t &Args) const noexcept {
- ASSERT(sizeof...(S) == std::tuple_size<args_t>::value); // Sanity check.
+ STATIC_ASSERT(sizeof...(S) == std::tuple_size<args_t>::value,
+ "wrong number of type parameters");
F(std::get<S>(Args)...);
}
///@}
} // End namespace
template <typename T, typename... Ts>
Invoker::invoker_t
Invoker::wrap(std::function<void(T, Ts...)> &&F) noexcept {
return std::unique_ptr<Invoker>(
new InvokerImpl<std::function<void(T, Ts...)>>(std::move(F)));
}
template <typename C, typename... Ts>
Invoker::F<Ts...> 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/core/SystemBase.hpp b/include/rosa/core/SystemBase.hpp
index a5ca0d2..4b6720e 100644
--- a/include/rosa/core/SystemBase.hpp
+++ b/include/rosa/core/SystemBase.hpp
@@ -1,138 +1,138 @@
//===-- rosa/core/SystemBase.hpp -----------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/core/SystemBase.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Base implementation of the \c rosa::System interface.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_CORE_SYSTEMBASE_HPP
#define ROSA_CORE_SYSTEMBASE_HPP
#include "rosa/core/System.hpp"
#include <atomic>
namespace rosa {
/// Base implementation of the \c rosa::System interface.
///
/// This implementation provides only equality checking and *name* for
/// \c rosa::System, identifiers for \c rosa::Unit instances, and marking the
/// \c rosa::System cleaned for destruction.
///
/// \note Actual implementations of \c rosa::System and derived interfaces are
/// supposed to inherit from this implementation.
class SystemBase : public System {
protected:
/// Creates an instance.
///
/// \note Protected constructor restrict instantiation for subclasses.
///
/// \param Name name of the new instance
SystemBase(const std::string &Name) noexcept;
public:
/// Destroys \p this object.
///
/// \pre \p this object is marked cleaned:\code
/// isSystemCleaned()
/// \endcode
~SystemBase(void);
/// Tells whether \p this object is the same as \p Other.
///
/// Two \c rosa::System instances are considered equal if they share a common
/// \c rosa::SystemBase::Name member field. That should do among various
/// subclasses.
///
/// \param Other another \c rosa::System instance to compare to
///
/// \return whether \p this object and \p Other is the same
bool operator==(const System &Other) const noexcept override;
protected:
/// The textual name of \p this object implementing \c rosa::System.
const std::string Name;
private:
/// Number of \c rosa::Unit instances constructed by \p this object.
///
/// \note Should never be decremented!
std::atomic<size_t> UnitCount;
/// Indicates that \p this object has been cleaned and is ready for
/// destruction.
///
/// The field is initialized as \c false and can be set by
/// \c rosa::SystemBase::markCleaned.
///
/// \note Subclasses must set the flag upon destructing their instances, which
/// indicates to the destructor of the base-class that all the managed
/// resources has been properly released.
std::atomic<bool> SystemIsCleaned;
public:
/// Tells the name of \p this object
///
/// \note The returned reference remains valid as long as \p this object is
/// not destroyed.
///
/// \return reference to \c rosa::SystemBase::Name
const std::string &name(void) const noexcept override;
protected:
/// Tells the next unique identifier to be used for a newly created
/// \c rosa::Unit.
///
/// The functions takes the current value of the internal counter
/// \c rosa::SystemBase::UnitCount and then increments it.
///
/// \note This is the only function modifying
/// \c rosa::SystemBase::UnitCount.
///
/// \return \c rosa::id_t which is unique within the context of \p this
/// object.
id_t nextId(void) noexcept override;
/// Tells if \p this object has been marked cleaned and is ready for
/// destruction.
///
/// \return if \p this object is marked clean.
bool isSystemCleaned(void) const noexcept override;
/// Marks \p this object cleaned by setting
/// \c rosa::SystemBase::SystemIsCleaned.
///
/// \note Can be called only once when the System does not have any live
/// \c rosa::Unit instances.
///
/// \pre \p this object has not yet been marked as cleaned and it has no
/// \c rosa::Unit instances registered:\code
/// !isSystemCleaned() && empty()
/// \endcode
///
/// \post \p this object is marked cleaned:\code
/// isSystemCleaned()
/// \endcode
void markCleaned(void) noexcept override;
/// Tells the number of \c rosa::Unit instances constructed in the context of
/// \p this object so far, including those being already destroyed.
///
/// \return current value of \c rosa::SystemBase::UnitCount that is the number
/// of \c rosa::Unit instances created so far
size_t numberOfConstructedUnits(void) const noexcept override;
};
} // End namespace rosa
-#endif // ROSA_LIB_CORE_SYSTEMBASE_HPP
+#endif // ROSA_CORE_SYSTEMBASE_HPP
diff --git a/include/rosa/deluxe/DeluxeAgent.hpp b/include/rosa/deluxe/DeluxeAgent.hpp
index a75d727..7a94a57 100644
--- a/include/rosa/deluxe/DeluxeAgent.hpp
+++ b/include/rosa/deluxe/DeluxeAgent.hpp
@@ -1,1478 +1,1479 @@
//===-- 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 "rosa/deluxe/DeluxeTuple.hpp"
#include <map>
/// Local helper macros to deal with built-in types.
///
///@{
/// Creates function name for member functions in \c rosa::deluxe::DeluxeAgent.
///
/// \param N name suffix to use
#define DASLAVEHANDLERNAME(N) handleSlave_##N
/// Creates function name for member functions in \c rosa::deluxe::DeluxeAgent.
///
/// \param N name suffix to use
#define DAMASTERHANDLERNAME(N) handleMaster_##N
/// Defines member functions for handling messages from *slaves* in
/// \c rosa::deluxe::DeluxeAgent.
///
/// \see \c DeluxeAgentInputHandlers
///
/// \note No pre- and post-conditions are validated directly by these functions,
/// they rather rely on \c rosa::deluxe::DeluxeAgent::saveInput to do that.
///
/// \param T the type of input to handle
/// \param N name suffix for the function identifier
#define DASLAVEHANDLERDEFN(T, N) \
void DASLAVEHANDLERNAME(N)(atoms::Slave, id_t SlaveId, token_size_t Pos, \
T Value) noexcept { \
saveInput(SlaveId, Pos, Value); \
}
/// Defines member functions for handling messages from *master* in
/// \c rosa::deluxe::DeluxeAgent.
///
/// \see \c DeluxeAgentMasterInputHandlers
///
/// \note No pre- and post-conditions are validated directly by these functions,
/// they rather rely on \c rosa::deluxe::DeluxeAgent::saveMasterInput to do
/// that.
///
/// \param T the type of input to handle
/// \param N name suffix for the function identifier
#define DAMASTERHANDLERDEFN(T, N) \
void DAMASTERHANDLERNAME(N)(atoms::Master, id_t MasterId, token_size_t Pos, \
T Value) noexcept { \
saveMasterInput(MasterId, Pos, Value); \
}
/// Convenience macro for \c DASLAVEHANDLERDEFN with identical arguments.
///
/// \see \c DASLAVEHANDLERDEFN
///
/// This macro can be used instead of \c DASLAVEHANDLERDEFN if the actual value
/// of \p T can be used as a part of a valid identifier.
///
/// \param T the type of input to handle
#define DASLAVEHANDLERDEF(T) DASLAVEHANDLERDEFN(T, T)
/// Convenience macro for \c DAMASTERHANDLERDEFN with identical arguments.
///
/// \see \c DAMASTERHANDLERDEFN
///
/// This macro can be used instead of \c DAMASTERHANDLERDEFN if the actual value
/// of \p T can be used as a part of a valid identifier.
///
/// \param T the type of input to handle
#define DAMASTERHANDLERDEF(T) DAMASTERHANDLERDEFN(T, T)
/// Results in a \c THISMEMBER reference to a member function defined by
/// \c DASLAVEHANDLERDEFN.
///
/// Used in the constructor of \c rosa::deluxe::DeluxeAgent to initialize super
/// class \c rosa::Agent with member function defined by \c DASLAVEHANDLERDEFN.
///
/// \see \c DASLAVEHANDLERDEFN, \c THISMEMBER
///
/// \param N name suffix for the function identifier
#define DASLAVEHANDLERREF(N) THISMEMBER(DASLAVEHANDLERNAME(N))
/// Results in a \c THISMEMBER reference to a member function defined by
/// \c DAMASTERHANDLERDEFN.
///
/// Used in the constructor of \c rosa::deluxe::DeluxeAgent to initialize super
/// class \c rosa::Agent with member function defined by \c DAMASTERHANDLERDEFN.
///
/// \see \c DAMASTERHANDLERDEFN, \c THISMEMBER
///
/// \param N name suffix for the function identifier
#define DAMASTERHANDLERREF(N) THISMEMBER(DAMASTERHANDLERNAME(N))
///@}
namespace rosa {
namespace deluxe {
/// Specialization of \c rosa::Agent for *agent* role of the *deluxe interface*.
///
/// \see \c rosa::deluxe::DeluxeContext
///
/// \invariant 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. \c rosa::deluxe::DeluxeAgent::NumberOfMasterOutputs matches
/// \c rosa::deluxe::DeluxeAgent::NumberOfInputs. All master-output-related
/// container objects have a size matching \c
/// rosa::deluxe::DeluxeAgent::NumberOfMasterOutputs. Types and type-related
/// information of input and master-output values are consistent throughout all
/// the input-related and master-output-related containers, respectively. The
/// actual values in \c rosa::deluxe::DeluxeAgent::InputNextPos and \c
/// rosa::deluxe::DeluxeAgent::MasterInputNextPos are valid with respect to the
/// corresponding types. No *slave* is registered at more than one input
/// position. *Slave* registrations and corresponding reverse lookup
/// information are consistent.
///
/// \see Definition of \c rosa::deluxe::DeluxeAgent::inv on the class invariant
///
/// \note All member functions validate the class invariant as part of their
/// precondition. Moreover, non-const functions validate the invariant before
/// return as their postcondition.
class DeluxeAgent : public Agent {
/// Checks whether \p this object holds the class invariant.
///
/// \see Invariant of the class \c rosa::deluxe::DeluxeAgent
///
/// \return if \p this object holds the class invariant
bool inv(void) const noexcept;
/// The \c rosa::deluxe::DeluxeExecutionPolicy that controls the execution of
/// \c this object.
std::unique_ptr<DeluxeExecutionPolicy> 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::deluxe::DeluxeTUple.
///
/// \see \c rosa::deluxe::DeluxeAgent::master
const Token OutputType;
/// Number of inputs processed by \p this object.
const size_t NumberOfInputs;
/// The type of values \p this object processes from its *master*.
///
/// That is the types of values \p this object receives from its *master* in a
/// \c rosa::deluxe::DeluxeTuple.
///
/// \see \c rosa::deluxe::DeluxeAgent::master
const Token MasterInputType;
/// Number of outputs produces by \p this object for its *slaves*.
///
/// \note This values is equal to \c
/// rosa::deluxe::DeluxeAgent::NumberOfInputs.
///
/// \see \c rosa::deluxe::DeluxeAgent::slave.
const size_t NumberOfMasterOutputs;
private:
/// Types of input values produced by *slaves* of \p this object.
///
/// \note The \c rosa::Token values stored correspond to \c
/// rosa::deluxe::DeluxeTuple instances at each argument position. The \c
/// rosa::TypeNumber values from the stored \c rosa::Token values match the
/// corresponding values in \c rosa::deluxe::DeluxeAgent::InputValues in
/// order.
///
/// \note The position of a \c rosa::Token in the \c std::vector indicates
/// which argument of \p this object's processing function it belongs to. See
/// also \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
const std::vector<Token> InputTypes;
/// Indicates which element of an input is expected from any particular
/// *slave*.
///
/// The *slave* is supposed to send one \c rosa::deluxe::DeluxeTuple value
/// element by element in their order of definition. This member field tells
/// the element at which position in the tuple should be received next from
/// the *slave* at a given position.
///
/// \p this object is supposed to be triggered only when input values has been
/// received completely, that is all values in the field should hold the value
/// `0`.
///
/// \see \c rosa::deluxe::DeluxeAgent::handleTrigger
/// \c rosa::deluxe::DeluxeAgent::saveInput
std::vector<token_size_t> InputNextPos;
/// Indicates whether any particular input value has been changed since the
/// last trigger received from the system.
///
/// All the flags are reset to \c false upon handling a trigger and then set
/// to \c true by \c rosa::deluxe::DeluxeAgent::saveInput when storing a new
/// input value in \c rosa::deluxe::DeluxeAgent::InputValues.
///
/// \note The position of a flag in the \c std::vector indicates which
/// argument of \p this object's processing function it belongs to. See also
/// \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
std::vector<bool> InputChanged;
/// Tells at which position in \c rosa::deluxe::DeluxeAgent::InputValues the
/// input from any particular *slave* starts.
///
/// \note A value in the vector corresponds to the *slave* at the same
/// position and it is the sum of the elements of input values from *slaves*
/// at previous positions.
///
/// \see \c rosa::deluxe::DeluxeAgent::saveInput
const std::vector<token_size_t> InputStorageOffsets;
/// Stores the actual input values.
///
/// \note The types of stored values match the corresponding
/// \c rosa::TypeNumber values (in \c rosa::Token in order) in \c
/// rosa::deluxe::DeluxeAgent::InputTypes.
///
/// \note The position of a value in the \c rosa::AbstractTokenizedStorage
/// indicates which element of the tuple of which argument of \p this object's
/// processing function it is. See also \c
/// rosa::deluxe::DeluxeAgent::DeluxeAgent.
const std::unique_ptr<AbstractTokenizedStorage> InputValues;
/// Indicates which element of the master-input is expected from the *master*.
///
/// The *master* is supposed to send one \c rosa::deluxe::DeluxeTuple value
/// element by element in their order of definition. This member field tells
/// the element at which position should be received next.
///
/// \p this object is supposed to be triggered only when a complete
/// master-input has been received, that is the field should hold the value
/// `0`.
///
/// \see \c rosa::deluxe::DeluxeAgent::handleTrigger
/// \c rosa::deluxe::DeluxeAgent::saveMasterInput
token_size_t MasterInputNextPos;
/// Indicates whether the input value from the *master* has been changed since
/// the last trigger received from the system.
///
/// The flag is reset to \c false upon handling a trigger and then set to \c
/// true by \c rosa::deluxe::DeluxeAgent::saveMasterInput when storig a new
/// input value in \c rosa::deluxe::DeluxeAgent::MasterInputValue.
bool MasterInputChanged;
/// Stores the actual input value from *master*.
///
/// \note The type of the stored value matches the types indicated by \c
/// rosa::deluxe::DeluxeAgent::MasterInputType.
const std::unique_ptr<AbstractTokenizedStorage> MasterInputValue;
/// Types of output values produced by \p this object for its *slaves*.
///
/// That is the types of values \p this object sends to its *slaves* in a \c
/// rosa::deluxe::DeluxeTuple.
///
/// \note The position of a type in the \c std::vector indicates which
/// *slave* of \p this object the type belongs to. See also
/// \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
const std::vector<Token> MasterOutputTypes;
/// Alias for function objects used as trigger handler for
/// \c rosa::deluxe::DeluxeAgent.
///
/// \note The function used for \c H is to be \c noexcept.
///
/// \see \c rosa::deluxe::DeluxeAgent::FP
using H = std::function<void(void)>;
/// Handles trigger from the system.
///
/// The actual functions processing *slave* and *master* inputs and generating
/// optional output to *master* and *slaves* are captured in a lambda
/// expression that is in turn wrapped in a \c std::function object. The
/// lambda expression calls the master-input processing function with the
/// actual master-input data and sends its result -- if any -- to *slaves* by
/// calling \c rosa::deluxe::DeluxeAgent::handleMasterOutputs; then calls the
/// input processing function with the actual input data and sends its result
/// -- if any -- to *master* by calling \c
/// rosa::deluxe::DeluxeAgent::sendToMaster and *slaves* by calling \c
/// rosa::deluxe::DeluxeAgent::handleMasterOutputs. Also, all the flags stored
/// in \c rosa::deluxe::DeluxeAgent::InputChanged and \c
/// rosa::deluxe::DeluxeAgent::MasterInputChanged are reset when the current
/// values are processed. The function \c
/// rosa::deluxe::DeluxeAgent::handleTrigger needs only to call the
/// function object.
///
/// \see \c
/// rosa::deluxe::DeluxeAgent::triggerHandlerFromProcessingFunctions
const H FP;
/// The *master* to send values to.
///
/// \note *Masters* are set dynamically, hence it is possible that a
/// \c rosa::deluxe::DeluxeAgent instance does not have any *master* at a
/// given moment.
Optional<AgentHandle> Master;
/// The *slaves* sending input to \p this object.
///
/// \note The position of a *slave* in the \c std::vector indicates which
/// argument of \p this object's processing function it belongs to. See also
/// \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
///
/// \note *Slaves* are set dynamically, hence it is possible that a
/// \c rosa::deluxe::DeluxeAgent instance does have input positions without
/// any *slave* associated to them.
///
/// \note Reverse lookup information is maintained in
/// \c rosa::deluxe::DeluxeAgent::SlaveIds, which is to be kept in sync with
/// the *slaves* stored here.
std::vector<Optional<AgentHandle>> Slaves;
/// Associates \c rosa::id_t values to corresponding indices of registered
/// *slaves*.
///
/// \see \c rosa::deluxe::DeluxeAgent::Slaves
std::map<id_t, size_t> SlaveIds;
/// Tells the unique identifier of the *master* of \p this object, if any
/// registered.
///
/// \return the unique identifier of the *master*
///
/// \pre A *master* is registered for \p this object: \code
/// Master
/// \endcode
id_t masterId(void) const noexcept;
/// Tells whether types stored in \c rosa::TypeList \p As match the input
/// types of \p this object.
///
/// \tparam As \c rosa::TypeList containing types to match against values in
/// \c rosa::deluxe::DeluxeAgent::InputTypes
///
/// \note Instatiation of the template fails if \p As is not \c
/// rosa::TypeList.
///
/// \return if types in \p As are instances of \c rosa::deluxe::DeluxeTuple
/// and their types match \c rosa::Token values stored in \c
/// rosa::deluxe::DeluxeAgent::InputTypes
template <typename As> bool inputTypesMatch(void) const noexcept;
/// Tells whether types stored in \c rosa::TypeList \p Ts match the
/// master-output types of \p this object.
///
/// \tparam Ts \c rosa::TypeList containing types to match against values in
/// \c rosa::deluxe::DeluxeAgent::MasterOutputTypes
///
/// \note Instatiation of the template fails if \p As is not \c
/// rosa::TypeList.
///
/// \return if types in \p Ts match \c rosa::Token and in turn \c
/// rosa::TypeNumber values stored in \c
/// rosa::deluxe::DeluxeAgent::MasterOutputTypes
template <typename Ts> bool masterOutputTypesMatch(void) const noexcept;
/// Gives the current input value for slave position \p Pos.
///
/// \tparam Pos slave position to get input value for
/// \tparam Ts types of elements of the input value
/// \tparam S0 indices for accessing elements of the input value
///
/// \note The arguments provide types and indices statically as template
/// arguments \p Ts... \p S0..., respectively, so their actual values are
/// ignored.
///
/// \return current input value for slave position \p Pos
///
/// \pre Statically, the provided indices \p S0... match the length of \p
/// Ts...: \code
/// sizeof...(Ts) == sizeof...(S0)
/// \endcode Dynamically, \p Pos is a valid slave position and type arguments
/// \p Ts... match the corresponding input value: \code
/// Pos < NumberOfInputs && DeluxeTuple<Ts...>::TT == InputTypes[Pos]
/// \endcode
template <size_t Pos, typename... Ts, size_t... S0>
DeluxeTuple<Ts...> prepareInputValueAtPos(TypeList<Ts...>, Seq<S0...>) const
noexcept;
/// Gives an \c std::tuple containing the current input values and their
/// change flags so that they can be used for the processing function.
///
/// \tparam As types of the input values
/// \tparam S0 indices for accessing input values and their change flags
///
/// \note The only argument provides indices statically as template arguments
/// \p S0..., so its actual value is ignored.
///
/// \return current input values and their change flags prepared for invoking
/// the processing function with them
///
/// \pre Statically, all type arguments \p As... are instances of \c
/// rosa::deluxe::DeluxeTuple and the provided indices \p S0... match the
/// length of \p As...: \code
/// TypeListAllDeluxeTuple<TypeList<As...>>::Value &&
/// sizeof...(As) == sizeof...(S0)
/// \endcode Dynamically, type arguments \p As... match the input types of \p
/// this object: \code
/// inputTypesMatch<TypeList<As...>>()
/// \endcode
template <typename... As, size_t... S0>
std::tuple<std::pair<As, bool>...> prepareCurrentInputs(Seq<S0...>) const
noexcept;
/// Invokes a processing function matching the input, output, and
/// master-output types of \p this object with actual arguments provided in a
/// \c std::tuple.
///
/// \note \p Args providing the actual arguments for \p F is to be created by
/// \c rosa::deluxe::DeluxeAgent::prepareCurrentInputs.
///
/// \tparam T output type of the processing function
/// \tparam Ts types of master-output values of the processing function
/// \tparam As types of inputs for the processing function
/// \tparam S0 indices starting with `0` for extracting actual arguments from
/// \p Args
///
/// \param F the processing function to invoke
/// \param Args the actual arguments to invoke \p F with
///
/// \note The last argument provides indices statically as template arguments
/// \p S0..., so its actual value is ignored.
///
/// \return the result of \p F for actual arguments \p Args
///
/// \pre The provided sequence of indices \p S0... constitutes a proper
/// sequence for extracting all actual arguments for
/// \p F from \p Args: \code
/// sizeof...(As) == sizeof...(S0)
/// \endcode
template <typename T, typename... Ts, typename... As, size_t... S0>
static std::tuple<Optional<T>, Optional<Ts>...>
invokeWithTuple(std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)>
F,
const std::tuple<std::pair<As, bool>...> Args,
Seq<S0...>) noexcept;
/// Handles a master-output value for a particular *slave* position.
///
/// \p Value is a \c rosa::Optional resulted by a processing function and
/// contains a master-output value for the *slave* at position \p Pos. The
/// function takes the master-output value and sends its actual value, if any,
/// to the corresponding *slave*.
///
/// \note A master-output of type \c rosa::deluxe::EmptyDeluxeTuple indicates
/// no actual output and hence no message is generated for a position whose
/// corresponding master-output type is \c rosa::deluxe::EmptyDeluxeTuple.
///
/// \note The function provides position-based implementation for \c
/// rosa::deluxe::DeluxeAgent::handleMasterOutputs.
///
/// \tparam Pos the position of the master-output to send \p Value for
/// \tparam Ts types of elements in \p Value
///
/// \param Value \c rosa::deluxe::DeluxeTuple resulted by the processing
/// function for *slave* position \p Pos
///
/// \pre \p Pos is a valid master-output position and \p Value matches the
/// master-output type of \p this object at position \p Pos: \code
/// Pos < NumberOfMasterOutputs &&
/// DeluxeTuple<Ts...>::TT == MasterOutputTypes[Pos]
/// \endcode
template <size_t Pos, typename... Ts>
void
handleMasterOutputAtPos(const Optional<DeluxeTuple<Ts...>> &Value) noexcept;
/// Handles master-output values from \p Output.
///
/// \p Output is a \c std::tuple resulted by a processing function and
/// contains master-output values starting at position \p Offset. The function
/// takes master-output values and sends each actual value to the
/// corresponding *slave*.
///
/// \tparam Offset index of the first master-output value in \p Output
/// \tparam Ts output types stored in \p Output
/// \tparam S0 indices starting with `0` for extracting master-output values
/// from \p Output
///
/// \note Instantiation fails if any of the type arguments \p Ts... starting
/// at position \p Offset is not an instance of \c rosa::deluxe::DeluxeTuple
/// or the number of types \p Ts... is not consistent with the other template
/// arguments.
///
/// \param Output \c std::tuple resulted by a processing function
///
/// \pre Statically, type arguments \p Ts... starting at position \p Offset
/// are instances of \c rosa::deluxe::DeluxeTuple and the number of types \p
/// Ts... is consistent with the other template arguments: \code
/// TypeListAllDeluxeTuple<
/// typename TypeListDrop<Offset, TypeList<Ts...>>::Type>::Value &&
/// sizeof...(Ts) == Offset + sizeof...(S0)
/// \endcode Dynamically, \p Output matches the master-output types \p this
/// object was created with and the provided sequence of indices \p S0...
/// constitues a proper sequence for extracting all master-output values from
/// \p Output: \code
/// masterOutputTypesMatch<typename TypeListDrop<Offset,
/// TypeList<Ts...>>::Type>() &&
/// sizeof...(S0) == NumberOfMasterOutputs
/// \endcode
template <size_t Offset, typename... Ts, size_t... S0>
void handleMasterOutputs(const std::tuple<Optional<Ts>...> &Output,
Seq<S0...>) noexcept;
/// Wraps processing functions into a trigger handler.
///
/// \see \c rosa::deluxe::DeluxeAgent::FP
///
/// \note The function cannot be const qualified because the lambda
/// expression defined in it needs to capture \p this object by a non-const
/// reference
///
/// \tparam MTs types of elements of master-input processed by \p MF
/// \tparam T type of output
/// \tparam Ts types of master-output values
/// \tparam As types of input values
/// \tparam S0 indices for accessing master-input values
///
/// \note Instantiation fails if any of the type arguments \p T, \p Ts...,
/// and \p As... is not an instance of \c rosa::deluxe::DeluxeTuple.
///
/// \param MF function processing master-input and generating output
/// \param F function processing inputs and generating output
///
/// \note The last argument provides indices statically as template
/// arguments \p S0..., so its actual value is ignored.
///
/// \note A master-input type of \c rosa::deluxe::EmptyDeluxeTuple indicates
/// that \p this object does not receive master-input, \p MF is never called
/// if \p MTs is empty.
///
/// \return trigger handler function based on \p F and \p MF
///
/// \pre Statically, type arguments \p T, \p Ts..., and \p As... are
/// instances of \c rosa::deluxe::DeluxeTuple and the indices match
/// master-input elements: \code
/// TypeListAllDeluxeTuple<TypeList<T, Ts..., As...>>::Value &&
/// sizeof...(MTs) == sizeof...(S0)
/// \endcode Dynamically, template arguments \p MTs..., \p T, \p Ts..., and
/// \p As... match the corresponding types \p this object was created with:
/// \code
/// MasterInputType == DeluxeTuple<MTs...>::TT && OutputType == T::TT &&
/// inputTypesMatch<TypeList<As...>>() &&
/// masterOutputTypesMatch<TypeList<Ts...>>()
/// \endcode
template <typename... MTs, typename T, typename... Ts, typename... As,
size_t... S0>
H triggerHandlerFromProcessingFunctions(
std::function<std::tuple<Optional<Ts>...>(
std::pair<DeluxeTuple<MTs...>, bool>)> &&MF,
std::function<
std::tuple<Optional<T>, Optional<Ts>...>(std::pair<As, bool>...)> &&F,
Seq<S0...>) noexcept;
public:
/// Creates a new instance.
///
/// The constructor instantiates the base-class with functions to handle
/// messages as defined for the *deluxe interface*.
///
/// The function \p F generates a \c std::tuple of values: the first value is
/// the output for the *master* and the rest is for the *slaves*. All output
/// generated by the function is optional as an agent may decide not to output
/// anything at some situation.
///
/// \todo Enforce \p F and \p MF do not potentially throw exception.
///
/// \tparam MT type of master-input handled by \p MF
/// \tparam T type of output of \p F
/// \tparam Ts type of master-output values of \p F and \p MF
/// \tparam As types of input values of \p F
///
/// \note Instantiation fails if any of the type arguments \p MT, \p T, \p
/// Ts..., and \p As... is not an instance of \c rosa::deluxe::DeluxeTuple or
/// any of \p T and \p As... is \c rosa::deluxe::EmptyDeluxeTuple or the
/// number of inputs and master-outputs are not equal.
///
/// \note If \p MT is \c rosa::deluxe::EmptyDeluxeTuple, the constructed
/// object does not receive master-input. Similarly, if any of \p Ts... is \c
/// rosa::deluxe::EmptyDeluxeTuple, the constructed object does not generated
/// master-output for the corresponding *slave* position.
///
/// \param Kind kind of the new \c rosa::Unit instance
/// \param Id unique identifier of the new \c rosa::Unit instance
/// \param Name name of the new \c rosa::Unit instance
/// \param S \c rosa::MessagingSystem owning the new instance
/// \param MF function to process master-input values and generate
/// master-output with
/// \param F function to process input values and generate output and
/// master-output with
///
/// \pre Statically, all the type arguments \p MT, \p T, \p Ts..., and \p
/// As... are instances of \c rosa::deluxe::DeluxeTuple, with \p T and \p
/// As... containing at least one element, and the number of input and
/// master-output types are equal: \code
/// TypeListAllDeluxeTuple<TypeList<MT, T, Ts..., As...>::Value &&
/// T::Length > 0 && (true && ... && As::Length > 0) &&
/// sizeof...(Ts) == sizeof...(As)
///\endcode
/// Dynamically, the instance is created as of kind \c
/// rosa::deluxe::atoms::AgentKind: \code
/// Kind == rosa::deluxe::atoms::AgentKind
/// \endcode
///
/// \see \c rosa::deluxe::DeluxeTuple
template <typename MT, typename T, typename... Ts, typename... As,
typename = std::enable_if_t<
TypeListAllDeluxeTuple<TypeList<MT, T, Ts..., As...>>::Value &&
(T::Length > 0) && (true && ... && (As::Length > 0)) &&
sizeof...(Ts) == sizeof...(As)>>
DeluxeAgent(
const AtomValue Kind, const id_t Id, const std::string &Name,
MessagingSystem &S,
std::function<std::tuple<Optional<Ts>...>(std::pair<MT, bool>)> &&MF,
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)> &&F) noexcept;
/// Destroys \p this object.
~DeluxeAgent(void) noexcept;
/// 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<DeluxeExecutionPolicy> &&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<AgentHandle> master(void) const noexcept;
/// Registers a *master* for \p this object.
///
/// The new *master* is registered by overwriting the reference to any
/// already registered *master*. One can clear the registered reference by
/// passing an *empty* \c rosa::Optional object as actual argument.
///
/// \note The role of the referred *master* is validated by checking its
/// *kind*.
///
/// \note Any call to \c rosa::deluxe::DeluxeAgent::registerMaster should be
/// paired with a corresponding call of \c
/// rosa::deluxe::DeluxeAgent::registerSlave, which validates that
/// input/output types of master and slave matches.
///
/// \param _Master the *master* to register
///
/// \pre \p _Master is empty or of kind \c rosa::deluxe::atoms::AgentKind:
/// \code
/// !_Master || unwrapAgent(*_Master).Kind == rosa::deluxe::atoms::AgentKind
/// \endcode
void registerMaster(const Optional<AgentHandle> _Master) noexcept;
/// Tells the types of values consumed from the *slave* at a position.
///
/// That is the type of values \p this object expect to be sent to it in a \c
/// rosa::deluxe::DeluxeTuple by its *slave* registered at position \p Pos.
///
/// \see \c rosa::deluxe::DeluxeAgent::slave
///
/// \param Pos position of *slave*
///
/// \return \c rosa::Token representing the types of values consumed from
/// the *slave* at position \p Pos
///
/// \pre \p Pos is a valid index of input: \code
/// Pos < NumberOfInputs
/// \endcode
Token inputType(const size_t Pos) const noexcept;
/// Tells the types of values produced for the *slave* at a position.
///
/// That is the types of values \p this object potentially sends in a \c
/// rosa::deluxe::DeluxeTuple to its *slave* registered at position \p Pos.
///
/// \see \c rosa::deluxe::DeluxeAgent::slave
///
/// \param Pos position of *slave*
///
/// \return \c rosa::Token representing the types of values produced for
/// the *slave* at position \p Pos
///
/// \pre \p Pos is a valid index of input: \code
/// Pos < NumberOfMasterOutputs
/// \endcode
Token masterOutputType(const size_t Pos) const noexcept;
/// The *slave* of \p this object registered at a position, if any.
///
/// \see \c rosa::deluxe::DeluxeAgent::registerSlave
///
/// \param Pos position of *slave*
///
/// \return the *slave* registered for \p this object at position \p Pos
///
/// \pre \p Pos is a valid index of input: \code
/// Pos < NumberOfInputs
/// \endcode
Optional<AgentHandle> slave(const size_t Pos) const noexcept;
/// Registers a *slave* for \p this object at a position.
///
/// The new *slave* is registered by overwriting the reference to any already
/// registered *slave* at position \p Pos. One can clear the registered
/// reference by passing an *empty* \c rosa::Optional object as actual
/// argument. If \p Slave is already registered for another position, the
/// other position gets cleared.
///
/// \note The role of the referred *slave* is validated by checking its
/// *kind*.
///
/// \note The type of values produced by the referred *slave* is validated by
/// matching its `OutputType` against the corresponding value in
/// \c rosa::deluxe::DeluxeAgent::InputTypes.
///
/// \note The type of master-input values processed by the referred *slave* is
/// validated by matching its `MasterInputType` against the corresponding
/// value in \c rosa::deluxe::DeluxeAgent::MasterOutputTypes.
///
/// \param Pos position to register \p Slave at
/// \param Slave the *slave* to register
///
/// \pre \p Pos is a valid index of input, \p Slave is empty or of kind
/// \c rosa::deluxe::atoms::AgentKind or \c rosa::deluxe::atoms::SensorKind,
/// and \p Slave -- if not empty -- produces values of types matching the
/// expected input type at position \p Pos and processes values of types
/// matching the produced master-output type at position \p Pos:
/// \code
/// Pos < NumberOfInputs &&
/// (!Slave ||
/// (unwrapAgent(*Slave.)Kind == rosa::deluxe::atoms::SensorKind &&
/// static_cast<const DeluxeSensor &>(unwrapAgent(*Slave)).OutputType ==
/// InputTypes[Pos] &&
/// (emptyToken(MasterOutputTypes[Pos]) ||
/// static_cast<const DeluxeSensor &>(unwrapAgent(*Slave)).MasterInputType
/// == MasterOutputTypes[Pos])) ||
/// (unwrapAgent(*Slave).Kind == rosa::deluxe::atoms::AgentKind &&
/// static_cast<const DeluxeAgent &>(unwrapAgent(*Slave)).OutputType ==
/// InputTypes[Pos] &&
/// (emptyToken(MasterOutputTypes[Pos]) ||
/// static_cast<const DeluxeAgent &>(unwrapAgent(*Slave)).MasterInputType ==
/// MasterOutputTypes[Pos])))
/// \endcode
void registerSlave(const size_t Pos,
const Optional<AgentHandle> Slave) noexcept;
/// Tells the position of a registered *slave*.
///
/// \param Slave \c rosa::AgentHandle for the *slave* to check
///
/// \return position of \p Slave if it is registered and found,
/// \c rosa::deluxe::DeluxeAgent::NumberOfInputs otherwise.
size_t positionOfSlave(AgentHandle Slave) const noexcept;
private:
/// Sends a value to the *master* of \p this object.
///
/// \p Value is getting sent to \c rosa::deluxe::DeluxeAgent::Master if it
/// contains a valid handle for a \c rosa::deluxe::DeluxeAgent. The function
/// does nothing otherwise.
///
/// The elements from \p Value are sent one by one in separate messages to the
/// *master*.
///
/// \tparam Ts types of the elements in \p Value
/// \tparam S0 indices for accessing elements of \p Value
///
/// \param Value value to send
///
/// \note The second argument provides indices statically as template
/// arguments \p S0..., so its actual value is ignored.
///
/// \pre Statically, the indices match the elements: \code
/// sizeof...(Ts) == sizeof...(S0)
/// \endcode Dynamically, \p Ts match \c
/// rosa::deluxe::DeluxeiAgent::OutputType: \code
/// OutputType == TypeToken<Ts...>::Value
/// \endcode
template <typename... Ts, size_t... S0>
void sendToMaster(const DeluxeTuple<Ts...> &Value, Seq<S0...>) noexcept;
/// Sends a value to a *slave* of \p this object at position \p Pos.
///
/// \p Value is getting sent to \c rosa::deluxe::DeluxeAgent::Slaves[Pos] if
/// it contains a valid handle. The function does nothing otherwise.
///
/// The elements from \p Value are sent one by one in separate messages to the
/// *slave*.
///
/// \tparam Ts types of the elements in \p Value
/// \tparam S0 indices for accessing elements of \p Value
///
/// \param Pos the position of the *slave* to send \p Value to
/// \param Value value to send
///
/// \pre Statically, the indices match the elements: \code
/// sizeof...(Ts) == sizeof...(S0)
/// \endcode Dynamically, \p Pos is a valid *slave* position and \p Ts match
/// \c rosa::deluxe::DeluxeiAgent::MasterOutputTypes[Pos]: \code
/// Pos < NumberOfMasterOutputs &&
/// MasterOutputTypes[Pos] == TypeToken<Ts...>::Value
/// \endcode
template <typename... Ts, size_t... S0>
void sendToSlave(const size_t Pos, const DeluxeTuple<Ts...> &Value,
Seq<S0...>) noexcept;
/// Generates the next output by processing current input values upon trigger
/// from the system.
///
/// Executes \c rosa::deluxe::DeluxeAgent::FP.
///
/// \note The only argument is a \c rosa::AtomConstant, hence its actual
/// value is ignored.
///
/// \pre Master-input and all input from *slaves* are supposed to be
/// completely received upon triggering: \code
/// MasterInputNextPos == 0 &&
/// std::all_of(InputNextPos.begin(), InputNextPos.end(),
/// [](const token_size_t &I){return I == 0;})
/// \endcode
void handleTrigger(atoms::Trigger) noexcept;
/// Stores a new input value from a *slave*.
///
/// The function stores \p Value at position \p Pos in \c
/// rosa::deluxe::DeluxeAgent::InputValues at the position associated to \p Id
/// in \c rosa::deluxe::DeluxeAgent::SlaveIds and also sets the corresponding
/// flag in \c rosa::deluxe::DeluxeAgent::InputChanged. The function also
/// takes care of checking and updating \c
/// rosa::deluxe::DeluxeSensor::MasterInputNextPos at the corresponding
/// position: increments the value and resets it to `0` when the last element
/// is received.
///
/// \note Utilized by member functions of group \c DeluxeAgentInputHandlers.
///
/// \tparam T type of input to store
///
/// \param Id unique identifier of *slave*
/// \param Pos position of the value in the \c rosa::deluxe::DeluxeTuple
/// \param Value the input value to store
///
/// \pre The *slave* with \p Id is registered, \p Pos is the expected
/// position of input from the *slave*, and the input from it is expected to
/// be of type \p T: \code
/// SlaveIds.find(Id) != SlaveIds.end() &&
/// Pos == InputNextPos[SlaveIds.find(Id)->second] &&
/// typeAtPositionOfToken(InputTypes[SlaveIds.find(Id)->second], Pos) ==
/// TypeNumberOf<T>::Value
/// \endcode
template <typename T>
void saveInput(id_t Id, token_size_t Pos, T Value) noexcept;
/// Stores a new input value from the *master*.
///
/// The function stores \p Value at position \p Pos in \c
/// rosa::deluxe::DeluxeAgent::MasterInputValue and also sets the
/// flag \c rosa::deluxe::DeluxeAgent::MasterInputChanged. The function also
/// takes care of checking and updating \c
/// rosa::deluxe::DeluxeAgent::MasterInputNextPos: increments its value and
/// reset to `0` when the last element is received.
///
/// \note Utilized by member functions of group \c
/// DeluxeAgentMasterInputHandlers.
///
/// \tparam T type of input to store
///
/// \param Id unique identifier of the *master*
/// \param Pos position of the value in the \c rosa::deluxe::DeluxeTuple
/// \param Value the input value to store
///
/// \pre The *master* with \p Id is registered, \p Pos is the expected
/// position of master-input, and the input from the *master* at position \p
/// Pos is expected to be of type \p T: \code
/// Master && masterId() == Id && Pos == MasterInputNextPos &&
/// typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf<T>::Value
/// \endcode
template <typename T>
void saveMasterInput(id_t Id, token_size_t Pos, T Value) noexcept;
/// \defgroup DeluxeAgentInputHandlers Input handlers of
/// rosa::deluxe::DeluxeAgent
///
/// Definition of member functions handling messages from *slaves* with
/// different types of input
///
/// A *master* generally needs to be prepared to deal with values of any
/// built-in type to handle messages from its *slaves*. Each type requires a
/// separate message handler, which are implemented by these functions. The
/// functions instantiate \c rosa::deluxe::DeluxeAgent::saveInput with the
/// proper template argument and pass the content of the message on for
/// processing.
///
/// \note The member functions in this group are defined by \c
/// DASLAVEHANDLERDEF.
///
/// \note Keep these definitions in sync with \c rosa::BuiltinTypes.
///
///@{
DASLAVEHANDLERDEF(AtomValue)
DASLAVEHANDLERDEF(int16_t)
DASLAVEHANDLERDEF(int32_t)
DASLAVEHANDLERDEF(int64_t)
DASLAVEHANDLERDEF(int8_t)
DASLAVEHANDLERDEFN(long double, long_double)
DASLAVEHANDLERDEFN(std::string, std__string)
DASLAVEHANDLERDEF(uint16_t)
DASLAVEHANDLERDEF(uint32_t)
DASLAVEHANDLERDEF(uint64_t)
DASLAVEHANDLERDEF(uint8_t)
DASLAVEHANDLERDEF(unit_t)
DASLAVEHANDLERDEF(bool)
DASLAVEHANDLERDEF(double)
DASLAVEHANDLERDEF(float)
/// @}
/// \defgroup DeluxeAgentMasterInputHandlers Master-input handlers of
/// rosa::deluxe::DeluxeAgent
///
/// Definition of member functions handling messages from the *master* with
/// different types of input
///
/// A *slave* generally needs to be prepared to deal with values of any
/// built-in type to handle messages from its *master*. Each type requires a
/// separate message handler, which are implemented by these functions. The
/// functions instantiate \c rosa::deluxe::DeluxeAgent::saveMasterInput with
/// the proper template argument and pass the content of the message on for
/// processing.
///
/// \note The member functions in this group are defined by \c
/// DAMASTERHANDLERDEF.
///
/// \note Keep these definitions in sync with \c rosa::BuiltinTypes.
///
///@{
DAMASTERHANDLERDEF(AtomValue)
DAMASTERHANDLERDEF(int16_t)
DAMASTERHANDLERDEF(int32_t)
DAMASTERHANDLERDEF(int64_t)
DAMASTERHANDLERDEF(int8_t)
DAMASTERHANDLERDEFN(long double, long_double)
DAMASTERHANDLERDEFN(std::string, std__string)
DAMASTERHANDLERDEF(uint16_t)
DAMASTERHANDLERDEF(uint32_t)
DAMASTERHANDLERDEF(uint64_t)
DAMASTERHANDLERDEF(uint8_t)
DAMASTERHANDLERDEF(unit_t)
DAMASTERHANDLERDEF(bool)
DAMASTERHANDLERDEF(double)
DAMASTERHANDLERDEF(float)
/// @}
};
/// Anonymous namespace with implementation for \c
/// rosa::deluxe::DeluxeAgent::DeluxeAgent, \c
/// rosa::deluxe::DeluxeAgent::inputTypesMatch, and \c
/// rosa::deluxe::DeluxeAgent::masterOutputTypesMatch, consider it private.
namespace {
/// Calculates storage offsets for values of \p Ts... stored in a \c
/// rosa::TokenizedStorage.
///
/// \note Utilized by \c rosa::deluxe::DeluxeAgnet::DeluxeAgent to initialize \c
/// rosa::deluxe::DeluxeAgent::InputStorageOffsets.
///
/// \tparam Ts types whose offsets to calculate
/// \tparam S0 indices for referring to positions in \p Ts...
///
/// \note Instantiation fails if any of the type arguments \p Ts... is not an
/// instance of \c rosa::deluxe::DeluxeTuple.
///
/// \note The only argument provides indices statically as template
/// arguments \p S0..., so its actual value is ignored.
///
/// \return \c std::vector containing the calculated offsets
///
/// \pre Statically, all the type arguments \p Ts... are instances of \c
/// rosa::deluxe::DeluxeTuple and the indices match the types: \code
/// TypeListAllDeluxeTuple<TypeList<Ts...>>::Value &&
/// sizeof...(Ts) == sizeof...(S0)
/// \endcode
template <
typename... Ts, size_t... S0,
typename = std::enable_if_t<TypeListAllDeluxeTuple<TypeList<Ts...>>::Value>>
static std::vector<token_size_t> storageOffsets(Seq<S0...>) noexcept {
STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments");
std::vector<token_size_t> Offsets(sizeof...(Ts));
// Do nothing for no types.
if constexpr (sizeof...(Ts) != 0) {
Offsets[0] = 0; // The offset of the very first value is always `0`.
// Calculate further offsets...
(((S0 != sizeof...(Ts) - 1) &&
(Offsets[S0 + 1] = Offsets[S0] + Ts::Length)),
...);
}
return Offsets;
}
/// Template \c struct whose specializations provide a recursive implementation
/// for \c TypesMatchList.
///
/// \tparam As types to match
template <typename... As> struct TypesMatchImpl;
/// Template specialization for the case, when at least one type is to
/// be matched and that is an instance of \c rosa::deluxe::DeluxeTuple.
///
/// \tparam Ts types of elements in the \c rosa::deluxe::DeluxeTuple to match
/// \tparam As further types to match
template <typename... Ts, typename... As>
struct TypesMatchImpl<DeluxeTuple<Ts...>, As...> {
/// Tells whether types \c rosa::deluxe::DeluxeTuple<Ts...> and \p As... match
/// \c rosa::Token values stored in \p Tokens starting at position \p Pos.
///
/// The function has got a recursive implementation: it matches the first
/// type \c rosa::deluxe::DeluxeTuple<Ts...> against \c rosa::Token at
/// position \p Pos of \p Tokens, then further types \p As... are matched
/// recursively starting at position \c (Pos + 1).
///
/// \param Tokens container of \c rosa::Token values to match types against
/// \param Pos position in \p Tokens to start matching at
///
/// \return if types \c rosa::deluxe::DeluxeTuple<Ts...> and \p As... match \c
/// rosa::Token values stored in \p Tokens starting at position \p Pos
static bool f(const std::vector<Token> &Tokens, size_t Pos) noexcept {
return Pos < Tokens.size() && TypeToken<Ts...>::Value == Tokens[Pos] &&
TypesMatchImpl<As...>::f(Tokens, Pos + 1);
}
};
/// Template specialization for the case, when at least one type is to
/// be matched and that is *not* an instance of \c rosa::deluxe::DeluxeTuple.
///
/// \tparam T first type to match
/// \tparam As further types to match
template <typename T, typename... As>
struct TypesMatchImpl<T, As...> {
/// Tells whether types \p T and \p As... match \c rosa::Token values stored
/// in \p Tokens starting at position \p Pos.
///
/// This specialization is used only when \p T is not an instance of \c
/// rosa::deluxe::DeluxeTuple, in which case the match is not successful.
///
/// \note The function takes two parameters to match the general signature but
/// the actual values are ignored.
///
/// \return `false`
static bool f(const std::vector<Token> &, size_t) noexcept { return false; }
};
/// Template specialization for the terminal case, when no type remains to
/// check.
template <> struct TypesMatchImpl<> {
/// Tells whether \p Pos is the number of values stored in \p Tokens.
///
/// In this terminal case, there is no more types to match because all the
/// types are supposed to be already matched successfully. The whole list of
/// types already matched is a complete match if it covers all values in
/// \p Tokens. That is true if \p Pos points exactly to the end of \p Tokens.
///
/// \param Tokens container of \c rosa::Token values to match types against
/// \param Pos position in \p Tokens to start matching at
///
/// \return if \p Pos is the number of values stored in \p Tokens
static bool f(const std::vector<Token> &Tokens, size_t Pos) noexcept {
return Pos == Tokens.size();
}
};
/// Template \c struct that provides an implementation for \c
/// rosa::deluxe::DeluxeAgent::inputTypesMatch and \c
/// rosa::deluxe::DeluxeAgent::masterOutputTypesMatch.
///
/// \note Match a list of types \p List against a \c std::vector of
/// \c rosa::Token values, \c Tokens, like \code
/// bool match = TypesMatchList<List>::f(Tokens);
/// \endcode
/// If any type in \c rosa::TypeList \p Listis not an instance of \c
/// rosa::deluxe::DeluxeTuple, the match gives a negative result.
///
/// \tparam List \c rosa::TypeList that contains types to match
template <typename List> struct TypesMatchList;
/// Template specialization implementing the feature.
///
/// \tparam As types to match
template <typename... As> struct TypesMatchList<TypeList<As...>> {
/// Tells whether types \p As... match \c rosa::Token values stored in \p
/// Tokens.
///
/// The function unwraps the types from \c rosa::TypeList and utilizes \c
/// TypesMatchImpl to do the check.
///
/// \param Tokens container of \c rosa::Token values to match types against
///
/// \return if types \p As... match \c rosa::Token values stored in \p Tokens
static bool f(const std::vector<Token> &Tokens) noexcept {
return TypesMatchImpl<As...>::f(Tokens, 0);
}
};
} // End namespace
template <typename As>
bool DeluxeAgent::inputTypesMatch(void) const noexcept {
return TypesMatchList<As>::f(InputTypes);
}
template <typename Ts>
bool DeluxeAgent::masterOutputTypesMatch(void) const noexcept {
return TypesMatchList<Ts>::f(MasterOutputTypes);
}
template <size_t Pos, typename... Ts, size_t... S0>
DeluxeTuple<Ts...> DeluxeAgent::prepareInputValueAtPos(TypeList<Ts...>,
Seq<S0...>) const
noexcept {
using T = DeluxeTuple<Ts...>;
STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent type arguments");
ASSERT(inv() && Pos < NumberOfInputs && T::TT == InputTypes[Pos]);
const token_size_t StorageOffset = InputStorageOffsets[Pos];
// The below should hold because of the above, just leave it for sanity check.
ASSERT((true && ... &&
(static_cast<size_t>(static_cast<token_size_t>(S0)) == S0)));
// Get all elements of the tuple in a fold expression.
return T(*static_cast<const Ts *>(InputValues->pointerTo(
static_cast<token_size_t>(StorageOffset + S0)))...);
}
template <typename... As, size_t... S0>
std::tuple<std::pair<As, bool>...>
DeluxeAgent::prepareCurrentInputs(Seq<S0...>) const noexcept {
STATIC_ASSERT(TypeListAllDeluxeTuple<TypeList<As...>>::Value,
"not tuple types");
STATIC_ASSERT(sizeof...(As) == sizeof...(S0), "inconsistent type arguments");
ASSERT(inv() && inputTypesMatch<TypeList<As...>>());
return std::make_tuple(std::make_pair(
prepareInputValueAtPos<S0>(typename UnwrapDeluxeTuple<As>::Type(),
seq_t<As::Length>()),
InputChanged[S0])...);
}
template <typename T, typename... Ts, typename... As, size_t... S0>
std::tuple<Optional<T>, Optional<Ts>...> DeluxeAgent::invokeWithTuple(
std::function<
std::tuple<Optional<T>, Optional<Ts>...>(std::pair<As, bool>...)>
F,
const std::tuple<std::pair<As, bool>...> Args, Seq<S0...>) noexcept {
- ASSERT(sizeof...(As) == sizeof...(S0));
+ STATIC_ASSERT(sizeof...(As) == sizeof...(S0),
+ "wrong number of type parameters");
return F(std::get<S0>(Args)...);
}
template <size_t Pos, typename... Ts>
void DeluxeAgent::handleMasterOutputAtPos(
const Optional<DeluxeTuple<Ts...>> &Value) noexcept {
using MOT = DeluxeTuple<Ts...>;
ASSERT(inv() && Pos < NumberOfMasterOutputs &&
MOT::TT == MasterOutputTypes[Pos]);
// Do not do anything for master-output of type \c
// rosa::deluxe::EmptyDeluxeTuple and when \p Value is empty.
if constexpr (!std::is_same<MOT, EmptyDeluxeTuple>::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 DeluxeAgent::handleMasterOutputs(const std::tuple<Optional<Ts>...> &Output,
Seq<S0...>) noexcept {
using MOTs = typename TypeListDrop<Offset, TypeList<Ts...>>::Type;
STATIC_ASSERT(TypeListAllDeluxeTuple<MOTs>::Value,
"not tuple type arguments");
STATIC_ASSERT(sizeof...(Ts) == Offset + sizeof...(S0),
"inconsistent arguments");
ASSERT(inv() && masterOutputTypesMatch<MOTs>() &&
sizeof...(S0) == NumberOfMasterOutputs);
// Handle each master-output position in a fold expression.
(handleMasterOutputAtPos<S0>(std::get<Offset + S0>(Output)), ...);
ASSERT(inv());
}
template <typename... MTs, typename T, typename... Ts, typename... As,
size_t... S0>
DeluxeAgent::H DeluxeAgent::triggerHandlerFromProcessingFunctions(
std::function<
std::tuple<Optional<Ts>...>(std::pair<DeluxeTuple<MTs...>, bool>)> &&MF,
std::function<
std::tuple<Optional<T>, Optional<Ts>...>(std::pair<As, bool>...)> &&F,
Seq<S0...>) noexcept {
using MT = DeluxeTuple<MTs...>;
STATIC_ASSERT((TypeListAllDeluxeTuple<TypeList<T, Ts..., As...>>::Value),
"not tuple type arguments");
STATIC_ASSERT(sizeof...(MTs) == sizeof...(S0), "inconsistent arguments");
ASSERT(MasterInputType == MT::TT && OutputType == T::TT &&
inputTypesMatch<TypeList<As...>>() &&
masterOutputTypesMatch<TypeList<Ts...>>());
return [ this, MF, F ]() noexcept {
// \note These indices work for both inputs and master-outputs.
using SlaveIndices = seq_t<sizeof...(As)>;
// Handle master-input.
// Do not do anything for master-input type \c
// rosa::deluxe::EmptyDeluxeTuple.
if (!std::is_same<MT, EmptyDeluxeTuple>::value) {
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " handles master-input."
<< std::endl;
// The assert must hold if \p this object was successfuuly constructed.
ASSERT((true && ... &&
(static_cast<size_t>(static_cast<token_size_t>(S0)) == S0)));
const auto MasterInputArg = std::make_pair(
// Get all elements of the tuple in a fold expression.
MT(*static_cast<const MTs *>(
MasterInputValue->pointerTo(static_cast<token_size_t>(S0)))...),
MasterInputChanged);
MasterInputChanged = false;
const std::tuple<Optional<Ts>...> MasterOutput = MF(MasterInputArg);
handleMasterOutputs<0>(MasterOutput, SlaveIndices());
}
// Handle inputs.
// Call the processing function only if \p ExecutionPolicy allows.
if (ExecutionPolicy->shouldProcess(InputChanged)) {
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " handles input."
<< std::endl;
const auto InputArgs = prepareCurrentInputs<As...>(SlaveIndices());
std::fill(InputChanged.begin(), InputChanged.end(), false);
const std::tuple<Optional<T>, Optional<Ts>...> Output =
invokeWithTuple(F, InputArgs, SlaveIndices());
const auto OutputToMaster = std::get<0>(Output);
if (OutputToMaster) {
sendToMaster(*OutputToMaster, seq_t<T::Length>());
}
handleMasterOutputs<1>(Output, SlaveIndices());
} else {
LOG_TRACE_STREAM << "DeluxeAgent " << Name << " skips input."
<< std::endl;
}
};
}
template <typename MT, typename T, typename... Ts, typename... As, typename>
DeluxeAgent::DeluxeAgent(
const AtomValue Kind, const id_t Id, const std::string &Name,
MessagingSystem &S,
std::function<std::tuple<Optional<Ts>...>(std::pair<MT, bool>)> &&MF,
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)> &&F) noexcept
: Agent(Kind, Id, Name, S, THISMEMBER(handleTrigger),
DASLAVEHANDLERREF(AtomValue), DASLAVEHANDLERREF(int16_t),
DASLAVEHANDLERREF(int32_t), DASLAVEHANDLERREF(int64_t),
DASLAVEHANDLERREF(int8_t), DASLAVEHANDLERREF(long_double),
DASLAVEHANDLERREF(std__string), DASLAVEHANDLERREF(uint16_t),
DASLAVEHANDLERREF(uint32_t), DASLAVEHANDLERREF(uint64_t),
DASLAVEHANDLERREF(uint8_t), DASLAVEHANDLERREF(unit_t),
DASLAVEHANDLERREF(bool), DASLAVEHANDLERREF(double),
DASLAVEHANDLERREF(float), DAMASTERHANDLERREF(AtomValue),
DAMASTERHANDLERREF(int16_t), DAMASTERHANDLERREF(int32_t),
DAMASTERHANDLERREF(int64_t), DAMASTERHANDLERREF(int8_t),
DAMASTERHANDLERREF(long_double), DAMASTERHANDLERREF(std__string),
DAMASTERHANDLERREF(uint16_t), DAMASTERHANDLERREF(uint32_t),
DAMASTERHANDLERREF(uint64_t), DAMASTERHANDLERREF(uint8_t),
DAMASTERHANDLERREF(unit_t), DAMASTERHANDLERREF(bool),
DAMASTERHANDLERREF(double), DAMASTERHANDLERREF(float)),
ExecutionPolicy(DeluxeExecutionPolicy::decimation(1)), OutputType(T::TT),
NumberOfInputs(sizeof...(As)), MasterInputType(MT::TT),
NumberOfMasterOutputs(NumberOfInputs), InputTypes({As::TT...}),
InputNextPos(NumberOfInputs, 0), InputChanged(NumberOfInputs, false),
InputStorageOffsets(storageOffsets<As...>(seq_t<sizeof...(As)>())),
InputValues(new typename TokenizedStorageForTypeList<
typename TypeListUnwrapDeluxeTuple<TypeList<As...>>::Type>::
Type()),
MasterInputNextPos(0), MasterInputChanged(false),
MasterInputValue(new typename TokenizedStorageForTypeList<
typename UnwrapDeluxeTuple<MT>::Type>::Type()),
MasterOutputTypes({Ts::TT...}),
FP(triggerHandlerFromProcessingFunctions(std::move(MF), std::move(F),
seq_t<MT::Length>())),
Slaves(NumberOfInputs) {
ASSERT(Kind == atoms::AgentKind);
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " is created." << std::endl;
ASSERT(inv());
}
template <typename... Ts, size_t... S0>
void DeluxeAgent::sendToMaster(const DeluxeTuple<Ts...> &Value,
Seq<S0...>) noexcept {
STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments");
ASSERT(inv() && OutputType == TypeToken<Ts...>::Value);
// The assert must hold if \p this object was successfuuly constructed.
ASSERT((true && ... &&
(static_cast<size_t>(static_cast<token_size_t>(S0)) == S0)));
// Create a static constant array for these indices to be available as lvalue
// references when creating messages below. \c S0... when used directly in a
// fold expression is a temporary value, which would result in \c
// rosa::Message instances being created with rvalue references. Further, all
// other values would to copied into a temporary variable for making them
/// available as rvalue references (they are constant lvalue references here).
static constexpr std::array<token_size_t, sizeof...(S0)> Indices{{S0...}};
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id
<< ") sends to master ("
<< static_cast<bool>(Master && *Master) << "): " << Value
<< " (" << sizeof...(S0) << ")" << std::endl;
// There is a handle and the referred *master* is in a valid state.
if (Master && *Master) {
// Handle each element of the tuple in a fold expression.
(Master->sendMessage(Message::create(atoms::Slave::Value, Id, Indices[S0],
std::get<S0>(Value))),
...);
}
ASSERT(inv());
}
template <typename... Ts, size_t... S0>
void DeluxeAgent::sendToSlave(const size_t Pos, const DeluxeTuple<Ts...> &Value,
Seq<S0...>) noexcept {
STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments");
ASSERT(inv() && Pos < NumberOfMasterOutputs &&
MasterOutputTypes[Pos] == TypeToken<Ts...>::Value);
// The assert must hold if \p this object was successfuuly constructed.
ASSERT((true && ... &&
(static_cast<size_t>(static_cast<token_size_t>(S0)) == S0)));
// Create a static constant array for these indices to be available as lvalue
// references when creating messages below. \c S0... when used directly in a
// fold expression is a temporary value, which would result in \c
// rosa::Message instances being created with rvalue references. Further, all
// other values would to copied into a temporary variable for making them
/// available as rvalue references (they are constant lvalue references here).
static constexpr std::array<token_size_t, sizeof...(S0)> Indices{{S0...}};
// There is a handle and the referred *slave* is in a valid state.
auto Slave = Slaves[Pos];
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id
<< ") sends to slave (" << static_cast<bool>(Slave && *Slave)
<< ") at position " << Pos << ": " << Value << " ("
<< sizeof...(S0) << ")" << std::endl;
if (Slave && *Slave) {
// Handle each element of the tuple in a fold expression.
(Slave->sendMessage(Message::create(atoms::Master::Value, Id, Indices[S0],
std::get<S0>(Value))),
...);
}
}
template <typename T>
void DeluxeAgent::saveInput(id_t Id, token_size_t Pos, T Value) noexcept {
ASSERT(inv() && SlaveIds.find(Id) != SlaveIds.end() &&
Pos == InputNextPos[SlaveIds.find(Id)->second] &&
typeAtPositionOfToken(InputTypes[SlaveIds.find(Id)->second], Pos) ==
TypeNumberOf<T>::Value);
size_t SlavePos = SlaveIds.at(Id);
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id
<< ") saves value from slave at position " << SlavePos
<< ": (" << static_cast<size_t>(Pos) << ") " << Value
<< std::endl;
// Save value.
size_t StoragePos = (size_t)InputStorageOffsets[SlavePos] + Pos;
// This assert must hold if \p this object was successfully constructed.
ASSERT(static_cast<size_t>(static_cast<token_size_t>(StoragePos)) ==
StoragePos);
*static_cast<T *>(
InputValues->pointerTo(static_cast<token_size_t>(StoragePos))) = Value;
// Update position of next value.
if (++InputNextPos[SlavePos] == lengthOfToken(InputTypes[SlavePos])) {
InputNextPos[SlavePos] = 0;
}
// Set flag.
InputChanged[SlavePos] = true;
ASSERT(inv());
}
template <typename T>
void DeluxeAgent::saveMasterInput(id_t Id, token_size_t Pos, T Value) noexcept {
ASSERT(inv() && Master && masterId() == Id && Pos == MasterInputNextPos &&
typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf<T>::Value);
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id
<< ") saves value from master: (" << static_cast<size_t>(Pos)
<< ") " << Value << std::endl;
// Save value.
*static_cast<T *>(MasterInputValue->pointerTo(Pos)) = Value;
// Update position of next value.
if (++MasterInputNextPos == lengthOfToken(MasterInputType)) {
MasterInputNextPos = 0;
}
// Set flag.
MasterInputChanged = true;
ASSERT(inv());
}
} // End namespace deluxe
} // End namespace rosa
#undef DASLAVEHANDLEREF
#undef DAMASTERHANDLEREF
#undef DASLAVEHANDLEDEF
#undef DAMASTERHANDLEDEF
#undef DASLAVEHANDLEDEFN
#undef DAMASTERHANDLEDEFN
#undef DASLAVEHANDLENAME
#undef DAMASTERHANDLENAME
#endif // ROSA_DELUXE_DELUXEAGENT_HPP
diff --git a/include/rosa/deluxe/DeluxeSystem.hpp b/include/rosa/deluxe/DeluxeSystem.hpp
old mode 100755
new mode 100644
index f39ce5a..bc51cdf
--- a/include/rosa/deluxe/DeluxeSystem.hpp
+++ b/include/rosa/deluxe/DeluxeSystem.hpp
@@ -1,240 +1,240 @@
//===-- rosa/deluxe/DeluxeSystem.hpp ----------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/deluxe/DeluxeSystem.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2019
///
/// \brief Specialization of \c rosa::MessagingSystem for the *deluxe
/// interface*.
///
/// \see \c rosa::deluxe::DeluxeContext
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_DELUXE_DELUXESYSTEM_HPP
#define ROSA_DELUXE_DELUXESYSTEM_HPP
#include "rosa/core/MessagingSystem.hpp"
#include "rosa/deluxe/DeluxeAgent.hpp"
#include "rosa/deluxe/DeluxeSensor.hpp"
namespace rosa {
namespace deluxe {
/// Implements and extends the \c rosa::MessagingSystem interface to be
/// used by \c rosa::deluxe::DeluxeContext.
///
/// The class is a specialization of \c rosa::MessagingSystem, where objects
/// of two specialized subtypes of \c rosa::Agent, \c rosa::deluxe::DeluxeSensor
/// and \c rosa::deluxe::DeluxeAgent, constitute a system. The class extends the
/// \c rosa::MessagingSystem interface with features required to implement the
/// *deluxe interface*.
///
/// \see rosa::deluxe::DeluxeContext
class DeluxeSystem : public MessagingSystem {
friend class DeluxeContext;
friend class DeluxeExecutionPolicy;
public:
/// Returns an object implementing the \c rosa::deluxe::DeluxeSystem
/// interface.
///
/// \param Name name of the new instance
///
/// \return \c std::unique_ptr for the new instance of
/// \c rosa::DeluxeSystem
static std::unique_ptr<DeluxeSystem>
createSystem(const std::string &Name) noexcept;
protected:
/// Creates a new instance.
///
/// \note Protected constructor restricts instantiation for subclasses.
DeluxeSystem(void) noexcept = default;
public:
/// Creates a \c rosa::deluxe::DeluxeSensor instance owned by \p this object
/// and returns a \p rosa::AgentHandle for it.
///
/// \tparam MT type of master-input the new \c rosa::deluxe::DeluxeSensor
/// receives
/// \tparam T type of data the new \c rosa::deluxe::DeluxeSensor operates on
///
/// \note Type arguments \p MT and \p T must be instances of \c
/// rosa::deluxe::DeluxeTuple.
///
/// \param Name name of the new \c rosa::deluxe::DeluxeSensor
/// \param MF function to process master-input values
/// \param F function to generate the next value with during normal operation
///
/// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor.
///
/// \return \c rosa::AgentHandle for new \c rosa::deluxe::DeluxeSensor
template <typename MT, typename T>
AgentHandle createSensor(const std::string &Name,
std::function<void(std::pair<MT, bool>)> &&MF,
std::function<T(void)> &&F) noexcept;
/// Creates a \c rosa::deluxe::DeluxeAgent instance owned by \p this object
/// and returns a \c rosa::AgentHandle for it.
///
/// \tparam MT type of master-input the new \c rosa::deluxe::DeluxeAgent
/// receives
/// \tparam T type of data the new \c rosa::deluxe::DeluxeAgent outputs
/// \tparam Ts types of master-output the new \c rosa::deluxe::DeluxeAgent
/// produces
/// \tparam As types of inputs the new \c rosa::deluxe::DeluxeAgent takes
///
/// \note Type arguments \p MT, \p T, \p Ts..., and \p As... must be
/// instances of \c rosa::deluxe::DeluxeTuple.
///
/// \param Name name of the new \c rosa::deluxe::DeluxeAgent
/// \param MF function for the new \c rosa::deluxe::DeluxeAgent to process
/// master-input values and generate master-output with
/// \param F function for the new \c rosa::deluxe::DeluxeAgent to process
/// input values and generate output and master-output with
///
/// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
///
/// \return \c rosa::AgentHandle for new \c rosa::deluxe::DeluxeAgent
template <typename MT, typename T, typename... Ts, typename... As>
AgentHandle createAgent(
const std::string &Name,
std::function<std::tuple<Optional<Ts>...>(std::pair<MT, bool>)> &&MF,
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)> &&F) noexcept;
protected:
/// Tells whether a \c rosa::AgentHandle refers to a
/// \c rosa::deluxe::DeluxeSensor owned by \p this object.
///
/// \param H \c rosa::AgentHandle to check
///
/// \return whether \p H refers to a \c rosa::deluxe::DeluxeSensor owned by
/// \p this object
virtual bool isDeluxeSensor(const AgentHandle &H) const noexcept = 0;
/// Extracts a const qualified \c rosa::deluxe::DeluxeSensor reference from a
/// const qualified \c rosa::AgentHandle if possible.
///
/// The function returns a \c rosa::Optional object containing a const
/// qualified reference to a \c rosa::deluxe::DeluxeSensor object extracted
/// from a const qualified \c rosa::AgentHandle instance if the referred
/// object is of type \c rosa::deluxeDeluxeSensor and owned by \p this object.
/// The returned \c rosa::Optional object is empty otherwise.
///
/// \see rosa::deluxe::DeluxeSystem::isDeluxeSensor
///
/// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeSensor
/// from
///
/// \return const qualified reference to \c rosa::deluxe::DeluxeSensor if
/// \p H refers to an object which is of that type and is owned by \p this
/// object
Optional<const DeluxeSensor &> getDeluxeSensor(const AgentHandle &H) const
noexcept;
/// Extracts a \c rosa::deluxe::DeluxeSensor reference from a
/// \c rosa::AgentHandle if possible.
///
/// The function returns a \c rosa::Optional object containing a reference to
/// a \c rosa::deluxe::DeluxeSensor object extracted from a
/// \c rosa::AgentHandle instance if the referred object is of type
/// \c rosa::deluxeDeluxeSensor and owned by \p this object. The returned
/// \c rosa::Optional object is empty otherwise.
///
/// \see rosa::deluxe::DeluxeSystem::isDeluxeSensor
///
/// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeSensor
/// from
///
/// \return reference to \c rosa::deluxe::DeluxeSensor if \p H refers to an
/// object which is of that type and is owned by \p this object
Optional<DeluxeSensor &> getDeluxeSensor(AgentHandle &H) const noexcept;
/// Tells whether a \c rosa::AgentHandle refers to a
/// \c rosa::deluxe::DeluxeAgent owned by \p this object.
///
/// \param H \c rosa::AgentHandle to check
///
/// \return whether \p H refers to a \c rosa::deluxe::DeluxeAgent owned by
/// \p this object
virtual bool isDeluxeAgent(const AgentHandle &H) const noexcept = 0;
/// Extracts a const qualified \c rosa::deluxe::DeluxeAgent reference from a
/// const qualified \c rosa::AgentHandle if possible.
///
/// The function returns a \c rosa::Optional object containing a const
/// qualified reference to a \c rosa::deluxe::DeluxeAgent object extracted
/// from a const qualified \c rosa::AgentHandle instance if the referred
/// object is of type \c rosa::deluxeDeluxeAgent and owned by \p this object.
/// The returned \c rosa::Optional object is empty otherwise.
///
/// \see rosa::deluxe::DeluxeSystem::isDeluxeAgent
///
/// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeAgent
/// from
///
/// \return const qualified reference to \c rosa::deluxe::DeluxeAgent if \p H
/// refers to an object which is of that type and is owned by \p this object
Optional<const DeluxeAgent &> getDeluxeAgent(const AgentHandle &H) const
noexcept;
/// Extracts a \c rosa::deluxe::DeluxeAgent reference from a
/// \c rosa::AgentHandle if possible.
///
/// The function returns a \c rosa::Optional object containing a reference to
/// a \c rosa::deluxe::DeluxeAgent object extracted from a
/// \c rosa::AgentHandle instance if the referred object is of type
/// \c rosa::deluxeDeluxeAgent and owned by \p this object. The returned
/// \c rosa::Optional object is empty otherwise.
///
/// \see rosa::deluxe::DeluxeSystem::isDeluxeAgent
///
/// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeAgent
/// from
///
/// \return reference to \c rosa::deluxe::DeluxeAgent if \p H refers to an
/// object which is of that type and is owned by \p this object
Optional<DeluxeAgent &> getDeluxeAgent(AgentHandle &H) const noexcept;
};
template <typename MT, typename T>
AgentHandle
DeluxeSystem::createSensor(const std::string &Name,
std::function<void(std::pair<MT, bool>)> &&MF,
std::function<T(void)> &&F) noexcept {
Agent &DS = createUnit<DeluxeSensor, MessagingSystem>(
[&](const id_t Id, MessagingSystem &S) {
return new DeluxeSensor(atoms::SensorKind, Id, Name, S, std::move(MF),
std::move(F));
});
return {DS};
}
template <typename MT, typename T, typename... Ts, typename... As>
AgentHandle DeluxeSystem::createAgent(
const std::string &Name,
std::function<std::tuple<Optional<Ts>...>(std::pair<MT, bool>)> &&MF,
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)> &&F) noexcept {
Agent &DA = createUnit<DeluxeAgent, DeluxeSystem>(
[&](const id_t Id, DeluxeSystem &S) {
return new DeluxeAgent(atoms::AgentKind, Id, Name, S, std::move(MF),
std::move(F));
});
return {DA};
}
} // End namespace deluxe
} // End namespace rosa
-#endif // ROSA_LIB_DELUXE_DELUXESYSTEM_HPP
+#endif // ROSA_DELUXE_DELUXESYSTEM_HPP
diff --git a/lib/core/MessagingSystemImpl.hpp b/lib/core/MessagingSystemImpl.hpp
index 485711a..89a366b 100644
--- a/lib/core/MessagingSystemImpl.hpp
+++ b/lib/core/MessagingSystemImpl.hpp
@@ -1,109 +1,110 @@
//===-- core/MessagingSystemImpl.hpp ----------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file core/MessagingSystemImpl.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Declaration of a basic implementation of the \c rosa::MessagingSystem
/// interface.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_LIB_CORE_MESSAGINGSYSTEMIMPL_HPP
#define ROSA_LIB_CORE_MESSAGINGSYSTEMIMPL_HPP
#include "SystemImpl.hpp"
#include "rosa/core/MessagingSystem.hpp"
namespace rosa {
/// Implements \c rosa::MessagingSystem by extending \c rosa::SystemImpl with
/// adding a simple implementation of sending messages: directly invoking
/// \c rosa::Agent instances with given \c rosa::Message objects.
///
/// \note Keep in mind that sending a \c rosa::Message object with this
/// implementation translates into a direct function call.
class MessagingSystemImpl : public MessagingSystem, public SystemImpl {
/// Alies for the base-class \c rosa::SystemImpl.
using Base = SystemImpl;
public:
/// Creates an instance.
///
/// \param Name name of the new instance
MessagingSystemImpl(const std::string &Name) noexcept;
- /// \defgroup MessagingSystemImplCallForwarding Call forwardings of rosa::MessagingSystemImpl
+ /// \defgroup MessagingSystemImplCallForwarding Call forwardings of
+ /// rosa::MessagingSystemImpl
///
/// \c rosa::MessagingSystemImpl call forwardings
///
/// \note Simply forwarding calls to implementations provided by
/// \c rosa::MessagingSystem::Base for the \c rosa::System interface.
///
/// \todo How could we use the inherited implementations in a simpler way?
///@{
bool operator==(const System &Other) const noexcept override {
return Base::operator==(Other);
}
protected:
id_t nextId(void) noexcept override { return Base::nextId(); }
bool isSystemCleaned(void) const noexcept override {
return Base::isSystemCleaned();
}
void markCleaned(void) noexcept override { Base::markCleaned(); }
void registerUnit(Unit &U) noexcept override { Base::registerUnit(U); }
void destroyUnit(Unit &U) noexcept override { Base::destroyUnit(U); }
bool isUnitRegistered(const Unit &U) const noexcept override {
return Base::isUnitRegistered(U);
}
public:
const std::string &name(void) const noexcept override { return Base::name(); }
size_t numberOfConstructedUnits(void) const noexcept override {
return Base::numberOfConstructedUnits();
}
size_t numberOfLiveUnits(void) const noexcept override {
return Base::numberOfLiveUnits();
}
bool empty(void) const noexcept override { return Base::empty(); }
///@}
/// Sends a \c rosa::message_t instance to the \c rosa::Agent instance
/// referred by a \c rosa::AgentHandle -- by directly invoking the
/// \c rosa::Agent instance with the \c rosa::Message object.
///
/// \note If the given \c rosa::Message object cannot be handled by the
/// referred \c rosa::Agent instance, the \c rosa::Message object is simply
/// ignored.
///
/// \param H refers to the \c rosa::Agent instance to send to
/// \param M message to send
///
/// \pre The referred \c rosa::Agent instance is owned by \p this object and
/// also registered: \code
/// &unwrapSystem(H) == this && isUnitRegistered(unwrapAgent(H))
/// \endcode
void send(const AgentHandle &H, message_t &&M) noexcept override;
};
} // End namespace rosa
#endif // ROSA_LIB_CORE_MESSAGINGSYSTEMIMPL_HPP

File Metadata

Mime Type
text/x-diff
Expires
Thu, Jul 3, 7:08 PM (5 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
157314
Default Alt Text
(139 KB)

Event Timeline