Page MenuHomePhorge

No OneTemporary

Size
363 KB
Referenced Files
None
Subscribers
None
This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/apps/sa-ews1/sa-ews1.cpp b/apps/sa-ews1/sa-ews1.cpp
index 67a1ac6..836e3d1 100644
--- a/apps/sa-ews1/sa-ews1.cpp
+++ b/apps/sa-ews1/sa-ews1.cpp
@@ -1,316 +1,318 @@
//===-- apps/sa-ews1/sa-ews1.cpp --------------------------------*- C++ -*-===//
//
// The RoSA Framework -- Application SA-EWS1
//
//===----------------------------------------------------------------------===//
///
/// \file apps/sa-ews1/sa-ews1.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2019
///
/// \brief The application SA-EWS1 implements the case study from the paper:
/// M. Götzinger, N. Taherinejad, A. M. Rahmani, P. Liljeberg, A. Jantsch, and
/// H. Tenhunen: Enhancing the Early Warning Score System Using Data Confidence
/// DOI: 10.1007/978-3-319-58877-3_12
//===----------------------------------------------------------------------===//
#include "rosa/agent/Abstraction.hpp"
#include "rosa/agent/Confidence.hpp"
#include "rosa/config/version.h"
#include "rosa/deluxe/DeluxeContext.hpp"
#include "rosa/support/csv/CSVReader.hpp"
#include "rosa/support/csv/CSVWriter.hpp"
#include <fstream>
using namespace rosa;
using namespace rosa::agent;
using namespace rosa::deluxe;
using namespace rosa::terminal;
const std::string AppName = "SA-EWS1";
/// Paths for the CSV files for simulation.
///
///@{
const std::string HRCSVPath = "HR.csv";
const std::string BRCSVPath = "BR.csv";
const std::string SpO2CSVPath = "SpO2.csv";
const std::string BPSysCSVPath = "BPSys.csv";
const std::string BodyTempCSVPath = "BodyTemp.csv";
const std::string ScoreCSVPath = "Score.csv";
///@}
/// How many cycles of simulation to perform.
const size_t NumberOfSimulationCycles = 16;
/// Warning levels for abstraction.
enum WarningScore { No = 0, Low = 1, High = 2, Emergency = 3 };
/// Helper function creating a deluxe agent for pre-processing sensory values.
///
/// Received values are first validated for confidence. Values which the
/// validator does not mark confident are ignored. Confident values are
/// abstracted into a \c WarningScore value, which is the result of the
/// processing function.
///
/// \note The result, \c WarningScore, is returned as \c uint32_t because
/// enumeration types are not integrated into built-in types. Hence, a master
/// to these agents receives its input as \c uint32_t values, and may cast them
/// to \c WarningScore explicitly.
///
/// \tparam T type of values to receive from the sensor
///
/// \param C the deluxe context to create the agent in
/// \param Name name of the new agent
/// \param CC confidence validator to use
/// \param A abstraction to use
///
/// \return handle for the new agent
template <typename T>
AgentHandle createLowLevelAgent(std::unique_ptr<DeluxeContext> &C,
const std::string &Name,
const Confidence<T> &CC,
const Abstraction<T, WarningScore> &A) {
- using handler = DeluxeAgent::D<uint32_t, T>;
+ using handler = std::function<Optional<uint32_t>(std::pair<T, bool>)>;
using result = Optional<uint32_t>;
return C->createAgent(
Name, handler([&, Name](std::pair<T, bool> I) -> result {
LOG_INFO_STREAM << "\n******\n"
<< Name << " " << (I.second ? "<New>" : "<Old>")
<< " value: " << I.first << "\n******\n";
return (I.second && CC(I.first)) ? result(A(I.first)) : result();
}));
}
int main(void) {
LOG_INFO_STREAM
<< '\n'
<< library_string() << " -- " << Color::Red << AppName << "app"
<< Color::Default << '\n'
<< Color::Yellow
<< "CSV files are read from and written to the current working directory."
<< Color::Default << '\n';
std::unique_ptr<DeluxeContext> C = DeluxeContext::create(AppName);
//
// Create deluxe sensors.
//
LOG_INFO("Creating sensors.");
// All sensors are created without defining a normal generator function, but
// with the default value of the second argument. That, however, requires the
// data type to be explicitly defined. This is good for simulation only.
AgentHandle HRSensor = C->createSensor<int32_t>("HR Sensor");
AgentHandle BRSensor = C->createSensor<int32_t>("BR Sensor");
AgentHandle SpO2Sensor = C->createSensor<int32_t>("SpO2 Sensor");
AgentHandle BPSysSensor = C->createSensor<int32_t>("BPSys Sensor");
AgentHandle BodyTempSensor = C->createSensor<float>("BodyTemp Sensor");
//
// Create functionalities.
//
LOG_INFO("Creating Functionalities for Agents.");
//
// Define confidence validators.
//
// Lower bounds are inclusive and upper bounds are exclusive.
Confidence<int32_t> HRConfidence(0, 501);
Confidence<int32_t> BRConfidence(0, 301);
Confidence<int32_t> SpO2Confidence(0, 101);
Confidence<int32_t> BPSysConfidence(0,501);
Confidence<float> BodyTempConfidence(-60,
nextRepresentableFloatingPoint(50.0f));
//
// Define abstractions.
//
RangeAbstraction<int32_t, WarningScore> HRAbstraction(
{{{0, 40}, Emergency},
{{40, 51}, High},
{{51, 60}, Low},
{{60, 100}, No},
{{100, 110}, Low},
{{110, 129}, High},
{{129, 200}, Emergency}},
Emergency);
RangeAbstraction<int32_t, WarningScore> BRAbstraction({{{0, 9}, High},
{{9, 14}, No},
{{14, 20}, Low},
{{20, 29}, High},
{{29, 50}, Emergency}},
Emergency);
RangeAbstraction<int32_t, WarningScore> SpO2Abstraction({{{1, 85}, Emergency},
{{85, 90}, High},
{{90, 95}, Low},
{{95, 100}, No}},
Emergency);
RangeAbstraction<int32_t, WarningScore> BPSysAbstraction(
{{{0, 70}, Emergency},
{{70, 81}, High},
{{81, 101}, Low},
{{101, 149}, No},
{{149, 169}, Low},
{{169, 179}, High},
{{179, 200}, Emergency}},
Emergency);
RangeAbstraction<float, WarningScore> BodyTempAbstraction(
{{{0.f, 28.f}, Emergency},
{{28.f, 32.f}, High},
{{32.f, 35.f}, Low},
{{35.f, 38.f}, No},
{{38.f, 39.5f}, High},
{{39.5f, 100.f}, Emergency}},
Emergency);
//
// Create low-level deluxe agents with \c createLowLevelAgent.
//
LOG_INFO("Creating low-level agents.");
AgentHandle HRAgent =
createLowLevelAgent(C, "HR Agent", HRConfidence, HRAbstraction);
AgentHandle BRAgent =
createLowLevelAgent(C, "BR Agent", BRConfidence, BRAbstraction);
AgentHandle SpO2Agent =
createLowLevelAgent(C, "SpO2 Agent", SpO2Confidence, SpO2Abstraction);
AgentHandle BPSysAgent =
createLowLevelAgent(C, "BPSys Agent", BPSysConfidence, BPSysAbstraction);
AgentHandle BodyTempAgent = createLowLevelAgent(
C, "BodyTemp Agent", BodyTempConfidence, BodyTempAbstraction);
//
// Connect sensors to low-level agents.
//
LOG_INFO("Connect sensors to their corresponding low-level agents.");
C->connectSensor(HRAgent, 0, HRSensor, "HR Sensor Channel");
C->connectSensor(BRAgent, 0, BRSensor, "BR Sensor Channel");
C->connectSensor(SpO2Agent, 0, SpO2Sensor, "SpO2 Sensor Channel");
C->connectSensor(BPSysAgent, 0, BPSysSensor, "BPSys Sensor Channel");
C->connectSensor(BodyTempAgent, 0, BodyTempSensor, "BodyTemp Sensor Channel");
//
// Create a high-level deluxe agent.
//
LOG_INFO("Create high-level agent.");
// The new agent logs its input values and results in the the sum of them.
AgentHandle BodyAgent = C->createAgent(
"Body Agent",
- DeluxeAgent::D<uint32_t, uint32_t, uint32_t, uint32_t, uint32_t,
- uint32_t>(
+ std::function<Optional<uint32_t>(
+ std::pair<uint32_t, bool>, std::pair<uint32_t, bool>,
+ std::pair<uint32_t, bool>, std::pair<uint32_t, bool>,
+ std::pair<uint32_t, bool>)>(
[](std::pair<uint32_t, bool> HR, std::pair<uint32_t, bool> BR,
std::pair<uint32_t, bool> SpO2, std::pair<uint32_t, bool> BPSys,
std::pair<uint32_t, bool> BodyTemp) -> Optional<uint32_t> {
LOG_INFO_STREAM << "\n*******\nBody Agent trigged with values:\n"
<< (HR.second ? "<New>" : "<Old>")
<< " HR warning score: " << HR.first << "\n"
<< (BR.second ? "<New>" : "<Old>")
<< " BR warning score: " << BR.first << "\n"
<< (SpO2.second ? "<New>" : "<Old>")
<< " SpO2 warning score: " << SpO2.first << "\n"
<< (BPSys.second ? "<New>" : "<Old>")
<< " BPSys warning score: " << BPSys.first << "\n"
<< (BodyTemp.second ? "<New>" : "<Old>")
<< " BodyTemp warning score: " << BodyTemp.first
<< "\n******\n";
return {HR.first + BR.first + SpO2.first + BPSys.first +
BodyTemp.first};
}));
//
// Connect low-level agents to the high-level agent.
//
LOG_INFO("Connect low-level agents to the high-level agent.");
C->connectAgents(BodyAgent, 0, HRAgent, "HR Agent Channel");
C->connectAgents(BodyAgent, 1, BRAgent, "BR Agent Channel");
C->connectAgents(BodyAgent, 2, SpO2Agent, "SpO2 Agent Channel");
C->connectAgents(BodyAgent, 3, BPSysAgent, "BPSys Agent Channel");
C->connectAgents(BodyAgent, 4, BodyTempAgent, "BodyTemp Agent Channel");
//
// For simulation output, create a logger agent writing the output of the
// high-level agent into a CSV file.
//
LOG_INFO("Create a logger agent.");
// Create CSV writer.
std::ofstream ScoreCSV(ScoreCSVPath);
csv::CSVWriter<uint32_t> ScoreWriter(ScoreCSV);
// The agent writes each new input value into a CSV file and produces nothing.
AgentHandle LoggerAgent = C->createAgent(
"Logger Agent",
- DeluxeAgent::D<unit_t, uint32_t>(
+ std::function<Optional<unit_t>(std::pair<uint32_t, bool>)>(
[&ScoreWriter](std::pair<uint32_t, bool> Score) -> Optional<unit_t> {
if (Score.second) {
// The state of \p ScoreWriter is not checked, expecting good.
ScoreWriter << Score.first;
}
return {};
}));
//
// Connect the high-level agent to the logger agent.
//
LOG_INFO("Connect the high-level agent to the logger agent.");
C->connectAgents(LoggerAgent, 0, BodyAgent, "Body Agent Channel");
//
// Do simulation.
//
LOG_INFO("Setting up and performing simulation.");
//
// Initialize deluxe context for simulation.
//
C->initializeSimulation();
//
// Open CSV files and register them for their corresponding sensors.
//
// Type aliases for iterators.
using CSVInt = csv::CSVFlatIterator<int32_t>;
using CSVFloat = csv::CSVFlatIterator<float>;
std::ifstream HRCSV(HRCSVPath);
C->registerSensorValues(HRSensor, CSVInt(HRCSV), CSVInt());
std::ifstream BRCSV(BRCSVPath);
C->registerSensorValues(BRSensor, CSVInt(BRCSV), CSVInt());
std::ifstream SpO2CSV(SpO2CSVPath);
C->registerSensorValues(SpO2Sensor, CSVInt(SpO2CSV), CSVInt());
std::ifstream BPSysCSV(BPSysCSVPath);
C->registerSensorValues(BPSysSensor, CSVInt(BPSysCSV), CSVInt());
std::ifstream BodyTempCSV(BodyTempCSVPath);
C->registerSensorValues(BodyTempSensor, CSVFloat(BodyTempCSV), CSVFloat());
//
// Simulate.
//
C->simulate(NumberOfSimulationCycles);
return 0;
}
diff --git a/examples/deluxe-interface/deluxe-interface.cpp b/examples/deluxe-interface/deluxe-interface.cpp
old mode 100755
new mode 100644
index 43222e4..78638ff
--- a/examples/deluxe-interface/deluxe-interface.cpp
+++ b/examples/deluxe-interface/deluxe-interface.cpp
@@ -1,191 +1,338 @@
//===-- examples/deluxe-interface/deluxe-interface.cpp ----------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file examples/deluxe-interface/deluxe-interface.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
-/// \date 2017
+/// \date 2017-2019
///
/// \brief A simple example on the \c rosa::deluxe::DeluxeContext and related
/// classes.
//===----------------------------------------------------------------------===//
#include "rosa/config/version.h"
#include "rosa/deluxe/DeluxeContext.hpp"
#include <algorithm>
#include <cmath>
#include <vector>
using namespace rosa;
using namespace rosa::deluxe;
using namespace rosa::terminal;
/// How many cycles of simulation to perform.
const size_t NumberOfSimulationCycles = 16;
-/// Helper function creating a deluxe agent for logging and forwarding values.
-///
-/// Received values are dumped to \c LOG_INFO_STREAM and then returned as
-/// result.
-///
-/// \tparam T type of values to handle
-///
-/// \param C the deluxe context to create the agent in
-/// \param Name name of the new agent
-///
-/// \return handle for the new agent
-template <typename T>
-AgentHandle createLowLevelAgent(std::unique_ptr<DeluxeContext> &C,
- const std::string &Name) {
- using handler = DeluxeAgent::D<T, T>;
- using result = Optional<T>;
- return C->createAgent(
- Name, handler([&, Name](std::pair<T, bool> I) -> result {
- LOG_INFO_STREAM << "\n******\n"
- << Name << " " << (I.second ? "<New>" : "<Old>")
- << " value: " << I.first << "\n******\n";
- return {I.first};
- }));
-}
-
int main(void) {
LOG_INFO_STREAM << '\n'
<< library_string() << " -- " << Color::Red
<< "deluxe-interface example" << Color::Default << '\n';
std::unique_ptr<DeluxeContext> C = DeluxeContext::create("Deluxe");
//
// Create deluxe sensors.
//
LOG_INFO("Creating sensors.");
// All sensors are created without defining a normal generator function, but
- // with the default value of the second argument. That, however, requires the
+ // with the default value of the last argument. That, however, requires the
// data type to be explicitly defined. This is good for simulation only.
+
+ // The first and second sensors do not receive master-input.
+ AgentHandle BoolSensor = C->createSensor<bool>("BoolSensor");
AgentHandle IntSensor = C->createSensor<int32_t>("IntSensor");
- AgentHandle FloatSensor = C->createSensor<float>("FloatSensor");
+ // This sensor receives master-input and dumps it to \c LOG_INFO_STREAM.
+ const std::string FloatSensorName = "FloatSensor";
+ AgentHandle FloatSensor = C->createSensor<uint32_t, float>(
+ FloatSensorName, [&FloatSensorName](std::pair<uint32_t, bool> I) {
+ LOG_INFO_STREAM << "\n******\n"
+ << FloatSensorName
+ << " master-input " << (I.second ? "<New>" : "<Old>")
+ << " value: " << I.first << "\n******\n";
+ });
+
+ // This sensor do not receive master-input but produces tuples.
+ using TupleType = DeluxeTuple<float, float>;
+ AgentHandle TupleSensor = C->createSensor<TupleType>("TupleSensor");
+
+ //
// Check and set execution policy for sensors.
+ //
LOG_INFO("Execution policies for sensors.");
LOG_INFO(std::to_string(*C->getExecutionPolicy(IntSensor)));
C->setExecutionPolicy(IntSensor, DeluxeExecutionPolicy::decimation(2));
C->setExecutionPolicy(FloatSensor, DeluxeExecutionPolicy::decimation(2));
LOG_INFO(std::to_string(*C->getExecutionPolicy(IntSensor)));
//
// Create low-level deluxe agents with \c createLowLevelAgent.
//
LOG_INFO("Creating low-level agents.");
- AgentHandle IntAgent = createLowLevelAgent<int32_t>(C, "IntAgent");
- AgentHandle FloatAgent = createLowLevelAgent<float>(C, "FloatAgent");
+ // All agents below dump their received values to \c LOG_INFO_STREAM on each
+ // triggering.
+
+ // This agent does not receive master-input and does not produce
+ // master-output. It results in the value it received.
+ const std::string BoolAgentName = "BoolAgent";
+ using BoolResult = Optional<bool>;
+ using BoolHandler = std::function<BoolResult(std::pair<bool, bool>)>;
+ AgentHandle BoolAgent = C->createAgent(
+ BoolAgentName,
+ BoolHandler([&BoolAgentName](std::pair<bool, bool> I) -> BoolResult {
+ LOG_INFO_STREAM << "\n******\n"
+ << BoolAgentName << " "
+ << (I.second ? "<New>" : "<Old>")
+ << " value: " << I.first << "\n******\n";
+ return {I.first};
+ }));
+
+ // This agent receives master-input but does not produce master-output. The
+ // agent maintains a state in \c IntAgentOffset. The master-input handler
+ // updates \c IntAgentOffset according to each received (new) value from its
+ // master. The slave-input handler results in the sum of the received value
+ // and the actual value of \c IntAgentOffset.
+ const std::string IntAgentName = "IntAgent";
+ using IntMasterHandler = std::function<void(std::pair<uint32_t, bool>)>;
+ using IntResult = Optional<int32_t>;
+ using IntHandler = std::function<IntResult(std::pair<int32_t, bool>)>;
+ uint32_t IntAgentOffset = 0;
+ AgentHandle IntAgent = C->createAgent(
+ IntAgentName,
+ // Master-input handler.
+ IntMasterHandler([&IntAgentName,
+ &IntAgentOffset](std::pair<uint32_t, bool> I) {
+ LOG_INFO_STREAM << "\n******\n"
+ << IntAgentName
+ << " master-input " << (I.second ? "<New>" : "<Old>")
+ << " value: " << I.first << "\n******\n";
+ if (I.second) {
+ IntAgentOffset = I.first;
+ }
+ }),
+ // Slave-input handler.
+ IntHandler([&IntAgentName,
+ &IntAgentOffset](std::pair<int32_t, bool> I) -> IntResult {
+ LOG_INFO_STREAM << "\n******\n"
+ << IntAgentName << " " << (I.second ? "<New>" : "<Old>")
+ << " value: " << I.first << "\n******\n";
+ return {I.first + IntAgentOffset};
+ }));
+
+ // This agent receives master-input and produces master-output. The
+ // master-input handler propagaates each received (new) value to its slave as
+ // master-output. The slave-input handler results in the value it received and
+ // produces no actual master-output.
+ const std::string FloatAgentName = "FloatAgent";
+ using FloatMasterResult = std::tuple<Optional<uint32_t>>;
+ using FloatMasterHandler =
+ std::function<FloatMasterResult(std::pair<uint32_t, bool>)>;
+ using FloatResult = std::tuple<Optional<float>, Optional<uint32_t>>;
+ using FloatHandler = std::function<FloatResult(std::pair<float, bool>)>;
+ AgentHandle FloatAgent = C->createAgent(
+ FloatAgentName,
+ // Master-input handler.
+ FloatMasterHandler([&FloatAgentName](
+ std::pair<uint32_t, bool> I) -> FloatMasterResult {
+ LOG_INFO_STREAM << "\n******\n"
+ << FloatAgentName
+ << " master-input " << (I.second ? "<New>" : "<Old>")
+ << " value: " << I.first << "\n******\n";
+ const auto Output =
+ I.second ? Optional<uint32_t>(I.first) : Optional<uint32_t>();
+ return {Output};
+ }),
+ // Slave-input handler.
+ FloatHandler([&FloatAgentName](std::pair<float, bool> I) -> FloatResult {
+ LOG_INFO_STREAM << "\n******\n"
+ << FloatAgentName << " "
+ << (I.second ? "<New>" : "<Old>")
+ << " value: " << I.first << "\n******\n";
+ return {{I.first}, {}};
+ }));
+
+ // This agent does not receive master-input and does not produce
+ // master-output. It results in the sum of the values it receives in a tuple.
+ const std::string TupleAgentName = "TupleAgent";
+ using TupleSumResult = Optional<DeluxeTuple<double>>;
+ using TupleHandler =
+ std::function<TupleSumResult(std::pair<TupleType, bool>)>;
+ AgentHandle TupleAgent = C->createAgent(
+ TupleAgentName,
+ TupleHandler(
+ [&TupleAgentName](std::pair<TupleType, bool> I) -> TupleSumResult {
+ LOG_INFO_STREAM << "\n******\n"
+ << TupleAgentName << " "
+ << (I.second ? "<New>" : "<Old>")
+ << " value: " << I.first << "\n******\n";
+ return {std::get<0>(I.first) + std::get<1>(I.first)};
+ }));
+ //
// Set execution policies for low-level agents.
+ //
LOG_INFO("Setting Execution policies for low-level agents.");
C->setExecutionPolicy(IntAgent, DeluxeExecutionPolicy::awaitAll({0}));
C->setExecutionPolicy(FloatAgent, DeluxeExecutionPolicy::awaitAll({0}));
//
// Connect sensors to low-level agents.
//
LOG_INFO("Connect sensors to their corresponding low-level agents.");
+ C->connectSensor(BoolAgent, 0, BoolSensor, "Bool Sensor Channel");
C->connectSensor(IntAgent, 0, IntSensor, "Int Sensor Channel");
C->connectSensor(FloatAgent, 0, FloatSensor, "Float Sensor Channel");
+ C->connectSensor(TupleAgent, 0, TupleSensor, "Tuple Sensor Channel");
//
// Create a high-level deluxe agent.
//
LOG_INFO("Create high-level agent.");
- // The new agent logs its input values and results in the the sum of them.
+ using SingleDoubleOutputType = Optional<DeluxeTuple<double>>;
+ using SingleUInt32OutputType = Optional<DeluxeTuple<uint32_t>>;
+ using NoOutputType = Optional<EmptyDeluxeTuple>;
+
+ // This agent does not receive master-input but produces master-output for its
+ // slaves at positions `1` and `2` but not for that at position `0`. The agent
+ // maintains a state in \c SumAgentState. The handler increments \c
+ // SumAgentState upon each received (new) `true` value from its slave at
+ // position `0`. Whenever \c SumAgentState has been updated, it is sent to the
+ // slaves at positions `1` and `2`. The handler results in the sum of the
+ // values received from slaves at positions `1`, `2`, and `3`.
+ using SumResult =
+ std::tuple<SingleDoubleOutputType, NoOutputType, SingleUInt32OutputType,
+ SingleUInt32OutputType, NoOutputType>;
+ using SumHandler = std::function<SumResult(
+ std::pair<DeluxeTuple<bool>, bool>, std::pair<DeluxeTuple<int32_t>, bool>,
+ std::pair<DeluxeTuple<float>, bool>,
+ std::pair<DeluxeTuple<double>, bool>)>;
+ uint32_t SumAgentState = 0;
AgentHandle SumAgent = C->createAgent(
- "Sum Agent", DeluxeAgent::D<double, int32_t, float>(
- [](std::pair<int32_t, bool> I1,
- std::pair<float, bool> I2) -> Optional<double> {
- LOG_INFO_STREAM
- << "\n*******\nSum Agent triggered with values:\n"
- << (I1.second ? "<New>" : "<Old>")
- << " int value: " << I1.first << "\n"
- << (I2.second ? "<New>" : "<Old>")
- << " float value: " << I2.first << "\n******\n";
- return {I1.first + I2.first};
- }));
+ "Sum Agent",
+ SumHandler([&SumAgentState](
+ std::pair<DeluxeTuple<bool>, bool> I0,
+ std::pair<DeluxeTuple<int32_t>, bool> I1,
+ std::pair<DeluxeTuple<float>, bool> I2,
+ std::pair<DeluxeTuple<double>, bool> I3) -> SumResult {
+ const auto V0 = std::get<0>(I0.first);
+ const auto V1 = std::get<0>(I1.first);
+ const auto V2 = std::get<0>(I2.first);
+ const auto V3 = std::get<0>(I3.first);
+ LOG_INFO_STREAM << "\n*******\nSum Agent triggered with values:\n"
+ << (I0.second ? "<New>" : "<Old>")
+ << " bool value: " << V0 << "\n"
+ << (I1.second ? "<New>" : "<Old>")
+ << " int value: " << V1 << "\n"
+ << (I2.second ? "<New>" : "<Old>")
+ << " float value: " << V2 << "\n"
+ << (I3.second ? "<New>" : "<Old>")
+ << " double value: " << V3 << "\n******\n";
+ if (I0.second && V0) {
+ ++SumAgentState;
+ }
+ const SingleUInt32OutputType MasterOutput =
+ I0.second && V0
+ ? SingleUInt32OutputType(DeluxeTuple<uint32_t>(SumAgentState))
+ : SingleUInt32OutputType();
+ const DeluxeTuple<double> Output = {V1 + V2 + V3};
+ return {{Output}, {}, {MasterOutput}, {MasterOutput}, {}};
+ }));
//
// Connect low-level agents to the high-level agent.
//
LOG_INFO("Connect low-level agents to the high-level agent.");
- C->connectAgents(SumAgent, 0, IntAgent, "Int Agent Channel");
- C->connectAgents(SumAgent, 1, FloatAgent, "Float Agent Channel");
+ C->connectAgents(SumAgent, 0, BoolAgent, "Bool Agent Channel");
+ C->connectAgents(SumAgent, 1, IntAgent, "Int Agent Channel");
+ C->connectAgents(SumAgent, 2, FloatAgent, "Float Agent Channel");
+ C->connectAgents(SumAgent, 3, TupleAgent, "Tuple Agent Channel");
//
// For simulation output, create a logger agent writing the output of the
// high-level agent into a log stream.
//
LOG_INFO("Create a logger agent.");
- // The agent logs each new input value and produces nothing.
+ // The agent dumps each received (new) value to \c LOG_INFO_STREAM and
+ // produces nothing; does not receive mater-input and does not produce
+ // master-output.
AgentHandle LoggerAgent =
C->createAgent("Logger Agent",
- DeluxeAgent::D<unit_t, double>(
+ std::function<Optional<unit_t>(std::pair<double, bool>)>(
[](std::pair<double, bool> Sum) -> Optional<unit_t> {
if (Sum.second) {
LOG_INFO_STREAM << "Result: " << Sum.first << "\n";
}
return {};
}));
//
// Connect the high-level agent to the logger agent.
//
LOG_INFO("Connect the high-level agent to the logger agent.");
C->connectAgents(LoggerAgent, 0, SumAgent, "Sum Agent Channel");
//
// Do simulation.
//
LOG_INFO("Setting up and performing simulation.");
//
// Initialize deluxe context for simulation.
//
C->initializeSimulation();
//
// Create some vectors and register them for their corresponding sensors.
//
+ std::vector<bool> BoolValues(NumberOfSimulationCycles);
+ std::generate(BoolValues.begin(), BoolValues.end(),
+ [i = 0](void) mutable -> bool { return (++i % 4) == 0; });
+ C->registerSensorValues(BoolSensor, BoolValues.begin(), BoolValues.end());
+
std::vector<int32_t> IntValues(NumberOfSimulationCycles);
std::generate(IntValues.begin(), IntValues.end(),
[i = 0](void) mutable { return ++i; });
C->registerSensorValues(IntSensor, IntValues.begin(), IntValues.end());
std::vector<float> FloatValues(NumberOfSimulationCycles);
std::generate(FloatValues.begin(), FloatValues.end(),
[f = 0.5f](void) mutable {
f += 0.3f;
return std::floor(f) + 0.5f;
});
C->registerSensorValues(FloatSensor, FloatValues.begin(), FloatValues.end());
+ std::vector<TupleType> TupleValues(NumberOfSimulationCycles);
+ std::generate(TupleValues.begin(), TupleValues.end(),
+ [f1 = 0.f, f2 = 3.14f](void) mutable -> TupleType {
+ f1 += f2;
+ f2 -= f1;
+ return {f1, f2};
+ });
+ C->registerSensorValues(TupleSensor, TupleValues.begin(), TupleValues.end());
+
//
// Simulate.
//
C->simulate(NumberOfSimulationCycles);
return 0;
}
diff --git a/examples/messaging/messaging.cpp b/examples/messaging/messaging.cpp
index 7d1269b..640a2d1 100644
--- a/examples/messaging/messaging.cpp
+++ b/examples/messaging/messaging.cpp
@@ -1,126 +1,127 @@
//===-- examples/messaging/messaging.cpp ------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file examples/messaging/messaging.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
-/// \date 2017
+/// \date 2017-2019
///
/// \brief An example showcasing features related to the \c rosa::Message class.
///
//===----------------------------------------------------------------------===//
#include "rosa/config/version.h"
#include "rosa/core/MessageHandler.hpp"
#include "rosa/support/log.h"
#include "rosa/support/terminal_colors.h"
using namespace rosa;
using namespace rosa::terminal;
int main(void) {
LOG_INFO_STREAM << library_string() << " -- " << Color::Red << "messaging"
<< Color::Default << '\n';
auto &Log = LOG_INFO_STREAM << '\n';
// Message interface.
auto PMsg = Message::create<uint8_t, uint16_t>(1, 2);
auto &Msg = *PMsg;
Log << "Checking on a 'Message with TypeList<<uint8_t, uint16_t>>':"
<< "\n Size: " << Msg.Size
<< "\n Pos 0 is uint8_t: " << Msg.isTypeAt<uint8_t>(0)
<< "\n Pos 1 is uint16_t: " << Msg.isTypeAt<uint16_t>(1)
<< "\n Pos 2 is uint32_t: " << Msg.isTypeAt<uint32_t>(1)
<< "\n Value at pos 0: " << PRINTABLE(Msg.valueAt<uint8_t>(0))
<< "\n Value at pos 1: " << Msg.valueAt<uint16_t>(1) << "\n\n";
// MessageMatcher.
using MyMatcher = MsgMatcher<uint8_t, uint16_t>;
Log << "Matching against 'TypeList<uint8_t, uint16_t>':"
<< "\n matching: " << MyMatcher::doesStronglyMatch(Msg);
auto Vs = MyMatcher::extractedValues(Msg);
Log << "\n value: '(" << PRINTABLE(std::get<0>(Vs)) << ", "
<< std::get<1>(Vs) << ")'\n\n";
using MyWrongMatcher = MsgMatcher<uint8_t, uint8_t>;
Log << "Matching against 'TypeList<uint8_t, uint8_t>':"
<< "\n matching: " << MyWrongMatcher::doesStronglyMatch(Msg) << "\n\n";
using MyAtom = AtomConstant<atom("atom")>;
const MyAtom &A = MyAtom::Value;
using MyNAtom = AtomConstant<atom("noatom")>;
auto PAMsg = Message::create(A);
auto &AMsg = *PAMsg;
Log << "Checking on a 'Message with TypeList<AtomConstant<atom(\"atom\")>>':"
<< "\n Size: " << AMsg.Size
<< "\n Pos 0 is 'AtomValue': " << AMsg.isTypeAt<AtomValue>(0)
<< "\n Pos 0 is 'AtomConstant<atom(\"noatom\")>': "
<< AMsg.isTypeAt<MyNAtom>(0) << "\n\n";
using MyAtomMatcher = MsgMatcher<MyAtom>;
Log << "Matching against 'TypeList<AtomConstant<atom(\"atom\")>>':"
<< "\n matching: " << MyAtomMatcher::doesStronglyMatch(AMsg)
<< "\n value: '("
- << to_string(std::get<0>(MyAtomMatcher::extractedValues(AMsg))) << ")'"
+ << std::to_string(std::get<0>(MyAtomMatcher::extractedValues(AMsg)))
+ << ")'"
<< "\n\n";
using MyWrongAtomMatcher = MsgMatcher<MyNAtom>;
Log << "Matching against 'TypeList<AtomConstant<atom(\"noatom\")>>':"
<< "\n matching: " << MyWrongAtomMatcher::doesStronglyMatch(AMsg)
<< "\n\n";
// Invoker.
auto IP = Invoker::wrap(Invoker::F<MyAtom>([&Log](MyAtom) noexcept->void {
Log << "** Handling 'Message with "
"TypeList<AtomConstant<atom(\"atom\")>>'.\n";
}));
auto &I = *IP; // Get a reference from the pointer.
Log << "Invoking a function of signature 'void(AtomConstant<atom(\"atom\")>) "
"noexcept':"
<< "\n with 'Message with TypeList<uint8_t, uint16_t>'"
<< "\n does Message match Invoker: " << I.match(Msg)
<< "\n invoking...";
I(Msg);
Log << "\n with 'Message with TypeList<AtomConstant<atom(\"atom\")>>'..."
<< "\n does Message match Invoker: " << I.match(AMsg)
<< "\n invoking...";
I(AMsg);
Log << "\n\n";
// MessageHandler.
MessageHandler Handler{
Invoker::F<uint8_t, uint16_t>([&Log](uint8_t, uint16_t) {
Log << "** Handling 'Message with TypeList<uint8_t, uint16_t>'\n";
}),
Invoker::F<MyAtom>([&Log](MyAtom) {
Log << "** Handling 'Message with "
"TypeList<AtomConstant<atom(\"atom\")>>'\n";
})};
auto PANMsg = Message::create(MyNAtom::Value);
auto &ANMsg = *PANMsg;
Log << "Handling Messages with 'MessageHandler "
"{ Invoker::F<uint8_t, uint16_t>, "
"Invoker::F<AtomConstant<atom(\"atom\")>> }':"
<< "\n 'Message with TypeList<uint8_t, uint16_t>'"
<< "\n can handle: " << Handler.canHandle(Msg) << "\n handling...";
Handler(Msg);
Log << "\n 'Message with TypeList<AtomConstant<atom(\"atom\")>>'"
<< "\n can handle: " << Handler.canHandle(AMsg) << "\n handling...";
Handler(AMsg);
Log << "\n 'Message with TypeList<AtomConstant<atom(\"noatom\")>>'"
<< "\n can handle: " << Handler.canHandle(ANMsg)
<< "\n handling...";
Handler(ANMsg);
Log << "\n\n";
Log << "Terminating, destroying automatic variables.\n";
return 0;
}
diff --git a/examples/type-facilities/type-facilities.cpp b/examples/type-facilities/type-facilities.cpp
index d714a73..b4a3ade 100644
--- a/examples/type-facilities/type-facilities.cpp
+++ b/examples/type-facilities/type-facilities.cpp
@@ -1,88 +1,88 @@
//===-- examples/type-facilities/type-facilities.cpp ------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file examples/type-facilities/type-facilities.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
-/// \date 2017
+/// \date 2017-2019
///
/// \brief An example showcasing various type-related support facilities.
///
//===----------------------------------------------------------------------===//
#include "rosa/config/version.h"
#include "rosa/support/log.h"
#include "rosa/support/terminal_colors.h"
#include "rosa/support/type_token.hpp"
using namespace rosa;
using namespace rosa::terminal;
int main(void) {
LOG_INFO_STREAM << library_string() << " -- " << Color::Red
<< "type facilities" << Color::Default << '\n';
auto &Log = LOG_TRACE_STREAM;
Log << "\nNumberOfBuiltinTypes: " << NumberOfBuiltinTypes
<< "\nTokenBits: " << token::TokenBits
<< "\nRepresentationBits: " << token::RepresentationBits
<< "\nMaxTokenizableListSize: " << token::MaxTokenizableListSize
<< "\n\n";
Log << "Type number information on 'uint8_t':";
constexpr TypeNumber TN = TypeNumberOf<uint8_t>::Value;
Log << "\n type number: " << PRINTABLE_TN(TN)
<< "\n size: " << TypeForNumber<TN>::Size
<< "\n name: " << TypeForNumber<TN>::Name << "\n\n";
Log << "Type number information on 'std::string':";
constexpr TypeNumber TNS = TypeNumberOf<std::string>::Value;
Log << "\n type number: " << PRINTABLE_TN(TNS)
<< "\n size: " << TypeForNumber<TNS>::Size
<< "\n name: " << TypeForNumber<TNS>::Name << "\n\n";
Log << "Type number information of AtomConstants:";
using Atom1 = AtomConstant<atom("atom1")>;
using Atom2 = AtomConstant<atom("atom2")>;
Log << "\n std::is_same<Atom1, Atom2>::value: "
<< std::is_same<Atom1, Atom2>::value << "\n TypeNumberOf<Atom1>::Value: "
<< PRINTABLE_TN(TypeNumberOf<Atom1>::Value)
<< "\n TypeNumberOf<Atom2>::Value: "
<< PRINTABLE_TN(TypeNumberOf<Atom2>::Value)
<< "\n name: " << TypeForNumber<TypeNumberOf<Atom1>::Value>::Name
<< "\n\n";
Log << "Type token information on 'TypeList<uint8_t, uint16_t, "
"std::string>':";
// \c rosa::Token is generated statically.
constexpr Token T = TypeToken<uint8_t, uint16_t, std::string>::Value;
STATIC_ASSERT(
(T == TypeListToken<TypeList<uint8_t, uint16_t, std::string>>::Value),
"alias template definition is wrong");
Token T_ = T; // We need a non-const value for dropping head later.
// Iterate over encoded entries in \c T_.
while (!emptyToken(T_)) {
Log << "\n token: " << PRINTABLE_TOKEN(T_)
<< "\n valid: " << validToken(T_) << "\n empty: " << emptyToken(T_)
- << "\n length: " << lengthOfToken(T_)
+ << "\n length: " << static_cast<size_t>(lengthOfToken(T_))
<< "\n full size: " << sizeOfValuesOfToken(T_)
<< "\n head type number: " << PRINTABLE_TN(headOfToken(T_))
<< "\n size of head: " << sizeOfHeadOfToken(T_)
<< "\n name of head: " << nameOfHeadOfToken(T_)
<< "\n is head uint8_t: " << isHeadOfTokenTheSameType<uint8_t>(T_)
<< "\n is head uint16_t: " << isHeadOfTokenTheSameType<uint16_t>(T_)
<< "\n is head std::string: "
<< isHeadOfTokenTheSameType<std::string>(T_) << "\nDropping head...";
dropHeadOfToken(T_);
}
// Here when Token became empty.
Log << "\n token: " << PRINTABLE_TOKEN(T_) << "\n empty: " << emptyToken(T_)
<< '\n';
return 0;
}
diff --git a/include/rosa/core/Invoker.hpp b/include/rosa/core/Invoker.hpp
index 9fa294f..10786aa 100644
--- a/include/rosa/core/Invoker.hpp
+++ b/include/rosa/core/Invoker.hpp
@@ -1,259 +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(typename GenSeq<sizeof...(Ts) + 1>::Type(),
- Matcher::extractedValues(Msg));
+ 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 {
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/Message.hpp b/include/rosa/core/Message.hpp
index ce9e6ec..7a1fb74 100644
--- a/include/rosa/core/Message.hpp
+++ b/include/rosa/core/Message.hpp
@@ -1,258 +1,259 @@
//===-- rosa/core/Message.hpp -----------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/core/Message.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
-/// \date 2017
+/// \date 2017-2019
///
/// \brief Declaration of \c rosa::Message base-class.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_CORE_MESSAGE_HPP
#define ROSA_CORE_MESSAGE_HPP
#include "rosa/support/log.h"
#include "rosa/support/tokenized_storages.hpp"
#include "rosa/core/forward_declarations.h"
namespace rosa {
/// *Message* interface.
///
/// The interface provides means to check the type of the stored values, but
/// actual data is to be managed by derived implementations.
///
/// A \c rosa::Message instance is an immutable data object that obtains its
/// data upon creation and provides only constant references for the stored
/// values.
///
/// \note Any reference obtained from a \c rosa::Message instance remains valid
/// only as long as the owning \c rosa::Message object is not destroyed.
///
/// \todo Some member functions of \c rosa::Message duplicate member functions
/// of \c rosa::TokenizedStorage, which cannot be easily factored out into a
/// common base class due to eventual diamond inheritance issues in derived
/// classes. Could this duplication be avoided?
class Message {
protected:
/// Creates a new instance.
///
/// \note No implementation for empty list.
///
/// \tparam Type type of the mandatory first argument
/// \tparam Types types of any further arguments
///
/// \note the actual arguments are ignored by the constructor it is only
/// their type that matters. The actual values are supposed to be handled by
/// any implementation derived from \c rosa::Message.
///
/// \pre \p Type and \p Types are all built-in types and the number of stored
/// values does not exceed \c rosa::token::MaxTokenizableListSize.
template <typename Type, typename... Types>
Message(const Type &, const Types &...) noexcept;
/// No copying and moving of \c rosa::Message instances.
///@{
Message(const Message &) = delete;
Message(Message &&) = delete;
Message &operator=(const Message &) = delete;
Message &operator=(Message &&) = delete;
///@}
public:
/// Creates a \c rosa::message_t object from constant lvalue references.
///
/// \tparam Type type of the mandatory first argument
/// \tparam Types types of any further arguments
///
/// \param T the first value to include in the \c rosa::Message
/// \param Ts optional further values to include in the \c rosa::Message
///
/// \return new \c rosa::message_t object created from the given arguments
template <typename Type, typename... Types>
static message_t create(const Type &T, const Types &... Ts) noexcept;
/// Creates a \c rosa::message_t object from rvalue references.
///
/// \tparam Type type of the mandatory first argument
/// \tparam Types types of any further arguments
///
/// \param T the first value to include in the \c rosa::Message
/// \param Ts optional further values to include in the \c rosa::Message
///
/// \return new \c rosa::message_t object created from the given arguments
template <typename Type, typename... Types>
static message_t create(Type &&T, Types &&... Ts) noexcept;
/// Represents the types of the values stored in \p this object.
///
/// A valid, non-empty \c rosa::Token representing the types of the values
/// stored in \p this object.
const Token T;
/// The number of values stored in \p this object.
///
/// That is the number of types encoded in \c rosa::Message::T.
- const size_t Size;
+ const token_size_t Size;
/// Destroys \p this object.
virtual ~Message(void);
/// Tells if the value stored at a given index is of a given type.
///
/// \note Any \c rosa::AtomConstant is encoded in \c rosa::Token as
/// the \c rosa::AtomValue wrapped into it.
///
/// \tparam Type type to match against
///
/// \param Pos index the type of the value at is to be matched against \p Type
///
/// \return if the value at index \p Pos of type \p Type
///
/// \pre \p Pos is a valid index:\code
/// Pos < Size
/// \endcode
- template <typename Type> bool isTypeAt(const size_t Pos) const noexcept;
+ template <typename Type> bool isTypeAt(const token_size_t Pos) const noexcept;
/// Gives a constant reference of a value of a given type stored at a given
/// index.
///
/// \tparam Type type to give a reference of
///
/// \param Pos index to set the reference for
///
/// \return constant reference of \p Type for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index and the value at index \p Pos is of type
/// \p Type:
/// \code
/// Pos < Size && isTypeAt<Type>(Pos)
/// \endcode
- template <typename Type> const Type &valueAt(const size_t Pos) const noexcept;
+ template <typename Type>
+ const Type &valueAt(const token_size_t Pos) const noexcept;
protected:
/// Provides an untyped pointer for the value at a given index.
///
/// \param Pos index to take a pointer for
///
/// \return untyped pointer for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index:\code
/// Pos < Size
/// \endcode
- virtual const void *pointerTo(const size_t Pos) const noexcept = 0;
+ virtual const void *pointerTo(const token_size_t Pos) const noexcept = 0;
};
/// Nested namespace with implementation for \c rosa::Message, consider it
/// private.
namespace {
/// Template class for an implementation of \c rosa::Message.
///
/// \tparam Types types whose values are to be stored
template <typename... Types> class LocalMessage;
/// Implementation of the template \c rosa::LocalMessage providing facilities
/// for storing values as a \c rosa::Message object.
///
/// \tparam Type type of the first mandatory value of the \c rosa::Message
/// \tparam Types of any further values
template <typename Type, typename... Types>
class LocalMessage<Type, Types...> final
: public Message,
private TokenizedStorage<Type, Types...> {
public:
/// Creates an instance from constant lvalue references.
///
/// \param T the mandatory first value to store in the \c rosa::Message object
/// \param Ts optional further values to store in the \c rosa::Message object
LocalMessage(const Type &T, const Types &... Ts) noexcept
: Message(T, Ts...),
TokenizedStorage<Type, Types...>(T, Ts...) {
ASSERT(this->T == this->ST && Size == this->size()); // Sanity check.
}
/// Creates an instance from rvalue references.
///
/// \param T the mandatory first value to store in the \c rosa::Message object
/// \param Ts optional further values to store in the \c rosa::Message object
LocalMessage(Type &&T, Types &&... Ts) noexcept
: Message(T, Ts...),
TokenizedStorage<Type, Types...>(std::move(T), std::move(Ts)...) {
ASSERT(this->T == this->ST && Size == this->size()); // Sanity check.
}
/// Provides an untyped pointer for the constant value stored at a position.
///
/// \param Pos the index of the value to return an untyped pointer for
///
/// \return untyped pointer for the constant value stored at index \p Pos
///
/// \pre \p Pos is a valid index:\code
/// Pos < Size
/// \endcode
- const void *pointerTo(const size_t Pos) const noexcept override {
+ const void *pointerTo(const token_size_t Pos) const noexcept override {
ASSERT(Pos < Size);
return TokenizedStorage<Type, Types...>::pointerTo(Pos);
}
/// Aborts the program!
///
/// Since \c rosa::Message instances are supposed to be immutable, the
/// non-const inherited function is overridden so that it aborts execution.
- void *pointerTo(const size_t) noexcept override {
+ void *pointerTo(const token_size_t) noexcept override {
ROSA_CRITICAL("Unallowed operation of rosa::LocalMessage");
}
};
} // End namespace
template <typename Type, typename... Types>
Message::Message(const Type &, const Types &...) noexcept
: T(TypeToken<typename std::decay<Type>::type,
typename std::decay<Types>::type...>::Value),
Size(lengthOfToken(T)) {
ASSERT(validToken(T) &&
lengthOfToken(T) == (1 + sizeof...(Types))); // Sanity check.
- LOG_TRACE("Creating Message with Token(" + to_string(T) + ")");
+ LOG_TRACE("Creating Message with Token(" + std::to_string(T) + ")");
}
/// \note The implementation instantiates a private local template class
/// \c LocalMessage.
template <typename Type, typename... Types>
message_t Message::create(const Type &T, const Types &... Ts) noexcept {
return message_t(new LocalMessage<Type, Types...>(T, Ts...));
}
/// \note The implementation instantiates a private local template class
/// \c LocalMessage.
template <typename Type, typename... Types>
message_t Message::create(Type &&T, Types &&... Ts) noexcept {
return message_t(
new LocalMessage<Type, Types...>(std::move(T), std::move(Ts)...));
}
template <typename Type>
-bool Message::isTypeAt(const size_t Pos) const noexcept {
+bool Message::isTypeAt(const token_size_t Pos) const noexcept {
ASSERT(Pos < Size);
Token TT = T;
dropNOfToken(TT, Pos);
return isHeadOfTokenTheSameType<Type>(TT);
}
template <typename Type>
-const Type &Message::valueAt(const size_t Pos) const noexcept {
+const Type &Message::valueAt(const token_size_t Pos) const noexcept {
ASSERT(Pos < Size && isTypeAt<Type>(Pos));
return *static_cast<const Type *>(pointerTo(Pos));
}
} // End namespace rosa
#endif // ROSA_CORE_MESSAGE_HPP
diff --git a/include/rosa/core/MessageMatcher.hpp b/include/rosa/core/MessageMatcher.hpp
index 71bd69f..0092cb9 100644
--- a/include/rosa/core/MessageMatcher.hpp
+++ b/include/rosa/core/MessageMatcher.hpp
@@ -1,201 +1,201 @@
//===-- rosa/core/MessageMatcher.hpp ----------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/core/MessageMatcher.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
-/// \date 2017
+/// \date 2017-2019
///
/// \brief Facilities for checking and matching types of values stored in
/// \c rosa::Message instances.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_CORE_MESSAGEMATCHER_HPP
#define ROSA_CORE_MESSAGEMATCHER_HPP
#include "rosa/core/Message.hpp"
#include <tuple>
namespace rosa {
/// Provides features to type-check a \c rosa::Message instance and extract
/// stored values from it into an \c std::tuple instance with matching type
/// arguments.
///
/// \tparam List \c rosa::TypeList to check the stored values against
template <typename List> struct MessageMatcher;
/// Definition of \c rosa::MessageMatcher for non-empty lists of types, like
/// \c rosa::Message itself.
///
/// \tparam Type first mandatory type
/// \tparam Types any further types
template <typename Type, typename... Types>
struct MessageMatcher<TypeList<Type, Types...>> {
/// \c rosa::Token associated to the given \c rosa::TypeList.
static constexpr Token T = TypeToken<Type, Types...>::Value;
/// Tells if the values stored in a \c rosa::Message instance are matching
/// types given as \c rosa::TypeList<T, Types...>, considering
/// \c rosa::AtomConstant instead of \c rosa::AtomValue.
///
/// \param Msg \c rosa::Message to match
///
/// \return whether the types of values stored in \p Msg matches
/// \c rosa::TypeList<Type, Types...>
static inline bool doesStronglyMatch(const Message &Msg) noexcept;
/// Gives a \c std::tuple with references to the values stored in a
/// type-matching instance of \c rosa::Message.
///
/// \param Msg \c rosa::Message to extract values from
///
/// \return \c std::tuple with references to the values stored in \p Msg
///
/// \pre Types of the values stored in \p Msg matches
/// \c rosa::TypeList<Type, Types...>:\code
/// doesStronglyMatch(Msg)
/// \endcode
static inline std::tuple<const Type &, const Types &...>
extractedValues(const Message &Msg) noexcept;
};
/// Turns a list of types into a \c rosa::TypeList for \c rosa::MessageMatcher.
template <typename Type, typename... Types>
using MsgMatcher = MessageMatcher<TypeList<Type, Types...>>;
/// Nested namespace with implementation for features of
/// \c rosa::MessageMatcher, consider it private.
namespace {
/// \defgroup MessageMatcherImpl Implementation for rosa::MessageMatcher
///
/// An implementation of type-checking and value extraction for
/// \c rosa::MessageMatcher.
///
///@{
/// Template declaration of \c MessageMatcherImpl.
///
/// \tparam List \c rosa::TypeList to match against
template <typename List> struct MessageMatcherImpl;
/// Specialization for \c rosa::EmptyTypeList.
template <> struct MessageMatcherImpl<EmptyTypeList> {
static bool doesStronglyMatchFrom(const Message &Msg,
- const size_t Pos) noexcept {
+ const token_size_t Pos) noexcept {
// Matching EmptyTypeList only if reached the end of the stored types.
return Pos == Msg.Size;
}
static std::tuple<> extractedValuesFrom(const Message &Msg,
- const size_t Pos) noexcept {
+ const token_size_t Pos) noexcept {
// It is valid to extract an empty list only if we reached the end of
// stored values.
ASSERT(doesStronglyMatchFrom(Msg, Pos));
return std::tie();
}
};
/// Specialization for \c rosa::AtomValue in the head.
template <AtomValue V, typename... Ts>
struct MessageMatcherImpl<TypeList<AtomConstant<V>, Ts...>> {
static bool doesHeadStronglyMatchAt(const Message &Msg,
- const size_t Pos) noexcept {
+ const token_size_t Pos) noexcept {
// Matching a \c rosa::AtomConstant in the head if there is a type stored at
// \p Pos, the stored type is \c rosa::AtomValue, and the corresponding
// value matches the \c rosa::AtomValue \p V.
return Pos < Msg.Size && Msg.isTypeAt<AtomValue>(Pos) &&
Msg.valueAt<AtomValue>(Pos) == V;
}
static bool doesStronglyMatchFrom(const Message &Msg,
- const size_t Pos) noexcept {
+ const token_size_t Pos) noexcept {
// Matching a non-empty list if the head is matching and the rest of the
// list is matching.
return doesHeadStronglyMatchAt(Msg, Pos) &&
MessageMatcherImpl<TypeList<Ts...>>::doesStronglyMatchFrom(Msg,
Pos + 1);
}
static std::tuple<const AtomConstant<V> &, const Ts &...>
- extractedValuesFrom(const Message &Msg, const size_t Pos) noexcept {
+ extractedValuesFrom(const Message &Msg, const token_size_t Pos) noexcept {
// Extracting for a non-empty list with a matching \c rosa::AtomConstant in
// the head by getting the encoded \c rosa::AtomConstant and concatenating
// it with values extracted for the rest of the list.
ASSERT(doesHeadStronglyMatchAt(Msg, Pos));
return std::tuple_cat(
std::tie(AtomConstant<V>::Value),
MessageMatcherImpl<TypeList<Ts...>>::extractedValuesFrom(Msg, Pos + 1));
}
};
/// Definition for the general case when a regular built-in type (not a
/// \c rosa::AtomConstant) is in the head.
template <typename T, typename... Ts>
struct MessageMatcherImpl<TypeList<T, Ts...>> {
static bool doesHeadStronglyMatchAt(const Message &Msg,
- const size_t Pos) noexcept {
+ const token_size_t Pos) noexcept {
// Matching the head if there is a type stored at \p Pos, and the stored
// type is \p T.
return Pos < Msg.Size && Msg.isTypeAt<T>(Pos);
}
static bool doesStronglyMatchFrom(const Message &Msg,
- const size_t Pos) noexcept {
+ const token_size_t Pos) noexcept {
// Matching a non-empty list if the head is matching and the rest of the
// list is matching.
return doesHeadStronglyMatchAt(Msg, Pos) &&
MessageMatcherImpl<TypeList<Ts...>>::doesStronglyMatchFrom(Msg,
Pos + 1);
}
static std::tuple<const T &, const Ts &...>
- extractedValuesFrom(const Message &Msg, const size_t Pos) noexcept {
+ extractedValuesFrom(const Message &Msg, const token_size_t Pos) noexcept {
// Extracting for a non-empty list with a matching head by getting the
// value for the head and concatenating it with values extracted for the
// rest of the list.
ASSERT(doesHeadStronglyMatchAt(Msg, Pos));
return std::tuple_cat(
std::tie(Msg.valueAt<T>(Pos)),
MessageMatcherImpl<TypeList<Ts...>>::extractedValuesFrom(Msg, Pos + 1));
}
};
///@}
} // End namespace
template <typename Type, typename... Types>
bool MessageMatcher<TypeList<Type, Types...>>::doesStronglyMatch(
const Message &Msg) noexcept {
// \note Fail quick on \c rosa::MessageMatcher::T, then match against list
// with squashed integers the way \c rosa::Token is generated.
return T == Msg.T &&
MessageMatcherImpl<typename SquashedTypeList<
TypeList<Type, Types...>>::Type>::doesStronglyMatchFrom(Msg, 0);
}
template <typename Type, typename... Types>
std::tuple<const Type &, const Types &...>
MessageMatcher<TypeList<Type, Types...>>::extractedValues(
const Message &Msg) noexcept {
ASSERT(doesStronglyMatch(Msg));
// \note Match against a list with squashed integers as \c rosa::Token is
// generated.
return MessageMatcherImpl<typename SquashedTypeList<
TypeList<Type, Types...>>::Type>::extractedValuesFrom(Msg, 0);
}
} // End namespace rosa
#endif // ROSA_CORE_MESSAGEMATCHER_HPP
diff --git a/include/rosa/core/Unit.h b/include/rosa/core/Unit.h
index 377443a..47c42f6 100644
--- a/include/rosa/core/Unit.h
+++ b/include/rosa/core/Unit.h
@@ -1,114 +1,114 @@
//===-- rosa/core/Unit.h ----------------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/core/Unit.h
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Declaration of \c rosa::Unit base-class.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_CORE_UNIT_H
#define ROSA_CORE_UNIT_H
#include "rosa/support/atom.hpp"
#include "rosa/core/forward_declarations.h"
#include <ostream>
#include <string>
namespace rosa {
/// Base class for every entity in a \c rosa::System that has to be identified
/// and traced.
///
/// \note Life-cycle of \c rosa::Unit instances is supposed to be managed by the
/// \c rosa::System owning the instance, do not create and destroy any
/// \c rosa::Unit directly.
class Unit {
public:
/// Identifies the *kind* of \p this object.
///
/// \note Kind is dependent on the \c rosa::System owning \p this object.
const AtomValue Kind;
/// Unique identifier for \p this object.
///
/// \note The unique identifier is assigned by the \c rosa::System owning
/// \p this object upon creation.
const id_t Id;
/// Textual identifier of \p this object.
///
/// \note Textual identifiers of \c rosa::Unit instances are not necessarily
/// unique in their owning \c rosa::System.
const std::string Name;
protected:
/// The \c rosa::System owning \p this object.
System &S;
public:
- /// Full qualified name of \p this object.
+ /// Fully qualified name of \p this object.
const std::string FullName;
public:
/// Creates a new instnace.
///
/// \param Kind the kind of the new instance
/// \param Id the unique identifier of the new instance
/// \param Name the name of the new instance
/// \param S \c rosa::System owning the new instance
///
/// \pre \p Name is not empty:\code
/// !Name.empty()
/// \endcode
Unit(const AtomValue Kind, const id_t Id, const std::string &Name,
System &S) noexcept;
/// No copying and moving of \c rosa::Unit instances is possible.
///@{
Unit(const Unit &) = delete;
Unit(Unit &&) = delete;
Unit &operator=(const Unit &) = delete;
Unit &operator=(Unit &&) = delete;
///@}
/// Destroys \p this object.
virtual ~Unit(void);
/// Dumps \p this object into a \c std::string for tracing purposes.
///
/// Subclasses are supposed to override this function.
///
/// \return \c std::string representing the state of \p this object
virtual std::string dump(void) const noexcept;
protected:
/// Returns a reference to the \c rosa::System owning \p this object.
///
/// \note Subclasses may override the function to return a reference of a
/// subtype of \c rosa::System.
///
/// \return reference of \c rosa::Unit::S
virtual System &system() const noexcept;
};
/// Dumps a \c rosa::Unit instance to a given \c std::ostream.
///
/// \param [in,out] OS output stream to dump to
/// \param U \c rosa::Unit to dump
///
/// \return \p OS after dumping \p U to it
std::ostream &operator<<(std::ostream &OS, const Unit &U);
} // End namespace rosa
#endif // ROSA_CORE_UNIT_H
diff --git a/include/rosa/deluxe/DeluxeAgent.hpp b/include/rosa/deluxe/DeluxeAgent.hpp
old mode 100755
new mode 100644
index 809290a..7a94a57
--- a/include/rosa/deluxe/DeluxeAgent.hpp
+++ b/include/rosa/deluxe/DeluxeAgent.hpp
@@ -1,706 +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 DAHANDLERNAME(N) handleSlave_##N
+#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 DAHANDLERDEFN(T, N) \
- void DAHANDLERNAME(N)(atoms::Slave, id_t SlaveId, T Value) noexcept { \
- saveInput(SlaveId, Value); \
+#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 DAHANDLERDEFN with identical arguments.
+/// Convenience macro for \c DASLAVEHANDLERDEFN with identical arguments.
///
-/// \see \c DAHANDLERDEFN
+/// \see \c DASLAVEHANDLERDEFN
///
-/// This macro can be used instead of \c DAHANDLERDEFN if the actual value of
-/// \p T can be used as a part of a valid identifier.
+/// 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 DAHANDLERDEF(T) DAHANDLERDEFN(T, T)
+#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 DAHANDLERDEFN.
+/// \c DAMASTERHANDLERDEFN.
///
/// Used in the constructor of \c rosa::deluxe::DeluxeAgent to initialize super
-/// class \c rosa::Agent with member function defined by \c DAHANDLERDEFN.
+/// class \c rosa::Agent with member function defined by \c DAMASTERHANDLERDEFN.
///
-/// \see \c DAHANDLERDEFN, \c THISMEMBER
+/// \see \c DAMASTERHANDLERDEFN, \c THISMEMBER
///
/// \param N name suffix for the function identifier
-#define DAHANDLERREF(N) THISMEMBER(DAHANDLERNAME(N))
+#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. Types of input values are consistent throughout all the
-/// input-related containers. No *slave* is registered at more than one input
-/// position. *Slave* registrations and corresponding reverse lookup information
-/// are consistent.
+/// 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:
- /// Template alias for function objects used to process input and generate
- /// output for \c rosa::deluxe::DeluxeAgent.
- ///
- /// The output generated by the function is optional as an agent may decide
- /// not to output anything at some situation.
- ///
- /// \note The function used for \c D is to be \c noexcept.
- ///
- /// \tparam T type of output
- /// \tparam As types of input values
- template <typename T, typename... As>
- using D = std::function<Optional<T>(std::pair<As, bool>...)>;
-
/// The type of values produced by \p this object.
///
- /// That is the type of values \p this object sends to its *master*.
+ /// That is the types of values \p this object sends to its *master* in a \c
+ /// rosa::deluxe::DeluxeTUple.
///
/// \see \c rosa::deluxe::DeluxeAgent::master
- const TypeNumber OutputType;
+ const Token OutputType;
/// Number of inputs processed by \p this object.
const size_t NumberOfInputs;
-private:
+ /// 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::TypeNumber values stored here match the corresponding
- /// values in \c rosa::deluxe::DeluxeAgent::InputValues.
+ /// \note The \c rosa::Token values stored correspond to \c
+ /// rosa::deluxe::DeluxeTuple instances at each argument position. The \c
+ /// rosa::TypeNumber values from the stored \c rosa::Token values match the
+ /// corresponding values in \c rosa::deluxe::DeluxeAgent::InputValues in
+ /// order.
///
- /// \note The position of a type in the \c std::vector indicates which
- /// argument of \p this object's processing function it belongs to. See also
- /// \c rosa::deluxe::DeluxeAgent::D.
- const std::vector<TypeNumber> InputTypes;
+ /// \note The position of a \c rosa::Token in the \c std::vector indicates
+ /// which argument of \p this object's processing function it belongs to. See
+ /// also \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
+ const std::vector<Token> InputTypes;
+
+ /// Indicates which element of an input is expected from any particular
+ /// *slave*.
+ ///
+ /// The *slave* is supposed to send one \c rosa::deluxe::DeluxeTuple value
+ /// element by element in their order of definition. This member field tells
+ /// the element at which position in the tuple should be received next from
+ /// the *slave* at a given position.
+ ///
+ /// \p this object is supposed to be triggered only when input values has been
+ /// received completely, that is all values in the field should hold the value
+ /// `0`.
+ ///
+ /// \see \c rosa::deluxe::DeluxeAgent::handleTrigger
+ /// \c rosa::deluxe::DeluxeAgent::saveInput
+ std::vector<token_size_t> InputNextPos;
/// Indicates whether any particular input value has been changed since the
/// last trigger received from the system.
///
/// All the flags are reset to \c false upon handling a trigger and then set
/// to \c true by \c rosa::deluxe::DeluxeAgent::saveInput when storing a new
/// input value in \c rosa::deluxe::DeluxeAgent::InputValues.
///
/// \note The position of a flag in the \c std::vector indicates which
/// argument of \p this object's processing function it belongs to. See also
- /// \c rosa::deluxe::DeluxeAgent::D.
+ /// \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
std::vector<bool> InputChanged;
+ /// Tells at which position in \c rosa::deluxe::DeluxeAgent::InputValues the
+ /// input from any particular *slave* starts.
+ ///
+ /// \note A value in the vector corresponds to the *slave* at the same
+ /// position and it is the sum of the elements of input values from *slaves*
+ /// at previous positions.
+ ///
+ /// \see \c rosa::deluxe::DeluxeAgent::saveInput
+ const std::vector<token_size_t> InputStorageOffsets;
+
/// Stores the actual input values.
///
/// \note The types of stored values match the corresponding
- /// \c rosa::TypeNumber values in \c rosa::deluxe::DeluxeAgent::InputTypes.
+ /// \c rosa::TypeNumber values (in \c rosa::Token in order) in \c
+ /// rosa::deluxe::DeluxeAgent::InputTypes.
///
/// \note The position of a value in the \c rosa::AbstractTokenizedStorage
- /// indicates which argument of \p this object's processing function it is.
- /// See also \c rosa::deluxe::DeluxeAgent::D.
+ /// 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 function processing *slave* inputs and generating optional
- /// output to *master* is captured in a lambda expression that is in turn
- /// wrapped in a \c std::function object. The lambda expression calls the
- /// processing function with the actual input data and sends its result -- if
- /// any -- to *master* by calling \c rosa::deluxe::DeluxeAgent::sendToMaster.
- /// Also, all the flags stored in \c rose::deluxe::DeluxeAgent::InputChanged
- /// are reset when the current input values are processed. The function
- /// \c rosa::deluxe::DeluxeAgent::handleTrigger needs only to call the
+ /// 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::triggerHandlerFromProcessingFunction
+ /// \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::D.
+ /// \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 whether types \p As... match the input types of \p this object.
+ /// Tells the unique identifier of the *master* of \p this object, if any
+ /// registered.
///
- /// \tparam As types to match against values in
- /// \c rosa::deluxe::DeluxeAgent::InputTypes
+ /// \return the unique identifier of the *master*
///
- /// \return if types \p As... match \c rosa::TypeNumber values stored in
+ /// \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
- template <typename... As> bool inputTypesMatch(void) const noexcept;
+ ///
+ /// \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 The type arguments \p As... match the input types of \p this object
- /// and the provided indices \p S0... constitute a proper sequence for
- /// accessing input values and their change flags: \code
- /// inputTypesMatch<As...>() && sizeof...(As) == sizeof...(S0)
+ /// \pre Statically, all type arguments \p As... are instances of \c
+ /// rosa::deluxe::DeluxeTuple and the provided indices \p S0... match the
+ /// length of \p As...: \code
+ /// TypeListAllDeluxeTuple<TypeList<As...>>::Value &&
+ /// sizeof...(As) == sizeof...(S0)
+ /// \endcode Dynamically, type arguments \p As... match the input types of \p
+ /// this object: \code
+ /// inputTypesMatch<TypeList<As...>>()
/// \endcode
template <typename... As, size_t... S0>
std::tuple<std::pair<As, bool>...> prepareCurrentInputs(Seq<S0...>) const
noexcept;
- /// Invokes a processing function matching the output and input types of
- /// \p this object with actual arguments provided in a \c std::tuple.
+ /// 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... As, size_t... S0>
- static Optional<T> invokeWithTuple(D<T, As...> F,
- std::tuple<std::pair<As, bool>...> Args,
- Seq<S0...>) noexcept;
+ 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 a processing function into a trigger handler.
+ /// 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
///
- /// \pre Template arguments \p T and \p As... match the corresponding
- /// types \p this object was created with: \code
- /// OutputType == TypeNumberOf<T>::Value && inputTypesMatch<As...>()
+ /// \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 T, typename... As>
- H triggerHandlerFromProcessingFunction(D<T, As...> &&F) noexcept;
+ 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*.
///
- /// \todo Enforce F does not potentially throw exception.
+ /// 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 T and \p As...
- /// is not a built-in type.
+ /// \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 F function to process input values and generate output with
- ///
- /// \pre Statically, all of the type arguments \p T and \p As... is a
- /// built-in type: \code
- /// TypeListSubsetOf<TypeList<T, As...>, BuiltinTypes>::Value
- /// \endcode Dynamically, the instance is created as of kind
- /// \c rosa::deluxe::atoms::AgentKind: \code
+ /// \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
- template <typename T, typename... As,
+ ///
+ /// \see \c rosa::deluxe::DeluxeTuple
+ template <typename MT, typename T, typename... Ts, typename... As,
typename = std::enable_if_t<
- TypeListSubsetOf<TypeList<T, As...>, BuiltinTypes>::Value>>
- DeluxeAgent(const AtomValue Kind, const id_t Id, const std::string &Name,
- MessagingSystem &S, D<T, As...> &&F) noexcept;
+ 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 type of values consumed from the *slave* at a position.
+ /// Tells the types of values consumed from the *slave* at a position.
///
- /// That is the type of values \p this object expect to be sent to it by its
- /// *slave* registered at position \p Pos.
+ /// That is the type of values \p this object expect to be sent to it in a \c
+ /// rosa::deluxe::DeluxeTuple by its *slave* registered at position \p Pos.
///
/// \see \c rosa::deluxe::DeluxeAgent::slave
///
/// \param Pos position of *slave*
///
- /// \return \c rosa::TypeNumber representing the type of values consumed from
+ /// \return \c rosa::Token representing the types of values consumed from
/// the *slave* at position \p Pos
///
/// \pre \p Pos is a valid index of input: \code
/// Pos < NumberOfInputs
/// \endcode
- TypeNumber inputType(const size_t Pos) const noexcept;
+ Token inputType(const size_t Pos) const noexcept;
+
+ /// Tells the 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:
+ /// 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]) ||
+ /// 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]))
+ /// 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.
///
- /// \tparam T type of the value to send
+ /// The elements from \p Value are sent one by one in separate messages to the
+ /// *master*.
+ ///
+ /// \tparam Ts types of the elements in \p Value
+ /// \tparam S0 indices for accessing elements of \p Value
+ ///
+ /// \param Value value to send
+ ///
+ /// \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 \p T matches \c rosa::deluxe::DeluxeiAgent::OutputType: \code
- /// OutputType == TypeNumberOf<T>::Value
+ /// \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 T> void sendToMaster(const T &Value) noexcept;
+ template <typename... Ts, size_t... S0>
+ void sendToSlave(const size_t Pos, const DeluxeTuple<Ts...> &Value,
+ Seq<S0...>) noexcept;
/// Generates the next output by processing current input values upon trigger
/// from the system.
///
/// Executes \c rosa::deluxe::DeluxeAgent::FP.
///
/// \note The only argument is a \c rosa::AtomConstant, hence its actual
/// value is ignored.
+ ///
+ /// \pre Master-input and all input from *slaves* are supposed to be
+ /// completely received upon triggering: \code
+ /// MasterInputNextPos == 0 &&
+ /// std::all_of(InputNextPos.begin(), InputNextPos.end(),
+ /// [](const token_size_t &I){return I == 0;})
+ /// \endcode
void handleTrigger(atoms::Trigger) noexcept;
/// Stores a new input value from a *slave*.
///
- /// The function stores \p Value in \c rosa::deluxe::DeluxeAgent::InputValues
- /// at the position associated to \p Id in
- /// \c rosa::deluxe::DeluxeAgent::SlaveIds and also sets the corresponding
- /// flag in \c rosa::deluxe::DeluxeAgent::InputChanged.
+ /// The function stores \p Value at position \p Pos in \c
+ /// rosa::deluxe::DeluxeAgent::InputValues at the position associated to \p Id
+ /// in \c rosa::deluxe::DeluxeAgent::SlaveIds and also sets the corresponding
+ /// flag in \c rosa::deluxe::DeluxeAgent::InputChanged. The function also
+ /// takes care of checking and updating \c
+ /// rosa::deluxe::DeluxeSensor::MasterInputNextPos at the corresponding
+ /// position: increments the value and resets it to `0` when the last element
+ /// is received.
///
/// \note Utilized by member functions of group \c DeluxeAgentInputHandlers.
///
/// \tparam T type of input to store
///
/// \param Id unique identifier of *slave*
+ /// \param Pos position of the value in the \c rosa::deluxe::DeluxeTuple
/// \param Value the input value to store
///
- /// \pre The *slave* with \p Id is registered and the input from it is
- /// expected to be of type \p T: \code
+ /// \pre The *slave* with \p Id is registered, \p Pos is the expected
+ /// position of input from the *slave*, and the input from it is expected to
+ /// be of type \p T: \code
/// SlaveIds.find(Id) != SlaveIds.end() &&
- /// InputTypes[SlaveIds.find(Id)->second] == TypeNumberOf<T>::Value
+ /// Pos == InputNextPos[SlaveIds.find(Id)->second] &&
+ /// typeAtPositionOfToken(InputTypes[SlaveIds.find(Id)->second], Pos) ==
+ /// TypeNumberOf<T>::Value
+ /// \endcode
+ template <typename T>
+ void saveInput(id_t Id, 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 saveInput(id_t Id, T Value) noexcept;
+ template <typename T>
+ void saveMasterInput(id_t Id, token_size_t Pos, T Value) noexcept;
- /// \defgroup DeluxeAgentInputHandlers Input handlers of rosa::deluxe::DeluxeAgent
+ /// \defgroup DeluxeAgentInputHandlers Input handlers of
+ /// rosa::deluxe::DeluxeAgent
///
/// Definition of member functions handling messages from *slaves* with
/// different types of input
///
/// A *master* generally needs to be prepared to deal with values of any
- /// built-in type. Each type requires a separate message handler, which are
- /// implemented by these functions. The functions instantiate
- /// \c rosa::deluxe::DeluxeAgent::saveInput with the proper template argument
- /// and pass the content of the message on for processing.
+ /// 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 DAHANDLERDEF.
+ /// \note The member functions in this group are defined by \c
+ /// DASLAVEHANDLERDEF.
///
/// \note Keep these definitions in sync with \c rosa::BuiltinTypes.
///
///@{
- DAHANDLERDEF(AtomValue)
- DAHANDLERDEF(int16_t)
- DAHANDLERDEF(int32_t)
- DAHANDLERDEF(int64_t)
- DAHANDLERDEF(int8_t)
- DAHANDLERDEFN(long double, long_double)
- DAHANDLERDEFN(std::string, std__string)
- DAHANDLERDEF(uint16_t)
- DAHANDLERDEF(uint32_t)
- DAHANDLERDEF(uint64_t)
- DAHANDLERDEF(uint8_t)
- DAHANDLERDEF(unit_t)
- DAHANDLERDEF(bool)
- DAHANDLERDEF(double)
- DAHANDLERDEF(float)
+ 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::inputTypesMatch, consider it private.
+/// 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 {
-/// Template \c struct whose specializations provide a recursive implementation
-/// for \c rosa::deluxe::DeluxeAgent::inputTypesMatch.
+/// 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 Matching a list of types \p As... against a \c std::vector of
-/// \c rosa::TypeNumber values, \c InputTypes, like \code
-/// bool match = InputTypesMatchImpl<As...>::f(InputTypes, 0);
+/// \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 InputTypesMatchImpl;
+template <typename... As> struct TypesMatchImpl;
-/// Template specialization for the general case, when at least one type is to
-/// be matched.
+/// Template specialization for the case, when at least one type is to
+/// be matched and that is an instance of \c rosa::deluxe::DeluxeTuple.
///
-/// \tparam A first type to match
+/// \tparam Ts types of elements in the \c rosa::deluxe::DeluxeTuple to match
/// \tparam As further types to match
-template <typename A, typename... As> struct InputTypesMatchImpl<A, As...> {
- /// Tells whether types \p A, \p As... match \c rosa::TypeNumber values
- /// stored in \p InputTypes starting at position \p Pos.
+template <typename... Ts, typename... As>
+struct TypesMatchImpl<DeluxeTuple<Ts...>, As...> {
+ /// Tells whether types \c rosa::deluxe::DeluxeTuple<Ts...> and \p As... match
+ /// \c rosa::Token values stored in \p Tokens starting at position \p Pos.
///
/// The function has got a recursive implementation: it matches the first
- /// type \p A against \c rosa::TypeNumber at position \p Pos of \p
- /// InputTypes, then further types \p As.. are matched recursively starting
- /// at position \c (Pos + 1).
- ///
- /// \param InputTypes container of \c rosa::TypeNumber values to match
- /// types against
- /// \param Pos position in \p InputTypes to start matching at
- ///
- /// \return if types \p A, \p As... match \c rosa::TypeNumber values stored
- /// in \p InputTypes starting at position \p Pos
- static bool f(const std::vector<TypeNumber> &InputTypes,
- size_t Pos) noexcept {
- return Pos < InputTypes.size() &&
- TypeNumberOf<A>::Value == InputTypes[Pos] &&
- InputTypesMatchImpl<As...>::f(InputTypes, Pos + 1);
+ /// type \c rosa::deluxe::DeluxeTuple<Ts...> against \c rosa::Token at
+ /// position \p Pos of \p Tokens, then further types \p As... are matched
+ /// recursively starting at position \c (Pos + 1).
+ ///
+ /// \param Tokens container of \c rosa::Token values to match types against
+ /// \param Pos position in \p Tokens to start matching at
+ ///
+ /// \return if types \c rosa::deluxe::DeluxeTuple<Ts...> and \p As... match \c
+ /// rosa::Token values stored in \p Tokens starting at position \p Pos
+ static bool f(const std::vector<Token> &Tokens, size_t Pos) noexcept {
+ return Pos < Tokens.size() && TypeToken<Ts...>::Value == Tokens[Pos] &&
+ TypesMatchImpl<As...>::f(Tokens, Pos + 1);
}
};
+/// Template specialization for the case, when at least one type is to
+/// be matched and that is *not* an instance of \c rosa::deluxe::DeluxeTuple.
+///
+/// \tparam T first type to match
+/// \tparam As further types to match
+template <typename T, typename... As>
+struct TypesMatchImpl<T, As...> {
+ /// Tells whether types \p T and \p As... match \c rosa::Token values stored
+ /// in \p Tokens starting at position \p Pos.
+ ///
+ /// This specialization is used only when \p T is not an instance of \c
+ /// rosa::deluxe::DeluxeTuple, in which case the match is not successful.
+ ///
+ /// \note The function takes two parameters to match the general signature but
+ /// the actual values are ignored.
+ ///
+ /// \return `false`
+ static bool f(const std::vector<Token> &, size_t) noexcept { return false; }
+};
+
/// Template specialization for the terminal case, when no type remains to
/// check.
-template <> struct InputTypesMatchImpl<> {
- /// Tells whether \p Pos is the number of values stored in \p InputTypes.
+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 matchi because all the
+ /// 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 InputTypes. That is true if \p Pos points exactly to the end of
- /// \p InputTypes.
+ /// \p Tokens. That is true if \p Pos points exactly to the end of \p Tokens.
///
- /// \param InputTypes container of \c rosa::TypeNumber values to match
- /// types against
- /// \param Pos position in \p InputTypes to start matching at
+ /// \param Tokens container of \c rosa::Token values to match types against
+ /// \param Pos position in \p Tokens to start matching at
///
- /// \return if \p Pos is the number of values stored in \p InputTypes
- static bool f(const std::vector<TypeNumber> &InputTypes,
- size_t Pos) noexcept {
- return Pos == InputTypes.size();
+ /// \return if \p Pos is the number of values stored in \p Tokens
+ static bool f(const std::vector<Token> &Tokens, size_t Pos) noexcept {
+ return Pos == Tokens.size();
+ }
+};
+
+/// Template \c struct that provides an implementation for \c
+/// rosa::deluxe::DeluxeAgent::inputTypesMatch and \c
+/// rosa::deluxe::DeluxeAgent::masterOutputTypesMatch.
+///
+/// \note Match a list of types \p 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>
+template <typename As>
bool DeluxeAgent::inputTypesMatch(void) const noexcept {
- return InputTypesMatchImpl<As...>::f(InputTypes, 0);
+ 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 {
- // Need to indirectly reference \c rosa::deluxe::DeluxeAgent::inputTypesMatch
- // inside \c ASSERT because of the comma in its template argument list.
- auto MFP = &DeluxeAgent::inputTypesMatch<As...>;
- ASSERT(inv() && (this->*MFP)() && sizeof...(As) == sizeof...(S0));
-
- return std::make_tuple(
- std::make_pair(*static_cast<const As *>(InputValues->pointerTo(S0)),
- InputChanged[S0])...);
+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... As, size_t... S0>
-Optional<T> DeluxeAgent::invokeWithTuple(
- D<T, As...> F,
- std::tuple<std::pair<As, bool>...> Args,
- Seq<S0...>) noexcept {
+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 {
STATIC_ASSERT(sizeof...(As) == sizeof...(S0),
"wrong number of type parameters");
return F(std::get<S0>(Args)...);
}
-template <typename T, typename... As>
-DeluxeAgent::H
-DeluxeAgent::triggerHandlerFromProcessingFunction(D<T, As...> &&F) noexcept {
- // Need to indirectly reference \c rosa::deluxe::DeluxeAgent::inputTypesMatch
- // inside \c ASSERT because of the comma in its template argument list.
- auto MFP = &DeluxeAgent::inputTypesMatch<As...>;
- ASSERT(OutputType == TypeNumberOf<T>::Value && (this->*MFP)());
-
- return [ this, F ]() noexcept {
+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)) {
- using Indices = typename GenSeq<sizeof...(As)>::Type;
- auto Args = prepareCurrentInputs<As...>(Indices());
+ LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " handles input."
+ << std::endl;
+ const auto InputArgs = prepareCurrentInputs<As...>(SlaveIndices());
std::fill(InputChanged.begin(), InputChanged.end(), false);
- Optional<T> R = invokeWithTuple(F, Args, Indices());
- if (R) {
- sendToMaster(*R);
+ 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 T, typename... As, typename>
-DeluxeAgent::DeluxeAgent(const AtomValue Kind, const id_t Id,
- const std::string &Name, MessagingSystem &S,
- D<T, As...> &&F) noexcept
+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),
- DAHANDLERREF(AtomValue), DAHANDLERREF(int16_t),
- DAHANDLERREF(int32_t), DAHANDLERREF(int64_t), DAHANDLERREF(int8_t),
- DAHANDLERREF(long_double), DAHANDLERREF(std__string),
- DAHANDLERREF(uint16_t), DAHANDLERREF(uint32_t),
- DAHANDLERREF(uint64_t), DAHANDLERREF(uint8_t), DAHANDLERREF(unit_t),
- DAHANDLERREF(bool), DAHANDLERREF(double), DAHANDLERREF(float)),
- ExecutionPolicy(DeluxeExecutionPolicy::decimation(1)),
- OutputType(TypeNumberOf<T>::Value), NumberOfInputs(sizeof...(As)),
- InputTypes({TypeNumberOf<As>::Value...}),
- InputChanged(NumberOfInputs, false),
- InputValues(new TokenizedStorage<As...>()),
- FP(triggerHandlerFromProcessingFunction(std::move(F))),
+ 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("DeluxeAgent is created.");
+ LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " is created." << std::endl;
ASSERT(inv());
}
-template <typename T>
-void DeluxeAgent::sendToMaster(const T &Value) noexcept {
- ASSERT(inv() && OutputType == TypeNumberOf<T>::Value);
+template <typename... Ts, size_t... S0>
+void DeluxeAgent::sendToMaster(const DeluxeTuple<Ts...> &Value,
+ Seq<S0...>) noexcept {
+ STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments");
+ ASSERT(inv() && OutputType == TypeToken<Ts...>::Value);
+
+ // The assert must hold if \p this object was successfuuly constructed.
+ ASSERT((true && ... &&
+ (static_cast<size_t>(static_cast<token_size_t>(S0)) == S0)));
+ // Create a static constant array for these indices to be available as lvalue
+ // references when creating messages below. \c S0... when used directly in a
+ // fold expression is a temporary value, which would result in \c
+ // rosa::Message instances being created with rvalue references. Further, all
+ // other values would to copied into a temporary variable for making them
+ /// available as rvalue references (they are constant lvalue references here).
+ static constexpr std::array<token_size_t, sizeof...(S0)> Indices{{S0...}};
+
+ LOG_TRACE_STREAM << "DeluxeAgent " << 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) {
- Master->sendMessage(Message::create(atoms::Slave::Value, Id, Value));
+ // Handle each element of the tuple in a fold expression.
+ (Master->sendMessage(Message::create(atoms::Slave::Value, Id, Indices[S0],
+ std::get<S0>(Value))),
+ ...);
}
ASSERT(inv());
}
-template <typename T> void DeluxeAgent::saveInput(id_t Id, T Value) noexcept {
+template <typename... Ts, size_t... S0>
+void DeluxeAgent::sendToSlave(const size_t Pos, const DeluxeTuple<Ts...> &Value,
+ Seq<S0...>) noexcept {
+ STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments");
+ ASSERT(inv() && Pos < NumberOfMasterOutputs &&
+ MasterOutputTypes[Pos] == 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() &&
- InputTypes[SlaveIds.find(Id)->second] == TypeNumberOf<T>::Value);
+ Pos == InputNextPos[SlaveIds.find(Id)->second] &&
+ typeAtPositionOfToken(InputTypes[SlaveIds.find(Id)->second], Pos) ==
+ TypeNumberOf<T>::Value);
+
+ size_t SlavePos = SlaveIds.at(Id);
+
+ LOG_TRACE_STREAM << "DeluxeAgent " << 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;
+ }
- size_t Pos = SlaveIds.at(Id);
- *static_cast<T *>(InputValues->pointerTo(Pos)) = Value;
- InputChanged[Pos] = true;
+ // 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 DAHANDLEREF
-#undef DAHANDLEDEF
-#undef DAHANDLEDEFN
-#undef DAHANDLENAME
+#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/DeluxeAtoms.hpp b/include/rosa/deluxe/DeluxeAtoms.hpp
index a2aab57..70963b2 100755
--- a/include/rosa/deluxe/DeluxeAtoms.hpp
+++ b/include/rosa/deluxe/DeluxeAtoms.hpp
@@ -1,61 +1,59 @@
//===-- rosa/deluxe/DeluxeAtoms.hpp -----------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/deluxe/DeluxeAtoms.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
-/// \date 2017
+/// \date 2017-2019
///
/// \brief Definition of \c rosa::AtomValue values and \c rosa::AtomConstant
/// types for the implementation of the *deluxe interface*.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_DELUXE_DELUXEATOMS_HPP
#define ROSA_DELUXE_DELUXEATOMS_HPP
#include "rosa/support/atom.hpp"
namespace rosa {
namespace deluxe {
/// Contains some definitions used in the implementation of the *deluxe
/// interface* to denote various roles and events
///
/// \see \c rosa::deluxe::DeluxeContext
///
/// \note Do not apply `using namespace` to this namespace as that may result in
/// some identifiers in the original namespace being hidden by those of
/// \c rosa::deluxe::atoms.
namespace atoms {
/// Value to be used as the *kind* of \c rosa::deluxe::DeluxeSensor.
///
/// \see \c rosa::Unit::Kind
constexpr AtomValue SensorKind = atom("dl_sensor");
/// Value to be used as the *kind* of \c rosa::deluxe::DeluxeAgent.
///
/// \see \c rosa::Unit::Kind
constexpr AtomValue AgentKind = atom("dl_agent");
/// Type alias denoting system trigger messages.
using Trigger = AtomConstant<atom("dl_trigger")>;
/// Type alias denoting messages from a slave.
using Slave = AtomConstant<atom("dl_slave")>;
/// Type alias denoting messages from a master.
-///
-/// \note This one is not used at the moment.
using Master = AtomConstant<atom("dl_master")>;
} // End namespace atoms
} // End namespace deluxe
} // End namespace rosa
#endif // ROSA_DELUXE_DELUXEATOMS_HPP
diff --git a/include/rosa/deluxe/DeluxeContext.hpp b/include/rosa/deluxe/DeluxeContext.hpp
old mode 100755
new mode 100644
index 6ee0c03..a54a2da
--- a/include/rosa/deluxe/DeluxeContext.hpp
+++ b/include/rosa/deluxe/DeluxeContext.hpp
@@ -1,336 +1,932 @@
//===-- rosa/deluxe/DeluxeContext.hpp ---------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/deluxe/DeluxeContext.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2019
///
/// \brief Public interface for the *deluxe interface* for working with agent
/// systems.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_DELUXE_DELUXECONTEXT_HPP
#define ROSA_DELUXE_DELUXECONTEXT_HPP
#include "rosa/deluxe/DeluxeSystem.hpp"
#include "rosa/support/types.hpp"
#include <iterator>
#include <memory>
#include <set>
/// Local helper macro to log and return a
/// \c rosa::deluxe::DeluxeContext::ErrorCode value.
///
/// Creates a debug message with the stringified value and returns the value.
///
/// \param Err \c rosa::deluxe::DeluxeContext::ErrorCode value to log and
/// return
#define DCRETERROR(Err) \
{ \
LOG_DEBUG(#Err); \
return Err; \
}
namespace rosa {
namespace deluxe {
/// Defines the *deluxe interface*.
+///
+/// \todo The classes \c rosa::deluxe::DeluxeSensor and \c
+/// rosa::deluxe::DeluxeAgent share some common features in relation to their
+/// *slave* role in the *deluxe interface*. But their definitions are completely
+/// independent. It could be investigated how to lift their common parts into a
+/// new *deluxe slave* class, which would serve as base for both, to avoid code
+/// duplication.
class DeluxeContext {
/// A system owned by \p this object.
///
/// \note The reference is kept in a \c std::shared_ptr because of the member
/// function \c rosa::deluxe::DeluxeContext::getSystem.
std::shared_ptr<DeluxeSystem> System;
/// References to all *sensors* and *agents* created by \p this object.
std::set<AgentHandle> DeluxeUnits;
public:
/// Errors that may be resulted by some of the member functions of the class.
enum struct ErrorCode {
NoError,
TypeMismatch,
NotSensor,
NotAgent,
NotUnit,
WrongPosition,
AlreadyHasSlave,
AlreadyHasMaster,
AlreadyHasValueStream,
UnsuitableExecutionPolicy
};
/// Returns a new instance of \c rosa::deluxe::DeluxeContext.
///
/// \param Name name of the underlying \c rosa::DeluxeSystem
///
/// \return \c std::unique_ptr for the new instance of
/// \c rosa::deluxe::DeluxeContext with a new, empty \c rosa::DeluxeSystem
static std::unique_ptr<DeluxeContext>
create(const std::string &Name) noexcept;
private:
/// Creates a new instance.
///
/// \note Private constructor restricts instantiation to member functions of
/// the class.
///
/// \param Name name of the underlying \c rosa::MessagingSystem
DeluxeContext(const std::string &Name) noexcept;
public:
/// Destroys \p this object.
~DeluxeContext(void) noexcept;
/// Returns a reference for the underlying \c rosa::MessagingSystem.
///
/// \note One cannot do much with a \c rosa::MessagingSystem currently, this
/// is for future use.
///
/// \return reference for the underlying \c rosa::MessagingSystem.
std::weak_ptr<MessagingSystem> getSystem(void) const noexcept;
+private:
+ /// Creates a new *sensor* in the context of \p this object.
+ ///
+ /// The new *sensor* handles master-input by \p MF.
+ ///
+ /// \tparam MT type of master-input the new *sensor* handles
+ /// \tparam T type of data the new *sensor* operates on
+ ///
+ /// \note Instantiation fails if any of the type arguments \p MT and \p T
+ /// is not an instance of \c rosa::deluxe::DeluxeTuple or \p T is \c
+ /// rosa::deluxe::EmptyDeluxeTuple.
+ ///
+ /// \param Name name of the new *sensor*
+ /// \param MF function for the new *sensors* to process master-input
+ /// values with
+ /// \param F function for the new *sensor* to generate the next value with
+ /// during normal operation
+ ///
+ /// \note \p F is not used during simulation, in which case
+ /// \c rosa::deluxe::DeluxeContext::registerSensorValues is used to
+ /// register an alternative simulation data source with \c
+ /// rosa::deluxe::DeluxeSensor::registerSimulationDataSource. One may
+ /// safely keep relying on the default value of \p F as long as only
+ /// simulation of the system is to be done.
+ ///
+ /// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor.
+ ///
+ /// \return \c rosa::AgentHandle for the new *sensor*
+ template <typename MT, typename T,
+ typename =
+ std::enable_if<TypeListAllDeluxeTuple<TypeList<MT, T>>::Value &&
+ !std::is_same<T, EmptyDeluxeTuple>::value>>
+ AgentHandle createSensorImpl(const std::string &Name,
+ std::function<void(std::pair<MT, bool>)> &&MF,
+ std::function<T(void)> &&F) noexcept;
+
+public:
/// Creates a new *sensor* in the context of \p this object.
///
+ /// The new *sensor* does not receive master-input.
+ ///
/// \tparam T type of data the new *sensor* operates on
///
+ /// \note Instantiation fails if type argument \p T is neither a built-in type
+ /// nor an instance of \c rosa::deluxe::DeluxeTuple with at least one element.
+ ///
/// \param Name name of the new *sensor*
/// \param F function for the new *sensor* to generate the next value with
/// during normal operation
///
/// \note \p F is not used during simulation, in which case
/// \c rosa::deluxe::DeluxeContext::registerSensorValues is used to register
/// an alternative simulation data source with
/// \c rosa::deluxe::DeluxeSensor::registerSimulationDataSource. One may
/// safely keep relying on the default value of \p F as long as only
/// simulation of the system is to be done.
///
+ /// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor.
+ ///
+ /// \return \c rosa::AgentHandle for the new *sensor*
+ template <typename T, typename = std::enable_if_t<
+ TypeListContains<BuiltinTypes, T>::Value ||
+ (IsDeluxeTuple<T>::Value &&
+ !std::is_same<T, EmptyDeluxeTuple>::value)>>
+ AgentHandle createSensor(
+ const std::string &Name,
+ std::function<T(void)> &&F = [](void) { return T(); }) noexcept;
+
+ /// Creates a new *sensor* in the context of \p this object.
+ ///
+ /// The new *sensor* handles master-input by \p MF.
+ ///
+ /// \tparam MT type of master-input the new *sensor* handles
+ /// \tparam T type of data the new *sensor* operates on
+ ///
+ /// \note The type arguments \p MT and \p T must be either all built-in types
+ /// or all instances of \c rosa::deluxe::DeluxeTuple. Moreover, \p T cannot be
+ /// \c rosa::deluxe::EmptyDeluxeTuple. Instantiation fails if these conditions
+ /// do not hold.
+ ///
+ /// \param Name name of the new *sensor*
+ /// \param MF function for the new *sensors* to process master-input
+ /// values with \param F function for the new *sensor* to generate
+ /// the next value with during normal operation
+ ///
+ /// \note \p F is not used during simulation, in which case
+ /// \c rosa::deluxe::DeluxeContext::registerSensorValues is used to
+ /// register an alternative simulation data source with \c
+ /// rosa::deluxe::DeluxeSensor::registerSimulationDataSource. One may
+ /// safely keep relying on the default value of \p F as long as only
+ /// simulation of the system is to be done.
+ ///
+ /// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor.
+ ///
/// \return \c rosa::AgentHandle for the new *sensor*
- template <typename T>
- AgentHandle createSensor(const std::string &Name,
- DeluxeSensor::D<T> &&F = [](void) {
- return T();
- }) noexcept;
+ template <typename MT, typename T,
+ typename = std::enable_if<
+ TypeListSubsetOf<TypeList<MT, T>, BuiltinTypes>::Value ||
+ (TypeListAllDeluxeTuple<TypeList<MT, T>>::Value &&
+ !std::is_same<T, EmptyDeluxeTuple>::value)>>
+ AgentHandle createSensor(
+ const std::string &Name, std::function<void(std::pair<MT, bool>)> &&MF,
+ std::function<T(void)> &&F = [](void) { return T(); }) noexcept;
+
+private:
+ /// Creates a new *agent* in the context of \p this object.
+ ///
+ /// The new *agent* receives master-input by \p MF and produces
+ /// master-output.
+ ///
+ /// \tparam MT type of master-input the new *agent* handles
+ /// \tparam T type of data the new *agent* outputs
+ /// \tparam Ts types of master-output the new *agent* produces
+ /// \tparam As types of inputs the new *agent* takes
+ ///
+ /// \note Instantiation fails if any of the type arguments \p MT, \p T, \p
+ /// Ts..., and \p As... is not an instance of \c rosa::deluxe::DeluxeTuple or
+ /// any of \p T and \p As... is \c rosa::deluxe::EmptyDeluxeTuple.
+ ///
+ /// \param Name name of the new *agent*
+ /// \param MF function for the new *agent* to process master-input
+ /// values with \param F function for the new *agent* to process
+ /// input values and generate output with
+ ///
+ /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
+ ///
+ /// \return \c rosa::AgentHandle for the new *agent*
+ template <typename MT, typename T, typename... Ts, typename... As,
+ typename = std::enable_if_t<
+ TypeListAllDeluxeTuple<TypeList<MT, T, Ts..., As...>>::Value &&
+ !std::is_same<T, EmptyDeluxeTuple>::value &&
+ (true && ... && (!std::is_same<As, EmptyDeluxeTuple>::value))>>
+ AgentHandle createAgentImpl(
+ const std::string &Name,
+ std::function<std::tuple<Optional<Ts>...>(std::pair<MT, bool>)> &&MF,
+ std::function<std::tuple<Optional<T>, Optional<Ts>...>(
+ std::pair<As, bool>...)> &&F) noexcept;
+public:
/// Creates a new *agent* in the context of \p this object.
///
+ /// The new *agent* neither receives master-input nor produces
+ /// master-output.
+ ///
/// \tparam T type of data the new *agent* outputs
/// \tparam As types of inputs the new *agent* takes
///
+ /// \note The type arguments \p T and \p As... must be either all built-in
+ /// types or all instances of \c rosa::deluxe::DeluxeTuple. Moreover, none of
+ /// them can be \c rosa::deluxe::EmptyDeluxeTuple. Instantiation fails if
+ /// these conditions do not hold.
+ ///
/// \param Name name of the new *agent*
/// \param F function for the new *agent* to process input values and
/// generate output with
///
+ /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
+ ///
/// \return \c rosa::AgentHandle for the new *agent*
- template <typename T, typename... As>
- AgentHandle createAgent(const std::string &Name,
- DeluxeAgent::D<T, As...> &&F) noexcept;
+ template <
+ typename T, typename... As,
+ typename = std::enable_if_t<
+ TypeListSubsetOf<TypeList<T, As...>, BuiltinTypes>::Value ||
+ (TypeListAllDeluxeTuple<TypeList<T, As...>>::Value &&
+ !std::is_same<T, EmptyDeluxeTuple>::value &&
+ (true && ... && (!std::is_same<As, EmptyDeluxeTuple>::value)))>>
+ AgentHandle
+ createAgent(const std::string &Name,
+ std::function<Optional<T>(std::pair<As, bool>...)> &&F) noexcept;
+
+ /// Creates a new *agent* in the context of \p this object.
+ ///
+ /// The new *agent* receives master-input by \p MF but does not
+ /// produce master-output.
+ ///
+ /// \tparam MT type of master-input the new *agent* handles
+ /// \tparam T type of data the new *agent* outputs
+ /// \tparam As types of inputs the new *agent* takes
+ ///
+ /// \note The type arguments \p MT, \p T, and \p As... must be either all
+ /// built-in types or all instances of \c rosa::deluxe::DeluxeTuple. Moreover,
+ /// none of \p T and \p As... can be \c rosa::deluxe::EmptyDeluxeTuple.
+ /// Instantiation fails if these conditions do not hold.
+ ///
+ /// \param Name name of the new *agent*
+ /// \param MF function for the new *agent* to process master-input
+ /// values with
+ /// \param F function for the new *agent* to process input values and
+ /// generate output with
+ ///
+ /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
+ ///
+ /// \return \c rosa::AgentHandle for the new *agent*
+ template <
+ typename MT, typename T, typename... As,
+ typename = std::enable_if_t<
+ TypeListSubsetOf<TypeList<MT, T, As...>, BuiltinTypes>::Value ||
+ (TypeListAllDeluxeTuple<TypeList<MT, T, As...>>::Value &&
+ !std::is_same<T, EmptyDeluxeTuple>::value &&
+ (true && ... && (!std::is_same<As, EmptyDeluxeTuple>::value)))>>
+ AgentHandle
+ createAgent(const std::string &Name,
+ std::function<void(std::pair<MT, bool>)> &&MF,
+ std::function<Optional<T>(std::pair<As, bool>...)> &&F) noexcept;
+
+ /// Creates a new *agent* in the context of \p this object.
+ ///
+ /// The new *agent* does not receive master-input but produces
+ /// master-output.
+ ///
+ /// \tparam T type of data the new *agent* outputs
+ /// \tparam Ts types of master-output the new *agent* produces
+ /// \tparam As types of inputs the new *agent* takes
+ ///
+ /// \note The type arguments \p T, \p Ts, and \p As... must be either all
+ /// built-in types or all instances of \c rosa::deluxe::DeluxeTuple. Moreover,
+ /// none of \p T and \p As... can be \c rosa::deluxe::EmptyDeluxeTuple.
+ /// Instantiation fails if these conditions do not hold.
+ ///
+ /// \param Name name of the new *agent*
+ /// \param F function for the new *agent* to process input values and
+ /// generate output with
+ ///
+ /// \note \p F does not produce master-output for a given position if the
+ /// corresponding type is \c rosa::deluxe::EmptyDeluxeTuple. It is not
+ /// possible to disable master-output at any position by using built-in types.
+ ///
+ /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
+ ///
+ /// \return \c rosa::AgentHandle for the new *agent*
+ template <
+ typename T, typename... Ts, typename... As,
+ typename = std::enable_if_t<
+ TypeListSubsetOf<TypeList<T, Ts..., As...>, BuiltinTypes>::Value ||
+ (TypeListAllDeluxeTuple<TypeList<T, Ts..., As...>>::Value &&
+ !std::is_same<T, EmptyDeluxeTuple>::value &&
+ (true && ... && (!std::is_same<As, EmptyDeluxeTuple>::value)))>>
+ AgentHandle
+ createAgent(const std::string &Name,
+ std::function<std::tuple<Optional<T>, Optional<Ts>...>(
+ std::pair<As, bool>...)> &&F) noexcept;
+
+ /// Creates a new *agent* in the context of \p this object.
+ ///
+ /// The new *agent* receives master-input by \p MF and produces
+ /// master-output.
+ ///
+ /// \tparam MT type of master-input the new *agent* handles
+ /// \tparam T type of data the new *agent* outputs
+ /// \tparam Ts types of master-output the new *agent* produces
+ /// \tparam As types of inputs the new *agent* takes
+ ///
+ /// \note The type arguments \p MT, \p T, \p Ts, and \p As... must be either
+ /// all built-in types or all instances of \c rosa::deluxe::DeluxeTuple.
+ /// Moreover, none of \p T and \p As... can be \c
+ /// rosa::deluxe::EmptyDeluxeTuple. Instantiation fails if these conditions
+ /// do not hold.
+ ///
+ /// \param Name name of the new *agent*
+ /// \param MF function for the new *agent* to process master-input
+ /// values with
+ /// \param F function for the new *agent* to process input values and
+ /// generate output with
+ ///
+ /// \note \p F does not produce master-output for a given position if the
+ /// corresponding type is \c rosa::deluxe::EmptyDeluxeTuple. It is not
+ /// possible to disable master-output at any position by using built-in types.
+ ///
+ /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
+ ///
+ /// \return \c rosa::AgentHandle for the new *agent*
+ template <
+ typename MT, typename T, typename... Ts, typename... As,
+ typename = std::enable_if_t<
+ TypeListSubsetOf<TypeList<MT, T, Ts..., As...>,
+ BuiltinTypes>::Value ||
+ (TypeListAllDeluxeTuple<TypeList<MT, T, Ts..., As...>>::Value &&
+ !std::is_same<T, EmptyDeluxeTuple>::value &&
+ (true && ... && (!std::is_same<As, EmptyDeluxeTuple>::value)))>>
+ 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;
/// Returns the current execution policy of the referred \p Unit
///
/// \see \c rosa::deluxe::DeluxeExecutionPolicy
///
/// \note The referred \p Unit is either *sensor* or *agent*.
///
/// \note The returned reference is valid only as long as \c
/// rosa::deluxe::DeluxeContext::setExecutionPolicy() is not called with the
/// *unit* referred by \p Unit and the *unit* is not destroyed.
///
/// \param Unit the *unit* whose execution policy is to be obtained
///
/// \return the \c rosa::deluxe::DeluxeExecutionPolicy from \p Unit if \p Unit
/// is valid
Optional<const DeluxeExecutionPolicy &>
getExecutionPolicy(AgentHandle Unit) const noexcept;
/// Sets the current execution policy of the referred \p Unit to \p
/// ExecutionPolicy.
///
/// \see \c rosa::deluxe::DeluxeExecutionPolicy
///
/// \note The referred \p Unit is either *sensor* or *agent*.
///
/// \param Unit the *unit* whose execution policy is to be set
/// \param ExecutionPolicy the new execution policy for \p Unit
///
/// \return how successful setting \p ExecutionPolicy for \p Unit was
///
/// \note The function may return the following
/// \c rosa::deluxe::DeluxeContext::ErrorCode values:
/// `ErrorCode` | Comment
/// ----------- | -------
/// `NoError` | Success
/// `NotUnit` | Referred \p Unit is not valid
/// `UnsuitableExecutionPolicy` | \p ExecutionPolicy cannot handle \p Unit
ErrorCode setExecutionPolicy(
AgentHandle Unit,
std::unique_ptr<DeluxeExecutionPolicy> &&ExecutionPolicy) noexcept;
- /// Connectes a *sensor* to an *agent* in the context of \p this object.
+ /// Connects a *sensor* to an *agent* in the context of \p this object.
///
/// \param Agent the *agent* to connect to
/// \param Pos the index of slot of \p Agent to connect \p Sensor to
/// \param Sensor the *sensor* to connect
/// \param Description optional textual description of the connection
///
- /// \return how successfull connecting \p Sensor to \p Agent at slot index
- /// \p Pos was
+ /// \return how successfull connecting \p Sensor to \p Agent at slot
+ /// index \p Pos was
///
/// \note The function may return the following
/// \c rosa::deluxe::DeluxeContext::ErrorCode values:
/// `ErrorCode` | Comment
/// ----------- | -------
/// `NoError` | Success
/// `NotAgent` | Referred \p Agent is not \c rosa::deluxe::DeluxeAgent
/// `NotSensor` | Referred \p Sensor is not \c rosa::deluxe::DeluxeSensor
/// `WrongPosition` | \p Pos is not a valid input position of \p Agent
- /// `TypeMismatch` | Expected input type at position \p Pos of \p Agent is other than the output type of \p Sensor
+ /// `TypeMismatch` | Expected input type at position \p Pos of \p Agent is other thanthe output type of \p Sensor or expected master-input of \p Sensor is other than master-output at position \p Pos of \p Agent if any
/// `AlreadyHasSlave` | \p Agent at position \p Pos already has a *slave* registered
/// `AlreadyHasMaster` | \p Sensor already has a *master* registered
ErrorCode connectSensor(AgentHandle Agent, const size_t Pos,
AgentHandle Sensor,
const std::string &Description = "") noexcept;
/// Connectes two *agents* in the context of \p this object.
///
/// \param Master the *agent* to connect to
/// \param Pos the index of slot of \p Master to connect \p Slave to
/// \param Slave the *agent* to connect
/// \param Description optional textual description of the connection
///
- /// \return how succesfull connecting \p Slave to \p Master at slot index
- /// \p Pos was
+ /// \return how succesfull connecting \p Slave to \p Master at slot
+ /// index \p Pos was
///
/// \note The function may return the following
/// \c rosa::deluxe::DeluxeContext::ErrorCode values:
/// `ErrorCode` | Comment
/// ----------- | -------
/// `NoError` | Success
/// `NotAgent` | Referred \p Master or \p Slave is not \c rosa::deluxe::DeluxeAgent
/// `WrongPosition` | \p Pos is not a valid input position of \p Master
- /// `TypeMismatch` | Expected input type at position \p Pos of \p Master is other than the output type of \p Slave
+ /// `TypeMismatch` | Expected input type at position \p Pos of \p Master is other than the output type of \p Slave or expected master-input of \p Slave is other than master-output at position \p Pos of \p Master if any
/// `AlreadyHasSlave` | \p Master at position \p Pos already has a *slave* registered
/// `AlreadyHasMaster` | \p Slave already has a *master* registered
ErrorCode connectAgents(AgentHandle Master, const size_t Pos,
AgentHandle Slave,
const std::string &Description = "") noexcept;
- /// Initializes \c this object and others managed by \p this object for
- /// setting up and performing simulation.
+ /// Initializes \c this object and others managed by \p this object
+ /// for setting up and performing simulation.
///
/// \see \c rosa::deluxe::DeluxeContext::registerSensorValues,
/// \c rosa::deluxe::DeluxeContext::simulate
///
/// Need to clear simulation data sources from all the *sensors*.
void initializeSimulation(void) noexcept;
- /// Registers a stream providing values for a *sensor* during simulation.
+public:
+ /// Registers a stream providing values for a *sensor* during
+ /// simulation.
///
/// \tparam Iterator type of iterator providing values for \p Sensor
- /// \tparam T type of values \p Sensor is operating on, always use default!
+ /// \tparam T type of values \p Sensor is operating on, always use
+ /// default!
+ ///
+ /// \note Instantiation fails if type argument \p T is neither a built-in type
+ /// nor an instance of \c rosa::deluxe::DeluxeTuple with at least one element.
///
/// \param Sensor the *sensor* to register values for
/// \param Start provides values for \p Sensor
/// \param End denotes the end of stream of values
- /// \param Default value to be used when input stream is depleted during
- /// simulation
+ /// \param Default value to be used when input stream is depleted
+ /// during simulation
///
/// \return how successful registering \p Source for \p Sensor
///
/// \note The function may return the following
/// \c rosa::deluxe::DeluxeContext::ErrorCode values:
/// `ErrorCode` | Comment
/// ----------- | -------
/// `NoError` | Success
- /// `TypeMismatch` | \p Sensor generates values of a type other than \p T
- /// `NotSensor` | Referred \p Sensor is not \c rosa::deluxe::DeluxeSensor
- /// `AlreadyHasValueStream` | \p Sensor already has simulation data source set
- template <typename Iterator, typename T = typename Iterator::value_type>
+ /// `TypeMismatch` | \p Sensor generates values of a type other than
+ /// \p T `NotSensor` | Referred \p Sensor is not \c
+ /// rosa::deluxe::DeluxeSensor `AlreadyHasValueStream` | \p Sensor already has
+ /// simulation data source set
+ template <
+ typename Iterator, typename T = typename Iterator::value_type,
+ typename = std::enable_if_t<TypeListContains<BuiltinTypes, T>::Value ||
+ (IsDeluxeTuple<T>::Value &&
+ !std::is_same<T, EmptyDeluxeTuple>::value)>>
ErrorCode registerSensorValues(AgentHandle Sensor, Iterator &&Start,
const Iterator &End, T Default = {}) noexcept;
/// Performs the system contained by \p this object.
///
- /// The function performs \p NumCycles cycle of simulation. In each cycle,
- /// all the *agents* and *sensors* registered in
- /// \c rosa::deluxe::DeluxeContext::DeluxeUnits are trigged for execution.
+ /// The function performs \p NumCycles cycle of simulation. In each
+ /// cycle, all the *agents* and *sensors* registered in \c
+ /// rosa::deluxe::DeluxeContext::DeluxeUnits are trigged for
+ /// execution.
///
/// \param NumCycles number of cycles to perform
///
- /// \pre All the *sensors* in the system contained by \p this object generate
- /// their output from simulation data sources.
+ /// \pre All the *sensors* in the system contained by \p this object
+ /// generate their output from simulation data sources.
void simulate(const size_t NumCycles) const noexcept;
};
+/// Anonymous namespace with helper features for implementing
+/// \c rosa::deluxe::DeluxeContext, consider it private.
+namespace {
+
+/// Maps any type \p T to \c rosa::deluxe::EmptyDeluxeTuple.
+template <typename T> struct MapToEmptyDeluxeTuple {
+ using Type = EmptyDeluxeTuple;
+};
+
+/// Convenience template alias for \c MapToEmptyDeluxeTuple.
template <typename T>
-AgentHandle DeluxeContext::createSensor(const std::string &Name,
- DeluxeSensor::D<T> &&F) noexcept {
- AgentHandle H = System->createSensor<T>(Name, std::move(F));
+using empty_deluxe_t = typename MapToEmptyDeluxeTuple<T>::Type;
+
+/// Converts a \c std::tuple of \c rosa::Optional built-in types into a
+/// corresponding \c std::tuple of \c rosa::Optional with each actual value
+/// wrapped in \c rosa::deluxe::DeluxeTuple.
+///
+/// \tparam Ts types of the values
+/// \tparam S0 indices for accessing values in \p Values
+///
+/// \param Values the \c std::tuple of \c rosa::Optional with built-in values
+///
+/// \note The second argument provides indices statically as template arguments
+/// \p S0..., so its actual value is ignored.
+///
+/// \return a \c std::tuple of \c rosa::Optional corresponding to \p Values
+/// with each actual value wrapped in \c rosa::deluxe::DeluxeTuple
+///
+/// \pre Statically, all type arguments \p Ts... are built-in types and the
+/// provided indices \p S0... match the length of \p Ts...: \code
+/// TypeListSubsetOf<TypeList<Ts...>, BuiltinTypes>::Value &&
+/// sizeof...(Ts) == sizeof...(S0)
+/// \endcode
+template <typename... Ts, size_t... S0>
+std::tuple<Optional<DeluxeTuple<Ts>>...>
+wrapBuiltinInDeluxeTuple(const std::tuple<Optional<Ts>...> &Values,
+ Seq<S0...>) noexcept {
+ STATIC_ASSERT((TypeListSubsetOf<TypeList<Ts...>, BuiltinTypes>::Value),
+ "not built-in types");
+ STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent type arguments");
+
+ return std::make_tuple(std::get<S0>(Values)
+ ? Optional<DeluxeTuple<Ts>>(
+ make_deluxe_tuple<Ts>(*std::get<S0>(Values)))
+ : Optional<DeluxeTuple<Ts>>()...);
+}
+
+} // End namespace
+
+template <typename MT, typename T, typename>
+AgentHandle
+DeluxeContext::createSensorImpl(const std::string &Name,
+ std::function<void(std::pair<MT, bool>)> &&MF,
+ std::function<T(void)> &&F) noexcept {
+ AgentHandle H = System->createSensor(Name, std::move(MF), std::move(F));
DeluxeUnits.emplace(H);
return H;
}
-template <typename T, typename... As>
-AgentHandle DeluxeContext::createAgent(const std::string &Name,
- DeluxeAgent::D<T, As...> &&F) noexcept {
- AgentHandle H = System->createAgent(Name, std::move(F));
+template <typename T, typename>
+AgentHandle DeluxeContext::createSensor(const std::string &Name,
+ std::function<T(void)> &&F) noexcept {
+ auto EmptyMF = std::function<void(std::pair<EmptyDeluxeTuple, bool>)>(
+ [](std::pair<EmptyDeluxeTuple, bool>) {});
+
+ if constexpr (TypeListContains<BuiltinTypes, T>::Value) {
+ using OutputType = DeluxeTuple<T>;
+ return createSensorImpl(
+ Name, std::move(EmptyMF),
+ std::function<OutputType(void)>(
+ [F{std::move(F)}](void) { return OutputType(F()); }));
+
+ } else if constexpr (IsDeluxeTuple<T>::Value &&
+ !std::is_same<T, EmptyDeluxeTuple>::value) {
+ return createSensorImpl(Name, std::move(EmptyMF), std::move(F));
+
+ } else {
+ ASSERT(false && "Unexpected type argument");
+ }
+}
+
+template <typename MT, typename T, typename>
+AgentHandle
+DeluxeContext::createSensor(const std::string &Name,
+ std::function<void(std::pair<MT, bool>)> &&MF,
+ std::function<T(void)> &&F) noexcept {
+
+ if constexpr (TypeListSubsetOf<TypeList<MT, T>, BuiltinTypes>::Value) {
+ using MasterInputType = DeluxeTuple<MT>;
+ using OutputType = DeluxeTuple<T>;
+ return createSensorImpl(
+ Name,
+ std::function<void(std::pair<MasterInputType, bool>)>(
+ [MF{std::move(MF)}](std::pair<MasterInputType, bool> Arg) {
+ MF({std::get<0>(Arg.first), Arg.second});
+ }),
+ std::function<OutputType(void)>(
+ [F{std::move(F)}](void) { return OutputType(F()); }));
+
+ } else if constexpr (TypeListAllDeluxeTuple<TypeList<MT, T>>::Value &&
+ !std::is_same<T, EmptyDeluxeTuple>::value) {
+ return createSensorImpl(Name, std::move(MF), std::move(F));
+
+ } else {
+ ASSERT(false && "Unexpected type arguments");
+ }
+}
+
+template <typename MT, typename T, typename... Ts, typename... As, typename>
+AgentHandle DeluxeContext::createAgentImpl(
+ const std::string &Name,
+ std::function<std::tuple<Optional<Ts>...>(std::pair<MT, bool>)> &&MF,
+ std::function<std::tuple<Optional<T>, Optional<Ts>...>(
+ std::pair<As, bool>...)> &&F) noexcept {
+ AgentHandle H = System->createAgent(Name, std::move(MF), std::move(F));
DeluxeUnits.emplace(H);
return H;
}
-template <typename Iterator, typename T>
+template <typename T, typename... As, typename>
+AgentHandle DeluxeContext::createAgent(
+ const std::string &Name,
+ std::function<Optional<T>(std::pair<As, bool>...)> &&F) noexcept {
+
+ using NoMasterOutputType = std::tuple<Optional<empty_deluxe_t<As>>...>;
+ auto EmptyMF =
+ std::function<NoMasterOutputType(std::pair<EmptyDeluxeTuple, bool>)>(
+ [](std::pair<EmptyDeluxeTuple, bool>) {
+ return NoMasterOutputType();
+ });
+
+ if constexpr (TypeListSubsetOf<TypeList<T, As...>, BuiltinTypes>::Value) {
+ using OutputType = DeluxeTuple<T>;
+ return createAgentImpl(
+ Name, std::move(EmptyMF),
+ std::function<
+ std::tuple<Optional<OutputType>, Optional<empty_deluxe_t<As>>...>(
+ std::pair<DeluxeTuple<As>, bool>...)>(
+ [F{std::move(F)}](std::pair<DeluxeTuple<As>, bool>... Args) {
+ const auto Result = F({std::get<0>(Args.first), Args.second}...);
+ return std::tuple_cat(
+ wrapBuiltinInDeluxeTuple(std::tuple(Result), seq_t<1>()),
+ NoMasterOutputType());
+ }));
+
+ } else if constexpr (TypeListAllDeluxeTuple<TypeList<T, As...>>::Value &&
+ !std::is_same<T, EmptyDeluxeTuple>::value &&
+ (true && ... &&
+ (!std::is_same<As, EmptyDeluxeTuple>::value))) {
+ return createAgentImpl(
+ Name, std::move(EmptyMF),
+ std::function<std::tuple<Optional<T>, Optional<empty_deluxe_t<As>>...>(
+ std::pair<As, bool>...)>(
+ [F{std::move(F)}](std::pair<As, bool>... Args) {
+ const auto Result = F(Args...);
+ return std::tuple_cat(std::tuple(Result), NoMasterOutputType());
+ }));
+
+ } else {
+ ASSERT(false && "Unexpected type arguments");
+ }
+}
+
+template <typename MT, typename T, typename... As, typename>
+AgentHandle DeluxeContext::createAgent(
+ const std::string &Name, std::function<void(std::pair<MT, bool>)> &&MF,
+ std::function<Optional<T>(std::pair<As, bool>...)> &&F) noexcept {
+
+ using NoMasterOutputType = std::tuple<Optional<empty_deluxe_t<As>>...>;
+
+ if constexpr (TypeListSubsetOf<TypeList<MT, T, As...>, BuiltinTypes>::Value) {
+ using MasterInputType = DeluxeTuple<MT>;
+ using OutputType = DeluxeTuple<T>;
+ return createAgentImpl(
+ Name,
+ std::function<NoMasterOutputType(std::pair<MasterInputType, bool>)>(
+ [MF{std::move(MF)}](std::pair<MasterInputType, bool> Arg) {
+ MF({std::get<0>(Arg.first), Arg.second});
+ return NoMasterOutputType();
+ }),
+ std::function<
+ std::tuple<Optional<OutputType>, Optional<empty_deluxe_t<As>>...>(
+ std::pair<DeluxeTuple<As>, bool>...)>(
+ [F{std::move(F)}](std::pair<DeluxeTuple<As>, bool>... Args) {
+ const auto Result = F({std::get<0>(Args.first), Args.second}...);
+ return std::tuple_cat(
+ wrapBuiltinInDeluxeTuple(std::tuple(Result), seq_t<1>()),
+ NoMasterOutputType());
+ }));
+
+ } else if constexpr (TypeListAllDeluxeTuple<TypeList<MT, T, As...>>::Value &&
+ !std::is_same<T, EmptyDeluxeTuple>::value &&
+ (true && ... &&
+ (!std::is_same<As, EmptyDeluxeTuple>::value))) {
+ return createAgentImpl(
+ Name,
+ std::function<NoMasterOutputType(std::pair<MT, bool>)>(
+ [MF{std::move(MF)}](std::pair<MT, bool> Arg) {
+ MF(Arg);
+ return NoMasterOutputType();
+ }),
+ std::function<std::tuple<Optional<T>, Optional<empty_deluxe_t<As>>...>(
+ std::pair<As, bool>...)>(
+ [F{std::move(F)}](std::pair<As, bool>... Args) {
+ const auto Result = F(Args...);
+ return std::tuple_cat(std::tuple(Result), NoMasterOutputType());
+ }));
+
+ } else {
+ ASSERT(false && "Unexpected type arguments");
+ }
+}
+
+template <typename T, typename... Ts, typename... As, typename>
+AgentHandle DeluxeContext::createAgent(
+ const std::string &Name,
+ std::function<std::tuple<Optional<T>, Optional<Ts>...>(
+ std::pair<As, bool>...)> &&F) noexcept {
+
+ if constexpr (TypeListSubsetOf<TypeList<T, Ts..., As...>,
+ BuiltinTypes>::Value) {
+ using MasterOutputType = std::tuple<Optional<DeluxeTuple<Ts>>...>;
+ using OutputType = DeluxeTuple<T>;
+ return createAgentImpl(
+ Name,
+ std::function<MasterOutputType(std::pair<EmptyDeluxeTuple, bool>)>(
+ [](std::pair<EmptyDeluxeTuple, bool>) {
+ return MasterOutputType();
+ }),
+ std::function<
+ std::tuple<Optional<OutputType>, Optional<DeluxeTuple<Ts>>...>(
+ std::pair<DeluxeTuple<As>, bool>...)>(
+ [F{std::move(F)}](std::pair<DeluxeTuple<As>, bool>... Args) {
+ const auto Result = F({std::get<0>(Args.first), Args.second}...);
+ return wrapBuiltinInDeluxeTuple(Result,
+ seq_t<1 + sizeof...(Ts)>());
+ }));
+
+ } else if constexpr (TypeListAllDeluxeTuple<
+ TypeList<T, Ts..., As...>>::Value &&
+ !std::is_same<T, EmptyDeluxeTuple>::value &&
+ (true && ... &&
+ (!std::is_same<As, EmptyDeluxeTuple>::value))) {
+ using MasterOutputType = std::tuple<Optional<Ts>...>;
+ return createAgentImpl(
+ Name,
+ std::function<MasterOutputType(std::pair<EmptyDeluxeTuple, bool>)>(
+ [](std::pair<EmptyDeluxeTuple, bool>) {
+ return MasterOutputType();
+ }),
+ std::function<std::tuple<Optional<T>, Optional<Ts>...>(
+ std::pair<As, bool>...)>(
+ [F{std::move(F)}](std::pair<As, bool>... Args) {
+ const auto Output = F(Args...);
+ return Output;
+ }));
+
+ } else {
+ ASSERT(false && "Unexpected type arguments");
+ }
+}
+
+template <typename MT, typename T, typename... Ts, typename... As, typename>
+AgentHandle DeluxeContext::createAgent(
+ const std::string &Name,
+ std::function<std::tuple<Optional<Ts>...>(std::pair<MT, bool>)> &&MF,
+ std::function<std::tuple<Optional<T>, Optional<Ts>...>(
+ std::pair<As, bool>...)> &&F) noexcept {
+
+ if constexpr (TypeListSubsetOf<TypeList<MT, T, Ts..., As...>,
+ BuiltinTypes>::Value) {
+ using MasterInputType = DeluxeTuple<MT>;
+ using MasterOutputType = std::tuple<Optional<DeluxeTuple<Ts>>...>;
+ using OutputType = DeluxeTuple<T>;
+ return createAgentImpl(
+ Name,
+ std::function<MasterOutputType(std::pair<MasterInputType, bool>)>(
+ [MF{std::move(MF)}](std::pair<MasterInputType, bool> Arg) {
+ const auto Result = MF({std::get<0>(Arg.first), Arg.second});
+ return wrapBuiltinInDeluxeTuple(Result, seq_t<sizeof...(Ts)>());
+ }),
+ std::function<
+ std::tuple<Optional<OutputType>, Optional<DeluxeTuple<Ts>>...>(
+ std::pair<DeluxeTuple<As>, bool>...)>(
+ [F{std::move(F)}](std::pair<DeluxeTuple<As>, bool>... Args) {
+ const auto Result = F({std::get<0>(Args.first), Args.second}...);
+ return wrapBuiltinInDeluxeTuple(Result,
+ seq_t<1 + sizeof...(Ts)>());
+ }));
+
+ } else if constexpr (TypeListAllDeluxeTuple<
+ TypeList<MT, T, Ts..., As...>>::Value &&
+ !std::is_same<T, EmptyDeluxeTuple>::value &&
+ (true && ... &&
+ (!std::is_same<As, EmptyDeluxeTuple>::value))) {
+ using MasterOutputType = std::tuple<Optional<Ts>...>;
+ return createAgentImpl(
+ Name,
+ std::function<MasterOutputType(std::pair<MT, bool>)>(
+ [MF{std::move(MF)}](std::pair<MT, bool> Arg) {
+ const auto Output = MF(Arg);
+ return Output;
+ }),
+ std::function<std::tuple<Optional<T>, Optional<Ts>...>(
+ std::pair<As, bool>...)>(
+ [F{std::move(F)}](std::pair<As, bool>... Args) {
+ const auto Output = F(Args...);
+ return Output;
+ }));
+
+ } else {
+ ASSERT(false && "Unexpected type arguments");
+ }
+}
+
+template <typename Iterator, typename T, typename>
DeluxeContext::ErrorCode
DeluxeContext::registerSensorValues(AgentHandle Sensor, Iterator &&Start,
const Iterator &End, T Default) noexcept {
// Get the type of values provided by \p Iterator.
STATIC_ASSERT((std::is_same<T, typename Iterator::value_type>::value),
"type mismatch");
// Make sure preconditions are met.
if (!System->isDeluxeSensor(Sensor)) {
DCRETERROR(ErrorCode::NotSensor);
}
auto S = System->getDeluxeSensor(Sensor);
ASSERT(S); // Sanity check.
- if (S->OutputType != TypeNumberOf<T>::Value) {
- DCRETERROR(ErrorCode::TypeMismatch);
- } else if (S->simulationDataSourceIsSet()) {
+ if (S->simulationDataSourceIsSet()) {
DCRETERROR(ErrorCode::AlreadyHasValueStream);
}
- // Register input stream.
- // \note Need to capture parameters by value so having local copies.
- S->registerSimulationDataSource(
- DeluxeSensor::D<T>([=](void) mutable noexcept {
- if (Start != End) {
- LOG_TRACE_STREAM << "Reading next value for sensor '" << S->FullName
- << "': " << *Start << '\n';
- return *Start++;
- } else {
- LOG_TRACE_STREAM << "Providing default value for sensor '"
- << S->FullName << "': " << Default << '\n';
- return Default;
- }
- }));
+ if constexpr (TypeListContains<BuiltinTypes, T>::Value) {
+ if (S->OutputType != TypeToken<T>::Value) {
+ DCRETERROR(ErrorCode::TypeMismatch);
+ }
+
+ // Register input stream.
+ // \note Need to capture parameters by value so having local copies.
+ S->registerSimulationDataSource(std::function<DeluxeTuple<T>(void)>([=
+ ](void) mutable noexcept->DeluxeTuple<T> {
+ if (Start != End) {
+ LOG_TRACE_STREAM << "Reading next value for sensor '" << S->FullName
+ << "': " << *Start << '\n';
+ return make_deluxe_tuple<T>(*Start++);
+ } else {
+ LOG_TRACE_STREAM << "Providing default value for sensor '"
+ << S->FullName << "': " << Default << '\n';
+ return make_deluxe_tuple<T>(Default);
+ }
+ }));
+
+ } else if constexpr (IsDeluxeTuple<T>::Value &&
+ !std::is_same<T, EmptyDeluxeTuple>::value) {
+ if (S->OutputType != T::TT) {
+ DCRETERROR(ErrorCode::TypeMismatch);
+ }
+
+ // Register input stream.
+ // \note Need to capture parameters by value so having local copies.
+ S->registerSimulationDataSource(
+ std::function<T(void)>([=](void) mutable noexcept->T {
+ if (Start != End) {
+ LOG_TRACE_STREAM << "Reading next value for sensor '" << S->FullName
+ << "': " << *Start << '\n';
+ return *Start++;
+ } else {
+ LOG_TRACE_STREAM << "Providing default value for sensor '"
+ << S->FullName << "': " << Default << '\n';
+ return Default;
+ }
+ }));
+
+ } else {
+ ASSERT(false && "Unexpected type argument");
+ }
+
return ErrorCode::NoError;
}
} // End namespace deluxe
} // End namespace rosa
// Undef local macro if not used in the corresponding implementation.
#ifndef ROSA_LIB_DELUXE_DELUXECONTEXT_CPP
#undef DCRETERROR
#endif
#endif // ROSA_DELUXE_DELUXECONTEXT_HPP
diff --git a/include/rosa/deluxe/DeluxeSensor.hpp b/include/rosa/deluxe/DeluxeSensor.hpp
old mode 100755
new mode 100644
index 55e766c..95bb94d
--- a/include/rosa/deluxe/DeluxeSensor.hpp
+++ b/include/rosa/deluxe/DeluxeSensor.hpp
@@ -1,315 +1,668 @@
//===-- rosa/deluxe/DeluxeSensor.hpp ----------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/deluxe/DeluxeSensor.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2019
///
/// \brief Specialization of \c rosa::Agent for *sensor* role of the the *deluxe
/// interface*.
///
/// \see \c rosa::deluxe::DeluxeContext
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_DELUXE_DELUXESENSOR_HPP
#define ROSA_DELUXE_DELUXESENSOR_HPP
#include "rosa/core/Agent.hpp"
#include "rosa/deluxe/DeluxeAtoms.hpp"
#include "rosa/deluxe/DeluxeExecutionPolicy.h"
+#include "rosa/deluxe/DeluxeTuple.hpp"
+
+/// Local helper macros to deal with built-in types.
+///
+///@{
+
+/// Creates function name for member functions in \c rosa::deluxe::DeluxeSensor.
+///
+/// \param N name suffix to use
+#define DSMASTERHANDLERNAME(N) handleMaster_##N
+
+/// Defines member functions for handling messages from *master* in
+/// \c rosa::deluxe::DeluxeSensor.
+///
+/// \see \c DeluxeSensorMasterInputHandlers
+///
+/// \note No pre- and post-conditions are validated directly by these functions,
+/// they rather rely on \c rosa::deluxe::DeluxeSensor::saveMasterInput to do
+/// that.
+///
+/// \param T the type of input to handle
+/// \param N name suffix for the function identifier
+#define DSMASTERHANDLERDEFN(T, N) \
+ void DSMASTERHANDLERNAME(N)(atoms::Master, id_t MasterId, token_size_t Pos, \
+ T Value) noexcept { \
+ saveMasterInput(MasterId, Pos, Value); \
+ }
+
+/// Convenience macro for \c DSMASTERHANDLERDEFN with identical arguments.
+///
+/// \see \c DSMASTERHANDLERDEFN
+///
+/// This macro can be used instead of \c DSMASTERHANDLERDEFN if the actual value
+/// of \p T can be used as a part of a valid identifier.
+///
+/// \param T the type of input to handle
+#define DSMASTERHANDLERDEF(T) DSMASTERHANDLERDEFN(T, T)
+
+/// Results in a \c THISMEMBER reference to a member function defined by
+/// \c DSMASTERHANDLERDEFN.
+///
+/// Used in the constructor of \c rosa::deluxe::DeluxeSensor to initialize super
+/// class \c rosa::Agent with member function defined by \c DSMASTERHANDLERDEFN.
+///
+/// \see \c DSMASTERHANDLERDEFN, \c THISMEMBER
+///
+/// \param N name suffix for the function identifier
+#define DSMASTERHANDLERREF(N) THISMEMBER(DSMASTERHANDLERNAME(N))
+
+///@}
namespace rosa {
namespace deluxe {
/// Specialization of \c rosa::Agent for *sensor* role of the *deluxe
/// interface*.
///
/// \see \c rosa::deluxe::DeluxeContext
///
-/// \invariant There is a compatible *execution policy* set
+/// \invariant There is a compatible *execution policy* set; the actual value in
+/// \c rosa::deluxe::DeluxeSensor::MasterInputNextPos is valid with respect to
+/// the corresponding types.
///
/// \see Definition of \c rosa::deluxe::DeluxeSensor::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 DeluxeSensor : public Agent {
/// Checks whether \p this object holds the class invariant.
///
/// \see Invariant of the class \c rosa::deluxe::DeluxeSensor
///
/// \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:
- /// Template alias for function objects used as data source for
- /// \c rosa::deluxe::DeluxeSensor.
+ /// The type of values produced by \p this object.
///
- /// \note The function used for \c D is to be \c noexcept.
+ /// That is the types of values \p this object sends to its *master* in a
+ /// \c rosa::deluxe::DeluxeTuple.
///
- /// \tparam T type of data provided by the function
- template <typename T> using D = std::function<T(void)>;
+ /// \see \c rosa::deluxe::DeluxeSensor::master
+ const Token OutputType;
- /// The type of values produced by \p this object.
+ /// The type of values \p this object processes from its *master*.
///
- /// That is the type of values \p this object sends to its *master*.
+ /// That is the types of values \p this object receives from its *master* in a
+ /// \c rosa::deluxe::DeluxeTuple.
///
/// \see \c rosa::deluxe::DeluxeSensor::master
- const TypeNumber OutputType;
+ const Token MasterInputType;
private:
+ /// Indicates which element of the master-input is expected from the *master*.
+ ///
+ /// The *master* is supposed to send one \c rosa::deluxe::DeluxeTuple value
+ /// element by element in their order of definition. This member field tells
+ /// the element at which position should be received next.
+ ///
+ /// \p this object is supposed to be triggered only when a complete
+ /// master-input has been received, that is the field should hold the value
+ /// `0`.
+ ///
+ /// \see \c rosa::deluxe::DeluxeSensor::handleTrigger
+ /// \c rosa::deluxe::DeluxeSensor::saveMasterInput
+ token_size_t MasterInputNextPos;
+
+ /// Indicates whether the input value from the *master* has been changed since
+ /// the last trigger received from the system.
+ ///
+ /// The flag is reset to \c false upon handling a trigger and then set to \c
+ /// true by \c rosa::deluxe::DeluxeSensor::saveMasterInput when storig a new
+ /// input value in \c rosa::deluxe::DeluxeSensor::MasterInputValue.
+ bool MasterInputChanged;
+
+ /// Stores the actual input value from *master*.
+ ///
+ /// \note The type of the stored value matches the types indicated by \c
+ /// rosa::deluxe::DeluxeSensor::MasterInputType.
+ const std::unique_ptr<AbstractTokenizedStorage> MasterInputValue;
+
/// Alias for function objects used as trigger handler for
/// \c rosa::deluxe::DeluxeSensor.
///
/// \note The function used for \c H is to be \c noexcept.
///
/// \see \c DeluxeSensorTriggerHandlers
using H = std::function<void(void)>;
- /// \defgroup DeluxeSensorTriggerHandlers Trigger handlers of rosa::deluxe::DeluxeSensor
+ /// \defgroup DeluxeSensorTriggerHandlers Trigger handlers of
+ /// rosa::deluxe::DeluxeSensor
///
/// \brief Trigger handler functions of \c rosa::deluxe::DeluxeSensor
///
- /// The actual data source functions are captured in a lambda expression that
- /// is in turn wrapped in a \c std::function object. The lambda expression
- /// calls the data source function to obtain the next sensory value and sends
- /// it to *master* by calling \c rosa::deluxe::DeluxeSensor::sendToMaster. The
- /// function \c rosa::deluxe::DeluxeSensor::handleTrigger needs only to call
- /// the proper function object.
+ /// The actual data source functions and master-input processing function are
+ /// captured in lambda expressions that are in turn wrapped in \c
+ /// std::function objects. The lambda expression calls a processing function,
+ /// either to handle master-input or obtain the next sensory value from data
+ /// source. The next sensory value is sent it to *master* by calling \c
+ /// rosa::deluxe::DeluxeSensor::sendToMaster. Also, the flag \c
+ /// rosa::deluxe::DeluxeSensor::MasterInputChanged is reset when the current
+ /// value is passed to the master-input processing function. The function \c
+ /// rosa::deluxe::DeluxeSensor::handleTrigger needs only to call the proper
+ /// function object.
+
+ /// Processes master-input.
+ ///
+ /// \ingroup DeluxeSensorTriggerHandlers
+ ///
+ /// The function is called upon the sensor is trigged by the system.
+ const H MFP;
- /// Handles trigger during normal execution.
+ /// Produces the next sensory value during normal execution.
///
/// \ingroup DeluxeSensorTriggerHandlers
///
/// The function is used during normal execution. During simulation, the
/// simulation environment sets \c rosa::deluxe::DeluxeSensor::SFP, which is
/// used instead of \c rosa::deluxe::DeluxeSensor::FP.
const H FP;
- /// Handles trigger during simulation.
+ /// Produces the next sensory value during simulation.
///
/// \ingroup DeluxeSensorTriggerHandlers
///
/// The function is empty by default. The simulation environment sets it to be
/// used during simulation.
H SFP;
/// The *master* to send values to.
///
/// \note *Masters* are set dynamically, hence it is possible that a
/// \c rosa::deluxe::DeluxeSensor instance does not have any *master* at a
/// given moment.
Optional<AgentHandle> Master;
+ /// Tells the unique identifier of the *master* of \p this object, if any
+ /// registered.
+ ///
+ /// \return the unique identifier of the *master*
+ ///
+ /// \pre A *master* is registered for \p this object: \code
+ /// Master
+ /// \endcode
+ id_t masterId(void) const noexcept;
+
+ /// Wraps a master-input processing function into a trigger handler.
+ ///
+ /// \see \c rosa::deluxe::DeluxeSensor::MFP and \c DeluxeSensorTriggerHandlers
+ ///
+ /// \tparam Ts types of elements of master-input processed by \p MF
+ /// \tparam S0 indices for accessing master-input values
+ ///
+ /// \param MF function that processes master-input
+ ///
+ /// \note The second argument provides indices statically as template
+ /// arguments \p S0..., so its actual value is ignored.
+ ///
+ /// \note A master-input type of \c rosa::deluxe::EmptyDeluxeTuple indicates
+ /// that \p this object does not receive master-input, \p MF is never called
+ /// if \p Ts is empty.
+ ///
+ /// \return trigger handler function based on \p MF
+ ///
+ /// \pre Statically, the indices match the elements: \code
+ /// sizeof...(Ts) == sizeof...(S0)
+ /// \endcode Dynamically, \p Ts... match \c
+ /// rosa::deluxe::DeluxeSensor::MasterInputType: \code
+ /// MasterInputType == DeluxeTuple<Ts...>::TT
+ /// \endcode
+ template <typename... Ts, size_t... S0>
+ H triggerHandlerFromProcessingFunction(
+ std::function<void(std::pair<DeluxeTuple<Ts...>, bool>)> &&MF,
+ Seq<S0...>) noexcept;
+
/// Wraps a data source function into a trigger handler.
///
- /// \see \c DeluxeSensorTriggerHandlers
+ /// \see \c rosa::deluxe::DeluxeSensor::FP, \c
+ /// rosa::deluxe::DeluxeSensor::SFP, and \c DeluxeSensorTriggerHandlers
///
/// \tparam T type of data provided by \p F
///
/// \param F function to generate value with
/// \param inSimulation if F is a data source for Simulation
///
- /// \pre \p T matches \c rosa::deluxe::DeluxeSensor::OutputType: \code
- /// OutputType == TypeNumberOf<T>::Value
+ /// \return trigger handler function based on \p F
+ ///
+ /// \pre Statically, the type agument \p T is an instance of \c
+ /// rosa::deluxe::DeluxeTuple: \code
+ /// IsDeluxeTuple<T>::Value
+ /// \endcode Dynamically, \p T matches \c
+ /// rosa::deluxe::DeluxeSensor::OutputType: \code
+ /// OutputType == T::TT
/// \endcode
template <typename T>
- H triggerHandlerFromDataSource(D<T> &&F, bool inSimulation) noexcept;
+ H triggerHandlerFromDataSource(std::function<T(void)> &&F,
+ bool inSimulation) noexcept;
public:
/// Creates a new instance.
///
/// The constructor instantiates the base-class with functions to handle
/// messages as defined for the *deluxe interface*.
///
- /// \todo Enforce F does not potentially throw exception.
+ /// \todo Enforce \p F and \p MF do not potentially throw exception.
///
+ /// \tparam MT type of master-input handled by \p MF
/// \tparam T type of data to operate on
///
+ /// \note Instantiation fails if any of the type arguments \p MT and \p T is
+ /// not an instance of \c rosa::deluxe::DeluxeTuple or \p T is \c
+ /// rosa::deluxe::EmptyDeluxeTuple.
+ ///
+ /// \note If \p MT is \c rosa::deluxe::EmptyDeluxeTuple, the constructed
+ /// object does not receive master-input.
+ ///
/// \param Kind kind of the new \c rosa::Unit instance
/// \param Id unique identifier of the new \c rosa::Unit instance
/// \param Name name of the new \c rosa::Unit instance
/// \param S \c rosa::MessagingSystem owning the new instance
+ /// \param MF function to process master-input values with
/// \param F function to generate the next value with during normal operation
///
- /// \pre Statically, \p T is a built-in type:\code
- /// TypeListContains<BuiltinTypes, T>::Value
+ /// \pre Statically, \p MT and \p T are instances of \c
+ /// rosa::deluxe::DeluxeTuple and \p T contains at least one element:\code
+ /// TypeListAllDeluxeTuple<TypeList<MT, T>>::Value && T::Length > 0
/// \endcode
/// Dynamically, the instance is created as of kind
/// \c rosa::deluxe::atoms::SensorKind:
/// \code
/// Kind == rosa::deluxe::atoms::SensorKind
/// \endcode
- template <typename T, typename = std::enable_if_t<
- TypeListContains<BuiltinTypes, T>::Value>>
+ ///
+ /// \see \c rosa::deluxe::DeluxeTuple
+ template <
+ typename MT, typename T,
+ typename = std::enable_if_t<
+ TypeListAllDeluxeTuple<TypeList<MT, T>>::Value && (T::Length > 0)>>
DeluxeSensor(const AtomValue Kind, const id_t Id, const std::string &Name,
- MessagingSystem &S, D<T> &&F) noexcept;
+ MessagingSystem &S,
+ std::function<void(std::pair<MT, bool>)> &&MF,
+ std::function<T(void)> &&F) noexcept;
/// Destroys \p this object.
~DeluxeSensor(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::DeluxeSensor::setExecutionPolicy() is not called and \p this
/// object is not destroyed.
///
/// \return \c rosa::deluxe::DeluxeSensor::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.
///
/// \see \c rosa::deluxe::DeluxeSensor::registerMaster
///
/// \return the *master* registered for \p this object
Optional<AgentHandle> master(void) const noexcept;
/// Registers a *master* for \p this object.
///
/// The new *master* is registered by overwriting the reference to any
/// already registered *master*. One can clear the registered reference by
/// passing an *empty* \c rosa::Optional object as actual argument.
///
/// \note The role of the referred *master* is validated by checking its
/// *kind*.
///
+ /// \note Any call to \c rosa::deluxe::DeluxeSensor::registerMaster should be
+ /// paired with a corresponding call of \c
+ /// rosa::deluxe::DeluxeAgent::registerSlave, which validates that
+ /// input/output types of master and slave matches.
+ ///
/// \param _Master the *master* to register
///
/// \pre \p Master is empty or of kind \c rosa::deluxe::atoms::AgentKind:
/// \code
/// !_Master || unwrapAgent(*_Master).Kind == rosa::deluxe::atoms::AgentKind
/// \endcode
void registerMaster(const Optional<AgentHandle> _Master) noexcept;
/// Clears the simulation trigger handler of \p this object.
///
/// The function assigns \c rosa::deluxe::DeluxeSensor::SFP with \c nullptr.
void clearSimulationDataSource(void) noexcept;
/// Tells whether a simulation trigger handler is set for \p this object.
///
/// The function returns whether \c rosa::deluxe::DeluxeSensor::SFP is not
/// \c nullptr.
///
/// \return if a simulation trigger handler is set for \p this object.
bool simulationDataSourceIsSet(void) const noexcept;
/// Registers a simulation data source for \p this object.
///
/// A new simulation trigger handler wrapping \p SF is stored in
/// \c rosa::deluxe::DeluxeSensor::SFP by overwriting any already registered
/// simulation data source.
///
/// \todo Enforce SF does not potentially throw exception.
///
- /// \tparam T type of data provided by \p SF
+ /// \tparam Ts types of elements of values provided by \p SF
///
/// \param SF function to generate value with
///
- /// \pre \p T matches \c rosa::deluxe::DeluxeSensor::OutputType: \code
- /// OutputType == TypeNumberOf<T>::Value
+ /// \pre \p Ts... match \c rosa::deluxe::DeluxeSensor::OutputType: \code
+ /// OutputType == TypeToken<Ts...>::Value
/// \endcode
- template <typename T> void registerSimulationDataSource(D<T> &&SF) noexcept;
+ template <typename... Ts>
+ void registerSimulationDataSource(
+ std::function<DeluxeTuple<Ts...>(void)> &&SF) noexcept;
private:
/// Sends a value to the *master* of \p this object.
///
/// \p Value is getting sent to \c rosa::deluxe::DeluxeSensor::Master if it
/// contains a valid handle for a \c rosa::deluxe::DeluxeAgent. The function
/// does nothing otherwise.
///
- /// \tparam T type of the value to send
+ /// The elements from \p Value are sent one by one in separate messages to the
+ /// *master*.
+ ///
+ /// \tparam Ts types of the elements in \p Value
+ /// \tparam S0 indices for accessing elements of \p Value
///
/// \param Value value to send
///
- /// \pre \p T matches \c rosa::deluxe::DeluxeSensor::OutputType: \code
- /// OutputType == TypeNumberOf<T>::Value
+ /// \note The second argument provides indices statically as template
+ /// arguments \p S0..., so its actual value is ignored.
+ ///
+ /// \pre Statically, the indices match the elements: \code
+ /// sizeof...(Ts) == sizeof...(S0)
+ /// \endcode Dynamically, \p Ts match \c
+ /// rosa::deluxe::DeluxeSensor::OutputType: \code
+ /// OutputType == TypeToken<Ts...>::Value
/// \endcode
- template <typename T> void sendToMaster(const T &Value) noexcept;
+ template <typename... Ts, size_t... S0>
+ void sendToMaster(const DeluxeTuple<Ts...> &Value, Seq<S0...>) noexcept;
- /// Generates the next sensory value upon trigger from the system.
+ /// Handles master-input and generates the next sensory value upon trigger
+ /// from the system.
///
- /// Executes \c rosa::deluxe::DeluxeSensor::FP or
- /// \c rosa::deluxe::DeluxeSensor::SFP if set.
+ /// Executes \c rosa::deluxe::DeluxeSensor::MFP for processing master-input
+ /// and data generating function \c rosa::deluxe::DeluxeSensor::FP or \c
+ /// rosa::deluxe::DeluxeSensor::SFP if set.
///
/// \note The only argument is a \c rosa::AtomConstant, hence its actual
/// value is ignored.
+ ///
+ /// \pre Master-input is supposed to be completely received upon triggering:
+ /// \code
+ /// MasterInputNextPos == 0
+ /// \endcode
void handleTrigger(atoms::Trigger) noexcept;
+
+ /// Stores a new input value from the *master*.
+ ///
+ /// The function stores \p Value at position \p Pos in \c
+ /// rosa::deluxe::DeluxeSensor::MasterInputValue and also sets the
+ /// flag \c rosa::deluxe::DeluxeSensor::MasterInputChanged. The function also
+ /// takes care of checking and updating \c
+ /// rosa::deluxe::DeluxeSensor::MasterInputNextPos: increments its value and
+ /// resets it to `0` when the last element is received.
+ ///
+ /// \note Utilized by member functions of group \c
+ /// DeluxeSensorMasterInputHandlers.
+ ///
+ /// \tparam T type of input to store
+ ///
+ /// \param Id unique identifier of the *master*
+ /// \param Pos position of the value in the \c rosa::deluxe::DeluxeTuple
+ /// \param Value the input value to store
+ ///
+ /// \pre The *master* with \p Id is registered, \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 DeluxeSensorMasterInputHandlers Master-input handlers of
+ /// rosa::deluxe::DeluxeSensor
+ ///
+ /// Definition of member functions handling messages from the *master* with
+ /// different types of input
+ ///
+ /// A *slave* generally needs to be prepared to deal with values of any
+ /// built-in type to handle messages from its *master*. Each type requires a
+ /// separate message handler, which are implemented by these functions. The
+ /// functions instantiate \c rosa::deluxe::DeluxeSensor::saveMasterInput with
+ /// the proper template argument and pass the content of the message on for
+ /// processing.
+ ///
+ /// \note The member functions in this group are defined by \c
+ /// DSMASTERHANDLERDEF.
+ ///
+ /// \note Keep these definitions in sync with \c rosa::BuiltinTypes.
+ ///
+ ///@{
+
+ DSMASTERHANDLERDEF(AtomValue)
+ DSMASTERHANDLERDEF(int16_t)
+ DSMASTERHANDLERDEF(int32_t)
+ DSMASTERHANDLERDEF(int64_t)
+ DSMASTERHANDLERDEF(int8_t)
+ DSMASTERHANDLERDEFN(long double, long_double)
+ DSMASTERHANDLERDEFN(std::string, std__string)
+ DSMASTERHANDLERDEF(uint16_t)
+ DSMASTERHANDLERDEF(uint32_t)
+ DSMASTERHANDLERDEF(uint64_t)
+ DSMASTERHANDLERDEF(uint8_t)
+ DSMASTERHANDLERDEF(unit_t)
+ DSMASTERHANDLERDEF(bool)
+ DSMASTERHANDLERDEF(double)
+ DSMASTERHANDLERDEF(float)
+
+ /// @}
};
+template <typename... Ts, size_t... S0>
+DeluxeSensor::H DeluxeSensor::triggerHandlerFromProcessingFunction(
+ std::function<void(std::pair<DeluxeTuple<Ts...>, bool>)> &&MF,
+ Seq<S0...>) noexcept {
+ using MT = DeluxeTuple<Ts...>;
+ STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments");
+ ASSERT(MasterInputType == MT::TT);
+
+ // NOTE: Clang 6 warns about unused lambda captures; we suppress that
+ // warning (those variables need to be captured).
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-lambda-capture"
+#endif // defined __clang__
+ return [ this, MF ](void) noexcept {
+ // Do not do anything for master-input type \c
+ // rosa::deluxe::EmptyDeluxeTuple.
+ if constexpr (!std::is_same<MT, EmptyDeluxeTuple>::value) {
+ LOG_TRACE_STREAM << "DeluxeSensor " << 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.
+ DeluxeTuple<Ts...>(*static_cast<const Ts *>(
+ MasterInputValue->pointerTo(static_cast<token_size_t>(S0)))...),
+ MasterInputChanged);
+ MasterInputChanged = false;
+ MF(MasterInputArg);
+ }
+ };
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif // defined __clang__
+}
+
template <typename T>
DeluxeSensor::H
-DeluxeSensor::triggerHandlerFromDataSource(D<T> &&F,
+DeluxeSensor::triggerHandlerFromDataSource(std::function<T(void)> &&F,
bool inSimulation) noexcept {
- ASSERT(OutputType == TypeNumberOf<T>::Value);
+ STATIC_ASSERT(IsDeluxeTuple<T>::Value, "not tuple type argument");
+ ASSERT(OutputType == T::TT);
return [ this, F, inSimulation ](void) noexcept {
// Get value and send it to master only if \p ExecutionPolicy allows it.
if (ExecutionPolicy->shouldProcess({})) {
- sendToMaster(F());
- } else if (inSimulation) {
- // But read input value in Simulation anyway as input values are provided
- // for the highest execution frequency for simulation
- F();
+ LOG_TRACE_STREAM << "DeluxeSensor " << Name << " obtains next value."
+ << std::endl;
+ sendToMaster(F(), seq_t<T::Length>());
+ } else {
+ LOG_TRACE_STREAM << "DeluxeSensor " << Name << " skips next value."
+ << std::endl;
+ if (inSimulation) {
+ // But read input value in Simulation anyway as input values are
+ // provided for the highest execution frequency for simulation
+ F();
+ }
}
};
}
-template <typename T, typename>
+template <typename MT, typename T, typename>
DeluxeSensor::DeluxeSensor(const AtomValue Kind, const id_t Id,
const std::string &Name, MessagingSystem &S,
- D<T> &&F) noexcept
- : Agent(Kind, Id, Name, S, THISMEMBER(handleTrigger)),
- ExecutionPolicy(DeluxeExecutionPolicy::decimation(1)),
- OutputType(TypeNumberOf<T>::Value),
+ std::function<void(std::pair<MT, bool>)> &&MF,
+ std::function<T(void)> &&F) noexcept
+ : Agent(Kind, Id, Name, S, THISMEMBER(handleTrigger),
+ DSMASTERHANDLERREF(AtomValue), DSMASTERHANDLERREF(int16_t),
+ DSMASTERHANDLERREF(int32_t), DSMASTERHANDLERREF(int64_t),
+ DSMASTERHANDLERREF(int8_t), DSMASTERHANDLERREF(long_double),
+ DSMASTERHANDLERREF(std__string), DSMASTERHANDLERREF(uint16_t),
+ DSMASTERHANDLERREF(uint32_t), DSMASTERHANDLERREF(uint64_t),
+ DSMASTERHANDLERREF(uint8_t), DSMASTERHANDLERREF(unit_t),
+ DSMASTERHANDLERREF(bool), DSMASTERHANDLERREF(double),
+ DSMASTERHANDLERREF(float)),
+ ExecutionPolicy(DeluxeExecutionPolicy::decimation(1)), OutputType(T::TT),
+ MasterInputType(MT::TT), MasterInputChanged(false),
+ MasterInputValue(new typename TokenizedStorageForTypeList<
+ typename UnwrapDeluxeTuple<MT>::Type>::Type()),
+ MFP(triggerHandlerFromProcessingFunction(std::move(MF),
+ seq_t<MT::Length>())),
FP(triggerHandlerFromDataSource(std::move(F), false)), SFP(nullptr) {
ASSERT(Kind == atoms::SensorKind);
- LOG_TRACE("DeluxeSensor is created.");
+ LOG_TRACE_STREAM << "DeluxeSensor " << FullName << " is created."
+ << std::endl;
ASSERT(inv());
}
-template <typename T>
-void DeluxeSensor::registerSimulationDataSource(D<T> &&SF) noexcept {
- ASSERT(inv() && OutputType == TypeNumberOf<T>::Value);
+template <typename... Ts>
+void DeluxeSensor::registerSimulationDataSource(
+ std::function<DeluxeTuple<Ts...>(void)> &&SF) noexcept {
+ ASSERT(OutputType == TypeToken<Ts...>::Value);
SFP = triggerHandlerFromDataSource(std::move(SF), true);
ASSERT(inv());
}
-template <typename T>
-void DeluxeSensor::sendToMaster(const T &Value) noexcept {
- ASSERT(inv() && OutputType == TypeNumberOf<T>::Value);
+template <typename... Ts, size_t... S0>
+void DeluxeSensor::sendToMaster(const DeluxeTuple<Ts...> &Value,
+ Seq<S0...>) noexcept {
+ STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments");
+ ASSERT(OutputType == TypeToken<Ts...>::Value);
+
+ // The assert must hold if \p this object was successfuuly constructed.
+ ASSERT((true && ... &&
+ (static_cast<size_t>(static_cast<token_size_t>(S0)) == S0)));
+ // Create a static constant array for these indices to be available as lvalue
+ // references when creating messages below. \c S0... when used directly in a
+ // fold expression is a temporary value, which would result in \c
+ // rosa::Message instances being created with rvalue references. Further, all
+ // other values would to copied into a temporary variable for making them
+ /// available as rvalue references (they are constant lvalue references here).
+ static constexpr std::array<token_size_t, sizeof...(S0)> Indices{{S0...}};
+
+ LOG_TRACE_STREAM << "DeluxeSensor " << FullName << "(" << Id
+ << ") sends to master("
+ << static_cast<bool>(Master && *Master) << "): " << Value
+ << std::endl;
// There is a handle and the referred *master* is in a valid state.
if (Master && *Master) {
- Master->sendMessage(Message::create(atoms::Slave::Value, Id, Value));
+ // Handle each element of the tuple in a fold expression.
+ (Master->sendMessage(Message::create(atoms::Slave::Value, Id, Indices[S0],
+ std::get<S0>(Value))),
+ ...);
}
ASSERT(inv());
}
+template <typename T>
+void DeluxeSensor::saveMasterInput(id_t Id, token_size_t Pos,
+ T Value) noexcept {
+ ASSERT(Master && masterId() == Id && Pos == MasterInputNextPos &&
+ typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf<T>::Value);
+
+ LOG_TRACE_STREAM << "DeluxeSensor " << 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;
+}
+
} // End namespace deluxe
} // End namespace rosa
+#undef DSMASTERHANDLEREF
+#undef DSMASTERHANDLEDEF
+#undef DSMASTERHANDLEDEFN
+#undef DSMASTERHANDLENAME
+
#endif // ROSA_DELUXE_DELUXESENSOR_HPP
diff --git a/include/rosa/deluxe/DeluxeSystem.hpp b/include/rosa/deluxe/DeluxeSystem.hpp
old mode 100755
new mode 100644
index 7e08e82..bc51cdf
--- a/include/rosa/deluxe/DeluxeSystem.hpp
+++ b/include/rosa/deluxe/DeluxeSystem.hpp
@@ -1,211 +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 T>
+ template <typename MT, typename T>
AgentHandle createSensor(const std::string &Name,
- DeluxeSensor::D<T> &&F) noexcept;
+ 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 with
+ /// 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 T, typename... As>
- AgentHandle createAgent(const std::string &Name,
- DeluxeAgent::D<T, As...> &&F) noexcept;
+ 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 T>
-AgentHandle DeluxeSystem::createSensor(const std::string &Name,
- DeluxeSensor::D<T> &&F) 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(F));
+ return new DeluxeSensor(atoms::SensorKind, Id, Name, S, std::move(MF),
+ std::move(F));
});
return {DS};
}
-template <typename T, typename... As>
-AgentHandle DeluxeSystem::createAgent(const std::string &Name,
- DeluxeAgent::D<T, As...> &&F) noexcept {
-
+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(F));
+ return new DeluxeAgent(atoms::AgentKind, Id, Name, S, std::move(MF),
+ std::move(F));
});
return {DA};
}
} // End namespace deluxe
} // End namespace rosa
#endif // ROSA_DELUXE_DELUXESYSTEM_HPP
diff --git a/include/rosa/deluxe/DeluxeTuple.hpp b/include/rosa/deluxe/DeluxeTuple.hpp
new file mode 100644
index 0000000..e57d29b
--- /dev/null
+++ b/include/rosa/deluxe/DeluxeTuple.hpp
@@ -0,0 +1,359 @@
+//===-- rosa/deluxe/DeluxeTuple.hpp -----------------------------*- C++ -*-===//
+//
+// The RoSA Framework
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file rosa/deluxe/DeluxeTuple.hpp
+///
+/// \author David Juhasz (david.juhasz@tuwien.ac.at)
+///
+/// \date 2019
+///
+/// \brief Facilities for handling multiple input/output values for connections
+/// in the *deluxe interface*.
+///
+/// \see \c rosa::deluxe::DeluxeContext
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef ROSA_DELUXE_DELUXETUPLE_HPP
+#define ROSA_DELUXE_DELUXETUPLE_HPP
+
+#include "rosa/support/sequence.hpp"
+#include "rosa/support/type_token.hpp"
+#include <ostream>
+#include <tuple>
+
+namespace rosa {
+namespace deluxe {
+
+/// A tuple to manage multiple input/output values in the *deluxe interface*.
+///
+/// \tparam Ts types of elements of the tuple
+///
+/// \note The template may be instantiated only with built-in types and the
+/// number of those type may not exceed the capacity of a \c rosa::Token.
+template <typename... Ts>
+struct DeluxeTuple : public std::tuple<Ts...> {
+ // Statically enforce that the class template is instantiated only with
+ // built-in types.
+ STATIC_ASSERT((TypeListSubsetOf<TypeList<Ts...>, BuiltinTypes>::Value),
+ "not built-in types");
+ // Statically enforce that the class template is instantiated with not too
+ // many types.
+ // \note Instantiation would fail on \c rosa::deluxe::DeluxeTuple::TT if there
+ // are too any types; this assertion is for more readable error reporting.
+ STATIC_ASSERT(sizeof...(Ts) <= token::MaxTokenizableListSize,
+ "Too many types");
+
+ /// How many elements the instance has.
+ static constexpr token_size_t Length = sizeof...(Ts);
+
+ /// What types the class contains.
+ ///
+ /// Type information encoded as \c rosa::Token.
+ static constexpr Token TT = TypeToken<Ts...>::Value;
+
+ /// Default constructor, zero-initializes elements.
+ DeluxeTuple(void) = default;
+
+ /// Constructor, initializes the underlying \c std::tuple with lvalue
+ /// references.
+ ///
+ /// \param Args value references to the values to store
+ DeluxeTuple(const std::decay_t<Ts> &... Args) : std::tuple<Ts...>(Args...) {}
+
+ /// Constructor, initializes the underlying \c std::tuple with rvalue
+ /// references.
+ ///
+ /// \param Args rvalue references to the values to store
+ DeluxeTuple(std::decay_t<Ts> &&... Args)
+ : std::tuple<Ts...>(std::move(Args)...) {}
+
+ /// Default copy-constructor.
+ DeluxeTuple(const DeluxeTuple &) = default;
+
+ /// Default move-constructor.
+ DeluxeTuple(DeluxeTuple &&) = default;
+
+ /// Default copy-assignment.
+ DeluxeTuple &operator=(const DeluxeTuple &) = default;
+
+ /// Default move-assignment.
+ DeluxeTuple &operator=(DeluxeTuple &&) = default;
+
+private:
+ /// Dumps \p this object to a given \c std::ostream.
+ ///
+ /// \note Provides implementation for \c rosa::deluxe::DeluxeTuple::dump.
+ ///
+ /// \tparam S0 Indices for accessing elements.
+ ///
+ /// \param [in,out] OS output stream to dump to
+ ///
+ /// \note The second argument provides indices statically as template
+ /// arguments \p S0..., so its actual value is ignored.
+ ///
+ /// \pre Statically, \p S0... matches number of types \p this object was
+ /// created: \code
+ /// sizeof...(S0) == sizeof...(Ts)
+ /// \endcode
+ template <size_t... S0>
+ void dump(std::ostream &OS, Seq<S0...>) const noexcept;
+
+public:
+ /// Dumps \p this object to a given \c std::ostream.
+ ///
+ /// \param [in,out] OS output stream to dump to
+ void dump(std::ostream &OS) const noexcept;
+};
+
+template <typename... Ts>
+template <size_t... S0>
+void DeluxeTuple<Ts...>::dump(std::ostream &OS, Seq<S0...>) const noexcept {
+ STATIC_ASSERT(sizeof...(S0) == sizeof...(Ts), "inconsistent type arguments");
+ // Convert value to std::string with std::to_string except for a value of
+ // std::string that does not need conversion.
+ auto dump_to_string = [](const auto &V) {
+ if constexpr (std::is_same<std::decay_t<decltype(V)>, std::string>::value) {
+ return V;
+ } else {
+ return std::to_string(V);
+ }
+ };
+ OS << "{";
+ (OS << ... << (" " + dump_to_string(std::get<S0>(*this))));
+ OS << " }";
+}
+
+template <typename... Ts>
+void DeluxeTuple<Ts...>::dump(std::ostream &OS) const noexcept {
+ dump(OS, seq_t<sizeof...(Ts)>());
+}
+
+/// Type alias for a \c rosa::deluxe::DeluxeTuple that contains no elements.
+using EmptyDeluxeTuple = DeluxeTuple<>;
+
+/// Template specialization for \c rosa::deluxe::EmptyDeluxeTuple.
+template <> struct DeluxeTuple<> : public std::tuple<> {
+ /// How many elements the instance has.
+ static constexpr token_size_t Length = 0;
+
+ /// What types the class contains.
+ ///
+ /// Type information encoded as \c rosa::Token.
+ static constexpr Token TT = TypeToken<>::Value;
+
+ /// Constructor, initializes the underlying \c std::tuple.
+ DeluxeTuple(void) : std::tuple<>() {}
+
+ /// Default copy-constructor.
+ DeluxeTuple(const DeluxeTuple &) = default;
+
+ // Default move-constructor.
+ DeluxeTuple(DeluxeTuple &&) = default;
+
+ /// Default copy-assignment.
+ DeluxeTuple &operator=(const DeluxeTuple &) = default;
+
+ // Default move-assignment,
+ DeluxeTuple &operator=(DeluxeTuple &&) = default;
+
+ /// Dumps \p this object to a given \c std::ostream.
+ ///
+ /// \param [in,out] OS output stream to dump to
+ static void dump(std::ostream &OS) noexcept;
+};
+
+/// Creates a \c rosa::deluxe::DeluxeTuple instance from the given lvalues
+/// references.
+///
+/// \tparam Ts types of elements of the tuple
+///
+/// \see \c rosa::deluxe::DeluxeTuple
+///
+/// \param Args values to store in the tuple
+///
+/// \return an instance of \c rosa::deluxe::DeluxeTuple<Ts...> with \p Args as
+/// elements
+template <typename... Ts>
+inline DeluxeTuple<Ts...> make_deluxe_tuple(const Ts &... Args) noexcept {
+ return DeluxeTuple<Ts...>(Args...);
+}
+
+/// Creates a \c rosa::deluxe::DeluxeTuple instance from the given rvalue
+/// references.
+///
+/// \tparam Ts types of elements of the tuple
+///
+/// \see \c rosa::deluxe::DeluxeTuple
+///
+/// \param Args values to store in the tuple
+///
+/// \return an instance of \c rosa::deluxe::DeluxeTuple<Ts...> with \p Args as
+/// elements
+template <typename... Ts>
+inline DeluxeTuple<Ts...> make_deluxe_tuple(Ts&&... Args) noexcept {
+ return DeluxeTuple<Ts...>(std::move(Args)...);
+}
+
+/// \defgroup UnwrapDeluxeTuple Implementation of
+/// rosa::deluxe::UnwrapDeluxeTuple
+///
+/// \brief Unwraps element types from an instance of \c
+/// rosa::deluxe::DeluxeTuple into a \c rosa::TypeList
+///
+/// Types can be unwrapped from a \c rosa::deluxe::DeluxeTuple instance as \code
+/// typename UnwrapDeluxeTuple<List>::Type
+/// \endcode
+///
+/// For example, the following expression evaluates to `true`: \code
+/// std::is_same<typename UnwrapDeluxeTuple<DeluxeTuple<T1, T2>>::Type,
+/// TypeList<T1, T2>>::value
+/// \endcode
+///@{
+
+/// Declaration of the template.
+///
+/// \tparam Tuple \c rosa::deluxe::DeluxeTuple to unwrap
+template <typename Tuple> struct UnwrapDeluxeTuple;
+
+/// Implementation of the template for \c rosa::deluxe::DeluxeTuple instances.
+template <typename... Ts> struct UnwrapDeluxeTuple<DeluxeTuple<Ts...>> {
+ using Type = TypeList<Ts...>;
+};
+
+///@}
+
+/// \defgroup TypeListUnwrapDeluxeTuple Implementation of
+/// \c rosa::deluxe::TypeListUnwrapDeluxeTuple
+///
+/// \brief Unwraps element types from instances of \c
+/// rosa::deluxe::DeluxeTuple in a \c rosa::TypeList.
+///
+/// Types can be unwrapped from \c rosa::deluxe::DeluxeTuple instances as \code
+/// typename TypeListUnwrapDeluxeTuple<List>::Type
+/// \endcode
+///
+/// For example, the following expression evaluates to `true`: \code
+/// std::is_same<
+/// typename TypeListUnwrapDeluxeTuple<TypeList<T0,
+/// DeluxeTuple<T1, T2>,
+/// T3>>::Type,
+/// TypeList<T0, T1, T2, T3>
+/// >::value
+/// \endcode
+///@{
+
+/// Declaration of the template.
+///
+/// \tparam List \c rosa::TypeList to check
+template <typename List> struct TypeListUnwrapDeluxeTuple;
+
+/// Specialization for \c rosa::EmptyTypeList.
+template <> struct TypeListUnwrapDeluxeTuple<EmptyTypeList> {
+ using Type = EmptyTypeList;
+};
+
+/// Specialization for the case when the first type in \p List is an instance of
+/// \c rosa::deluxe::DeluxeTuple.
+template <typename... As, typename... Ts>
+struct TypeListUnwrapDeluxeTuple<TypeList<DeluxeTuple<As...>, Ts...>> {
+ using Type = typename TypeListConcat<
+ typename UnwrapDeluxeTuple<DeluxeTuple<As...>>::Type,
+ typename TypeListUnwrapDeluxeTuple<TypeList<Ts...>>::Type>::Type;
+};
+
+/// Implementation for a general first type in \p List.
+template <typename T, typename... Ts>
+struct TypeListUnwrapDeluxeTuple<TypeList<T, Ts...>> {
+ using Type = typename TypeListPush<
+ T, typename TypeListUnwrapDeluxeTuple<TypeList<Ts...>>::Type>::Type;
+};
+
+///@}
+
+/// \defgroup IsDeluxeTuple Implementation of \c rosa::deluxe::IsDeluxeTuple
+///
+/// \brief Tells if a type is an instance of \c rosa::deluxe::DeluxeTuple.
+///
+/// Whether a type \c T is an instance of \c rosa::deluxe::DeluxeTuple can be
+/// checked as \code
+/// IsDeluxeTuple<T>::Value
+/// \endcode
+///@{
+
+/// Declaration of the template.
+///
+/// \tparam T type to check
+template <typename T> struct IsDeluxeTuple;
+
+/// Specialization for the case when the type is an instance of \c
+/// rosa::deluxe::DeluxeTuple.
+template <typename... Ts>
+struct IsDeluxeTuple<DeluxeTuple<Ts...>> {
+ static constexpr bool Value = true;
+};
+
+/// Implementation for a general case of type \p T.
+template <typename T>
+struct IsDeluxeTuple {
+ static constexpr bool Value = false;
+};
+
+///@}
+
+/// \defgroup TypeListAllDeluxeTuple Implementation of
+/// \c rosa::deluxe::TypeListAllDeluxeTuple
+///
+/// \brief Tells if all types in a \c rosa::TypeList is an instance of \c
+/// rosa::deluxe::DeluxeTuple.
+///
+/// Whether a \c rosa::TypeList \c List contains instances of \c
+/// rosa::deluxe::DeluxeTuple only can be checked as \code
+/// TypeListAllDeluxeTuple<List>::Value
+/// \endcode
+///@{
+
+/// Declaration of the template.
+///
+/// \tparam List \c rosa::TypeList to check
+template <typename List> struct TypeListAllDeluxeTuple;
+
+/// Specialization for \c rosa::EmptyTypeList.
+template <> struct TypeListAllDeluxeTuple<EmptyTypeList> {
+ static constexpr bool Value = true;
+};
+
+/// Implementation for the general case when there is at leasst one element in
+/// the list.
+template <typename T, typename... Ts>
+struct TypeListAllDeluxeTuple<TypeList<T, Ts...>> {
+ static constexpr bool Value =
+ IsDeluxeTuple<T>::Value && TypeListAllDeluxeTuple<TypeList<Ts...>>::Value;
+};
+
+///@}
+
+} // End namespace deluxe
+} // End namespace rosa
+
+namespace std {
+
+/// Dumps a \c rosa::deluxe::Deluxe instance to a given \c std::ostream.
+///
+/// \param [in,out] OS output stream to dump to
+/// \param Tuple \c rosa::deluxe::Deluxe to dump
+///
+/// \return \p OS after dumping \p Tuple to it
+template <typename... Ts>
+ostream &operator<<(ostream &OS,
+ const rosa::deluxe::DeluxeTuple<Ts...> &Tuple) {
+ Tuple.dump(OS);
+ return OS;
+}
+
+} // End namespace std
+
+#endif // ROSA_DELUXE_DELUXETUPLE_HPP
diff --git a/include/rosa/support/atom.hpp b/include/rosa/support/atom.hpp
index 60fbe06..1dfe3e0 100644
--- a/include/rosa/support/atom.hpp
+++ b/include/rosa/support/atom.hpp
@@ -1,179 +1,209 @@
//===-- rosa/support/atom.hpp -----------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/support/atom.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
-/// \date 2017
+/// \date 2017-2019
///
/// \brief Facility for *atoms*, short strings statically encoded as integers.
///
/// \note This implementation is based on the \c atom implementation of CAF.
/// \todo Check license.
///
/// *Atoms* can be used to turn short string literals into statically generated
/// types. The literals may consist of at most \c 10 non-special characters,
/// legal characters are \c _0-9A-Za-z and the whitespace character. Special
/// characters are turned into whitespace, which may result in different string
/// literals being encoded into the same integer value, if any of those contain
/// at least one special character.
///
/// \note The usage of special characters in the string literals used to create
/// *atoms* cannot be checked by the compiler.
///
/// Example:
///
/// \code
/// constexpr AtomValue NameValue = atom("name");
/// using NameAtom = AtomConstant<NameValue>;
///
/// [](NameAtom){ std::cout << "Argument of type NameAtom"; }(NameAtom::Value)
/// \endcode
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_SUPPORT_ATOM_HPP
#define ROSA_SUPPORT_ATOM_HPP
#include "rosa/support/debug.hpp"
namespace rosa {
/// Maximal length of valid atom strings.
constexpr size_t MaxAtomLength = 10;
/// Underlying integer type of atom values.
using atom_t = uint64_t;
/// Turn \c rosa::atom_t into a strongly typed enumeration.
///
/// Values of \c rosa::atom_t casted to \c rosa::AtomValue may be used in a
/// type-safe way.
enum class AtomValue : atom_t {};
/// Anonymous namespace with implementational details, consider it private.
namespace {
// clang-format off
/// Encodes ASCII characters to 6-bit encoding.
constexpr unsigned char AtomEncodingTable[] = {
/* ..0 ..1 ..2 ..3 ..4 ..5 ..6 ..7 ..8 ..9 ..A ..B ..C ..D ..E ..F */
/* 0.. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 1.. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 2.. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 3.. */ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0, 0,
/* 4.. */ 0, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
/* 5.. */ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 0, 0, 0, 0, 37,
/* 6.. */ 0, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
/* 7.. */ 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 0, 0, 0, 0, 0};
// clang-format on
/// Decodes 6-bit characters to ASCII
constexpr char AtomDecodingTable[] = " 0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ_"
"abcdefghijklmnopqrstuvwxyz";
/// Encodes one character and updates the integer representation.
///
/// \param Current an encoded value
/// \param CharCode a character to add to \p Current
///
/// \return \p Current updated with \p CharCode
constexpr atom_t nextInterim(atom_t Current, size_t CharCode) {
return (Current << 6) | AtomEncodingTable[(CharCode <= 0x7F) ? CharCode : 0];
}
/// Encodes a C-string into an integer value to be used as \c rosa::AtomValue.
///
/// \param CStr a string to encode
/// \param Interim encoded value to add \p CStr to it
///
/// \return \p Interim updated with \p CStr
constexpr atom_t atomValue(const char *CStr, atom_t Interim = 0xF) {
return (*CStr == '\0')
? Interim
: atomValue(CStr + 1,
nextInterim(Interim, static_cast<size_t>(*CStr)));
}
} // End namespace
-/// Converts a \c rosa::AtomValue into \c std::string.
-///
-/// \param What value to convert
-///
-/// \return \c std::string encoded in \p What
-std::string to_string(const AtomValue &What);
-
/// Converts a \c std::string into a \c rosa::AtomValue.
///
/// \param S \c std::string to convert
///
/// \return \c rosa::AtomValue representing \p S
AtomValue atom_from_string(const std::string &S);
/// Converts a string-literal into a \c rosa::AtomValue.
///
/// \tparam Size the length of \p Str
///
/// \param Str the string-literal to convert
///
/// \return \c rosa::AtomValue representating \p Str
///
/// \pre \p Str is not too long:\code
/// Size <= MaxAtomLength + 1
/// \endcode
template <size_t Size> constexpr AtomValue atom(char const (&Str)[Size]) {
// Last character is the NULL terminator.
STATIC_ASSERT(Size <= MaxAtomLength + 1,
"Too many characters in atom definition");
return static_cast<AtomValue>(atomValue(Str));
}
/// Lifts a \c rosa::AtomValue to a compile-time constant.
///
/// \tparam V \c rosa::AtomValue to lift
template <AtomValue V> struct AtomConstant {
/// Constructor has to do nothing.
constexpr AtomConstant(void) {}
/// Returns the wrapped value.
///
/// \return \p V
constexpr operator AtomValue(void) const { return V; }
/// Returns the wrapped value as of type \c rosa::atom_t.
///
/// \return \c rosa::atom_t value from \p V
static constexpr atom_t value() { return static_cast<atom_t>(V); }
/// An instance *of this constant* (*not* a \c rosa::AtomValue).
static const AtomConstant Value;
};
// Implementation of the static member field \c rosa::AtomConstant::Value.
template <AtomValue V>
const AtomConstant<V> AtomConstant<V>::Value = AtomConstant<V>{};
+} // End namespace rosa
+
+namespace std {
+
+/// Converts a \c rosa::AtomValue into \c std::string.
+///
+/// \param What value to convert
+///
+/// \return \c std::string encoded in \p What
+string to_string(const rosa::AtomValue &What);
+
+/// Dumps a \c rosa::AtomValue to a given \c std::ostream.
+///
+/// \param [in,out] OS output stream to dump to
+/// \param A \c rosa::AtomValue to dump
+///
+/// \return \p OS after dumping \p N to it
+inline ostream &operator<<(ostream &OS, const rosa::AtomValue &A) {
+ OS << to_string(A);
+ return OS;
+}
+
/// Converts a \c rosa::AtomConstant into \c std::string.
///
/// \tparam V \c rosa::AtomValue to convert
///
/// \note The actual argument of type `const rosa::AtomConstant<V>` is ignored
/// because the \c rosa::AtomValue to convert is encoded in the type itself.
///
/// \return the original string encoded in \p V
-template <AtomValue V> std::string to_string(const AtomConstant<V> &) {
+template <rosa::AtomValue V> string to_string(const rosa::AtomConstant<V> &) {
return to_string(V);
}
-} // End namespace rosa
+/// Dumps a \c rosa::AtomConstant to a given \c std::ostream.
+///
+/// \tparam V the \c rosa::AtomValue to dump
+///
+/// \param [in,out] OS output stream to dump to
+/// \param A \c rosa::AtomConstant providing \p V
+///
+/// \return \p OS after dumping \p V to it
+template <rosa::AtomValue V>
+inline ostream &operator<<(ostream &OS, const rosa::AtomConstant<V> &A) {
+ (void)A; // Shut compiler about unused parameter.
+ OS << to_string(V);
+ return OS;
+}
+
+} // End namespace std
#endif // ROSA_SUPPORT_ATOM_HPP
diff --git a/include/rosa/support/sequence.hpp b/include/rosa/support/sequence.hpp
index 5764a80..62b8408 100755
--- a/include/rosa/support/sequence.hpp
+++ b/include/rosa/support/sequence.hpp
@@ -1,51 +1,57 @@
//===-- rosa/support/sequence.hpp -------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/support/sequence.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
-/// \date 2017
+/// \date 2017-2019
///
/// \brief Template facilities to statically generate a sequence of numbers.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_SUPPORT_SEQUENCE_HPP
#define ROSA_SUPPORT_SEQUENCE_HPP
#include <cstddef>
namespace rosa {
/// \defgroup Seq Implementation of rosa::Seq
///
/// Facility to statically generate sequences of numbers.
///
///@{
/// Template with an empty struct to store a sequence of numbers in compile time
/// as template arguments.
///
/// Generate a sequence of numbers from `0` up to (including) `(N - 1)` like
/// \code
/// typename GenSeq<N>::Type
/// \endcode
template <size_t...> struct Seq {};
/// Sequence generator, the general case when counting down by extending the
/// sequence.
template <size_t N, size_t... S> struct GenSeq : GenSeq<N - 1, N - 1, S...> {};
/// Sequence generator, the terminal case when storing the generated sequence
/// into \c Seq.
template <size_t... S> struct GenSeq<0, S...> { using Type = Seq<S...>; };
///@}
+/// Convenience template alias for using \c rosa::GenSeq to obtain an instance
+/// of \c rosa::Seq.
+///
+/// \see \c rosa::Seq and \c rosa::GenSeq
+template <size_t... S> using seq_t = typename GenSeq<S...>::Type;
+
} // End namespace rosa
#endif // ROSA_SUPPORT_SEQUENCE_HPP
diff --git a/include/rosa/support/squashed_int.hpp b/include/rosa/support/squashed_int.hpp
index 48df20a..2d69d4b 100644
--- a/include/rosa/support/squashed_int.hpp
+++ b/include/rosa/support/squashed_int.hpp
@@ -1,132 +1,140 @@
//===-- rosa/support/squashed_int.hpp ---------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/support/squashed_int.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
-/// \date 2017
+/// \date 2017-2019
///
/// \brief Facilities for squashing integer types into standard equivalents.
///
/// \note This implementation is partially based on the \c squashed_int
/// implementation of CAF.
/// \todo Check license.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_SUPPORT_SQUASHED_INT_HPP
#define ROSA_SUPPORT_SQUASHED_INT_HPP
#include "rosa/support/type_list.hpp"
#include "rosa/support/type_pair.hpp"
namespace rosa {
/// Compile-time list of integer types.
///
/// \note This list is used to select a proper type as \c rosa::type_nr_t,
/// always make sure that \c rosa::type_nr_t remains correct whenever changing
/// the list.
using IntegerTypesBySize = TypeList< // bytes
none_t, // 0
TypePair<int8_t, uint8_t>, // 1
TypePair<int16_t, uint16_t>, // 2
none_t, // 3
TypePair<int32_t, uint32_t>, // 4
none_t, // 5
none_t, // 6
none_t, // 7
TypePair<int64_t, uint64_t> // 8
>;
-/// Squashes integer types into \c [u]int_[8|16|32|64]_t equivalents.
+/// Squashes integral types (except \c bool) into \c [u]int_[8|16|32|64]_t
+/// equivalents.
///
/// The squashed type for a type \c T can be obtained as \code
/// typename SquashedInt<T>::Type
/// \endcode
///
-/// \tparam T the integer type to squash
+/// \tparam T the integral type to squash
///
/// \pre \p T is an integral type:\code
/// std::is_integral<T>::value
/// \endcode
template <typename T> struct SquashedInt {
- STATIC_ASSERT((std::is_integral<T>::value), "squashing a non-integral type");
+ STATIC_ASSERT((std::is_integral<T>::value && !std::is_same<T, bool>::value),
+ "squashing a non-integral type or bool");
using TPair = typename TypeListAt<IntegerTypesBySize, sizeof(T)>::Type;
using Type =
typename std::conditional<std::is_signed<T>::value, typename TPair::First,
typename TPair::Second>::type;
};
/// Convenience alias for obtaining a squashed integer type.
template <typename T> using squashed_int_t = typename SquashedInt<T>::Type;
/// \defgroup SquashedType Implementation for squashing types
///
/// \brief Squashes a type.
///
/// The squashed type for a type \c T can be obtained as \code
/// typename SquashedType<T>::Type
/// \endcode
/// The resulting type is squashed with \c rosa::SquashedInt if \c T is
-/// integral, and remains \p T otherwise.
+/// integral but not \c bool, and remains \p T otherwise.
///@{
/// Definition for the general case, when squashing a non-integral type.
///
/// \tparam T the type to squash
/// \tparam IsIntegral Always use the default value!
template <typename T, bool IsIntegral = std::is_integral<T>::value>
struct SquashedType {
using Type = T;
};
/// Specialization for the case when squashing an integral type.
///
/// \tparam T the type to squash
template <typename T> struct SquashedType<T, true> {
using Type = squashed_int_t<T>;
};
+/// Specialization for the type \c bool.
+///
+/// \note The type \c bool is an integral type and would be squashed by the
+/// general case to \c uint8_t without this specialization.
+template <> struct SquashedType<bool, true> { using Type = bool; };
+
///@}
/// Convenience alias for obtaining a squashed type.
template <typename T> using squashed_t = typename SquashedType<T>::Type;
/// \defgroup SquashedTypeList Implementation for squashing lists of types
///
/// \brief Squashes a \c rosa::TypeList elementwise.
///
/// Replaces all types in a \c rosa::TypeList with their corresponding squashed
/// types by using \c rosa::SquashedType. The squashed \c rosa::TypeList
/// corresponding to \c List can be obtained as \code
/// typename SquashedTypeList<List>::Type
/// \endcode
///@{
/// Declaration of the template.
///
/// \tparam List \c rosa::TypeList to squash
template <typename List> struct SquashedTypeList;
// Specialization for \c rosa::EmptyTypeList.
template <> struct SquashedTypeList<EmptyTypeList> {
using Type = EmptyTypeList;
};
/// Specialization for non-empty \c rosa::TypeList.
template <typename T, typename... Ts>
struct SquashedTypeList<TypeList<T, Ts...>> {
using Type = typename TypeListPush<
squashed_t<T>, typename SquashedTypeList<TypeList<Ts...>>::Type>::Type;
};
///@}
} // End namespace rosa
#endif // ROSA_SUPPORT_SQUASHED_INT_HPP
diff --git a/include/rosa/support/tokenized_storages.hpp b/include/rosa/support/tokenized_storages.hpp
index 0f5bdbf..dca7f4e 100755
--- a/include/rosa/support/tokenized_storages.hpp
+++ b/include/rosa/support/tokenized_storages.hpp
@@ -1,510 +1,620 @@
//===-- rosa/support/tokenized_storages.hpp ---------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/support/tokenized_storages.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
-/// \date 2017
+/// \date 2017-2019
///
/// \brief Definition of storage helper template for storing values in a
/// type-safe way based on type tokens.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_SUPPORT_TOKENIZED_STORAGES_HPP
#define ROSA_SUPPORT_TOKENIZED_STORAGES_HPP
#include "rosa/support/type_token.hpp"
#include <memory>
#include <vector>
namespace rosa {
/// Defines a simple interface for storing and accessing values of different
/// types.
///
/// While the interface provides features to access values and know their
/// types, it is the users responsibility to use particular values according to
/// their actual types. No facilities for type-safe access of values is
/// provided by the class.
///
/// \see \c rosa::TokenizedStorage for a type-safe specialization of the
/// interface.
class AbstractTokenizedStorage {
protected:
/// Protected constructor restricts instantiation for derived classes.
AbstractTokenizedStorage(void) noexcept = default;
public:
/// No copying and moving of \c rosa::AbstractTokenizedStorage instances.
///@{
AbstractTokenizedStorage(const AbstractTokenizedStorage&) = delete;
AbstractTokenizedStorage &operator=(const AbstractTokenizedStorage&) = delete;
AbstractTokenizedStorage(AbstractTokenizedStorage&& Other) = delete;
AbstractTokenizedStorage &operator=(AbstractTokenizedStorage&&) = delete;
///@}
/// Destroys \p this object.
virtual ~AbstractTokenizedStorage(void) noexcept = default;
/// Tells how many values are stored in \p this object.
///
/// \return number of values stored in \p this object
virtual size_t size(void) const noexcept = 0;
/// Tells the type of the value stored at a position.
///
/// \param Pos the index of the value whose type is to returned
///
/// \return \c rosa::TypeNumber for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index:\code
/// Pos < size()
/// \endcode
- virtual TypeNumber typeAt(const size_t Pos) const noexcept = 0;
+ virtual TypeNumber typeAt(const token_size_t Pos) const noexcept = 0;
/// Provides an untyped pointer for the value stored at a position.
///
/// \param Pos the index of the value to return an untyped pointer for
///
/// \return untyped pointer for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index:\code
/// Pos < size()
/// \endcode
- virtual void *pointerTo(const size_t Pos) noexcept = 0;
+ virtual void *pointerTo(const token_size_t Pos) noexcept = 0;
/// Provides a constant untyped pointer for the value stored at a position.
///
/// \param Pos the index of the value to return an untyped pointer for
///
/// \return constant untyped pointer for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index:\code
/// Pos < Offsets.size()
/// \endcode
- virtual const void *pointerTo(const size_t Pos) const noexcept = 0;
+ virtual const void *pointerTo(const token_size_t Pos) const noexcept = 0;
};
/// Template class storing values and providing dynamic type-safe access to
/// them in a lightweight way based on type tokens.
///
/// \see rosa/support/type_token.hpp
///
/// \tparam Types types whose values are to be stored
template <typename... Types> class TokenizedStorage;
+/// \defgroup TokenizedStorageForTypeList Implementation of
+/// rosa::TokenizedStorageForTypeList
+///
+/// \brief Transforms a \c rosa::TypeList instance to the corresponding
+/// \c rosa::TokenizedStorage instance.
+///
+/// A \c rosa::TypeList \c List instance can be turned into a corresponding \c
+/// rosa::TokenizedStorage instance as \code
+/// typename TokenizedStorageForTypeList<List>::Type
+/// \endcode
+///
+/// For example, the following expression evaluates to `true`: \code
+/// std::is_same<typename TokenizedStorageForTypeList<TypeList<T1, T2>>::Type,
+/// TokenizedStorage<T1, T2>>::value
+/// \endcode
+///@{
+
+/// Declaration of the template.
+///
+/// \tparam List \c rosa::TypeList to transform
+template <typename List> struct TokenizedStorageForTypeList;
+
+/// Implementation of the template for \c rosa::TypeList instances.
+template <typename... Ts>
+struct TokenizedStorageForTypeList<TypeList<Ts...>> {
+ using Type = TokenizedStorage<Ts...>;
+};
+
+///@}
+
/// Nested namespace with implementation for \c rosa::TokenizedStorage, consider
/// it private.
namespace {
/// Initializes a pre-allocated memory area with values from constant lvalue
/// references.
///
/// \tparam Types types whose values are to be stored
///
/// \param Arena pre-allocated memory area to store values to
/// \param Ts the values to store in \p Arena
///
/// \note \p Arena needs to be a valid pointer to a memory area big enough for
/// values of \p Types.
template <typename... Types>
inline void createArenaElements(void *const Arena,
const Types &... Ts) noexcept;
/// \defgroup createLvalueArenaElement Implementation of creating lvalue arena elements
///
/// Stores values from constant lvalue references into a pre-allocated memory
/// area.
///
/// \note To be used by the implementation of \c createArenaElements.
///
/// \todo Document these functions.
///@{
/// \note This terminal case is used for both constant lvalue references and
/// value references.
template <size_t Pos>
inline void createArenaElement(void *const,
const std::vector<size_t> &Offsets) {
ASSERT(Pos == Offsets.size());
}
template <size_t Pos, typename Type, typename... Types>
inline void createArenaElement(void *const Arena,
const std::vector<size_t> &Offsets,
const Type &T, const Types &... Ts) noexcept {
ASSERT(Arena != nullptr && Pos < Offsets.size());
new (static_cast<Type *>(static_cast<void *>(static_cast<uint8_t *>(Arena) +
Offsets[Pos]))) Type(T);
createArenaElement<Pos + 1>(Arena, Offsets, Ts...);
}
template <size_t Pos, AtomValue V, typename... Types>
inline void
createArenaElement(void *const Arena, const std::vector<size_t> &Offsets,
const AtomConstant<V> &, const Types &... Ts) noexcept {
ASSERT(Arena != nullptr && Pos < Offsets.size());
*static_cast<AtomValue *>(
static_cast<void *>(static_cast<uint8_t *>(Arena) + Offsets[Pos])) = V;
createArenaElement<Pos + 1>(Arena, Offsets, Ts...);
}
///@}
/// Implementation of the template.
///
-/// \tparam Type the type of the mandatory first value to store
-/// \tparam Types types of any further values to store
+/// \tparam Types types of values to store
///
/// \param Arena pre-allocated memory area to store values to
-/// \param T the first value to store in \p Arena˛
-/// \param Ts optional further values to store in \p Arena
+/// \param Ts values to store in \p Arena
///
/// \pre \p Arena is not \p nullptr.
-template <typename Type, typename... Types>
-inline void createArenaElements(void *const Arena, const Type &T,
+template <typename... Types>
+inline void createArenaElements(void *const Arena,
const Types &... Ts) noexcept {
ASSERT(Arena != nullptr);
- createArenaElement<0>(Arena, TokenizedStorage<Type, Types...>::Offsets, T,
- Ts...);
+ createArenaElement<0>(Arena, TokenizedStorage<Types...>::Offsets, Ts...);
}
/// Initializes a pre-allocated memory area with values from rvalue references.
///
/// \tparam Types types whose values are to be stored
///
/// \param Arena pre-allocated memory area to store values to
/// \param Ts the values to store in \p Arena
///
/// \note \p Arena needs to be a valid pointer to a memory area big enough for
/// values of \p Types.
template <typename... Types>
inline void createArenaElements(void *const Arena, Types &&... Ts) noexcept;
/// \defgroup createRvalueArenaElement Implementation of creating rvalue arena elements
///
/// Stores values from rvalue references into a pre-allocated memory area.
///
/// \note To be used by the implementation of \c createArenaElements.
///
/// \todo Document these functions.
///@{
template <size_t Pos, typename Type, typename... Types>
inline void createArenaElement(void *const Arena,
const std::vector<size_t> &Offsets, Type &&T,
Types &&... Ts) noexcept {
ASSERT(Arena != nullptr && Pos < Offsets.size());
new (static_cast<Type *>(static_cast<void *>(
static_cast<uint8_t *>(Arena) + Offsets[Pos]))) Type(std::move(T));
createArenaElement<Pos + 1>(Arena, Offsets, std::move(Ts)...);
}
template <size_t Pos, AtomValue V, typename... Types>
inline void createArenaElement(void *const Arena,
const std::vector<size_t> &Offsets,
AtomConstant<V> &&, Types &&... Ts) noexcept {
ASSERT(Arena != nullptr && Pos < Offsets.size());
*static_cast<AtomValue *>(
static_cast<void *>(static_cast<uint8_t *>(Arena) + Offsets[Pos])) = V;
createArenaElement<Pos + 1>(Arena, Offsets, std::move(Ts)...);
}
///@}
/// Implementation of the template.
///
-/// \tparam Type the type of the mandatory first value to store
-/// \tparam Types types of any further values to store
+/// \tparam Types types of values to store
///
/// \param Arena pre-allocated memory area to store values to
-/// \param T the first value to store in \p Arena
-/// \param Ts optional further values to store in \p Arena
+/// \param Ts values to store in \p Arena
///
/// \pre \p Arena is not \c nullptr.
-template <typename Type, typename... Types>
-inline void createArenaElements(void *const Arena, Type &&T,
- Types &&... Ts) noexcept {
+template <typename... Types>
+inline void createArenaElements(void *const Arena, Types &&... Ts) noexcept {
ASSERT(Arena != nullptr);
- createArenaElement<0>(Arena, TokenizedStorage<Type, Types...>::Offsets,
- std::move(T), std::move(Ts)...);
+ createArenaElement<0>(Arena, TokenizedStorage<Types...>::Offsets,
+ std::move(Ts)...);
}
/// Destroys values allocated by \c createArenaElements.
///
-/// \tparam Type type of the mandatory first value stored in \p Arena
-/// \tparam Types futher types whose values are stored in \p Arena
+/// \tparam Types types whose values are stored in \p Arena
///
/// \param Arena the memory area to destroy values from
///
/// \note \p Arena needs to be a valid pointer to a memory area where values of
/// \p Types are stored.
-template <typename Type, typename... Types>
+template <typename... Types>
inline void destroyArenaElements(void *const Arena) noexcept;
/// \defgroup destroyArenaElement Implementation of destroying arena elements
///
/// Destroys values from a memory area.
///
/// \note To be used by the implementation of \c destroyArenaElements.
///
/// \todo Document these functions.
///@{
template <size_t Pos>
inline void destroyArenaElement(void *const,
const std::vector<size_t> &Offsets) noexcept {
ASSERT(Pos == Offsets.size());
}
template <size_t Pos, typename Type, typename... Types>
inline void destroyArenaElement(void *const Arena,
const std::vector<size_t> &Offsets) noexcept {
ASSERT(Arena != nullptr && Pos < Offsets.size());
static_cast<Type *>(
static_cast<void *>(static_cast<uint8_t *>(Arena) + Offsets[Pos]))
->~Type();
destroyArenaElement<Pos + 1, Types...>(Arena, Offsets);
}
///@}
/// Implementation of the template.
///
-/// \tparam Type the type of the mandatory first value to destroy
-/// \tparam Types types of any further values to destroy
+/// \tparam Types types of values to destroy
///
/// \param Arena the memory area to destroy values from
///
/// \pre \p Arena is not \c nullptr.
-template <typename Type, typename... Types>
+template <typename... Types>
inline void destroyArenaElements(void *const Arena) noexcept {
ASSERT(Arena != nullptr);
- destroyArenaElement<0, Type, Types...>(
- Arena, TokenizedStorage<Type, Types...>::Offsets);
+ destroyArenaElement<0, Types...>(Arena, TokenizedStorage<Types...>::Offsets);
}
} // End namespace
/// Implementation of the template \c rosa::TokenizedStorage as a
/// specialization of \c rosa::AbstractTokenizedStorage.
///
/// The class provides facilities for storing values and providing type-safe
/// access to them.
///
-/// \tparam Type type of the first mandatory value to store
-/// \tparam Types of any further values to store
-template <typename Type, typename... Types>
-class TokenizedStorage<Type, Types...> : public AbstractTokenizedStorage {
+/// \tparam Types types of values to store
+template <typename... Types>
+class TokenizedStorage : public AbstractTokenizedStorage {
public:
/// \c rosa::Token for the stored values.
- static constexpr Token ST =
- TypeToken<typename std::decay<Type>::type,
- typename std::decay<Types>::type...>::Value;
+ static constexpr Token ST = TypeToken<std::decay_t<Types>...>::Value;
/// Byte offsets to access stored values in \c rosa::TokenizedStorage::Arena.
static const std::vector<size_t> Offsets;
private:
/// A BLOB storing all the values one after the other.
void *const Arena;
/// Generates byte offsets for accessing values stored in
/// \c rosa::TokenizedStorage::Arena.
///
/// \return \c std::vector containing byte offsets for accessing values stored
/// in \c rosa::TokenizedStorage::Arena
static std::vector<size_t> offsets(void) noexcept {
Token T = ST; // Need a mutable copy.
- const size_t N = lengthOfToken(T); // Number of types encoded in \c T.
- size_t I = 0; // Start indexing from position \c 0.
+ const token_size_t N = lengthOfToken(T); // Number of types encoded in \c T.
std::vector<size_t> O(N); // Allocate vector of proper size.
- O[0] = 0; // First offset is always \c 0.
- while (I < N - 1) {
- ASSERT(I + 1 < O.size() && lengthOfToken(T) == N - I);
- // Calculate next offset based on the previous one.
- // \note The offset of the last value is stored at `O[N - 1]`, which is
- // set when `I == N - 2`. Hence the limit of the loop.
- O[I + 1] = O[I] + sizeOfHeadOfToken(T);
- dropHeadOfToken(T), ++I;
+ // Do nothing for 0 elements.
+ if (N > 0) {
+ token_size_t I = 0; // Start indexing from position \c 0.
+ O[0] = 0; // First offset is always \c 0.
+ while (I < N - 1) {
+ ASSERT(I + 1 < O.size() && lengthOfToken(T) == N - I);
+ // Calculate next offset based on the previous one.
+ // \note The offset of the last value is stored at `O[N - 1]`, which is
+ // set when `I == N - 2`. Hence the limit of the loop.
+ O[I + 1] = O[I] + sizeOfHeadOfToken(T);
+ dropHeadOfToken(T), ++I;
+ }
+ ASSERT(I + 1 == O.size() && lengthOfToken(T) == 1);
}
- ASSERT(I + 1 == O.size() && lengthOfToken(T) == 1);
return O;
}
public:
/// Creates an instance with default values.
///
- /// \note This constructor requires that all actual template arguments \c Type
- /// and \c Types... are default constructible.
+ /// \note This constructor requires that all actual template arguments \p
+ /// Types... are default constructible.
TokenizedStorage(void) noexcept
: Arena(::operator new(sizeOfValuesOfToken(ST))) {
ASSERT(Arena != nullptr); // Sanity check.
- createArenaElements(Arena, Type(), Types()...);
+ createArenaElements(Arena, Types()...);
}
/// Creates an instance from constant lvalue references.
///
- /// \param T the mandatory first value to store
- /// \param Ts optional further values to store
- TokenizedStorage(const Type &T, const Types &... Ts) noexcept
+ /// \param Ts values to store
+ TokenizedStorage(const std::decay_t<Types> &... Ts) noexcept
: Arena(::operator new(sizeOfValuesOfToken(ST))) {
ASSERT(Arena != nullptr); // Sanity check.
- createArenaElements(Arena, T, Ts...);
+ createArenaElements(Arena, Ts...);
}
/// Creates an instance from rvalue references.
///
- /// \param T the mandatory first value to store
- /// \param Ts optional further values to store
- TokenizedStorage(Type &&T, Types &&... Ts) noexcept
+ /// \param Ts values to store
+ TokenizedStorage(std::decay_t<Types> &&... Ts) noexcept
: Arena(::operator new(sizeOfValuesOfToken(ST))) {
ASSERT(Arena != nullptr); // Sanity check.
- createArenaElements(Arena, std::move(T), std::move(Ts)...);
+ createArenaElements(Arena, std::move(Ts)...);
}
/// No copying and moving of \c rosa::TokenizedStorage instances.
///
/// \note This restriction may be relaxed as moving should be easy to
/// implement, only requires the possiblity to validate Arena pointer.
///@{
TokenizedStorage(const TokenizedStorage&) = delete;
TokenizedStorage &operator=(const TokenizedStorage&) = delete;
TokenizedStorage(TokenizedStorage&& Other) = delete;
TokenizedStorage &operator=(TokenizedStorage&&) = delete;
///@}
// Destroys \p this object.
~TokenizedStorage(void) {
- destroyArenaElements<Type, Types...>(Arena);
+ destroyArenaElements<std::decay_t<Types>...>(Arena);
::operator delete(Arena);
}
/// Tells how many values are stored in \p this object.
///
/// \return number of values stored in \p this object
size_t size(void) const noexcept override {
return Offsets.size();
}
/// Tells the type of the value stored at a position.
///
/// \param Pos the index of the value whose type is to returned
///
/// \return \c rosa::TypeNumber for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index:\code
/// Pos < size()
/// \endcode
- TypeNumber typeAt(const size_t Pos) const noexcept override {
+ TypeNumber typeAt(const token_size_t Pos) const noexcept override {
ASSERT(Pos < size());
Token TT = ST;
dropNOfToken(TT, Pos);
return headOfToken(TT);
}
/// Provides an untyped pointer for the value stored at a position.
///
/// \param Pos the index of the value to return an untyped pointer for
///
/// \return untyped pointer for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index:\code
/// Pos < size()
/// \endcode
- void *pointerTo(const size_t Pos) noexcept override {
+ void *pointerTo(const token_size_t Pos) noexcept override {
ASSERT(Pos < size());
return static_cast<uint8_t *>(Arena) + Offsets[Pos];
}
/// Provides a constant untyped pointer for the value stored at a position.
///
/// \param Pos the index of the value to return an untyped pointer for
///
/// \return constant untyped pointer for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index:\code
/// Pos < Offsets.size()
/// \endcode
- const void *pointerTo(const size_t Pos) const noexcept override {
+ const void *pointerTo(const token_size_t Pos) const noexcept override {
ASSERT(Pos < size());
return static_cast<const uint8_t *>(Arena) + Offsets[Pos];
}
/// Tells if the value stored at a given index is of a given type.
///
/// \note Any \c rosa::AtomConstant is encoded in \c rosa::Token as
/// the \c rosa::AtomValue wrapped into it.
///
/// \tparam T type to match against
///
/// \param Pos index the type of the value at is to be matched against \p Type
///
/// \return if the value at index \p Pos of type \p T
///
/// \pre \p Pos is a valid index:\code
/// Pos < Offsets.size()
/// \endcode
template <typename T> bool isTypeAt(const size_t Pos) const noexcept {
ASSERT(Pos < size());
Token TT = ST;
dropNOfToken(TT, Pos);
return isHeadOfTokenTheSameType<T>(TT);
}
/// Gives a reference of a value of a given type stored at a given index.
///
/// \note The constant variant of the function relies on this implementation,
/// the function may not modify \p this object!
///
/// \tparam T type to give a reference of
///
/// \param Pos index to set the reference for
///
- /// \return reference of \p Type for the value stored at index \p Pos
+ /// \return reference of \p T for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index and the value at index \p Pos is of type
/// \p T:
/// \code
/// Pos < Size && isTypeAt<T>(Pos)
/// \endcode
- template <typename T> Type &valueAt(const size_t Pos) noexcept {
- ASSERT(Pos < size() && isTypeAt<Type>(Pos));
- return *static_cast<Type *>(pointerTo(Pos));
+ template <typename T> T &valueAt(const token_size_t Pos) noexcept {
+ ASSERT(Pos < size() && isTypeAt<T>(Pos));
+ return *static_cast<T *>(pointerTo(Pos));
}
/// Gives a constant reference of a value of a given type stored at a given
/// index.
///
/// \tparam T type to give a reference of
///
/// \param Pos index to set the reference for
///
- /// \return constant reference of \p Type for the value stored at index \p Pos
+ /// \return constant reference of \p T for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index and the value at index \p Pos is of type
/// \p T:
/// \code
/// Pos < Size && isTypeAt<T>(Pos)
/// \endcode
- template <typename T> const Type &valueAt(const size_t Pos) const noexcept {
+ template <typename T>
+ const T &valueAt(const token_size_t Pos) const noexcept {
// \note Just use the non-const implementation as that does not modify
// \p this object.
return const_cast<TokenizedStorage *>(this)->valueAt<T>(Pos);
}
};
// Implementation of the static member field \c rosa::TokenizedStorage::Offsets.
-template <typename Type, typename... Types>
-const std::vector<size_t> TokenizedStorage<Type, Types...>::Offsets =
- TokenizedStorage<Type, Types...>::offsets();
+template <typename... Types>
+const std::vector<size_t>
+ TokenizedStorage<Types...>::Offsets = TokenizedStorage<Types...>::offsets();
+
+/// Specialization of the template \c rosa::TokenizedStorage for storing
+/// nothing.
+///
+/// \note The specialization implements the interface defined by \c
+/// rosa::AbstractTokenizedStorage but most of the functions cannot be called
+/// because nothing is stored in instances of the class.
+template <> class TokenizedStorage<> : public AbstractTokenizedStorage {
+public:
+ /// \c rosa::Token for the stored values.
+ static constexpr Token ST = TypeToken<>::Value;
+
+ /// Byte offsets to access stored values in \c rosa::TokenizedStorage::Arena.
+ static const std::vector<size_t> Offsets;
+
+ /// Creates an instance.
+ TokenizedStorage(void) noexcept {}
+
+ /// No copying and moving of \c rosa::TokenizedStorage instances.
+ ///
+ /// \note This restriction may be relaxed as moving should be easy to
+ /// implement, only requires the possiblity to validate Arena pointer.
+ ///@{
+ TokenizedStorage(const TokenizedStorage &) = delete;
+ TokenizedStorage &operator=(const TokenizedStorage &) = delete;
+ TokenizedStorage(TokenizedStorage &&Other) = delete;
+ TokenizedStorage &operator=(TokenizedStorage &&) = delete;
+ ///@}
+
+ // Destroys \p this object.
+ ~TokenizedStorage(void) {}
+
+ /// Tells how many values are stored in \p this object.
+ ///
+ /// \return `0`
+ size_t size(void) const noexcept override { return 0; }
+
+ /// Tells the type of the value stored at a position.
+ ///
+ /// \pre Do not call.
+ TypeNumber typeAt(const token_size_t) const noexcept override {
+ ASSERT(false);
+ return TypeNumber(0);
+ }
+
+ /// Provides an untyped pointer for the value stored at a position.
+ ///
+ /// \pre Do not call.
+ void *pointerTo(const token_size_t) noexcept override {
+ ASSERT(false);
+ return nullptr;
+ }
+
+ /// Provides a constant untyped pointer for the value stored at a position.
+ ///
+ /// \pre Do not call.
+ const void *pointerTo(const token_size_t) const noexcept override {
+ ASSERT(false);
+ return nullptr;
+ }
+
+ /// Tells if the value stored at a given index is of a given type.
+ ///
+ /// \pre Do not call.
+ template <typename> bool isTypeAt(const size_t) const noexcept {
+ ASSERT(false);
+ return false;
+ }
+
+ /// Gives a reference of a value of a given type stored at a given index.
+ ///
+ /// \tparam T type to give a reference of
+ /// \pre Do not call.
+ template <typename T> T &valueAt(const token_size_t) noexcept {
+ ASSERT(false);
+ return *static_cast<T *>(nullptr);
+ }
+
+ /// Gives a constant reference of a value of a given type stored at a given
+ /// index.
+ ///
+ /// \tparam T type to give a reference of
+ ///
+ /// \pre Do not call.
+ template <typename T> const T &valueAt(const token_size_t) const noexcept {
+ // \note Just use the non-const implementation as that does not modify
+ // \p this object.
+ return *static_cast<T *>(nullptr);
+ }
+};
} // End namespace rosa
#endif // ROSA_SUPPORT_TOKENIZED_STORAGES_HPP
diff --git a/include/rosa/support/type_list.hpp b/include/rosa/support/type_list.hpp
index bb50be3..b3e8107 100644
--- a/include/rosa/support/type_list.hpp
+++ b/include/rosa/support/type_list.hpp
@@ -1,444 +1,468 @@
//===-- rosa/support/type_list.hpp ------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/support/type_list.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
-/// \date 2017
+/// \date 2017-2019
///
/// \brief Facilities for types representing lists of types.
///
/// \note This implementation is partially based on the \c type_list
/// implementation of CAF.
/// \todo Check license.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_SUPPORT_TYPE_LIST_HPP
#define ROSA_SUPPORT_TYPE_LIST_HPP
#include "rosa/support/debug.hpp"
#include "rosa/support/types.hpp"
#include <type_traits>
namespace rosa {
/// A list of types.
///
/// \tparam Ts types to make a list of
template <typename... Ts> struct TypeList {
/// Constructor, needs to do nothing.
constexpr TypeList(void) {}
};
/// The empty \c rosa::Typelist.
using EmptyTypeList = TypeList<>;
/// \defgroup TypeListAtImpl Implementation of rosa::TypeListAt
///
/// \brief Gets the type at index \p Pos from a list of types.
///
/// \note Only to be used by the implementation of \c rosa::TypeListAt.
///@{
/// Declaration of the template.
///
/// \tparam Pos index to take the element from
/// \tparam Ts types
template <size_t Pos, typename... Ts> struct TypeListAtImpl;
/// Definition for the general case when \p Pos is not \c 0 and there is type in
/// the list.
template <size_t Pos, typename T, typename... Ts>
struct TypeListAtImpl<Pos, T, Ts...> {
using Type = typename TypeListAtImpl<Pos - 1, Ts...>::Type;
};
/// Specialization for the case when \p Pos is \c 0.
template <typename T, typename... Ts> struct TypeListAtImpl<0, T, Ts...> {
using Type = T;
};
/// Specialization for the case when there is no more type.
///
/// In this case, the found type is \c rosa::none_t.
template <size_t Pos> struct TypeListAtImpl<Pos> { using Type = none_t; };
///@}
/// \defgroup TypeListAt Definition of rosa::TypeListAt
///
/// \brief Gets the element at index \p Pos of \p List.
///
///
/// The type at index \c Pos in a \c rosa::TypeList \c List can be obtained as
/// \code
/// typename TypeListAt<List, Pos>::Type
/// \endcode
///
/// \note The resulting type is \c rosa::none_t if \code
/// TypeListSize<List>::Value < Pos
/// \endcode
///@{
/// Declaration of the template.
///
/// \tparam List \c rosa::TypeList to take an element from
/// \tparam Pos index to take the element from
template <typename List, size_t Pos> struct TypeListAt;
/// Implementation using \c rosa::TypeListAtImpl.
template <size_t Pos, typename... Ts> struct TypeListAt<TypeList<Ts...>, Pos> {
using Type = typename TypeListAtImpl<Pos, Ts...>::Type;
};
///@}
/// \defgroup TypeListIndexOfImpl Implementation of rosa::TypeListIndexOf
///
/// \brief Tells the index of the first occurence of a type in a list of types.
///
/// \note Only to be used by the implementation of \c rosa::TypeListIndexOf.
///@{
/// Declaration of the template.
///
/// \tparam Pos the number types already being checked from the beginning of the
/// list
/// \tparam X type to search for
/// \tparam Ts remaining list of types
template <size_t Pos, typename X, typename... Ts> struct TypeListIndexOfImpl;
/// Specialization for the case when the list is over.
///
/// In this case, the found index is \c -1.
template <size_t Pos, typename X> struct TypeListIndexOfImpl<Pos, X> {
static constexpr int Value = -1;
};
/// Specialization for the case when the first type in the remaining list
/// is a match.
template <size_t Pos, typename X, typename... Ts>
struct TypeListIndexOfImpl<Pos, X, X, Ts...> {
static constexpr int Value = Pos;
};
/// Implementation for the general case when need to continue looking.
template <size_t Pos, typename X, typename T, typename... Ts>
struct TypeListIndexOfImpl<Pos, X, T, Ts...> {
static constexpr int Value = TypeListIndexOfImpl<Pos + 1, X, Ts...>::Value;
};
///@}
/// \defgroup TypeListIndexOf Definition of rosa::TypeListIndexOf
///
/// \brief Tells the index of the first occurence of type in a
/// \c rosa::TypeList.
///
/// The index of the first occurence of type \c T in \c rosa::TypeList \c List
/// can be obtained as \code
/// TypeListIndexOf<List, T>::Value
/// \endcode
///
/// \note The resulting index is \c -1 if \c T is not present in \c List.
///@{
/// Declaration of the template.
///
/// \tparam List \c rosa::TypeList to search in
/// \tparam T type to search for
template <typename List, typename T> struct TypeListIndexOf;
/// Implementation of the template using \c rosa::TypeListIndexOfImpl.
template <typename... Ts, typename T>
struct TypeListIndexOf<TypeList<Ts...>, T> {
static constexpr int Value = TypeListIndexOfImpl<0, T, Ts...>::Value;
};
///@}
/// \defgroup TypeListHead Implementation of rosa::TypeListHead
///
/// \brief Gets the first element of a \c rosa::TypeList.
///
/// The first element of a \c rosa::TypeList \c List can be obtained as \code
/// typename TypeListHead<List>::Type
/// \endcode
///
/// \note The resulting type is \c rosa::none_t if \c List is
/// \c rosa::EmptyTypeList.
///@{
/// Declaration of the template.
///
/// \tparam List \c rosa::TypeList to get the first element of
template <typename List> struct TypeListHead;
/// Specialization for \c rosa::EmptyTypeList.
///
/// In this case, the found type is \c rosa::none_t.
template <> struct TypeListHead<EmptyTypeList> { using Type = none_t; };
/// Implementation for a non-empty \c rosa::TypeList.
template <typename T, typename... Ts> struct TypeListHead<TypeList<T, Ts...>> {
using Type = T;
};
///@}
/// \defgroup TypeListTail Implementation of rosa::TypeListTail
///
/// \brief Gets the tail of a \c rosa::TypeList.
///
/// The tail of a \c rosa::TypeList \c List, that is \c List except for its
/// first element, can be obtained as \code
/// typename TypeListTail<List>::Type
/// \endcode
///
/// \note If \c List is \c rosa::EmptyTypeList, then the resulting type is also
/// \c rosa::EmptyTypeList.
///@{
/// Declaration of the template.
///
/// \tparam List \c rosa::TypeList to take the tail of
template <typename List> struct TypeListTail;
/// Specialization for \c rosa::EmptyTypeList.
///
/// In this case, the resulting type is \c rosa::EmptyTypeList.
template <> struct TypeListTail<EmptyTypeList> { using Type = EmptyTypeList; };
/// Implementation for a non-empty \c rosa::TypeList.
template <typename T, typename... Ts> struct TypeListTail<TypeList<T, Ts...>> {
using Type = TypeList<Ts...>;
};
///@}
/// \defgroup TypeListPush Implementation of rosa::TypeListPush
///
/// \brief Extends a \c rosa::TypeList with a type.
///
/// Whether the new type is pushed in the front or in the back of the
/// \c rosa::TypeList depends on the order of template arguments, as shown in
/// the following example: \code
/// using List = TypeList<A>
/// typename TypeListPush<List, T>::Type; // TypeList<A, T>
/// typename TypeListPush<T, List>::Type; // TypeList<T, A>
/// \endcode
///@{
/// Declaration of the template.
///
/// \tparam P a type if \p Q is a \c rosa::TypeList, a \c rosa::TypeList
/// otherwise
/// \tparam Q a type if \p P is a \c rosa::TypeList, a \c rosa::TypeList
/// otherwise
template <typename P, typename Q> struct TypeListPush;
/// Implementation for the case when pushing at the back of the
/// \c rosa::TypeList.
template <typename... Ts, typename T> struct TypeListPush<TypeList<Ts...>, T> {
using Type = TypeList<Ts..., T>;
};
/// Implementation for the case when pushing to the front of the
/// \c rosa::TypeList.
template <typename T, typename... Ts> struct TypeListPush<T, TypeList<Ts...>> {
using Type = TypeList<T, Ts...>;
};
///@}
/// \defgroup TypeListDrop Implementation of rosa::TypeListDrop
///
/// \brief Drops some elements from the beginning of a \c rosa::TypeList.
///
/// The first \c N types of a \c rosa::TypeList \c List can be dropped as \code
/// typename TypeListDrop<N, List>::Type
/// \endcode
///@{
/// Declaration of the template.
///
/// \tparam N number of types to drop
/// \tparam List \c rosa::TypeList to drop the first \p N element of
template <size_t N, typename List> struct TypeListDrop;
/// Specialization for \c rosa::EmptyTypeList.
template <size_t N> struct TypeListDrop<N, EmptyTypeList> {
using Type = EmptyTypeList;
};
/// Implementation for a non-empty \c rosa::TypeList.
template <size_t N, typename T, typename... Ts>
struct TypeListDrop<N, TypeList<T, Ts...>> {
using Type = typename std::conditional<
- N == 0, TypeList<Ts...>,
+ N == 0, TypeList<T, Ts...>,
typename TypeListDrop<N - 1, TypeList<Ts...>>::Type>::type;
};
///@}
+/// \defgroup TypeListConcat Implementation of rosa::TypeListConcat
+///
+/// \brief Concatenates two \c rosa::TypeList instances.
+///
+/// Two instances of \c rosa::TypeList \c List1 and \c List2 can be
+/// concatenated as \code
+/// typename TypeListConcat<List1, List2>::Type
+/// \endcode
+///@{
+
+/// Declaration of the template
+///
+/// \tparam List1 the first instance of \c rosa::TypeList
+/// \tparam List2 the second instance of \c rosa::TypeList
+template <typename List1, typename List2> struct TypeListConcat;
+
+/// Implementation of the template for \c rosa::TypeList instances.
+template <typename... As, typename... Bs>
+struct TypeListConcat<TypeList<As...>, TypeList<Bs...>> {
+ using Type = TypeList<As..., Bs...>;
+};
+
+///@}
+
/// \defgroup TypeListSize Implementation of rosa::TypeListSize
///
/// \brief Tells the number of types stored in a \c rosa::TypeList.
///
/// The size of a \c rosa::TypeList \c List can be obtained as \code
/// TypeListSize<List>::Value
/// \endcode
///@{
/// Declaration of the template.
///
/// \tparam List \c rosa::TypeList to get the size of
template <typename List> struct TypeListSize;
/// Implementation of the template.
template <typename... Ts> struct TypeListSize<TypeList<Ts...>> {
static constexpr size_t Value = sizeof...(Ts);
};
template <typename... Ts> constexpr size_t TypeListSize<TypeList<Ts...>>::Value;
///@}
/// Tests whether a \c rosa::TypeList is empty.
///
/// \tparam List \c rosa::TypeList to check
template <typename List> struct TypeListEmpty {
/// Denotes whether \p List is an empty \c rosa::TypeList or not.
static constexpr bool Value = std::is_same<EmptyTypeList, List>::value;
};
/// \defgroup TypeListContains Implementation of rosa::TypeListContains
///
/// \brief Tells if a \c rosa::TypeList contains a given type.
///
/// Whether a \c rosa::TypeList \c List contains the type \c T can be checked as
/// \code
/// TypeListContains<List, T>::Value
/// \endcode
///@{
/// Declaration of the template.
///
/// \tparam List \c rosa::TypeList to search in
/// \tparam T type to search for
template <typename List, typename T> struct TypeListContains;
/// Implementation of the template.
template <typename... Ts, typename T>
struct TypeListContains<TypeList<Ts...>, T> {
static constexpr bool Value =
std::conditional<TypeListIndexOf<TypeList<Ts...>, T>::Value == -1,
std::false_type, std::true_type>::type::value;
};
///@}
/// \defgroup TypeListSubsetOf Implementation of rosa::TypeListSubsetOf
///
/// \brief Tells if a \c rosa::TypeList is a subset of another one.
///
/// Whether a \c rosa::TypeList \c ListA is a subset of another
/// \c rosa::TypeList \c ListB can be checked as \code
/// TypeListSubsetOf<ListA, ListB>::Value
/// \endcode
///@{
/// Declaration of the template.
///
/// \tparam ListA \c rosa::TypeList to check if is a subset of \p ListB
/// \tparam ListB \c rosa::TypeList to check if is a superset of \p ListA
/// \tparam Fwd always use the default value!
template <typename ListA, typename ListB, bool Fwd = true>
struct TypeListSubsetOf;
/// Specialization for the case when all the elements of the original \p ListA
/// was found in \p ListB.
template <typename List> struct TypeListSubsetOf<EmptyTypeList, List, true> {
static constexpr bool Value = true;
};
/// Specializaton for the case when an element of the original \p ListA cannot
/// be found in \p ListB.
template <typename ListA, typename ListB>
struct TypeListSubsetOf<ListA, ListB, false> {
static constexpr bool Value = false;
};
/// Definition for the general case.
template <typename T, typename... Ts, typename List>
struct TypeListSubsetOf<TypeList<T, Ts...>, List>
: TypeListSubsetOf<TypeList<Ts...>, List,
TypeListContains<List, T>::Value> {};
///@}
/// \defgroup TypeListFindImpl Implementation of rosa::TypeListFind
///
/// \brief Finds the first type in a list of types that satisfies a predicate.
///
/// \note Only to be used by the implementation of \c rosa::TypeListFind.
///@{
/// Declaration of the template.
///
/// \tparam Pred the predicate to check types against
/// \tparam Ts list of types to check
template <template <typename> class Pred, typename... Ts>
struct TypeListFindImpl;
/// Specialization for the case when no more types remain to check.
template <template <typename> class Pred> struct TypeListFindImpl<Pred> {
using Type = none_t;
};
/// Implementation for the general case when there is a type to check.
template <template <typename> class Pred, typename T, typename... Ts>
struct TypeListFindImpl<Pred, T, Ts...> {
using Type = typename std::conditional<
Pred<T>::Value, T, typename TypeListFindImpl<Pred, Ts...>::Type>::type;
};
///@}
/// \defgroup TypeListFind Definition of rosa::TypeListFind
///
/// \brief Finds the first element satisfying a predicate in a
/// \c rosa::TypeList.
///
/// The first type satisfying a predicate \c Pred in a \c rosa::TypeList
/// \c List can be obtained as \code
/// typename TypeListFind<List, Pred>::Type
/// \endcode
///
/// \note The resulting type is \c rosa::none_t if no type in \c List satisfies
/// \c Pred.
///@{
/// Declaration of the template.
///
/// \tparam List \c rosa::TypeList to search in
/// \tparam Pred predicate to check elements against
template <typename List, template <typename> class Pred> struct TypeListFind;
/// Implementation of the template using \c rosa::TypeListFindImpl.
template <typename... Ts, template <typename> class Pred>
struct TypeListFind<TypeList<Ts...>, Pred> {
using Type = typename TypeListFindImpl<Pred, Ts...>::Type;
};
///@}
} // End namespace rosa
#endif // ROSA_SUPPORT_TYPE_LIST_HPP
diff --git a/include/rosa/support/type_numbers.hpp b/include/rosa/support/type_numbers.hpp
index 3d46da5..cc85cc7 100644
--- a/include/rosa/support/type_numbers.hpp
+++ b/include/rosa/support/type_numbers.hpp
@@ -1,231 +1,240 @@
//===-- rosa/support/type_numbers.hpp ---------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/support/type_numbers.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
-/// \date 2017
+/// \date 2017-2019
///
/// \brief Facilities for registering supported types and representing them with
/// numbers.
///
/// \note This implementation is partially based on the \c type_number
/// implementation of CAF.
/// \todo Check license.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_SUPPORT_TYPE_NUMBERS_HPP
#define ROSA_SUPPORT_TYPE_NUMBERS_HPP
#include "rosa/support/atom.hpp"
#include "rosa/support/math.hpp"
#include "rosa/support/squashed_int.hpp"
#include "rosa/support/type_helper.hpp"
#include "rosa/support/types.hpp"
#include <array>
#include <string>
namespace rosa {
/// Compile-time list of all built-in types.
/// \note Appending new types to the end of this list maintains backward
/// compatibility in the sense that old builtin types have the same type number
/// associated to them in both the old and new versions. But changing any of
/// the already present types in the list breaks that backward compatibility.
/// Should compatibility be broken, step \c rosa::TypeNumberVersion below!
/// \note Keep this list in sync with the definition of
/// \c rosa::NumberedTypeNames.
/// \note The built-in types are explicitly listed in the definition of
/// rosa::deluxe::DeluxeAgent. Keep those definitions in sync with this list.
using BuiltinTypes = TypeList<AtomValue, ///< atom
int16_t, ///< i16
int32_t, ///< i32
int64_t, ///< i64
int8_t, ///< i8
long double, ///< ldouble
std::string, ///< str
uint16_t, ///< u16
uint32_t, ///< u32
uint64_t, ///< u64
uint8_t, ///< u8
unit_t, ///< unit
bool, ///< bool
double, ///< double
float ///< float
>;
/// Indicates the version number of \c rosa::BuiltinTypes.
///
/// Software with the same version number are supposed to have backward
/// compatible type numbering.
///
/// \sa \c rosa::BultinTypes on backward compatibility.
constexpr size_t TypeNumberVersion = 0;
/// The number of built-in types.
static constexpr size_t NumberOfBuiltinTypes =
TypeListSize<BuiltinTypes>::Value;
/// Anonymous namespace for helper facilities, consider it private.
namespace {
-/// Tells if a type is not \c rosa::UnitType.
+/// Tells if a type is not \c rosa::NoneType.
///
/// \tparam T the type to check
-template <typename T> struct IsNotUnitType {
- /// Denotes if \p T is the \c rosa::UnitType or not.
- static constexpr bool Value = !std::is_same<T, UnitType>::value;
+template <typename T> struct IsNotNoneType {
+ /// Denotes if \p T is the \c rosa::NoneType or not.
+ static constexpr bool Value = !std::is_same<T, NoneType>::value;
};
} // End namespace
/// Integer type to store type numbers.
/// \note The narrowest unsigned integer type that is wide enough to represent
/// \c NumberOfBuiltinTypes different values.
-using type_nr_t =
- typename TypeListFind<typename TypeListDrop<log2(NumberOfBuiltinTypes) / 8,
- IntegerTypesBySize>::Type,
- IsNotUnitType>::Type::Second;
+using type_nr_t = typename TypeListFind<
+ typename TypeListDrop<log2(NumberOfBuiltinTypes) / 8 + 1,
+ IntegerTypesBySize>::Type,
+ IsNotNoneType>::Type::Second;
/// Turn \c rosa::type_nr_t into a strongly typed enumeration.
///
/// Values of \c rosa::type_nr_t casted to \c rosa::TypeNumbers can be used in a
/// type-safe way.
enum class TypeNumber : type_nr_t {};
/// A type to cast type numbers into in order to output them to streams as
/// numbers and not ASCII-codes.
///
/// \note Use it for safety, necessary for printing \c uint8_t values.
using printable_tn_t = printable_t<type_nr_t>;
/// Casts a \c rosa::TypeNumber into \c rosa::printable_tn_t.
///
/// \param TN \c rosa::TypeNumber to cast.
#define PRINTABLE_TN(TN) static_cast<printable_tn_t>(TN)
-/// Converts a \c rosa::TypeNumber into \c std::string.
-///
-/// \param TN \c rosa::TypeNumber to convert
-///
-/// \return \c std::string representing \p TN
-inline std::string to_string(const TypeNumber TN) {
- return std::to_string(static_cast<type_nr_t>(TN));
-}
-
/// \name TypeNumberOf
/// \brief Computes \c rosa::TypeNumber for a type.
///
/// The \c rosa::TypeNumber for a type \c T can be obtained as \code
/// TypeNumberOf<T>::Value
/// \endcode
///
/// \note \c rosa::TypeNumber for a type is based on the corresponding squashed
-/// type, except for \c bool and \c rosa::AtomConstant types.
+/// type, except for \c rosa::AtomConstant types.
///
/// \sa \c rosa::SquashedType
///
/// \note \c rosa::TypeNumber is the index of the type in \c rosa::BuiltinTypes
/// starting from \c 1; index \c 0 indicates a non-builtin type.
///@{
/// Definition of the template for the general case.
///
/// \tparam T type to get \c rosa::TypeNumber for
template <typename T> struct TypeNumberOf {
static constexpr TypeNumber Value = static_cast<TypeNumber>(
TypeListIndexOf<BuiltinTypes, squashed_t<T>>::Value + 1);
};
-/// Specialization for \c bool.
-template <> struct TypeNumberOf<bool> {
- static constexpr TypeNumber Value =
- static_cast<TypeNumber>(TypeListIndexOf<BuiltinTypes, bool>::Value + 1);
-};
-
/// Specialization for \c rosa::AtomConstant.
///
/// \note For a \c rosa::AtomConstant type, \c rosa::TypeNumber is based on the
/// \c rosa::AtomValue wrapped into the actual \c rosa::AtomConstant.
template <AtomValue V> struct TypeNumberOf<AtomConstant<V>> {
static constexpr TypeNumber Value = TypeNumberOf<AtomValue>::Value;
};
///@}
// clang-format off
/// List of type names for all builtin-types, indexed via \c rosa::TypeNumber.
///
/// \note Keep this definition in sync with \c rosa::BuiltinTypes.
constexpr std::array<const char *, NumberOfBuiltinTypes> NumberedTypeNames {{
"atom",
"i16",
"i32",
"i64",
"i8",
"ldouble",
"str",
"u16",
"u32",
"u64",
"u8",
"unit",
"bool",
"double",
"float"
}};
// clang-format on
/// Tells if a \c rosa::TypeNumber is valid in the software.
///
/// \note A \c rosa::TypeNumber generated by an incompatible version may be
/// valid but may denote a type that is different from the \c rosa::TypeNumber
/// denotes in the current software. That is why this validation needs to be
/// done in connection to checking \c rosa::TypeNumberVersion as well.
///
/// \param TN \c rosa::TypeNumber to validate in the context of the current
/// software
///
/// \return Whether \p TN is valid in the current software
constexpr bool validTypeNumber(const TypeNumber TN) {
// \todo Duplication of static_cast into a const variable would be
// possible in C++14.
return 0 < static_cast<type_nr_t>(TN) &&
static_cast<type_nr_t>(TN) <= NumberOfBuiltinTypes;
}
/// Provides information about the type corresponding to a \c rosa::TypeNumber.
///
/// \tparam TN \c rosa::TypeNumber to get information for
///
/// \pre Statically, \p TN is a valid \c rosa::TypeNumber:
/// \code
/// validTypeNumber(TN)
/// \endcode
template <TypeNumber TN> struct TypeForNumber {
STATIC_ASSERT(validTypeNumber(TN), "not a valid type number");
/// \p TN as \c rosa::type_nr_t.
static constexpr type_nr_t TNI = static_cast<type_nr_t>(TN);
/// The builtin-type corresponding to \p TN.
using Type = typename TypeListAt<BuiltinTypes, TNI - 1>::Type;
/// The size of \c Type.
static constexpr size_t Size = sizeof(Type);
/// Textual representation of the builtin-type.
static constexpr const char *Name = NumberedTypeNames[TNI - 1];
};
} // End namespace rosa
+namespace std {
+
+/// Converts a \c rosa::TypeNumber into \c std::string.
+///
+/// \param TN \c rosa::TypeNumber to convert
+///
+/// \return \c std::string representing \p TN
+inline string to_string(const rosa::TypeNumber TN) {
+ return to_string(static_cast<rosa::type_nr_t>(TN));
+}
+
+/// Dumps a \c rosa::TypeNumber to a given \c std::ostream.
+///
+/// \param [in,out] OS output stream to dump to
+/// \param TN \c rosa::TypeNumber to dump
+///
+/// \return \p OS after dumping \p TN to it
+inline ostream &operator<<(ostream &OS, const rosa::TypeNumber &TN) {
+ OS << to_string(TN);
+ return OS;
+}
+
+} // End namespace std
+
#endif // ROSA_SUPPORT_TYPE_NUMBERS_HPP
diff --git a/include/rosa/support/type_token.hpp b/include/rosa/support/type_token.hpp
index 94db36f..99f2cec 100644
--- a/include/rosa/support/type_token.hpp
+++ b/include/rosa/support/type_token.hpp
@@ -1,279 +1,321 @@
//===-- rosa/support/type_token.hpp -----------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/support/type_token.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
-/// \date 2017
+/// \date 2017-2019
///
/// \brief Facilities for encoding TypeLists as unsigned integer values.
///
/// \note **On the compatibility between different versions of the type token
/// implementation:**
/// Different software versions produce compatible type tokens as long as
/// *backward compatibility* of \c rosa::BuiltinTypes is maintained (denoted by
/// \c rosa::TypeNumberVersion) and the type token implementation uses the same
/// type as \c rosa::token_t (boiling down to the same \c rosa::token::TokenBits
/// value) and the same \c rosa::token::RepresentationBits value. Thus,
/// interacting software need to cross-validate the aforementioned values to
/// check compatibility. Interoperation between compatible sofware is limited
/// to backward compatiblity, that is builtin types defined in both software
/// versions are handled correctly but a newer system may produce a type token
/// which is invalid in an old one. Therefore, tokens obtained from a compatible
/// remote system need to be validated.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_SUPPORT_TYPE_TOKEN_HPP
#define ROSA_SUPPORT_TYPE_TOKEN_HPP
#include "rosa/support/type_numbers.hpp"
namespace rosa {
/// Integer type to store type tokens.
///
/// \note The trade-off between the binary overhead of type encoding and the
/// maximal size of encodable lists can be tuned by using unsigned integer types
/// of different widths as \c rosa::token_t.
using token_t = uint64_t;
/// Sanity check in case someone would change \c rosa::token_t.
STATIC_ASSERT(std::is_unsigned<token_t>::value,
"token_t is not an unsigned integer");
/// Turn \c rosa::token_t into a strongly typed enumeration.
///
/// Values of \c rosa::token_t casted to \c rosa::Token can be used in a
/// type-safe way.
enum class Token : token_t {};
/// A type to cast tokens into in order to output them to streams as
/// numbers and not ASCII-codes.
///
/// \note Use it for safety, necessary for printing \c uint8_t values.
using printable_token_t = printable_t<token_t>;
/// Casts a \c rosa::Token into \c rosa::printable_token_t.
///
/// \param T \c rosa::Token to cast
#define PRINTABLE_TOKEN(T) static_cast<printable_token_t>(T)
-/// Converts a \c rosa::Token into \c std::string.
-///
-/// \param T \c rosa::Token to convert
-///
-/// \return \c std::string representing \p T
-inline std::string to_string(const Token T) {
- return std::to_string(static_cast<token_t>(T));
-}
-
/// Encloses constants related to the implementation of \c rosa::Token.
namespace token {
/// The number of bits in one \c rosa::Token.
constexpr size_t TokenBits = sizeof(Token) * 8;
/// The number of bits a builtin type can be uniquely encoded into, that is any
/// valid \c rosa::TypeNumber can fit into.
///
/// \note There is one extra bit position added for encoding so that providing a
/// better chance to maintain backward comaptibility when \c rosa::BuiltinTypes
/// is extended.
constexpr size_t RepresentationBits = log2(NumberOfBuiltinTypes) + 1;
/// Maximal size of uniquely tokenizable \c rosa::TypeList.
constexpr size_t MaxTokenizableListSize = TokenBits / RepresentationBits;
} // End namespace token
+/// Integer type to store length of a \c rosa::Token.
+///
+/// \note The narrowest unsigned integer type that is wide enough to
+/// represent \c rosa::token::MaxTokenizableListSize different values.
+using token_size_t = typename TypeListFind<
+ typename TypeListDrop<log2(token::MaxTokenizableListSize) / 8 + 1,
+ IntegerTypesBySize>::Type,
+ IsNotNoneType>::Type::Second;
+
/// \defgroup TypeListTokenImpl Implementation of rosa::TypeListToken
///
/// \brief Generates a \c rosa::Token for a squashed \c rosa::TypeList.
///
/// \note Only to be used by the implementation of \c rosa::TypeListToken.
///@{
/// Declaration of the template.
///
/// \tparam List \c rosa::TypeList to generate \c rosa::Token for
template <typename List> struct TypeListTokenImpl;
/// Specialization for \c rosa::EmptyTypeList.
template <> struct TypeListTokenImpl<EmptyTypeList> {
static constexpr Token Value = static_cast<Token>(0);
};
/// Specialization for a non-empty \c rosa::TypeList.
template <typename T, typename... Ts>
struct TypeListTokenImpl<TypeList<T, Ts...>> {
static constexpr TypeNumber TN = TypeNumberOf<T>::Value;
// Check if the generated type number is valid.
STATIC_ASSERT(validTypeNumber(TN), "non-builtin type");
static constexpr Token Value = static_cast<Token>(
(static_cast<token_t>(TypeListTokenImpl<TypeList<Ts...>>::Value)
<< token::RepresentationBits) |
static_cast<type_nr_t>(TN));
};
///@}
/// \name TypeListToken
///
/// \brief Generates a \c rosa::Token for a \c rosa::TypeList.
///
/// \c rosa::Token for a \c rosa::TypeList \c List can be obtained as \code
/// TypeListToken<List>::Value
/// \endcode
///
/// \note The \c rosa::TypeList cannot have more than
/// \c rosa::token::MaxTokenizableListSize elements and must be a subset of
/// \c rosa::BuiltinTypes with respect to squashed integers.
///
/// \note A generated \c rosa::Token uniquely represents a list of types, except
/// for \c rosa::AtomConstant types. Observe that any \c rosa::AtomConstant is
/// encoded as the type \c rosa::AtomValue. The type information on all separate
/// \c rosa::AtomConstant types are lost and replaced by the \c rosa::AtomValue
/// type whose actual value needs to be used in order to obtain the original
/// \c rosa::AtomConstant type and so the full type information on the encoded
/// \c rosa::TypeList.
///
///@{
/// Declaration of the template.
///
/// \tparam List \c rosa::TypeList to generate \c rosa::Token for
template <typename List> struct TypeListToken;
/// Implementation using \c rosa::TypeListTokenImpl.
template <typename... Ts> struct TypeListToken<TypeList<Ts...>> {
- /// \note \c rosa::TypeNumber is computed against \c rosa::squased_int_t for
- /// integral types, so let's do the same here.
+ /// \note \c rosa::TypeNumber is computed against \c rosa::squased_t, so let's
+ /// do the same here.
using List = typename SquashedTypeList<TypeList<Ts...>>::Type;
/// Check the length of the list here.
/// \note Type validation is done one-by-one in \c rosa::TypeListTokenImpl.
STATIC_ASSERT((TypeListSize<List>::Value <= token::MaxTokenizableListSize),
"too long list of types");
/// The \c rosa::Token for \p List.
static constexpr Token Value = TypeListTokenImpl<List>::Value;
};
///@}
/// Convenience template to generate \c rosa::Token for a list of types.
template <typename... Ts> using TypeToken = TypeListToken<TypeList<Ts...>>;
/// Tells if a given \c rosa::Token is valid.
///
/// A \c rosa::Token is considered valid if it can be decoded by the current
/// software.
///
/// \note Validation gives a correct result only when \p T was generated by a
/// compatible software.
///
/// \sa Note for type_token.hpp on compatibility.
///
/// \param T \c rosa::Token to validate
///
/// \return if \p T is valid
bool validToken(const Token T);
/// Tells if a \c rosa::Token does encode an empty list of types.
///
/// \param T \c rosa::Token to check
///
/// \return if \p T encodes an empty list of types
bool emptyToken(const Token T);
/// Tells how many types are encoded in a \c rosa::Token.
///
/// \param T \c rosa::Token to check
///
/// \return how many types are encoded in \p T
-size_t lengthOfToken(const Token T);
+token_size_t lengthOfToken(const Token T);
/// Tells the full memory size of the types encoded in a \c rosa::Token.
///
/// The full memory size of the types encoded in a \c rosa::Token is the sum of
/// the separate memory sizes of all the types encoded in the \c rosa::Token.
///
/// \param T \c rosa::Token to check
///
/// \return full memory size of the types encoded in \p T
///
/// \pre \p T is valid:\code
/// validToken(T)
/// \endcode
size_t sizeOfValuesOfToken(const Token T);
/// Extracts the \c rosa::TypeNumber of the first encoded type of a
/// \c rosa::Token.
///
/// \note The returned type number is not validated.
///
/// \param T \c rosa::Token to take the first \c rosa::TypeNumber from
///
/// \return the first \c rosa::TypeNumber encoded in \p T
inline TypeNumber headOfToken(const Token T) {
return static_cast<TypeNumber>(static_cast<token_t>(T) &
((1 << token::RepresentationBits) - 1));
}
/// Tells the memory size of the first type encoded in a \c rosa::Token.
///
/// \param T \c rosa::Token to check the first encoded type of
///
/// \return memory size of the first type encoded in \p T
///
/// \pre \p T is not empty and valid:\code
/// !empty(T) && validToken(T)
/// \endcode
size_t sizeOfHeadOfToken(const Token T);
/// Gives the textual representation of the first type encoded in a
/// \c rosa::Token.
///
/// \param T \c rosa::Token to take the name of its first encoded type
///
/// \return textual representation of the first type encoded in \p T
///
/// \pre \p T is not empty and valid:\code
/// !empty(T) && validToken(T)
/// \endcode
const char *nameOfHeadOfToken(const Token T);
/// Updates a \c rosa::Token by dropping its first encoded type.
///
/// \param [in,out] T \c rosa::Token to drop the first encoded type of
void dropHeadOfToken(Token &T);
/// Updates a \c rosa::Token by dropping a number of its first encoded types
///
/// \param [in,out] T \c rosa::Token to drop the first \p N encoded types of
/// \param N the number of types to drop from \p T
void dropNOfToken(Token &T, const size_t N);
+/// Tells what type is encoded at Position \p Pos in the \c rosa::Token \p T
+///
+/// \param T \c rosa::Token to check
+/// \param Pos the position to check
+///
+/// \return \c rosa::TypeNumber of the type encodeded at position \p Pos of \c
+/// rosa::Token \p T
+///
+/// \pre \p T is valid and it encodes \p Pos types: \code
+/// validToken(T) && Pos < static_cast<size_t>(lengthOfToken(T))
+/// \endcode
+inline TypeNumber typeAtPositionOfToken(const Token T, const size_t Pos) {
+ ASSERT(validToken(T) && Pos < static_cast<size_t>(lengthOfToken(T)));
+ Token TT = T;
+ dropNOfToken(TT, Pos);
+ return headOfToken(TT);
+}
+
/// Tells if the first encoded type of a \c rosa::Token is a given type.
///
/// \tparam Type type to match the first encoded type of \p T against
///
/// \param T \c rosa::Token whose first encoded type is to be matched against
/// \p Type
///
/// \return if the first encoded type of \p T is \p Type
///
/// \pre \p T is not empty and valid:\code
/// !empty(T) && validToken(T)
/// \endcode
template <typename Type> bool isHeadOfTokenTheSameType(const Token T) {
ASSERT(!emptyToken(T) && validToken(T));
return TypeNumberOf<Type>::Value == headOfToken(T);
}
} // End namespace rosa
+namespace std {
+
+/// Converts a \c rosa::Token into \c std::string.
+///
+/// \param T \c rosa::Token to convert
+///
+/// \return \c std::string representing \p T
+inline string to_string(const rosa::Token T) {
+ return to_string(static_cast<rosa::token_t>(T));
+}
+
+/// Dumps a \c rosa::Token to a given \c std::ostream.
+///
+/// \param [in,out] OS output stream to dump to
+/// \param T \c rosa::Token to dump
+///
+/// \return \p OS after dumping \p N to it
+inline ostream &operator<<(ostream &OS, const rosa::Token &T) {
+ OS << to_string(T);
+ return OS;
+}
+
+} // End namespace std
+
#endif // ROSA_SUPPORT_TYPE_TOKEN_HPP
diff --git a/include/rosa/support/types.hpp b/include/rosa/support/types.hpp
index e00e04b..eb6a3b2 100644
--- a/include/rosa/support/types.hpp
+++ b/include/rosa/support/types.hpp
@@ -1,524 +1,550 @@
//===-- rosa/support/types.hpp ----------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/support/types.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
-/// \date 2017
+/// \date 2017-2019
///
/// \brief Implementation of some basic convenience types.
///
/// \note This implementation is partially based on the implementation of
/// corresponding parts of CAF.
/// \todo Check license.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_SUPPORT_TYPES_HPP
#define ROSA_SUPPORT_TYPES_HPP
#include "rosa/support/debug.hpp"
#include <string>
namespace rosa {
/* ************************************************************************** *
* Unit *
* ************************************************************************** */
/// A safe type to replace \c void.
///
/// \c rosa::UnitType is analogous to \c void, but can be safely returned,
/// stored, etc. to enable higher-order abstraction without cluttering code with
/// exceptions for \c void (which can't be stored, for example).
struct UnitType {
/// Constructor, needs to do nothing.
constexpr UnitType() noexcept {}
/// Copy-constructor, needs to do nothing.
constexpr UnitType(const UnitType &) noexcept {}
};
/// Aliasing \c rosa::UnitType as \c rosa::unit_t.
using unit_t = UnitType;
/// The value of \c rosa::unit_t.
///
/// \note Since a value of \c rosa::UnitType has no state, all instances of
/// \c rosa::UnitType is equal and considered *the \c rosa::unit_t value*.
static constexpr unit_t unit = unit_t{}; // NOLINT
-/// Returns the textual representation of any value of \c rosa::unit_t.
-///
-/// \return textual representation of \c rosa::UnitType.
-inline std::string to_string(const unit_t &) { return "unit"; }
-
/// \name LiftVoid
/// \brief Lifts a type to avoid \c void.
///
/// A type \c T can be lifted as \code
/// typename LiftVoid<T>::Type
/// \endcode
/// The resulted type is \c rosa::unit_t if \c T is \c void, and \c T itself
/// otherwise.
///@{
/// Definition for the general case.
///
/// \tparam T type to lift
template <typename T> struct LiftVoid { using Type = T; };
/// Specialization for \c void.
template <> struct LiftVoid<void> { using Type = unit_t; };
///@}
/// \name UnliftVoid
/// \brief Unlifts a type already lifted by \c rosa::LiftVoid.
///
/// A type \c T can be unlifted as \code
/// typename UnliftVoid<T>::Type
/// \endcode
/// The resulted type is \c void if \c T is \c rosa::unit_t -- that is \c void
/// lifted by \c rosa::LiftVoid --, and \c T itself otherwise.
///
///@{
/// Definition for the general case.
///
/// \tparam T type to unlift
template <typename T> struct UnliftVoid { using Type = T; };
/// Specialization for \c rosa::unit_t.
template <> struct UnliftVoid<unit_t> { using Type = void; };
///@}
/* ************************************************************************** *
* None *
* ************************************************************************** */
/// Represents *nothing*.
///
/// An instance of the type represents *nothing*, that can be used, e.g., for
/// clearing an instance of \c rosa::Optional by assigning an instance of
/// \c rosa::NoneType to it.
struct NoneType {
/// Constructor, needs to do nothing.
constexpr NoneType(void) {}
/// Evaluates the instance to \c bool.
///
/// A "nothing" is always evaluates to \c false.
constexpr explicit operator bool(void) const { return false; }
};
/// Aliasing type \c rosa::NoneType as \c rosa::none_t.
using none_t = NoneType;
/// The value of \c rosa::none_t.
///
/// \note Since a value of \c rosa::NoneType has no state, all instances of
/// \c rosa::NoneType is equal and considered *the \c rosa::none_t value*.
static constexpr none_t none = none_t{}; // NOLINT
-/// Returns the textual representation of any value of \c rosa::none_t.
-///
-/// \return textual representation of \c rosa::NoneType.
-inline std::string to_string(const none_t &) { return "none"; }
-
/* ************************************************************************** *
* Optional *
* ************************************************************************** */
/// \defgroup Optional Specializations of rosa::Optional
///
/// \brief Represents an optional value.
///
/// \note This implementation is compatible with \c std::optional of C++17.
///@{
/// Definition for the general case, optionally storing a value.
///
/// \tparam T type of the optional value
template <class T> class Optional {
public:
using Type = T;
/// Creates an instance without value.
///
/// \note Use it with its default parameter.
Optional(const none_t & = none) : Valid(false) {}
/// Creates a valid instance with value.
///
/// \tparam U type of the \p X
/// \tparam E always use it with default value!
///
/// \param X value to store in the object
///
/// \note The constructor is available for types that are convertible to \p T.
template <class U, class E = typename std::enable_if<
std::is_convertible<U, T>::value>::type>
Optional(U X) : Valid(false) {
cr(std::move(X));
}
/// Creates an instance as a copy of another one.
///
/// \param Other the instance whose state to copy
Optional(const Optional &Other) : Valid(false) {
if (Other.Valid) {
cr(Other.Value);
}
}
/// Creates an instance by moving the state of another one.
///
/// \param Other the instance whose state to obtain
Optional(Optional &&Other) noexcept(
std::is_nothrow_move_constructible<T>::value)
: Valid(false) {
if (Other.Valid) {
cr(std::move(Other.Value));
}
}
/// Destroys \p this object.
~Optional(void) { destroy(); }
/// Updates \p this object by copying the state of another one.
///
/// \param Other the instance whose state to copy
///
/// \return reference of the updated instance
Optional &operator=(const Optional &Other) {
if (Valid) {
if (Other.Valid) {
Value = Other.Value;
} else {
destroy();
}
} else if (Other.Valid) {
cr(Other.Value);
}
return *this;
}
/// Updates \p this object by moving the state of another one.
///
/// \param Other the instance whose state to obtain
///
/// \return reference of the updated instance
Optional &operator=(Optional &&Other) noexcept(
std::is_nothrow_destructible<T>::value
&&std::is_nothrow_move_assignable<T>::value) {
if (Valid) {
if (Other.Valid) {
Value = std::move(Other.Value);
} else {
destroy();
}
} else if (Other.Valid) {
cr(std::move(Other.Value));
}
return *this;
}
/// Checks whether \p this object contains a value.
///
/// \return if \p this object contains a value
explicit operator bool(void) const { return Valid; }
/// Checks whether \p this object does not contain a value.
///
/// \return if \p this object does not contain a value
bool operator!(void)const { return !Valid; }
/// Returns the value stored in \p this object.
///
/// \return reference of the stored value
///
/// \pre \p this object contains a value
T &operator*(void) {
ASSERT(Valid);
return Value;
}
/// Returns the value stored in \p this object.
///
/// \return reference of the stored value
///
/// \pre \p this object contains a value
const T &operator*(void)const {
ASSERT(Valid);
return Value;
}
/// Returns the value stored in \p this object.
///
/// \return pointer to the stored value
///
/// \pre \p this object contains a value
const T *operator->(void)const {
ASSERT(Valid);
return &Value;
}
/// Returns the value stored in \p this object.
///
/// \return pointer of the stored value
///
/// \pre \p this object contains a value
T *operator->(void) {
ASSERT(Valid);
return &Value;
}
/// Returns the value stored in \p this object.
///
/// \return reference of the stored value
///
/// \pre \p this object contains a value
T &value(void) {
ASSERT(Valid);
return Value;
}
/// Returns the value stored in \p this object.
///
/// \return reference of the stored value
///
/// \pre \p this object contains a value
const T &value(void) const {
ASSERT(Valid);
return Value;
}
/// Returns the stored value or a default.
///
/// If \p this object contains a value, then the stored value is returned. A
/// given default value is returned otherwise.
///
/// \param DefaultValue the value to return if \p this object does not contain
/// a value
///
/// \return reference to either the stored value or \p DefaultValue if \p this
/// object does not contain a value
const T &valueOr(const T &DefaultValue) const {
return Valid ? Value : DefaultValue;
}
private:
/// Deallocates the stored value if any.
void destroy(void) {
if (Valid) {
Value.~T();
Valid = false;
}
}
/// Updates the state of \p this object by moving a value into it.
///
/// \tparam V type of \p X
///
/// \param X value to move
///
/// \pre \p this object does not contain a value
template <class V> void cr(V &&X) {
ASSERT(!Valid);
Valid = true;
new (&Value) T(std::forward<V>(X));
}
/// Denotes if \p this object contains a value.
bool Valid;
/// Holds the stored value if any.
union {
T Value; ///< The stored value.
};
};
/// Specialization storing a reference.
///
/// The specialization allows \p rosa::Optional to hold a reference
/// rather than an actual value with minimal overhead.
///
/// \tparam T the base type whose reference is to be stored
template <typename T> class Optional<T &> {
public:
using Type = T;
/// Creates an instance without reference
///
/// \note Use it with its default parameter.
Optional(const none_t & = none) : Value(nullptr) {}
/// Creates a valid instance with reference.
///
/// \param X reference to store in the object
Optional(T &X) : Value(&X) {}
/// Creates a valid instance with reference.
///
/// \param X pointer to store in the object as reference
Optional(T *X) : Value(X) {}
/// Creates an instance as a copy of another one.
///
/// \param Other the instance whose state to copy
Optional(const Optional &Other) = default;
/// Updates \p this object by copying the state of another one.
///
/// \param Other the instance whose state to copy
///
/// \return reference of the updated instance
Optional &operator=(const Optional &Other) = default;
/// Checks whether \p this object contains a reference.
///
/// \return if \p this object contains a reference
explicit operator bool(void) const { return Value != nullptr; }
/// Checks whether \p this object does not contain a reference.
///
/// \return if \p this object does not contain a reference
bool operator!(void)const { return !Value; }
/// Returns the reference stored in \p this object.
///
/// \return the stored reference
///
/// \pre \p this object contains a reference
T &operator*(void) {
ASSERT(Value);
return *Value;
}
/// Returns the value stored in \p this object.
///
/// \return the stored reference
///
/// \pre \p this object contains a reference
const T &operator*(void)const {
ASSERT(Value);
return *Value;
}
/// Returns the value stored in \p this object.
///
/// \return the stored reference
///
/// \pre \p this object contains a reference
T *operator->(void) {
ASSERT(Value);
return Value;
}
/// Returns the value stored in \p this object.
///
/// \return the stored reference
///
/// \pre \p this object contains a reference
const T *operator->(void)const {
ASSERT(Value);
return Value;
}
/// Returns the value stored in \p this object.
///
/// \return the stored reference
///
/// \pre \p this object contains a reference
T &value(void) {
ASSERT(Value);
return *Value;
}
/// Returns the value stored in \p this object.
///
/// \return the stored reference
///
/// \pre \p this object contains a reference
const T &value(void) const {
ASSERT(Value);
return *Value;
}
/// Returns the stored reference or a default.
///
/// If \p this object contains a reference, then the stored reference is
/// returned. A given default value is returned otherwise.
///
/// \param DefaultValue the value to return if \p this object does not contain
/// a reference
///
/// \return either the stored reference or \p DefaultValue if \p this object
/// does not contain a reference
const T &valueOr(const T &DefaultValue) const {
return Value ? Value : DefaultValue;
}
private:
/// The stored reference as a pointer.
T *Value;
};
/// Specialization storing \c void.
///
/// The specialization allows \c rosa::Optional to implement a flag for \c void.
template <> class Optional<void> {
public:
using Type = unit_t;
/// Creates an instance with a \c false flag.
///
/// \note Use it with its default parameter.
Optional(none_t = none) : Value(false) {}
/// Creates an instance with a \c true flag.
///
/// \note The only argument is ignored because it can be *the \c rosa::unit_t
/// value* only.
Optional(unit_t) : Value(true) {}
/// Creates an instance as a copy of another one.
///
/// \param Other the instance whose state to copy
Optional(const Optional &Other) = default;
/// Updates \p this object by copying the state of another one.
///
/// \param Other the instance whose state to copy
///
/// \return reference of the updated instance
Optional &operator=(const Optional &Other) = default;
/// Checks whether \p this object contains a \p true flag.
///
/// \return if \p this object contains a \p true flag.
explicit operator bool(void) const { return Value; }
/// Checks whether \p this object contains a \p false flag.
///
/// \return if \p this object contains a \p false flag.
bool operator!(void)const { return !Value; }
private:
/// The stored flag.
bool Value;
};
///@}
} // End namespace rosa
+namespace std {
+
+/// Returns the textual representation of any value of \c rosa::unit_t.
+///
+/// \return textual representation of \c rosa::UnitType.
+inline std::string to_string(const rosa::unit_t &) { return "unit"; }
+
+/// Dumps a \c rosa::Unit to a given \c std::ostream.
+///
+/// \param [in,out] OS output stream to dump to
+/// \param U \c rosa::Unit to dump
+///
+/// \return \p OS after dumping \p U to it
+inline ostream &operator<<(ostream &OS, const rosa::unit_t &U) {
+ OS << to_string(U);
+ return OS;
+}
+
+/// Returns the textual representation of any value of \c rosa::none_t.
+///
+/// \return textual representation of \c rosa::NoneType.
+inline std::string to_string(const rosa::none_t &) { return "none"; }
+
+/// Dumps a \c rosa::none_t to a given \c std::ostream.
+///
+/// \param [in,out] OS output stream to dump to
+/// \param N \c rosa::none_t to dump
+///
+/// \return \p OS after dumping \p N to it
+inline ostream &operator<<(ostream &OS, const rosa::none_t &N) {
+ OS << to_string(N);
+ return OS;
+}
+
+} // End namespace std
+
#endif // ROSA_SUPPORT_TYPES_HPP
diff --git a/lib/core/Unit.cpp b/lib/core/Unit.cpp
index e693624..007ef55 100644
--- a/lib/core/Unit.cpp
+++ b/lib/core/Unit.cpp
@@ -1,53 +1,53 @@
//===-- core/Unit.cpp -------------------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file core/Unit.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
-/// \date 2017
+/// \date 2017-2019
///
/// \brief Implementation of rosa/core/Unit.h.
///
//===----------------------------------------------------------------------===//
#include "rosa/core/Unit.h"
#include "rosa/core/System.hpp" // NOLINT
#include "rosa/support/debug.hpp"
#include "rosa/support/log.h"
namespace rosa {
Unit::Unit(const AtomValue Kind, const id_t Id, const std::string &Name,
System &S) noexcept : Kind(Kind),
Id(Id),
Name(Name),
S(S),
FullName(Name + "@" + S.name()) {
ASSERT(!Name.empty());
- LOG_TRACE("Constructing Unit '" + FullName + "' of kind '" + to_string(Kind) +
- "'");
+ LOG_TRACE("Constructing Unit '" + FullName + "' of kind '" +
+ std::to_string(Kind) + "'");
}
Unit::~Unit(void) { LOG_TRACE("Destroying Unit '" + FullName + "'"); }
/// The default implementation of \c rosa::Unit::dump emits
/// \c rosa::Unit::FullName.
std::string Unit::dump(void) const noexcept {
LOG_TRACE("Dumping Unit '" + FullName + "'");
return "[Unit] " + FullName;
}
System &Unit::system(void) const noexcept { return S; }
std::ostream &operator<<(std::ostream &OS, const Unit &U) {
OS << U.dump();
return OS;
}
} // End namespace rosa
diff --git a/lib/deluxe/CMakeLists.txt b/lib/deluxe/CMakeLists.txt
old mode 100755
new mode 100644
index ab86f2c..56b7af1
--- a/lib/deluxe/CMakeLists.txt
+++ b/lib/deluxe/CMakeLists.txt
@@ -1,30 +1,32 @@
set(LIB_INCLUDE_DIR ${ROSA_MAIN_INCLUDE_DIR}/rosa/deluxe)
add_library(ROSADeluxe
${LIB_INCLUDE_DIR}/namespace.h
namespace.cpp
${LIB_INCLUDE_DIR}/DeluxeAtoms.hpp
DeluxeAtoms.cpp
+ ${LIB_INCLUDE_DIR}/DeluxeTuple.hpp
+ DeluxeTuple.cpp
executionpolicies/Decimation.h
executionpolicies/Decimation.cpp
executionpolicies/AwaitBase.h
executionpolicies/AwaitBase.cpp
executionpolicies/AwaitAll.h
executionpolicies/AwaitAll.cpp
executionpolicies/AwaitAny.h
executionpolicies/AwaitAny.cpp
${LIB_INCLUDE_DIR}/DeluxeExecutionPolicy.h
DeluxeExecutionPolicy.cpp
${LIB_INCLUDE_DIR}/DeluxeSensor.hpp
DeluxeSensor.cpp
${LIB_INCLUDE_DIR}/DeluxeAgent.hpp
DeluxeAgent.cpp
${LIB_INCLUDE_DIR}/DeluxeSystem.hpp
DeluxeSystem.cpp
DeluxeSystemImpl.hpp
DeluxeSystemImpl.cpp
${LIB_INCLUDE_DIR}/DeluxeContext.hpp
DeluxeContext.cpp
)
ROSA_add_library_dependencies(ROSADeluxe ROSACore)
diff --git a/lib/deluxe/DeluxeAgent.cpp b/lib/deluxe/DeluxeAgent.cpp
old mode 100755
new mode 100644
index 0096b02..8f6669e
--- a/lib/deluxe/DeluxeAgent.cpp
+++ b/lib/deluxe/DeluxeAgent.cpp
@@ -1,238 +1,313 @@
//===-- deluxe/DeluxeAgent.cpp ----------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file deluxe/DeluxeAgent.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2019
///
/// \brief Implementation of rosa/deluxe/DeluxeAgent.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/deluxe/DeluxeAgent.hpp"
#include "rosa/deluxe/DeluxeSystem.hpp"
#include <algorithm>
namespace rosa {
namespace deluxe {
bool DeluxeAgent::inv(void) const noexcept {
// Check execution policy.
// \note The \c rosa::System the \c rosa::Unit is created with is a
// \c rosa::DeluxeSystem.
const DeluxeSystem &DS = static_cast<DeluxeSystem &>(Unit::system());
if (!ExecutionPolicy || !ExecutionPolicy->canHandle(Self, DS)) {
return false;
}
+ // Check number of inputs and master-outputs.
+ if (NumberOfInputs != NumberOfMasterOutputs) {
+ return false;
+ }
+
// Check container sizes.
if (!(InputTypes.size() == NumberOfInputs &&
+ InputNextPos.size() == NumberOfInputs &&
InputChanged.size() == NumberOfInputs &&
- InputValues->size() == NumberOfInputs &&
- Slaves.size() == NumberOfInputs)) {
+ InputStorageOffsets.size() == NumberOfInputs &&
+ InputValues->size() ==
+ InputStorageOffsets[NumberOfInputs - 1] +
+ lengthOfToken(InputTypes[NumberOfInputs - 1]) &&
+ MasterOutputTypes.size() == NumberOfInputs // == NumberOfMasterOutputs
+ && Slaves.size() == NumberOfInputs)) {
return false;
}
+ // Stores storage offset for the next slave position checked in the following
+ // loop.
+ token_size_t InputStorageOffset = 0;
+
// Check *slave* types and validate *slave* registrations and reverse lookup
// information.
std::map<id_t, size_t> RefIds; // Build up a reference of SlaveIds in this.
for (size_t I = 0; I < NumberOfInputs; ++I) {
- // First, validate input types at position \c I.
- const TypeNumber T = InputTypes[I];
- if (InputValues->typeAt(I) != T) {
+ // First, validate the corresponding storage offset value.
+ if (InputStorageOffsets[I] != InputStorageOffset) {
+ return false;
+ }
+
+ // Fetch type-related information for the input position.
+ const Token T = InputTypes[I];
+ const token_size_t TL = lengthOfToken(T);
+ const size_t StorageOffset = InputStorageOffsets[I];
+
+ // Update storage offset for the next position.
+ InputStorageOffset += TL;
+
+ // Validate input types at position \c I.
+ for (token_size_t TI = 0; TI < TL; ++TI) {
+ const size_t ElemOffset = StorageOffset + TI;
+ // The assert must hold if \p this object was successfuuly constructed.
+ ASSERT(static_cast<size_t>(static_cast<token_size_t>(ElemOffset)) ==
+ ElemOffset);
+ if (InputValues->typeAt(static_cast<token_size_t>(ElemOffset)) !=
+ typeAtPositionOfToken(T, TI)) {
+ return false;
+ }
+ }
+
+ // Check the index of next expected element for position \c I.
+ if (InputNextPos[I] >= TL) {
return false;
}
// Check the registered *slave* at position \c I.
const auto &Slave = Slaves[I];
// If \c Slave is empty, nothing to check.
if (!Slave)
continue;
+ // Prepare master-output related info for the *slave*.
+ const Token MT = MasterOutputTypes[I];
+ const bool hasMT = !emptyToken(MT);
+
// \c Slave is not empty here.
- // Check the `OutputType` of the registered *slave*.
+ // Check the `OutputType` and `MasterInputType` of the registered *slave*.
const auto &A = unwrapAgent(*Slave);
if (!((A.Kind == atoms::SensorKind &&
- static_cast<const DeluxeSensor &>(A).OutputType == T) ||
+ static_cast<const DeluxeSensor &>(A).OutputType == T &&
+ (!hasMT ||
+ static_cast<const DeluxeSensor &>(A).MasterInputType == MT)) ||
(A.Kind == atoms::AgentKind &&
- static_cast<const DeluxeAgent &>(A).OutputType == T))) {
+ static_cast<const DeluxeAgent &>(A).OutputType == T &&
+ (!hasMT ||
+ static_cast<const DeluxeAgent &>(A).MasterInputType == MT)))) {
return false;
}
// Validate that the *slave* is not registered more than once.
if (std::any_of(
Slaves.begin() + I + 1, Slaves.end(),
[&Slave](const Optional<AgentHandle> &O) { return O && *Slave == *O; })) {
return false;
}
// Build the content of \c RefIds.
RefIds.emplace(A.Id, I);
}
// Validate *slave* reverse lookup information against our reference.
if (RefIds != SlaveIds) {
return false;
}
+ // Check the size of the master-input storage.
+ if (MasterInputValue->size() != lengthOfToken(MasterInputType)) {
+ return false;
+ }
+
+ // Check the index of next expected element from the *master*.
+ const token_size_t MITL = lengthOfToken(MasterInputType);
+ if ((MITL != 0 && MasterInputNextPos >= MITL) ||
+ (MITL == 0 && MasterInputNextPos != 0)) {
+ return false;
+ }
+
// All checks were successful, the invariant is held.
return true;
}
DeluxeAgent::~DeluxeAgent(void) noexcept {
ASSERT(inv());
- LOG_TRACE("Destroying DeluxeAgent...");
+ LOG_TRACE_STREAM << "Destroying DeluxeAgent " << FullName << "..."
+ << std::endl;
// Make sure \p this object is not a registered *slave*.
if (Master) {
ASSERT(unwrapAgent(*Master).Kind == atoms::AgentKind); // Sanity check.
DeluxeAgent &M = static_cast<DeluxeAgent&>(unwrapAgent(*Master));
ASSERT(M.positionOfSlave(self()) != M.NumberOfInputs); // Sanity check.
M.registerSlave(M.positionOfSlave(self()), {});
Master = {};
}
// Also, make sure \p this object is no acting *master*.
for (size_t Pos = 0; Pos < NumberOfInputs; ++Pos) {
registerSlave(Pos, {});
}
// Now there is no connection with other entities, safe to destroy.
}
+id_t DeluxeAgent::masterId(void) const noexcept {
+ ASSERT(inv() && Master);
+ return unwrapAgent(*Master).Id;
+}
+
const DeluxeExecutionPolicy &DeluxeAgent::executionPolicy(void) const noexcept {
ASSERT(inv());
return *ExecutionPolicy;
}
bool DeluxeAgent::setExecutionPolicy(
std::unique_ptr<DeluxeExecutionPolicy> &&EP) noexcept {
ASSERT(inv());
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " setting execution policy "
<< *EP << std::endl;
bool Success = false;
// \note The \c rosa::System the \c rosa::Unit is created with is a
// \c rosa::DeluxeSystem.
const DeluxeSystem &DS = static_cast<DeluxeSystem &>(Unit::system());
if (EP && EP->canHandle(self(), DS)) {
ExecutionPolicy.swap(EP);
Success = true;
} else {
LOG_TRACE_STREAM << "Execution policy " << *EP
<< " cannot handle DeluxeAgent " << FullName << std::endl;
}
ASSERT(inv());
return Success;
}
Optional<AgentHandle> DeluxeAgent::master(void) const noexcept {
ASSERT(inv());
return Master;
}
void DeluxeAgent::registerMaster(const Optional<AgentHandle> _Master) noexcept {
ASSERT(inv() && (!_Master || unwrapAgent(*_Master).Kind == atoms::AgentKind));
Master = _Master;
ASSERT(inv());
}
-TypeNumber DeluxeAgent::inputType(const size_t Pos) const noexcept {
+Token DeluxeAgent::inputType(const size_t Pos) const noexcept {
ASSERT(inv() && Pos < NumberOfInputs);
return InputTypes[Pos];
}
+Token DeluxeAgent::masterOutputType(const size_t Pos) const noexcept {
+ ASSERT(inv() && Pos < NumberOfMasterOutputs);
+ return MasterOutputTypes[Pos];
+}
+
Optional<AgentHandle> DeluxeAgent::slave(const size_t Pos) const noexcept {
ASSERT(inv() && Pos < NumberOfInputs);
return Slaves[Pos];
}
void DeluxeAgent::registerSlave(const size_t Pos,
const Optional<AgentHandle> Slave) noexcept {
ASSERT(inv() && Pos < NumberOfInputs &&
(!Slave ||
(unwrapAgent(*Slave).Kind == atoms::SensorKind &&
static_cast<const DeluxeSensor &>(unwrapAgent(*Slave)).OutputType ==
- InputTypes[Pos]) ||
+ InputTypes[Pos] &&
+ (emptyToken(MasterOutputTypes[Pos]) ||
+ static_cast<const DeluxeSensor &>(unwrapAgent(*Slave))
+ .MasterInputType == MasterOutputTypes[Pos])) ||
(unwrapAgent(*Slave).Kind == atoms::AgentKind &&
static_cast<const DeluxeAgent &>(unwrapAgent(*Slave)).OutputType ==
- InputTypes[Pos])));
+ InputTypes[Pos] &&
+ (emptyToken(MasterOutputTypes[Pos]) ||
+ static_cast<const DeluxeAgent &>(unwrapAgent(*Slave))
+ .MasterInputType == MasterOutputTypes[Pos]))));
// If registering an actual *slave*, not just clearing the slot, make sure
// the same *slave* is not registered to another slot.
if (Slave) {
auto It = SlaveIds.find(unwrapAgent(*Slave).Id);
if (It != SlaveIds.end()) {
Slaves[It->second] = {};//Optional<AgentHandle>();
SlaveIds.erase(It);
}
}
// Obtain the place whose content is to be replaced with \p Slave
auto &OldSlave = Slaves[Pos];
// If there is already a *slave* registered at \p Pos, clear reverse lookup
// information for it, and make sure it no longer has \p this object as
// *master*.
if (OldSlave) {
auto &A = unwrapAgent(*OldSlave);
ASSERT(SlaveIds.find(A.Id) != SlaveIds.end()); // Sanity check.
SlaveIds.erase(A.Id);
if (A.Kind == atoms::AgentKind) {
static_cast<DeluxeAgent &>(A).registerMaster({});
} else {
ASSERT(A.Kind == atoms::SensorKind); // Sanity check.
static_cast<DeluxeSensor &>(A).registerMaster({});
}
}
// Register \p Slave at \p Pos.
OldSlave = Slave;
// If registering an actual *slave*, not just clearing the slot, register
// reverse lookup information for the new *slave*.
if (Slave) {
SlaveIds.emplace(unwrapAgent(*Slave).Id, Pos);
}
ASSERT(inv());
}
size_t DeluxeAgent::positionOfSlave(const AgentHandle Slave) const noexcept {
ASSERT(inv());
bool Found = false;
size_t Pos = 0;
while (!Found && Pos < NumberOfInputs) {
auto &ExistingSlave = Slaves[Pos];
if (ExistingSlave && *ExistingSlave == Slave) {
Found = true;
} else {
++Pos;
}
}
ASSERT(Found || Pos == NumberOfInputs); // Sanity check.
return Pos;
}
void DeluxeAgent::handleTrigger(atoms::Trigger) noexcept {
ASSERT(inv());
FP();
ASSERT(inv());
}
} // End namespace deluxe
} // End namespace rosa
diff --git a/lib/deluxe/DeluxeContext.cpp b/lib/deluxe/DeluxeContext.cpp
index 0e363fe..7991c03 100755
--- a/lib/deluxe/DeluxeContext.cpp
+++ b/lib/deluxe/DeluxeContext.cpp
@@ -1,197 +1,201 @@
//===-- deluxe/DeluxeContext.cpp --------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file deluxe/DeluxeContext.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
-/// \date 2017
+/// \date 2017-2019
///
/// \brief Implementation for rosa/deluxe/DeluxeContext.hpp.
///
//===----------------------------------------------------------------------===//
#define ROSA_LIB_DELUXE_DELUXECONTEXT_CPP // For including helper macros.
#include "rosa/deluxe/DeluxeContext.hpp"
#include <algorithm>
namespace rosa {
namespace deluxe {
std::unique_ptr<DeluxeContext>
DeluxeContext::create(const std::string &Name) noexcept {
return std::unique_ptr<DeluxeContext>(new DeluxeContext(Name));
}
DeluxeContext::DeluxeContext(const std::string &Name) noexcept
: System(DeluxeSystem::createSystem(Name)) {
LOG_TRACE("DeluxeContext for '" + System->name() + "' is created.");
}
DeluxeContext::~DeluxeContext(void) noexcept {
// \c rosa::deluxe::DeluxeContext::System is not used outside, just clean it.
for(auto U : DeluxeUnits) {
System->destroyAgent(U);
}
// \note \c System will be marked clean by SystemImpl::~SystemImpl.
LOG_TRACE("DeluxeContext for '" + System->name() +
"' prepared for destruction.");
}
Optional<const DeluxeExecutionPolicy &>
DeluxeContext::getExecutionPolicy(AgentHandle Unit) const noexcept {
if (System->isDeluxeSensor(Unit)) {
return {System->getDeluxeSensor(Unit)->executionPolicy()};
} else if (System->isDeluxeAgent(Unit)) {
return {System->getDeluxeAgent(Unit)->executionPolicy()};
} else {
return {};
}
}
DeluxeContext::ErrorCode DeluxeContext::setExecutionPolicy(
AgentHandle Unit,
std::unique_ptr<DeluxeExecutionPolicy> &&ExecutionPolicy) noexcept {
// Generate trace log.
auto &Trace = LOG_TRACE_STREAM;
Trace << "Setting execution policy of " << System->unwrapAgent(Unit).FullName
<< " to ";
if (ExecutionPolicy) {
Trace << "'" << ExecutionPolicy->dump() << "'\n";
} else {
Trace << "[]\n";
DCRETERROR(ErrorCode::UnsuitableExecutionPolicy);
}
if (System->isDeluxeSensor(Unit)) {
const bool Success = System->getDeluxeSensor(Unit)->setExecutionPolicy(
std::move(ExecutionPolicy));
if (!Success) {
DCRETERROR(ErrorCode::UnsuitableExecutionPolicy);
} else {
return ErrorCode::NoError;
}
} else if (System->isDeluxeAgent(Unit)) {
const bool Success = System->getDeluxeAgent(Unit)->setExecutionPolicy(
std::move(ExecutionPolicy));
if (!Success) {
DCRETERROR(ErrorCode::UnsuitableExecutionPolicy);
} else {
return ErrorCode::NoError;
}
} else {
DCRETERROR(ErrorCode::NotUnit);
}
}
DeluxeContext::ErrorCode
DeluxeContext::connectSensor(AgentHandle Agent, const size_t Pos,
AgentHandle Sensor,
const std::string &Description) noexcept {
// Generate trace log.
auto &Trace = LOG_TRACE_STREAM;
Trace << "Establishing connection";
if (!Description.empty()) {
Trace << " '" << Description << "'";
}
Trace << " between '" << System->unwrapAgent(Sensor).FullName << "' and '"
<< System->unwrapAgent(Agent).FullName << "'\n";
// Make sure preconditions are met.
if (!System->isDeluxeAgent(Agent)) {
DCRETERROR(ErrorCode::NotAgent);
} else if (!System->isDeluxeSensor(Sensor)) {
DCRETERROR(ErrorCode::NotSensor);
}
auto A = System->getDeluxeAgent(Agent);
auto S = System->getDeluxeSensor(Sensor);
ASSERT(A && S); // Sanity check.
if (Pos >= A->NumberOfInputs) {
DCRETERROR(ErrorCode::WrongPosition);
- } else if (A->inputType(Pos) != S->OutputType) {
+ } else if (A->inputType(Pos) != S->OutputType ||
+ (!emptyToken(A->masterOutputType(Pos)) &&
+ A->masterOutputType(Pos) != S->MasterInputType)) {
DCRETERROR(ErrorCode::TypeMismatch);
} else if (A->slave(Pos)) {
DCRETERROR(ErrorCode::AlreadyHasSlave);
} else if (S->master()) {
DCRETERROR(ErrorCode::AlreadyHasMaster);
}
// Do register.
A->registerSlave(Pos, {Sensor});
S->registerMaster({Agent});
return ErrorCode::NoError;
}
DeluxeContext::ErrorCode
DeluxeContext::connectAgents(AgentHandle Master, const size_t Pos,
AgentHandle Slave,
const std::string &Description) noexcept {
// Generate trace log.
auto &Trace = LOG_TRACE_STREAM;
Trace << "Establishing connection";
if (!Description.empty()) {
Trace << " '" << Description << "'";
}
Trace << " between '" << System->unwrapAgent(Slave).FullName << "' and '"
<< System->unwrapAgent(Master).FullName << "'\n";
// Make sure preconditions are met.
if (!(System->isDeluxeAgent(Master) && System->isDeluxeAgent(Slave))) {
DCRETERROR(ErrorCode::NotAgent);
}
auto M = System->getDeluxeAgent(Master);
auto S = System->getDeluxeAgent(Slave);
ASSERT(M && S); // Sanity check.
if (Pos >= M->NumberOfInputs) {
DCRETERROR(ErrorCode::WrongPosition);
- } else if (M->inputType(Pos) != S->OutputType) {
+ } else if (M->inputType(Pos) != S->OutputType ||
+ (!emptyToken(M->masterOutputType(Pos)) &&
+ M->masterOutputType(Pos) != S->MasterInputType)) {
DCRETERROR(ErrorCode::TypeMismatch);
} else if (M->slave(Pos)) {
DCRETERROR(ErrorCode::AlreadyHasSlave);
} else if (S->master()) {
DCRETERROR(ErrorCode::AlreadyHasMaster);
}
// Do register.
M->registerSlave(Pos, {Slave});
S->registerMaster({Master});
return ErrorCode::NoError;
}
std::weak_ptr<MessagingSystem> DeluxeContext::getSystem(void) const noexcept {
return std::weak_ptr<MessagingSystem>(System);
}
void DeluxeContext::initializeSimulation(void) noexcept {
// Clear simulation data sources from sensors.
for (auto U : DeluxeUnits) {
if (auto S = System->getDeluxeSensor(U)) {
S->clearSimulationDataSource();
}
}
}
void DeluxeContext::simulate(const size_t NumCycles) const noexcept {
ASSERT(std::all_of(
DeluxeUnits.begin(), DeluxeUnits.end(), [&](const AgentHandle &H) {
return System->isDeluxeAgent(H) ||
System->isDeluxeSensor(H) &&
System->getDeluxeSensor(H)->simulationDataSourceIsSet();
}));
for (size_t I = 1; I <= NumCycles; ++I) {
LOG_TRACE("Simulation cycle: " + std::to_string(I));
for (auto U : DeluxeUnits) {
U.sendMessage(Message::create(atoms::Trigger::Value));
}
}
}
} // End namespace deluxe
} // End namespace rosa
diff --git a/lib/deluxe/DeluxeSensor.cpp b/lib/deluxe/DeluxeSensor.cpp
old mode 100755
new mode 100644
index c5269f7..90eeafe
--- a/lib/deluxe/DeluxeSensor.cpp
+++ b/lib/deluxe/DeluxeSensor.cpp
@@ -1,113 +1,132 @@
//===-- deluxe/DeluxeSensor.cpp ---------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file deluxe/DeluxeSensor.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2019
///
/// \brief Implementation of rosa/deluxe/DeluxeSensor.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/deluxe/DeluxeSensor.hpp"
#include "rosa/deluxe/DeluxeSystem.hpp"
namespace rosa {
namespace deluxe {
bool DeluxeSensor::inv(void) const noexcept {
// Check execution policy.
// \note The \c rosa::System the \c rosa::Unit is created with is a
// \c rosa::DeluxeSystem.
const DeluxeSystem &DS = static_cast<DeluxeSystem &>(Unit::system());
if (!ExecutionPolicy || !ExecutionPolicy->canHandle(Self, DS)) {
return false;
}
+ // Check the index of next expected element from the *master*.
+ const token_size_t MITL = lengthOfToken(MasterInputType);
+ if ((MITL != 0 && MasterInputNextPos >= MITL) ||
+ (MITL == 0 && MasterInputNextPos != 0)) {
+ return false;
+ }
+
// All checks were successful, the invariant is held.
return true;
}
+id_t DeluxeSensor::masterId(void) const noexcept {
+ ASSERT(Master);
+ return unwrapAgent(*Master).Id;
+}
+
DeluxeSensor::~DeluxeSensor(void) noexcept {
ASSERT(inv());
- LOG_TRACE("Destroying DeluxeSensor...");
+ LOG_TRACE_STREAM << "Destroying DeluxeSensor " << FullName << "..."
+ << std::endl;
// Make sure \p this object is not a registered *slave*.
if (Master) {
ASSERT(unwrapAgent(*Master).Kind == atoms::AgentKind); // Sanity check.
DeluxeAgent &M = static_cast<DeluxeAgent&>(unwrapAgent(*Master));
ASSERT(M.positionOfSlave(self()) != M.NumberOfInputs); // Sanity check.
M.registerSlave(M.positionOfSlave(self()), {});
Master = {};
}
}
const DeluxeExecutionPolicy &DeluxeSensor::executionPolicy(void) const
noexcept {
ASSERT(inv());
return *ExecutionPolicy;
}
bool DeluxeSensor::setExecutionPolicy(
std::unique_ptr<DeluxeExecutionPolicy> &&EP) noexcept {
ASSERT(inv());
LOG_TRACE_STREAM << "DeluxeSensor " << FullName
<< " setting execution policy " << *EP << std::endl;
bool Success = false;
// \note The \c rosa::System the \c rosa::Unit is created with is a
// \c rosa::DeluxeSystem.
const DeluxeSystem &DS = static_cast<DeluxeSystem &>(Unit::system());
if (EP && EP->canHandle(self(), DS)) {
ExecutionPolicy.swap(EP);
Success = true;
} else {
LOG_TRACE_STREAM << "Execution policy " << *EP
<< " cannot handle DeluxeSensor " << FullName << std::endl;
}
ASSERT(inv());
return Success;
}
Optional<AgentHandle> DeluxeSensor::master(void) const noexcept {
ASSERT(inv());
return Master;
}
void DeluxeSensor::registerMaster(const Optional<AgentHandle> _Master) noexcept {
ASSERT(inv() && (!_Master || unwrapAgent(*_Master).Kind == atoms::AgentKind));
Master = _Master;
ASSERT(inv());
}
void DeluxeSensor::clearSimulationDataSource(void) noexcept {
ASSERT(inv());
SFP = nullptr;
ASSERT(inv());
}
bool DeluxeSensor::simulationDataSourceIsSet(void) const noexcept {
ASSERT(inv());
return SFP != nullptr;
}
void DeluxeSensor::handleTrigger(atoms::Trigger) noexcept {
- ASSERT(inv());
+ ASSERT(inv() && MasterInputNextPos == 0);
+
+ // Process master-input.
+ MFP();
+
+ // Obtain the next sensory value.
+ // \note Execution policy is respected within the data source function.
// Use \c rosa::deluxe::DeluxeSensor::SFP if set, otherwise
// \c rosa::deluxe::DeluxeSensor::FP.
const H &F = SFP ? SFP : FP;
F();
ASSERT(inv());
}
} // End namespace deluxe
} // End namespace rosa
diff --git a/lib/deluxe/DeluxeTuple.cpp b/lib/deluxe/DeluxeTuple.cpp
new file mode 100644
index 0000000..41895fb
--- /dev/null
+++ b/lib/deluxe/DeluxeTuple.cpp
@@ -0,0 +1,28 @@
+//===-- deluxe/DeluxeTuple.cpp ----------------------------------*- C++ -*-===//
+//
+// The RoSA Framework
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file deluxe/DeluxeTuple.cpp
+///
+/// \author David Juhasz (david.juhasz@tuwien.ac.at)
+///
+/// \date 2019
+///
+/// \brief Implementation of rosa/deluxe/DeluxeTuple.hpp.
+///
+///
+//===----------------------------------------------------------------------===//
+
+#include "rosa/deluxe/DeluxeTuple.hpp"
+
+namespace rosa {
+namespace deluxe {
+
+void DeluxeTuple<>::dump(std::ostream &OS) noexcept {
+ OS << "{ }";
+}
+
+} // End namespace deluxe
+} // End namespace rosa
diff --git a/lib/support/atom.cpp b/lib/support/atom.cpp
index ee48d3d..9ef7c43 100644
--- a/lib/support/atom.cpp
+++ b/lib/support/atom.cpp
@@ -1,55 +1,59 @@
//===-- support/atom.cpp ----------------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file support/atom.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
-/// \date 2017
+/// \date 2017-2019
///
/// \brief Implementation of non-static part of atom facilities of
/// rosa/support/atom.hpp.
///
/// \note This implementation is based on the `atom` implementation of CAF.
/// \todo Check license.
///
//===----------------------------------------------------------------------===//
#include "rosa/support/atom.hpp"
#include <cstring>
namespace rosa {
-std::string to_string(const AtomValue &What) {
- auto X = static_cast<atom_t>(What);
- std::string S;
- S.reserve(MaxAtomLength + 1);
+AtomValue atom_from_string(const std::string &S) {
+ if (S.size() > MaxAtomLength) {
+ return atom("");
+ }
+ char AtomBuf[MaxAtomLength + 1];
+ std::memcpy(AtomBuf, S.c_str(), S.size());
+ AtomBuf[S.size()] = '\0';
+ return atom(AtomBuf);
+}
+
+} // End namespace rosa
+
+namespace std {
+
+string to_string(const rosa::AtomValue &What) {
+ auto X = static_cast<rosa::atom_t>(What);
+ string S;
+ S.reserve(rosa::MaxAtomLength + 1);
// Don't read characters before we found the leading 0xF.
// First four bits set?
bool ReadChars = ((X & 0xF000000000000000) >> 60) == 0xF;
uint64_t Mask = 0x0FC0000000000000;
for (int BitShift = 54; BitShift >= 0; BitShift -= 6, Mask >>= 6) {
if (ReadChars) {
- S += AtomDecodingTable[(X & Mask) >> BitShift];
+ S += rosa::AtomDecodingTable[(X & Mask) >> BitShift];
} else if (((X & Mask) >> BitShift) == 0xF) {
ReadChars = true;
}
}
return S;
}
-AtomValue atom_from_string(const std::string &S) {
- if (S.size() > MaxAtomLength) {
- return atom("");
- }
- char AtomBuf[MaxAtomLength + 1];
- std::memcpy(AtomBuf, S.c_str(), S.size());
- AtomBuf[S.size()] = '\0';
- return atom(AtomBuf);
-}
-
-} // End namespace rosa
+} // End namespace std
diff --git a/lib/support/tokenized_storages.cpp b/lib/support/tokenized_storages.cpp
index ceb8a47..59d8eb6 100755
--- a/lib/support/tokenized_storages.cpp
+++ b/lib/support/tokenized_storages.cpp
@@ -1,20 +1,25 @@
//===-- support/tokenized_storages.cpp --------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file support/tokenized_storages.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
-/// \date 2017
+/// \date 2017-2019
///
/// \brief Implementation of rosa/support/tokenized_storages.hpp.
///
-/// \note Empty implementation, source file here to have a compile database
-/// entry for rosa/support/tokenized_storages.hpp.
-///
//===----------------------------------------------------------------------===//
#include "rosa/support/tokenized_storages.hpp"
+
+namespace rosa {
+
+// Implementation of the static member field \c
+// rosa::TokenizedStorage<>::Offsets.
+const std::vector<size_t> TokenizedStorage<>::Offsets = std::vector<size_t>(0);
+
+} // End namespace rosa
\ No newline at end of file
diff --git a/lib/support/type_token.cpp b/lib/support/type_token.cpp
index 30c37c7..508b471 100644
--- a/lib/support/type_token.cpp
+++ b/lib/support/type_token.cpp
@@ -1,131 +1,131 @@
//===-- support/type_token.cpp ----------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file support/type_token.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
-/// \date 2017
+/// \date 2017-2019
///
/// \brief Implementation for rosa/support/type_token.hpp.
///
/// \todo Automatically generate proper cases for the switches with a big number
/// of handcrafted similar cases in them.
///
//===----------------------------------------------------------------------===//
#include "rosa/support/type_token.hpp"
namespace rosa {
using namespace token;
bool validToken(const Token T) {
Token TT = T;
bool Valid = true;
while (Valid && !emptyToken(TT)) {
Valid &= validTypeNumber(headOfToken(TT));
dropHeadOfToken(TT);
}
return Valid;
}
bool emptyToken(const Token T) { return static_cast<token_t>(T) == 0; }
-size_t lengthOfToken(const Token T) {
+token_size_t lengthOfToken(const Token T) {
Token TT = T;
- size_t N = 0;
+ token_size_t N = 0;
while (!emptyToken(TT)) {
++N;
dropHeadOfToken(TT);
}
return N;
}
size_t sizeOfValuesOfToken(const Token T) {
ASSERT(validToken(T));
Token TT = T;
size_t S = 0;
while (!emptyToken(TT)) {
S += sizeOfHeadOfToken(TT);
dropHeadOfToken(TT);
}
return S;
}
#define SIZECASE(N) \
{ \
case N: \
return TypeForNumber<static_cast<TypeNumber>(N)>::Size; \
}
size_t sizeOfHeadOfToken(const Token T) {
ASSERT(!emptyToken(T) && validToken(T));
switch (static_cast<type_nr_t>(headOfToken(T))) {
default: {
// Should never come here when \p T is valid and the case-list below covers
// \c rosa::BuiltinTypes.
ROSA_CRITICAL("unknown type number");
}
SIZECASE(1);
SIZECASE(2);
SIZECASE(3);
SIZECASE(4);
SIZECASE(5);
SIZECASE(6);
SIZECASE(7);
SIZECASE(8);
SIZECASE(9);
SIZECASE(10);
SIZECASE(11);
SIZECASE(12);
SIZECASE(13);
SIZECASE(14);
SIZECASE(15);
}
}
#define NAMECASE(N) \
{ \
case N: \
return TypeForNumber<static_cast<TypeNumber>(N)>::Name; \
}
const char *nameOfHeadOfToken(const Token T) {
ASSERT(!emptyToken(T) && validToken(T));
switch (static_cast<type_nr_t>(headOfToken(T))) {
default: {
// Should never come here when \p T is valid and the case-list below covers
// \c rosa::BuiltinTypes.
ROSA_CRITICAL("unknown type number");
}
NAMECASE(1);
NAMECASE(2);
NAMECASE(3);
NAMECASE(4);
NAMECASE(5);
NAMECASE(6);
NAMECASE(7);
NAMECASE(8);
NAMECASE(9);
NAMECASE(10);
NAMECASE(11);
NAMECASE(12);
NAMECASE(13);
NAMECASE(14);
NAMECASE(15);
}
}
void dropHeadOfToken(Token &T) {
T = static_cast<Token>(static_cast<token_t>(T) >> RepresentationBits);
}
void dropNOfToken(Token &T, const size_t N) {
T = static_cast<Token>(static_cast<token_t>(T) >> (N * RepresentationBits));
}
} // End namespace rosa

File Metadata

Mime Type
text/x-diff
Expires
Mon, Oct 20, 10:19 AM (21 h, 40 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
199687
Default Alt Text
(363 KB)

Event Timeline