Page MenuHomePhorge

No OneTemporary

Size
234 KB
Referenced Files
None
Subscribers
None
diff --git a/apps/sa-ews1/sa-ews1.cpp b/apps/sa-ews1/sa-ews1.cpp
index 836e3d1..b47e00d 100644
--- a/apps/sa-ews1/sa-ews1.cpp
+++ b/apps/sa-ews1/sa-ews1.cpp
@@ -1,318 +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 = 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",
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",
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>;
+ using CSVInt = csv::CSVIterator<int32_t>;
+ using CSVFloat = csv::CSVIterator<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/CSVFiles/CMakeLists.txt b/examples/CSVFiles/CMakeLists.txt
new file mode 100644
index 0000000..cfc92e7
--- /dev/null
+++ b/examples/CSVFiles/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_executable(csvfiles main.cpp)
+ROSA_add_library_dependencies(csvfiles ROSAConfig)
+ROSA_add_library_dependencies(csvfiles ROSADeluxe)
diff --git a/examples/CSVFiles/main.cpp b/examples/CSVFiles/main.cpp
new file mode 100644
index 0000000..b2c43ce
--- /dev/null
+++ b/examples/CSVFiles/main.cpp
@@ -0,0 +1,352 @@
+//===-- examples/CSVFiles/main.cpp ------------------*- C++ -*-===//
+//
+// The RoSA Framework
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file examples/basic-system/basic-system.cpp
+///
+/// \author Edwin Willegger (edwin.willegger@tuwien.ac.at)
+///
+/// \date 2019
+///
+/// \brief A simple example on the basic \c rosa::csv, \c rosa::iterator and
+/// \c rosa::writer classes. Focus is on the tuple impementations.
+///
+//===----------------------------------------------------------------------===//
+#include <cstdint>
+#include <cstddef>
+#include <iostream>
+#include <istream>
+#include <sstream>
+#include <fstream>
+#include <ostream>
+#include <string>
+#include <vector>
+#include <typeinfo>
+#include <map>
+#include <algorithm>
+#include <tuple>
+#include <type_traits>
+#include <unistd.h>
+
+//includes for an complete example to read and write
+//with sensors and agents.
+#include "rosa/deluxe/DeluxeContext.hpp"
+
+#include "rosa/config/version.h"
+
+//includes to test the basic functionality
+//to read and write tuples.
+#include "rosa/support/csv/CSVReader.hpp"
+#include "rosa/support/csv/CSVWriter.hpp"
+#include "rosa/support/iterator/split_tuple_iterator.hpp"
+#include "rosa/support/writer/split_tuple_writer.hpp"
+
+/// the name of the example
+const std::string ExampleName = "csvfiles";
+
+/// How many cycles of simulation to perform.
+const size_t NumberOfSimulationCycles = 10;
+
+/// Paths for the CSV files for simulation.
+/// input csv files
+const std::string csvPath = "../examples/CSVFiles/";
+const std::string csvFileWithHeader = csvPath + "HR-New.csv";
+const std::string csvFileNoHeader = csvPath + "HR.csv";
+const std::string csvFileHeaderSemi = csvPath + "HR-New-Semicolon.csv";
+/// output csv files
+const std::string csvFileWriteHea = csvPath + "csvwriter_noheader.csv";
+const std::string csvFileWriteNoHeaSplit = csvPath + "csvSplitwriter_noheader.csv";
+
+using namespace rosa;
+
+///
+/// This function tests the basic CSVIterator capablities, and shows you
+/// how you could work with this class.
+///
+void testtupleCSVReader(void){
+
+ //different streams to get the csv data out of the files
+ //file contains header and valid data entries, delimter = ','
+ std::ifstream file_header_data(csvFileWithHeader);
+ //file contains header and valid data entries, delimter = ','
+ std::ifstream file_header_data_2(csvFileWithHeader);
+ //file contains header and valid data entries, delimter = ','
+ std::ifstream file_header_data_3(csvFileWithHeader);
+ //file contains header and valid data entries, delimter = ','
+ std::ifstream file_header_data_4(csvFileWithHeader);
+ //file contains header and valid data entries, delimter = ','
+ std::ifstream file_header_data_5(csvFileWithHeader);
+ //file contains header and valid data entries, delimter = ','
+ std::ifstream file_header_data_6(csvFileWithHeader);
+ //file contains no header an valid data entries, delimter = ','
+ std::ifstream file2(csvFileNoHeader);
+ //file contains header and valid data entries, delimter = ';'
+ std::ifstream file3(csvFileHeaderSemi);
+
+ csv::CSVIterator<int, std::string, std::string, int, int> it(file_header_data);
+
+ it.setDelimeter(',');
+
+ it++;
+ it++;
+ //if you iterate over the end of file, the last values
+ //of the file will remain in the data structure but no
+ //error occurs.
+ it++;
+ it++;
+
+ //-------------------------------------------------------------------
+ // a possiblity to get the data out of the iterator
+ std::tuple<int, std::string, std::string, int, int> value = *it;
+
+ //
+ // Show the value of one iterator
+ //
+ LOG_INFO( "Values are: ");
+ LOG_INFO(std::get<0>(value) );
+ LOG_INFO(std::get<1>(value) );
+
+
+ //--------------------------------------------------------------------
+ //testing differnet parameters to the constructor
+
+ //uncomment to see that it is not possible to iterate over an vector in the tuple.
+ //rosa::csv::CSVIterator<double, std::vector<int>> it2(file, 1);
+
+ //try to skip a valid number of lines after the header
+ csv::CSVIterator<double, float, int, int, float> it2_0(file_header_data_2, 1);
+ //try to skip a valid number of lines after the header, but you assume that the file has no header
+ //uncomment this line to crash the programm
+ //csv::CSVIterator<double, float, int, int, float> it2_1(file_header_data_3, 0, csv::HeaderInformation::HasNoHeader);
+
+ //try to skip a valid number of lines after the header, but you assume that the file has no header
+ //uncomment this line to crash the program
+ //csv::CSVIterator<double, float, int, int, float> it2_2(file_header_data_4, 1, csv::HeaderInformation::HasNoHeader);
+
+ //try to skip a valid number of lines of a file without header
+ csv::CSVIterator<double, float, int, int, float> it2_3(file2, 1, csv::HeaderInformation::HasNoHeader);
+
+ //try to skip a valid number of lines after the header, but with different delimeter
+ csv::CSVIterator<double, float, int, int, float> it2_4(file3, 2, csv::HeaderInformation::HasHeader, ';');
+
+ // if you skip more lines than valid, you generate an infinte loop
+ //csv::CSVIterator<double, float, int, int, float> it3(file_header_data_5, 500);
+
+ //if you don't need data from all columns just select the number of columns you
+ //need. You get the data back from the first column (index 0) to the fourth column
+ //all values from the fifth column are ignored.
+ csv::CSVIterator<double, float, int, float> it4(file_header_data_6);
+}
+
+///
+/// This function tests the basic CSVTupleWriter capablities, and shows you
+/// how you could work with this class.
+///
+void testtupleCSVWriter(void){
+ //
+ // Create output writer with an file
+ //
+ std::ofstream file_header_out(csvFileWriteHea);
+ csv::CSVTupleWriter<int, float, std::string> wri(file_header_out);
+
+ //
+ // Create test tuples
+ //
+ std::tuple<int, float, std::string> values(5, 8.3, "hallo");
+ std::tuple<int, float, std::string> values2(3, 8.3, "end");
+
+ //
+ // Create test header lines for the test tuples
+ //
+ std::array<std::string, 3> header {"zero column", "first column", "second column"};
+
+ std::array<std::string, 4> headerWrong {"zero column", "first column", "second column", "third column"};
+
+ std::array<std::string, 2> headerWrongShort {"zero column", "first column"};
+
+ //if you uncomment this line than it would be possible for you to write the header into the stream
+ //in the next line.
+ //wri.write(values);
+ wri.writeHeader(header);
+ wri.write(values);
+ wri.write(values);
+ // it is not possible to write an additional header into the stream.
+ wri.writeHeader(header);
+ wri.write(values);
+ wri << values;
+ wri << values2;
+
+ //uncomment this line to see, that you can't write a header with the too many elements.
+ //wri.writeHeader(headerWrong);
+ //uncomment this line to see, that you can't write a header with the too few elements.
+ //wri.writeHeader(headerWrongShort);
+
+}
+
+///
+/// This function tests the basic splitTupleIterator capablities, and shows you
+/// how you could work with this class, this class is used if you want to split
+/// a CSVIterator in separate parts.
+///
+void testsplitTupleIterator(void)
+{
+ //
+ // Create deluxe context
+ //
+ std::unique_ptr<rosa::deluxe::DeluxeContext> C =
+ deluxe::DeluxeContext::create(ExampleName);
+
+ //
+ // 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.
+ // Three different sensors were created, this is just a random number taken.
+ AgentHandle Elem0Sensor = C->createSensor<int>("Element1 Sensor");
+ AgentHandle Elem1Sensor = C->createSensor<float>("Element2 Sensor");
+ AgentHandle Elem2Sensor = C->createSensor<std::string>("Element3 Sensor");
+
+
+ //
+ // Initialize deluxe context for simulation.
+ //
+ C->initializeSimulation();
+
+ // Type aliases for iterators
+ using Iterator = rosa::csv::CSVIterator<int, float, std::string>;
+ using IteratorValue = std::tuple<int, float, std::string>;
+
+ static_assert (std::is_same<typename Iterator::value_type, IteratorValue>::value, "Iterator must provide tuples" );
+
+ //
+ // Open CSV file and register the columns to the corresponding sensors.
+ //
+ std::ifstream TestCSV(csvFileWithHeader);
+
+ //
+ // Test data looks like:
+ // Element1, Element2, Element3, Element4, Element5 -- is the header line
+ // 3, 5, 8, 9.5, 17 -- first line of values
+ // 100, -8, 30, 18.8, 29 -- other line of values were also in the file
+ // 5, 20, -100, -200.1, -30 -- if you have less number of values than simulation rounds all values
+ // -- beyond your last value will be zero.
+
+ //get element iterator ranges
+ auto [Elem0Range, Elem1Range, Elem2Range] = iterator::splitTupleIterator(Iterator(TestCSV), Iterator());
+
+ //dissect a range into begin and end iterators by structred bindings
+ auto[Elem0Begin, Elem0End] = Elem0Range;
+
+ //deissect a range with functions
+ auto Elem1Begin = iterator::begin(Elem1Range);
+ auto Elem1End = iterator::end(Elem1Range);
+
+ C->registerSensorValues(Elem0Sensor, std::move(Elem0Begin), Elem0End);
+ C->registerSensorValues(Elem1Sensor, std::move(Elem1Begin), Elem1End);
+ C->registerSensorValues(Elem2Sensor, std::move(iterator::begin(Elem2Range)),
+ iterator::end(Elem2Range));
+
+ //
+ // Simulate.
+ //
+ C->simulate(NumberOfSimulationCycles);
+
+}
+
+
+///
+/// This function tests the basic splitTupleWriter capablities, and shows you
+/// how you could work with this class, this class is used if you want to split
+/// a CSVWriter in separate parts.
+///
+void testsplitTupleWriter(void){
+ //
+ // Create output writer with an file
+ //
+ std::ofstream file_header_out(csvFileWriteNoHeaSplit);
+ csv::CSVTupleWriter<int, float, std::string> wri(file_header_out);
+
+ // if you omit, the type definition in the template, than auto generated types were used,
+ // and they may not fit to the used CSVTupleWriter.
+ wri << std::make_tuple<int, float, std::string>(1000, 50.6, "tuple_created");
+ auto [T0Writer, T1Writer, T2Writer] = writer::splitTupleWriter(std::move(wri),
+ writer::IncompleteTuplePolicy::Ignore);
+ //writing elements in sequential order into the writer classes, but you can write the values into the writers in
+ //a random order.
+ T0Writer << (500);
+ T1Writer << (3.0);
+ T2Writer << "splitted writter";
+
+ T2Writer << "splitting is cool";
+ T0Writer << (-30);
+ T1Writer << (-0.004);
+
+ // you can also write more often values into a writer and later into the other writers
+ // all data will be processed correctly into the right order.
+ T0Writer << (1);
+ T0Writer << (2);
+ T1Writer << (-0.4);
+ T0Writer << (3);
+ T2Writer << "again";
+ T0Writer << (4);
+ T1Writer << (-0.1);
+ T1Writer << (-0.2);
+ T2Writer << "and";
+ T1Writer << (-0.3);
+ T2Writer << "splitting";
+ T2Writer << "once again";
+
+ // again writing data of one tuple entry to the different writers in a random fashion.
+ T1Writer << (-0.004);
+ T2Writer << "splitting is cool";
+ T0Writer << (-30);
+
+}
+
+int main(void){
+ LOG_INFO_STREAM << library_string() << " -- " << terminal::Color::Red
+ << ExampleName << " example" << terminal::Color::Default << '\n';
+
+ //
+ // Testing CSVWriter.
+ //
+ LOG_INFO("Testing CSVWriter CSVTupleItrator implementation: ");
+
+ testtupleCSVWriter();
+
+ //
+ // Testing CSVReader.
+ //
+ LOG_INFO("Testing CSVReader CSVTupleIterator implementation: ");
+
+ testtupleCSVReader();
+
+ //
+ // Testing SplitTupleIterator.
+ //
+ LOG_INFO("Testing SplitTupleIterator: ");
+
+ testsplitTupleIterator();
+
+ //
+ // Testing SplitTupleWriter.
+ //
+ LOG_INFO("Testing SplitTupleWriter: ");
+ testsplitTupleWriter();
+
+
+ //
+ // info that user knows programm has finished.
+ //
+ LOG_INFO( "All tests finished.");
+
+
+ return 0;
+}
+
diff --git a/include/rosa/deluxe/DeluxeAgent.hpp b/include/rosa/deluxe/DeluxeAgent.hpp
index 7a94a57..3fde782 100644
--- a/include/rosa/deluxe/DeluxeAgent.hpp
+++ b/include/rosa/deluxe/DeluxeAgent.hpp
@@ -1,1479 +1,1453 @@
//===-- rosa/deluxe/DeluxeAgent.hpp -----------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/deluxe/DeluxeAgent.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2019
///
/// \brief Specialization of \c rosa::Agent for *agent* role of the *deluxe
/// interface*.
///
/// \see \c rosa::deluxe::DeluxeContext
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_DELUXE_DELUXEAGENT_HPP
#define ROSA_DELUXE_DELUXEAGENT_HPP
#include "rosa/core/Agent.hpp"
#include "rosa/deluxe/DeluxeAtoms.hpp"
#include "rosa/deluxe/DeluxeExecutionPolicy.h"
#include "rosa/deluxe/DeluxeTuple.hpp"
#include <map>
/// Local helper macros to deal with built-in types.
///
///@{
/// Creates function name for member functions in \c rosa::deluxe::DeluxeAgent.
///
/// \param N name suffix to use
#define DASLAVEHANDLERNAME(N) handleSlave_##N
/// Creates function name for member functions in \c rosa::deluxe::DeluxeAgent.
///
/// \param N name suffix to use
#define DAMASTERHANDLERNAME(N) handleMaster_##N
/// Defines member functions for handling messages from *slaves* in
/// \c rosa::deluxe::DeluxeAgent.
///
/// \see \c DeluxeAgentInputHandlers
///
/// \note No pre- and post-conditions are validated directly by these functions,
/// they rather rely on \c rosa::deluxe::DeluxeAgent::saveInput to do that.
///
/// \param T the type of input to handle
/// \param N name suffix for the function identifier
#define DASLAVEHANDLERDEFN(T, N) \
void DASLAVEHANDLERNAME(N)(atoms::Slave, id_t SlaveId, token_size_t Pos, \
T Value) noexcept { \
saveInput(SlaveId, Pos, Value); \
}
/// Defines member functions for handling messages from *master* in
/// \c rosa::deluxe::DeluxeAgent.
///
/// \see \c DeluxeAgentMasterInputHandlers
///
/// \note No pre- and post-conditions are validated directly by these functions,
/// they rather rely on \c rosa::deluxe::DeluxeAgent::saveMasterInput to do
/// that.
///
/// \param T the type of input to handle
/// \param N name suffix for the function identifier
#define DAMASTERHANDLERDEFN(T, N) \
void DAMASTERHANDLERNAME(N)(atoms::Master, id_t MasterId, token_size_t Pos, \
T Value) noexcept { \
saveMasterInput(MasterId, Pos, Value); \
}
/// Convenience macro for \c DASLAVEHANDLERDEFN with identical arguments.
///
/// \see \c DASLAVEHANDLERDEFN
///
/// This macro can be used instead of \c DASLAVEHANDLERDEFN if the actual value
/// of \p T can be used as a part of a valid identifier.
///
/// \param T the type of input to handle
#define DASLAVEHANDLERDEF(T) DASLAVEHANDLERDEFN(T, T)
/// Convenience macro for \c DAMASTERHANDLERDEFN with identical arguments.
///
/// \see \c DAMASTERHANDLERDEFN
///
/// This macro can be used instead of \c DAMASTERHANDLERDEFN if the actual value
/// of \p T can be used as a part of a valid identifier.
///
/// \param T the type of input to handle
#define DAMASTERHANDLERDEF(T) DAMASTERHANDLERDEFN(T, T)
/// Results in a \c THISMEMBER reference to a member function defined by
/// \c DASLAVEHANDLERDEFN.
///
/// Used in the constructor of \c rosa::deluxe::DeluxeAgent to initialize super
/// class \c rosa::Agent with member function defined by \c DASLAVEHANDLERDEFN.
///
/// \see \c DASLAVEHANDLERDEFN, \c THISMEMBER
///
/// \param N name suffix for the function identifier
#define DASLAVEHANDLERREF(N) THISMEMBER(DASLAVEHANDLERNAME(N))
/// Results in a \c THISMEMBER reference to a member function defined by
/// \c DAMASTERHANDLERDEFN.
///
/// Used in the constructor of \c rosa::deluxe::DeluxeAgent to initialize super
/// class \c rosa::Agent with member function defined by \c DAMASTERHANDLERDEFN.
///
/// \see \c DAMASTERHANDLERDEFN, \c THISMEMBER
///
/// \param N name suffix for the function identifier
#define DAMASTERHANDLERREF(N) THISMEMBER(DAMASTERHANDLERNAME(N))
///@}
namespace rosa {
namespace deluxe {
/// Specialization of \c rosa::Agent for *agent* role of the *deluxe interface*.
///
/// \see \c rosa::deluxe::DeluxeContext
///
/// \invariant There is a compatible *execution policy* set, all input-related
/// container objects have a size matching \c
/// rosa::deluxe::DeluxeAgent::NumberOfInputs, thus having a corresponding entry
/// for each input. \c rosa::deluxe::DeluxeAgent::NumberOfMasterOutputs matches
/// \c rosa::deluxe::DeluxeAgent::NumberOfInputs. All master-output-related
/// container objects have a size matching \c
/// rosa::deluxe::DeluxeAgent::NumberOfMasterOutputs. Types and type-related
/// information of input and master-output values are consistent throughout all
/// the input-related and master-output-related containers, respectively. The
/// actual values in \c rosa::deluxe::DeluxeAgent::InputNextPos and \c
/// rosa::deluxe::DeluxeAgent::MasterInputNextPos are valid with respect to the
/// corresponding types. No *slave* is registered at more than one input
/// position. *Slave* registrations and corresponding reverse lookup
/// information are consistent.
///
/// \see Definition of \c rosa::deluxe::DeluxeAgent::inv on the class invariant
///
/// \note All member functions validate the class invariant as part of their
/// precondition. Moreover, non-const functions validate the invariant before
/// return as their postcondition.
class DeluxeAgent : public Agent {
/// Checks whether \p this object holds the class invariant.
///
/// \see Invariant of the class \c rosa::deluxe::DeluxeAgent
///
/// \return if \p this object holds the class invariant
bool inv(void) const noexcept;
/// The \c rosa::deluxe::DeluxeExecutionPolicy that controls the execution of
/// \c this object.
std::unique_ptr<DeluxeExecutionPolicy> ExecutionPolicy;
public:
/// The type of values produced by \p this object.
///
/// That is the types of values \p this object sends to its *master* in a \c
/// rosa::deluxe::DeluxeTUple.
///
/// \see \c rosa::deluxe::DeluxeAgent::master
const Token OutputType;
/// Number of inputs processed by \p this object.
const size_t NumberOfInputs;
/// The type of values \p this object processes from its *master*.
///
/// That is the types of values \p this object receives from its *master* in a
/// \c rosa::deluxe::DeluxeTuple.
///
/// \see \c rosa::deluxe::DeluxeAgent::master
const Token MasterInputType;
/// Number of outputs produces by \p this object for its *slaves*.
///
/// \note This values is equal to \c
/// rosa::deluxe::DeluxeAgent::NumberOfInputs.
///
/// \see \c rosa::deluxe::DeluxeAgent::slave.
const size_t NumberOfMasterOutputs;
private:
/// Types of input values produced by *slaves* of \p this object.
///
/// \note The \c rosa::Token values stored correspond to \c
/// rosa::deluxe::DeluxeTuple instances at each argument position. The \c
/// rosa::TypeNumber values from the stored \c rosa::Token values match the
/// corresponding values in \c rosa::deluxe::DeluxeAgent::InputValues in
/// order.
///
/// \note The position of a \c rosa::Token in the \c std::vector indicates
/// which argument of \p this object's processing function it belongs to. See
/// also \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
const std::vector<Token> InputTypes;
/// Indicates which element of an input is expected from any particular
/// *slave*.
///
/// The *slave* is supposed to send one \c rosa::deluxe::DeluxeTuple value
/// element by element in their order of definition. This member field tells
/// the element at which position in the tuple should be received next from
/// the *slave* at a given position.
///
/// \p this object is supposed to be triggered only when input values has been
/// received completely, that is all values in the field should hold the value
/// `0`.
///
/// \see \c rosa::deluxe::DeluxeAgent::handleTrigger
/// \c rosa::deluxe::DeluxeAgent::saveInput
std::vector<token_size_t> InputNextPos;
/// Indicates whether any particular input value has been changed since the
/// last trigger received from the system.
///
/// All the flags are reset to \c false upon handling a trigger and then set
/// to \c true by \c rosa::deluxe::DeluxeAgent::saveInput when storing a new
/// input value in \c rosa::deluxe::DeluxeAgent::InputValues.
///
/// \note The position of a flag in the \c std::vector indicates which
/// argument of \p this object's processing function it belongs to. See also
/// \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
std::vector<bool> InputChanged;
- /// Tells at which position in \c rosa::deluxe::DeluxeAgent::InputValues the
- /// input from any particular *slave* starts.
- ///
- /// \note A value in the vector corresponds to the *slave* at the same
- /// position and it is the sum of the elements of input values from *slaves*
- /// at previous positions.
- ///
- /// \see \c rosa::deluxe::DeluxeAgent::saveInput
- const std::vector<token_size_t> InputStorageOffsets;
-
/// Stores the actual input values.
///
/// \note The types of stored values match the corresponding
/// \c rosa::TypeNumber values (in \c rosa::Token in order) in \c
/// rosa::deluxe::DeluxeAgent::InputTypes.
///
- /// \note The position of a value in the \c rosa::AbstractTokenizedStorage
- /// indicates which element of the tuple of which argument of \p this object's
- /// processing function it is. See also \c
- /// rosa::deluxe::DeluxeAgent::DeluxeAgent.
- const std::unique_ptr<AbstractTokenizedStorage> InputValues;
+ /// \note The position of a \c rosa::AbstractTokenizedStorage in the \c
+ /// std::vector indicates which argument of \p this object's processing
+ /// function the tuple is; and the position of the value in the \c
+ /// rosa::AbstractTokenizedStorage indicates which element of that tuple the
+ /// value is. See also \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
+ const std::vector<std::unique_ptr<AbstractTokenizedStorage>> InputValues;
/// Indicates which element of the master-input is expected from the *master*.
///
/// The *master* is supposed to send one \c rosa::deluxe::DeluxeTuple value
/// element by element in their order of definition. This member field tells
/// the element at which position should be received next.
///
/// \p this object is supposed to be triggered only when a complete
/// master-input has been received, that is the field should hold the value
/// `0`.
///
/// \see \c rosa::deluxe::DeluxeAgent::handleTrigger
/// \c rosa::deluxe::DeluxeAgent::saveMasterInput
token_size_t MasterInputNextPos;
/// Indicates whether the input value from the *master* has been changed since
/// the last trigger received from the system.
///
/// The flag is reset to \c false upon handling a trigger and then set to \c
/// true by \c rosa::deluxe::DeluxeAgent::saveMasterInput when storig a new
/// input value in \c rosa::deluxe::DeluxeAgent::MasterInputValue.
bool MasterInputChanged;
/// Stores the actual input value from *master*.
///
/// \note The type of the stored value matches the types indicated by \c
/// rosa::deluxe::DeluxeAgent::MasterInputType.
const std::unique_ptr<AbstractTokenizedStorage> MasterInputValue;
/// Types of output values produced by \p this object for its *slaves*.
///
/// That is the types of values \p this object sends to its *slaves* in a \c
/// rosa::deluxe::DeluxeTuple.
///
/// \note The position of a type in the \c std::vector indicates which
/// *slave* of \p this object the type belongs to. See also
/// \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
const std::vector<Token> MasterOutputTypes;
/// Alias for function objects used as trigger handler for
/// \c rosa::deluxe::DeluxeAgent.
///
/// \note The function used for \c H is to be \c noexcept.
///
/// \see \c rosa::deluxe::DeluxeAgent::FP
using H = std::function<void(void)>;
/// Handles trigger from the system.
///
/// The actual functions processing *slave* and *master* inputs and generating
/// optional output to *master* and *slaves* are captured in a lambda
/// expression that is in turn wrapped in a \c std::function object. The
/// lambda expression calls the master-input processing function with the
/// actual master-input data and sends its result -- if any -- to *slaves* by
/// calling \c rosa::deluxe::DeluxeAgent::handleMasterOutputs; then calls the
/// input processing function with the actual input data and sends its result
/// -- if any -- to *master* by calling \c
/// rosa::deluxe::DeluxeAgent::sendToMaster and *slaves* by calling \c
/// rosa::deluxe::DeluxeAgent::handleMasterOutputs. Also, all the flags stored
/// in \c rosa::deluxe::DeluxeAgent::InputChanged and \c
/// rosa::deluxe::DeluxeAgent::MasterInputChanged are reset when the current
/// values are processed. The function \c
/// rosa::deluxe::DeluxeAgent::handleTrigger needs only to call the
/// function object.
///
/// \see \c
/// rosa::deluxe::DeluxeAgent::triggerHandlerFromProcessingFunctions
const H FP;
/// The *master* to send values to.
///
/// \note *Masters* are set dynamically, hence it is possible that a
/// \c rosa::deluxe::DeluxeAgent instance does not have any *master* at a
/// given moment.
Optional<AgentHandle> Master;
/// The *slaves* sending input to \p this object.
///
/// \note The position of a *slave* in the \c std::vector indicates which
/// argument of \p this object's processing function it belongs to. See also
/// \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
///
/// \note *Slaves* are set dynamically, hence it is possible that a
/// \c rosa::deluxe::DeluxeAgent instance does have input positions without
/// any *slave* associated to them.
///
/// \note Reverse lookup information is maintained in
/// \c rosa::deluxe::DeluxeAgent::SlaveIds, which is to be kept in sync with
/// the *slaves* stored here.
std::vector<Optional<AgentHandle>> Slaves;
/// Associates \c rosa::id_t values to corresponding indices of registered
/// *slaves*.
///
/// \see \c rosa::deluxe::DeluxeAgent::Slaves
std::map<id_t, size_t> SlaveIds;
/// Tells the unique identifier of the *master* of \p this object, if any
/// registered.
///
/// \return the unique identifier of the *master*
///
/// \pre A *master* is registered for \p this object: \code
/// Master
/// \endcode
id_t masterId(void) const noexcept;
/// Tells whether types stored in \c rosa::TypeList \p As match the input
/// types of \p this object.
///
/// \tparam As \c rosa::TypeList containing types to match against values in
/// \c rosa::deluxe::DeluxeAgent::InputTypes
///
/// \note Instatiation of the template fails if \p As is not \c
/// rosa::TypeList.
///
/// \return if types in \p As are instances of \c rosa::deluxe::DeluxeTuple
/// and their types match \c rosa::Token values stored in \c
/// rosa::deluxe::DeluxeAgent::InputTypes
template <typename As> bool inputTypesMatch(void) const noexcept;
/// Tells whether types stored in \c rosa::TypeList \p Ts match the
/// master-output types of \p this object.
///
/// \tparam Ts \c rosa::TypeList containing types to match against values in
/// \c rosa::deluxe::DeluxeAgent::MasterOutputTypes
///
/// \note Instatiation of the template fails if \p As is not \c
/// rosa::TypeList.
///
/// \return if types in \p Ts match \c rosa::Token and in turn \c
/// rosa::TypeNumber values stored in \c
/// rosa::deluxe::DeluxeAgent::MasterOutputTypes
template <typename Ts> bool masterOutputTypesMatch(void) const noexcept;
/// Gives the current input value for slave position \p Pos.
///
/// \tparam Pos slave position to get input value for
/// \tparam Ts types of elements of the input value
/// \tparam S0 indices for accessing elements of the input value
///
/// \note The arguments provide types and indices statically as template
/// arguments \p Ts... \p S0..., respectively, so their actual values are
/// ignored.
///
/// \return current input value for slave position \p Pos
///
/// \pre Statically, the provided indices \p S0... match the length of \p
/// Ts...: \code
/// sizeof...(Ts) == sizeof...(S0)
/// \endcode Dynamically, \p Pos is a valid slave position and type arguments
/// \p Ts... match the corresponding input value: \code
/// Pos < NumberOfInputs && DeluxeTuple<Ts...>::TT == InputTypes[Pos]
/// \endcode
template <size_t Pos, typename... Ts, size_t... S0>
DeluxeTuple<Ts...> prepareInputValueAtPos(TypeList<Ts...>, Seq<S0...>) const
noexcept;
/// Gives an \c std::tuple containing the current input values and their
/// change flags so that they can be used for the processing function.
///
/// \tparam As types of the input values
/// \tparam S0 indices for accessing input values and their change flags
///
/// \note The only argument provides indices statically as template arguments
/// \p S0..., so its actual value is ignored.
///
/// \return current input values and their change flags prepared for invoking
/// the processing function with them
///
/// \pre Statically, all type arguments \p As... are instances of \c
/// rosa::deluxe::DeluxeTuple and the provided indices \p S0... match the
/// length of \p As...: \code
/// TypeListAllDeluxeTuple<TypeList<As...>>::Value &&
/// sizeof...(As) == sizeof...(S0)
/// \endcode Dynamically, type arguments \p As... match the input types of \p
/// this object: \code
/// inputTypesMatch<TypeList<As...>>()
/// \endcode
template <typename... As, size_t... S0>
std::tuple<std::pair<As, bool>...> prepareCurrentInputs(Seq<S0...>) const
noexcept;
/// Invokes a processing function matching the input, output, and
/// master-output types of \p this object with actual arguments provided in a
/// \c std::tuple.
///
/// \note \p Args providing the actual arguments for \p F is to be created by
/// \c rosa::deluxe::DeluxeAgent::prepareCurrentInputs.
///
/// \tparam T output type of the processing function
/// \tparam Ts types of master-output values of the processing function
/// \tparam As types of inputs for the processing function
/// \tparam S0 indices starting with `0` for extracting actual arguments from
/// \p Args
///
/// \param F the processing function to invoke
/// \param Args the actual arguments to invoke \p F with
///
/// \note The last argument provides indices statically as template arguments
/// \p S0..., so its actual value is ignored.
///
/// \return the result of \p F for actual arguments \p Args
///
/// \pre The provided sequence of indices \p S0... constitutes a proper
/// sequence for extracting all actual arguments for
/// \p F from \p Args: \code
/// sizeof...(As) == sizeof...(S0)
/// \endcode
template <typename T, typename... Ts, typename... As, size_t... S0>
static std::tuple<Optional<T>, Optional<Ts>...>
invokeWithTuple(std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)>
F,
const std::tuple<std::pair<As, bool>...> Args,
Seq<S0...>) noexcept;
/// Handles a master-output value for a particular *slave* position.
///
/// \p Value is a \c rosa::Optional resulted by a processing function and
/// contains a master-output value for the *slave* at position \p Pos. The
/// function takes the master-output value and sends its actual value, if any,
/// to the corresponding *slave*.
///
/// \note A master-output of type \c rosa::deluxe::EmptyDeluxeTuple indicates
/// no actual output and hence no message is generated for a position whose
/// corresponding master-output type is \c rosa::deluxe::EmptyDeluxeTuple.
///
/// \note The function provides position-based implementation for \c
/// rosa::deluxe::DeluxeAgent::handleMasterOutputs.
///
/// \tparam Pos the position of the master-output to send \p Value for
/// \tparam Ts types of elements in \p Value
///
/// \param Value \c rosa::deluxe::DeluxeTuple resulted by the processing
/// function for *slave* position \p Pos
///
/// \pre \p Pos is a valid master-output position and \p Value matches the
/// master-output type of \p this object at position \p Pos: \code
/// Pos < NumberOfMasterOutputs &&
/// DeluxeTuple<Ts...>::TT == MasterOutputTypes[Pos]
/// \endcode
template <size_t Pos, typename... Ts>
void
handleMasterOutputAtPos(const Optional<DeluxeTuple<Ts...>> &Value) noexcept;
/// Handles master-output values from \p Output.
///
/// \p Output is a \c std::tuple resulted by a processing function and
/// contains master-output values starting at position \p Offset. The function
/// takes master-output values and sends each actual value to the
/// corresponding *slave*.
///
/// \tparam Offset index of the first master-output value in \p Output
/// \tparam Ts output types stored in \p Output
/// \tparam S0 indices starting with `0` for extracting master-output values
/// from \p Output
///
/// \note Instantiation fails if any of the type arguments \p Ts... starting
/// at position \p Offset is not an instance of \c rosa::deluxe::DeluxeTuple
/// or the number of types \p Ts... is not consistent with the other template
/// arguments.
///
/// \param Output \c std::tuple resulted by a processing function
///
/// \pre Statically, type arguments \p Ts... starting at position \p Offset
/// are instances of \c rosa::deluxe::DeluxeTuple and the number of types \p
/// Ts... is consistent with the other template arguments: \code
/// TypeListAllDeluxeTuple<
/// typename TypeListDrop<Offset, TypeList<Ts...>>::Type>::Value &&
/// sizeof...(Ts) == Offset + sizeof...(S0)
/// \endcode Dynamically, \p Output matches the master-output types \p this
/// object was created with and the provided sequence of indices \p S0...
/// constitues a proper sequence for extracting all master-output values from
/// \p Output: \code
/// masterOutputTypesMatch<typename TypeListDrop<Offset,
/// TypeList<Ts...>>::Type>() &&
/// sizeof...(S0) == NumberOfMasterOutputs
/// \endcode
template <size_t Offset, typename... Ts, size_t... S0>
void handleMasterOutputs(const std::tuple<Optional<Ts>...> &Output,
Seq<S0...>) noexcept;
/// Wraps processing functions into a trigger handler.
///
/// \see \c rosa::deluxe::DeluxeAgent::FP
///
/// \note The function cannot be const qualified because the lambda
/// expression defined in it needs to capture \p this object by a non-const
/// reference
///
/// \tparam MTs types of elements of master-input processed by \p MF
/// \tparam T type of output
/// \tparam Ts types of master-output values
/// \tparam As types of input values
/// \tparam S0 indices for accessing master-input values
///
/// \note Instantiation fails if any of the type arguments \p T, \p Ts...,
/// and \p As... is not an instance of \c rosa::deluxe::DeluxeTuple.
///
/// \param MF function processing master-input and generating output
/// \param F function processing inputs and generating output
///
/// \note The last argument provides indices statically as template
/// arguments \p S0..., so its actual value is ignored.
///
/// \note A master-input type of \c rosa::deluxe::EmptyDeluxeTuple indicates
/// that \p this object does not receive master-input, \p MF is never called
/// if \p MTs is empty.
///
/// \return trigger handler function based on \p F and \p MF
///
/// \pre Statically, type arguments \p T, \p Ts..., and \p As... are
/// instances of \c rosa::deluxe::DeluxeTuple and the indices match
/// master-input elements: \code
/// TypeListAllDeluxeTuple<TypeList<T, Ts..., As...>>::Value &&
/// sizeof...(MTs) == sizeof...(S0)
/// \endcode Dynamically, template arguments \p MTs..., \p T, \p Ts..., and
/// \p As... match the corresponding types \p this object was created with:
/// \code
/// MasterInputType == DeluxeTuple<MTs...>::TT && OutputType == T::TT &&
/// inputTypesMatch<TypeList<As...>>() &&
/// masterOutputTypesMatch<TypeList<Ts...>>()
/// \endcode
template <typename... MTs, typename T, typename... Ts, typename... As,
size_t... S0>
H triggerHandlerFromProcessingFunctions(
std::function<std::tuple<Optional<Ts>...>(
std::pair<DeluxeTuple<MTs...>, bool>)> &&MF,
std::function<
std::tuple<Optional<T>, Optional<Ts>...>(std::pair<As, bool>...)> &&F,
Seq<S0...>) noexcept;
public:
/// Creates a new instance.
///
/// The constructor instantiates the base-class with functions to handle
/// messages as defined for the *deluxe interface*.
///
/// The function \p F generates a \c std::tuple of values: the first value is
/// the output for the *master* and the rest is for the *slaves*. All output
/// generated by the function is optional as an agent may decide not to output
/// anything at some situation.
///
/// \todo Enforce \p F and \p MF do not potentially throw exception.
///
/// \tparam MT type of master-input handled by \p MF
/// \tparam T type of output of \p F
/// \tparam Ts type of master-output values of \p F and \p MF
/// \tparam As types of input values of \p F
///
/// \note Instantiation fails if any of the type arguments \p MT, \p T, \p
/// Ts..., and \p As... is not an instance of \c rosa::deluxe::DeluxeTuple or
/// any of \p T and \p As... is \c rosa::deluxe::EmptyDeluxeTuple or the
/// number of inputs and master-outputs are not equal.
///
/// \note If \p MT is \c rosa::deluxe::EmptyDeluxeTuple, the constructed
/// object does not receive master-input. Similarly, if any of \p Ts... is \c
/// rosa::deluxe::EmptyDeluxeTuple, the constructed object does not generated
/// master-output for the corresponding *slave* position.
///
/// \param Kind kind of the new \c rosa::Unit instance
/// \param Id unique identifier of the new \c rosa::Unit instance
/// \param Name name of the new \c rosa::Unit instance
/// \param S \c rosa::MessagingSystem owning the new instance
/// \param MF function to process master-input values and generate
/// master-output with
/// \param F function to process input values and generate output and
/// master-output with
///
/// \pre Statically, all the type arguments \p MT, \p T, \p Ts..., and \p
/// As... are instances of \c rosa::deluxe::DeluxeTuple, with \p T and \p
/// As... containing at least one element, and the number of input and
/// master-output types are equal: \code
/// TypeListAllDeluxeTuple<TypeList<MT, T, Ts..., As...>::Value &&
/// T::Length > 0 && (true && ... && As::Length > 0) &&
/// sizeof...(Ts) == sizeof...(As)
///\endcode
/// Dynamically, the instance is created as of kind \c
/// rosa::deluxe::atoms::AgentKind: \code
/// Kind == rosa::deluxe::atoms::AgentKind
/// \endcode
///
/// \see \c rosa::deluxe::DeluxeTuple
template <typename MT, typename T, typename... Ts, typename... As,
typename = std::enable_if_t<
TypeListAllDeluxeTuple<TypeList<MT, T, Ts..., As...>>::Value &&
(T::Length > 0) && (true && ... && (As::Length > 0)) &&
sizeof...(Ts) == sizeof...(As)>>
DeluxeAgent(
const AtomValue Kind, const id_t Id, const std::string &Name,
MessagingSystem &S,
std::function<std::tuple<Optional<Ts>...>(std::pair<MT, bool>)> &&MF,
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)> &&F) noexcept;
/// Destroys \p this object.
~DeluxeAgent(void) noexcept;
/// Returns the current execution policy of \p this object.
///
/// \see \c rosa::deluxe::DeluxeExecutionPolicy
///
/// \note The returned reference is valid only as long as \c
/// rosa::deluxe::DeluxeAgent::setExecutionPolicy() is not called and \p this
/// object is not destroyed.
///
/// \return \c rosa::deluxe::DeluxeAgent::ExecutionPolicy
const DeluxeExecutionPolicy &executionPolicy(void) const noexcept;
/// Sets the current execution policy of \p this object to \p EP.
///
/// \see \c rosa::deluxe::DeluxeExecutionPolicy
///
/// \note \p EP is set only if it can handle \p this object.
///
/// \param EP the new execution policy for \p this object
///
/// \return if \p EP was successfully set for \p this object.
bool setExecutionPolicy(std::unique_ptr<DeluxeExecutionPolicy> &&EP) noexcept;
/// The *master* of \p this object, if any is registered.
///
/// \see \c rosa::deluxe::DeluxeAgent::registerMaster
///
/// \return the *master* registered for \p this object
Optional<AgentHandle> master(void) const noexcept;
/// Registers a *master* for \p this object.
///
/// The new *master* is registered by overwriting the reference to any
/// already registered *master*. One can clear the registered reference by
/// passing an *empty* \c rosa::Optional object as actual argument.
///
/// \note The role of the referred *master* is validated by checking its
/// *kind*.
///
/// \note Any call to \c rosa::deluxe::DeluxeAgent::registerMaster should be
/// paired with a corresponding call of \c
/// rosa::deluxe::DeluxeAgent::registerSlave, which validates that
/// input/output types of master and slave matches.
///
/// \param _Master the *master* to register
///
/// \pre \p _Master is empty or of kind \c rosa::deluxe::atoms::AgentKind:
/// \code
/// !_Master || unwrapAgent(*_Master).Kind == rosa::deluxe::atoms::AgentKind
/// \endcode
void registerMaster(const Optional<AgentHandle> _Master) noexcept;
/// Tells the types of values consumed from the *slave* at a position.
///
/// That is the type of values \p this object expect to be sent to it in a \c
/// rosa::deluxe::DeluxeTuple by its *slave* registered at position \p Pos.
///
/// \see \c rosa::deluxe::DeluxeAgent::slave
///
/// \param Pos position of *slave*
///
/// \return \c rosa::Token representing the types of values consumed from
/// the *slave* at position \p Pos
///
/// \pre \p Pos is a valid index of input: \code
/// Pos < NumberOfInputs
/// \endcode
Token inputType(const size_t Pos) const noexcept;
/// Tells the types of values produced for the *slave* at a position.
///
/// That is the types of values \p this object potentially sends in a \c
/// rosa::deluxe::DeluxeTuple to its *slave* registered at position \p Pos.
///
/// \see \c rosa::deluxe::DeluxeAgent::slave
///
/// \param Pos position of *slave*
///
/// \return \c rosa::Token representing the types of values produced for
/// the *slave* at position \p Pos
///
/// \pre \p Pos is a valid index of input: \code
/// Pos < NumberOfMasterOutputs
/// \endcode
Token masterOutputType(const size_t Pos) const noexcept;
/// The *slave* of \p this object registered at a position, if any.
///
/// \see \c rosa::deluxe::DeluxeAgent::registerSlave
///
/// \param Pos position of *slave*
///
/// \return the *slave* registered for \p this object at position \p Pos
///
/// \pre \p Pos is a valid index of input: \code
/// Pos < NumberOfInputs
/// \endcode
Optional<AgentHandle> slave(const size_t Pos) const noexcept;
/// Registers a *slave* for \p this object at a position.
///
/// The new *slave* is registered by overwriting the reference to any already
/// registered *slave* at position \p Pos. One can clear the registered
/// reference by passing an *empty* \c rosa::Optional object as actual
/// argument. If \p Slave is already registered for another position, the
/// other position gets cleared.
///
/// \note The role of the referred *slave* is validated by checking its
/// *kind*.
///
/// \note The type of values produced by the referred *slave* is validated by
/// matching its `OutputType` against the corresponding value in
/// \c rosa::deluxe::DeluxeAgent::InputTypes.
///
/// \note The type of master-input values processed by the referred *slave* is
/// validated by matching its `MasterInputType` against the corresponding
/// value in \c rosa::deluxe::DeluxeAgent::MasterOutputTypes.
///
/// \param Pos position to register \p Slave at
/// \param Slave the *slave* to register
///
/// \pre \p Pos is a valid index of input, \p Slave is empty or of kind
/// \c rosa::deluxe::atoms::AgentKind or \c rosa::deluxe::atoms::SensorKind,
/// and \p Slave -- if not empty -- produces values of types matching the
/// expected input type at position \p Pos and processes values of types
/// matching the produced master-output type at position \p Pos:
/// \code
/// Pos < NumberOfInputs &&
/// (!Slave ||
/// (unwrapAgent(*Slave.)Kind == rosa::deluxe::atoms::SensorKind &&
/// static_cast<const DeluxeSensor &>(unwrapAgent(*Slave)).OutputType ==
/// InputTypes[Pos] &&
/// (emptyToken(MasterOutputTypes[Pos]) ||
/// static_cast<const DeluxeSensor &>(unwrapAgent(*Slave)).MasterInputType
/// == MasterOutputTypes[Pos])) ||
/// (unwrapAgent(*Slave).Kind == rosa::deluxe::atoms::AgentKind &&
/// static_cast<const DeluxeAgent &>(unwrapAgent(*Slave)).OutputType ==
/// InputTypes[Pos] &&
/// (emptyToken(MasterOutputTypes[Pos]) ||
/// static_cast<const DeluxeAgent &>(unwrapAgent(*Slave)).MasterInputType ==
/// MasterOutputTypes[Pos])))
/// \endcode
void registerSlave(const size_t Pos,
const Optional<AgentHandle> Slave) noexcept;
/// Tells the position of a registered *slave*.
///
/// \param Slave \c rosa::AgentHandle for the *slave* to check
///
/// \return position of \p Slave if it is registered and found,
/// \c rosa::deluxe::DeluxeAgent::NumberOfInputs otherwise.
size_t positionOfSlave(AgentHandle Slave) const noexcept;
private:
/// Sends a value to the *master* of \p this object.
///
/// \p Value is getting sent to \c rosa::deluxe::DeluxeAgent::Master if it
/// contains a valid handle for a \c rosa::deluxe::DeluxeAgent. The function
/// does nothing otherwise.
///
/// The elements from \p Value are sent one by one in separate messages to the
/// *master*.
///
/// \tparam Ts types of the elements in \p Value
/// \tparam S0 indices for accessing elements of \p Value
///
/// \param Value value to send
///
/// \note The second argument provides indices statically as template
/// arguments \p S0..., so its actual value is ignored.
///
/// \pre Statically, the indices match the elements: \code
/// sizeof...(Ts) == sizeof...(S0)
/// \endcode Dynamically, \p Ts match \c
/// rosa::deluxe::DeluxeiAgent::OutputType: \code
/// OutputType == TypeToken<Ts...>::Value
/// \endcode
template <typename... Ts, size_t... S0>
void sendToMaster(const DeluxeTuple<Ts...> &Value, Seq<S0...>) noexcept;
/// Sends a value to a *slave* of \p this object at position \p Pos.
///
/// \p Value is getting sent to \c rosa::deluxe::DeluxeAgent::Slaves[Pos] if
/// it contains a valid handle. The function does nothing otherwise.
///
/// The elements from \p Value are sent one by one in separate messages to the
/// *slave*.
///
/// \tparam Ts types of the elements in \p Value
/// \tparam S0 indices for accessing elements of \p Value
///
/// \param Pos the position of the *slave* to send \p Value to
/// \param Value value to send
///
/// \pre Statically, the indices match the elements: \code
/// sizeof...(Ts) == sizeof...(S0)
/// \endcode Dynamically, \p Pos is a valid *slave* position and \p Ts match
/// \c rosa::deluxe::DeluxeiAgent::MasterOutputTypes[Pos]: \code
/// Pos < NumberOfMasterOutputs &&
/// MasterOutputTypes[Pos] == TypeToken<Ts...>::Value
/// \endcode
template <typename... Ts, size_t... S0>
void sendToSlave(const size_t Pos, const DeluxeTuple<Ts...> &Value,
Seq<S0...>) noexcept;
/// Generates the next output by processing current input values upon trigger
/// from the system.
///
/// Executes \c rosa::deluxe::DeluxeAgent::FP.
///
/// \note The only argument is a \c rosa::AtomConstant, hence its actual
/// value is ignored.
///
/// \pre Master-input and all input from *slaves* are supposed to be
/// completely received upon triggering: \code
/// MasterInputNextPos == 0 &&
/// std::all_of(InputNextPos.begin(), InputNextPos.end(),
/// [](const token_size_t &I){return I == 0;})
/// \endcode
void handleTrigger(atoms::Trigger) noexcept;
/// Stores a new input value from a *slave*.
///
/// The function stores \p Value at position \p Pos in \c
/// rosa::deluxe::DeluxeAgent::InputValues at the position associated to \p Id
/// in \c rosa::deluxe::DeluxeAgent::SlaveIds and also sets the corresponding
/// flag in \c rosa::deluxe::DeluxeAgent::InputChanged. The function also
/// takes care of checking and updating \c
/// rosa::deluxe::DeluxeSensor::MasterInputNextPos at the corresponding
/// position: increments the value and resets it to `0` when the last element
/// is received.
///
/// \note Utilized by member functions of group \c DeluxeAgentInputHandlers.
///
/// \tparam T type of input to store
///
/// \param Id unique identifier of *slave*
/// \param Pos position of the value in the \c rosa::deluxe::DeluxeTuple
/// \param Value the input value to store
///
/// \pre The *slave* with \p Id is registered, \p Pos is the expected
/// position of input from the *slave*, and the input from it is expected to
/// be of type \p T: \code
/// SlaveIds.find(Id) != SlaveIds.end() &&
/// Pos == InputNextPos[SlaveIds.find(Id)->second] &&
/// typeAtPositionOfToken(InputTypes[SlaveIds.find(Id)->second], Pos) ==
/// TypeNumberOf<T>::Value
/// \endcode
template <typename T>
void saveInput(id_t Id, token_size_t Pos, T Value) noexcept;
/// Stores a new input value from the *master*.
///
/// The function stores \p Value at position \p Pos in \c
/// rosa::deluxe::DeluxeAgent::MasterInputValue and also sets the
/// flag \c rosa::deluxe::DeluxeAgent::MasterInputChanged. The function also
/// takes care of checking and updating \c
/// rosa::deluxe::DeluxeAgent::MasterInputNextPos: increments its value and
/// reset to `0` when the last element is received.
///
/// \note Utilized by member functions of group \c
/// DeluxeAgentMasterInputHandlers.
///
/// \tparam T type of input to store
///
/// \param Id unique identifier of the *master*
/// \param Pos position of the value in the \c rosa::deluxe::DeluxeTuple
/// \param Value the input value to store
///
/// \pre The *master* with \p Id is registered, \p Pos is the expected
/// position of master-input, and the input from the *master* at position \p
/// Pos is expected to be of type \p T: \code
/// Master && masterId() == Id && Pos == MasterInputNextPos &&
/// typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf<T>::Value
/// \endcode
template <typename T>
void saveMasterInput(id_t Id, token_size_t Pos, T Value) noexcept;
/// \defgroup DeluxeAgentInputHandlers Input handlers of
/// rosa::deluxe::DeluxeAgent
///
/// Definition of member functions handling messages from *slaves* with
/// different types of input
///
/// A *master* generally needs to be prepared to deal with values of any
/// built-in type to handle messages from its *slaves*. Each type requires a
/// separate message handler, which are implemented by these functions. The
/// functions instantiate \c rosa::deluxe::DeluxeAgent::saveInput with the
/// proper template argument and pass the content of the message on for
/// processing.
///
/// \note The member functions in this group are defined by \c
/// DASLAVEHANDLERDEF.
///
/// \note Keep these definitions in sync with \c rosa::BuiltinTypes.
///
///@{
DASLAVEHANDLERDEF(AtomValue)
DASLAVEHANDLERDEF(int16_t)
DASLAVEHANDLERDEF(int32_t)
DASLAVEHANDLERDEF(int64_t)
DASLAVEHANDLERDEF(int8_t)
DASLAVEHANDLERDEFN(long double, long_double)
DASLAVEHANDLERDEFN(std::string, std__string)
DASLAVEHANDLERDEF(uint16_t)
DASLAVEHANDLERDEF(uint32_t)
DASLAVEHANDLERDEF(uint64_t)
DASLAVEHANDLERDEF(uint8_t)
DASLAVEHANDLERDEF(unit_t)
DASLAVEHANDLERDEF(bool)
DASLAVEHANDLERDEF(double)
DASLAVEHANDLERDEF(float)
/// @}
/// \defgroup DeluxeAgentMasterInputHandlers Master-input handlers of
/// rosa::deluxe::DeluxeAgent
///
/// Definition of member functions handling messages from the *master* with
/// different types of input
///
/// A *slave* generally needs to be prepared to deal with values of any
/// built-in type to handle messages from its *master*. Each type requires a
/// separate message handler, which are implemented by these functions. The
/// functions instantiate \c rosa::deluxe::DeluxeAgent::saveMasterInput with
/// the proper template argument and pass the content of the message on for
/// processing.
///
/// \note The member functions in this group are defined by \c
/// DAMASTERHANDLERDEF.
///
/// \note Keep these definitions in sync with \c rosa::BuiltinTypes.
///
///@{
DAMASTERHANDLERDEF(AtomValue)
DAMASTERHANDLERDEF(int16_t)
DAMASTERHANDLERDEF(int32_t)
DAMASTERHANDLERDEF(int64_t)
DAMASTERHANDLERDEF(int8_t)
DAMASTERHANDLERDEFN(long double, long_double)
DAMASTERHANDLERDEFN(std::string, std__string)
DAMASTERHANDLERDEF(uint16_t)
DAMASTERHANDLERDEF(uint32_t)
DAMASTERHANDLERDEF(uint64_t)
DAMASTERHANDLERDEF(uint8_t)
DAMASTERHANDLERDEF(unit_t)
DAMASTERHANDLERDEF(bool)
DAMASTERHANDLERDEF(double)
DAMASTERHANDLERDEF(float)
/// @}
};
/// Anonymous namespace with implementation for \c
/// rosa::deluxe::DeluxeAgent::DeluxeAgent, \c
/// rosa::deluxe::DeluxeAgent::inputTypesMatch, and \c
/// rosa::deluxe::DeluxeAgent::masterOutputTypesMatch, consider it private.
namespace {
-/// Calculates storage offsets for values of \p Ts... stored in a \c
+/// Creates storages for data of types \p Ts... in a \c std::vector of \c
/// rosa::TokenizedStorage.
///
/// \note Utilized by \c rosa::deluxe::DeluxeAgnet::DeluxeAgent to initialize \c
-/// rosa::deluxe::DeluxeAgent::InputStorageOffsets.
+/// rosa::deluxe::DeluxeAgent::InputValues. That is due to not being able to use
+/// an initializer list directly; the initializer list always copies but \c
+/// std::unique_ptr is not copyable.
///
-/// \tparam Ts types whose offsets to calculate
-/// \tparam S0 indices for referring to positions in \p Ts...
+/// \tparam Ts types to create storages for
///
/// \note Instantiation fails if any of the type arguments \p Ts... is not an
/// instance of \c rosa::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
+/// \return \c std::vector with pointers for the created storage objects
///
/// \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)
+/// rosa::deluxe::DeluxeTuple: \code
+/// TypeListAllDeluxeTuple<TypeList<Ts...>>::Value
/// \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 <typename... Ts>
+std::vector<std::unique_ptr<AbstractTokenizedStorage>>
+makeInputStorages(void) noexcept {
+ std::vector<std::unique_ptr<AbstractTokenizedStorage>> InputStorages;
+ (InputStorages.push_back(
+ std::make_unique<typename TokenizedStorageForTypeList<
+ typename UnwrapDeluxeTuple<Ts>::Type>::Type>()),
+ ...);
+ return InputStorages;
}
/// Template \c struct whose specializations provide a recursive implementation
/// for \c TypesMatchList.
///
/// \tparam As types to match
template <typename... As> struct TypesMatchImpl;
/// Template specialization for the case, when at least one type is to
/// be matched and that is an instance of \c rosa::deluxe::DeluxeTuple.
///
/// \tparam Ts types of elements in the \c rosa::deluxe::DeluxeTuple to match
/// \tparam As further types to match
template <typename... Ts, typename... As>
struct TypesMatchImpl<DeluxeTuple<Ts...>, As...> {
/// Tells whether types \c rosa::deluxe::DeluxeTuple<Ts...> and \p As... match
/// \c rosa::Token values stored in \p Tokens starting at position \p Pos.
///
/// The function has got a recursive implementation: it matches the first
/// type \c rosa::deluxe::DeluxeTuple<Ts...> against \c rosa::Token at
/// position \p Pos of \p Tokens, then further types \p As... are matched
/// recursively starting at position \c (Pos + 1).
///
/// \param Tokens container of \c rosa::Token values to match types against
/// \param Pos position in \p Tokens to start matching at
///
/// \return if types \c rosa::deluxe::DeluxeTuple<Ts...> and \p As... match \c
/// rosa::Token values stored in \p Tokens starting at position \p Pos
static bool f(const std::vector<Token> &Tokens, size_t Pos) noexcept {
return Pos < Tokens.size() && TypeToken<Ts...>::Value == Tokens[Pos] &&
TypesMatchImpl<As...>::f(Tokens, Pos + 1);
}
};
/// Template specialization for the case, when at least one type is to
/// be matched and that is *not* an instance of \c rosa::deluxe::DeluxeTuple.
///
/// \tparam T first type to match
/// \tparam As further types to match
template <typename T, typename... As>
struct TypesMatchImpl<T, As...> {
/// Tells whether types \p T and \p As... match \c rosa::Token values stored
/// in \p Tokens starting at position \p Pos.
///
/// This specialization is used only when \p T is not an instance of \c
/// rosa::deluxe::DeluxeTuple, in which case the match is not successful.
///
/// \note The function takes two parameters to match the general signature but
/// the actual values are ignored.
///
/// \return `false`
static bool f(const std::vector<Token> &, size_t) noexcept { return false; }
};
/// Template specialization for the terminal case, when no type remains to
/// check.
template <> struct TypesMatchImpl<> {
/// Tells whether \p Pos is the number of values stored in \p Tokens.
///
/// In this terminal case, there is no more types to match because all the
/// types are supposed to be already matched successfully. The whole list of
/// types already matched is a complete match if it covers all values in
/// \p Tokens. That is true if \p Pos points exactly to the end of \p Tokens.
///
/// \param Tokens container of \c rosa::Token values to match types against
/// \param Pos position in \p Tokens to start matching at
///
/// \return if \p Pos is the number of values stored in \p Tokens
static bool f(const std::vector<Token> &Tokens, size_t Pos) noexcept {
return Pos == Tokens.size();
}
};
/// Template \c struct that provides an implementation for \c
/// rosa::deluxe::DeluxeAgent::inputTypesMatch and \c
/// rosa::deluxe::DeluxeAgent::masterOutputTypesMatch.
///
/// \note Match a list of types \p List against a \c std::vector of
/// \c rosa::Token values, \c Tokens, like \code
/// bool match = TypesMatchList<List>::f(Tokens);
/// \endcode
/// If any type in \c rosa::TypeList \p Listis not an instance of \c
/// rosa::deluxe::DeluxeTuple, the match gives a negative result.
///
/// \tparam List \c rosa::TypeList that contains types to match
template <typename List> struct TypesMatchList;
/// Template specialization implementing the feature.
///
/// \tparam As types to match
template <typename... As> struct TypesMatchList<TypeList<As...>> {
/// Tells whether types \p As... match \c rosa::Token values stored in \p
/// Tokens.
///
/// The function unwraps the types from \c rosa::TypeList and utilizes \c
/// TypesMatchImpl to do the check.
///
/// \param Tokens container of \c rosa::Token values to match types against
///
/// \return if types \p As... match \c rosa::Token values stored in \p Tokens
static bool f(const std::vector<Token> &Tokens) noexcept {
return TypesMatchImpl<As...>::f(Tokens, 0);
}
};
} // End namespace
template <typename As>
bool DeluxeAgent::inputTypesMatch(void) const noexcept {
return TypesMatchList<As>::f(InputTypes);
}
template <typename Ts>
bool DeluxeAgent::masterOutputTypesMatch(void) const noexcept {
return TypesMatchList<Ts>::f(MasterOutputTypes);
}
template <size_t Pos, typename... Ts, size_t... S0>
DeluxeTuple<Ts...> DeluxeAgent::prepareInputValueAtPos(TypeList<Ts...>,
Seq<S0...>) const
noexcept {
using T = DeluxeTuple<Ts...>;
STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent type arguments");
ASSERT(inv() && Pos < NumberOfInputs && T::TT == InputTypes[Pos]);
- const token_size_t StorageOffset = InputStorageOffsets[Pos];
-
// The below should hold because of the above, just leave it for sanity check.
ASSERT((true && ... &&
(static_cast<size_t>(static_cast<token_size_t>(S0)) == S0)));
+ const auto &SlaveInput = InputValues[Pos];
+
// 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)))...);
+ return T(*static_cast<const Ts *>(
+ SlaveInput->pointerTo(static_cast<token_size_t>(S0)))...);
}
template <typename... As, size_t... S0>
std::tuple<std::pair<As, bool>...>
DeluxeAgent::prepareCurrentInputs(Seq<S0...>) const noexcept {
STATIC_ASSERT(TypeListAllDeluxeTuple<TypeList<As...>>::Value,
"not tuple types");
STATIC_ASSERT(sizeof...(As) == sizeof...(S0), "inconsistent type arguments");
ASSERT(inv() && inputTypesMatch<TypeList<As...>>());
return std::make_tuple(std::make_pair(
prepareInputValueAtPos<S0>(typename UnwrapDeluxeTuple<As>::Type(),
seq_t<As::Length>()),
InputChanged[S0])...);
}
template <typename T, typename... Ts, typename... As, size_t... S0>
std::tuple<Optional<T>, Optional<Ts>...> DeluxeAgent::invokeWithTuple(
std::function<
std::tuple<Optional<T>, Optional<Ts>...>(std::pair<As, bool>...)>
F,
const std::tuple<std::pair<As, bool>...> Args, Seq<S0...>) noexcept {
STATIC_ASSERT(sizeof...(As) == sizeof...(S0),
"wrong number of type parameters");
return F(std::get<S0>(Args)...);
}
template <size_t Pos, typename... Ts>
void DeluxeAgent::handleMasterOutputAtPos(
const Optional<DeluxeTuple<Ts...>> &Value) noexcept {
using MOT = DeluxeTuple<Ts...>;
ASSERT(inv() && Pos < NumberOfMasterOutputs &&
MOT::TT == MasterOutputTypes[Pos]);
// Do not do anything for master-output of type \c
// rosa::deluxe::EmptyDeluxeTuple and when \p Value is empty.
if constexpr (!std::is_same<MOT, EmptyDeluxeTuple>::value) {
if (Value) {
sendToSlave(Pos, *Value, seq_t<MOT::Length>());
}
} else {
(void)Value;
}
ASSERT(inv());
}
template <size_t Offset, typename... Ts, size_t... S0>
void DeluxeAgent::handleMasterOutputs(const std::tuple<Optional<Ts>...> &Output,
Seq<S0...>) noexcept {
using MOTs = typename TypeListDrop<Offset, TypeList<Ts...>>::Type;
STATIC_ASSERT(TypeListAllDeluxeTuple<MOTs>::Value,
"not tuple type arguments");
STATIC_ASSERT(sizeof...(Ts) == Offset + sizeof...(S0),
"inconsistent arguments");
ASSERT(inv() && masterOutputTypesMatch<MOTs>() &&
sizeof...(S0) == NumberOfMasterOutputs);
// Handle each master-output position in a fold expression.
(handleMasterOutputAtPos<S0>(std::get<Offset + S0>(Output)), ...);
ASSERT(inv());
}
template <typename... MTs, typename T, typename... Ts, typename... As,
size_t... S0>
DeluxeAgent::H DeluxeAgent::triggerHandlerFromProcessingFunctions(
std::function<
std::tuple<Optional<Ts>...>(std::pair<DeluxeTuple<MTs...>, bool>)> &&MF,
std::function<
std::tuple<Optional<T>, Optional<Ts>...>(std::pair<As, bool>...)> &&F,
Seq<S0...>) noexcept {
using MT = DeluxeTuple<MTs...>;
STATIC_ASSERT((TypeListAllDeluxeTuple<TypeList<T, Ts..., As...>>::Value),
"not tuple type arguments");
STATIC_ASSERT(sizeof...(MTs) == sizeof...(S0), "inconsistent arguments");
ASSERT(MasterInputType == MT::TT && OutputType == T::TT &&
inputTypesMatch<TypeList<As...>>() &&
masterOutputTypesMatch<TypeList<Ts...>>());
return [ this, MF, F ]() noexcept {
// \note These indices work for both inputs and master-outputs.
using SlaveIndices = seq_t<sizeof...(As)>;
// Handle master-input.
// Do not do anything for master-input type \c
// rosa::deluxe::EmptyDeluxeTuple.
if (!std::is_same<MT, EmptyDeluxeTuple>::value) {
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " handles master-input."
<< std::endl;
// The assert must hold if \p this object was successfuuly constructed.
ASSERT((true && ... &&
(static_cast<size_t>(static_cast<token_size_t>(S0)) == S0)));
const auto MasterInputArg = std::make_pair(
// Get all elements of the tuple in a fold expression.
MT(*static_cast<const MTs *>(
MasterInputValue->pointerTo(static_cast<token_size_t>(S0)))...),
MasterInputChanged);
MasterInputChanged = false;
const std::tuple<Optional<Ts>...> MasterOutput = MF(MasterInputArg);
handleMasterOutputs<0>(MasterOutput, SlaveIndices());
}
// Handle inputs.
// Call the processing function only if \p ExecutionPolicy allows.
if (ExecutionPolicy->shouldProcess(InputChanged)) {
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " handles input."
<< std::endl;
const auto InputArgs = prepareCurrentInputs<As...>(SlaveIndices());
std::fill(InputChanged.begin(), InputChanged.end(), false);
const std::tuple<Optional<T>, Optional<Ts>...> Output =
invokeWithTuple(F, InputArgs, SlaveIndices());
const auto OutputToMaster = std::get<0>(Output);
if (OutputToMaster) {
sendToMaster(*OutputToMaster, seq_t<T::Length>());
}
handleMasterOutputs<1>(Output, SlaveIndices());
} else {
LOG_TRACE_STREAM << "DeluxeAgent " << Name << " skips input."
<< std::endl;
}
};
}
template <typename MT, typename T, typename... Ts, typename... As, typename>
DeluxeAgent::DeluxeAgent(
const AtomValue Kind, const id_t Id, const std::string &Name,
MessagingSystem &S,
std::function<std::tuple<Optional<Ts>...>(std::pair<MT, bool>)> &&MF,
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)> &&F) noexcept
: Agent(Kind, Id, Name, S, THISMEMBER(handleTrigger),
DASLAVEHANDLERREF(AtomValue), DASLAVEHANDLERREF(int16_t),
DASLAVEHANDLERREF(int32_t), DASLAVEHANDLERREF(int64_t),
DASLAVEHANDLERREF(int8_t), DASLAVEHANDLERREF(long_double),
DASLAVEHANDLERREF(std__string), DASLAVEHANDLERREF(uint16_t),
DASLAVEHANDLERREF(uint32_t), DASLAVEHANDLERREF(uint64_t),
DASLAVEHANDLERREF(uint8_t), DASLAVEHANDLERREF(unit_t),
DASLAVEHANDLERREF(bool), DASLAVEHANDLERREF(double),
DASLAVEHANDLERREF(float), DAMASTERHANDLERREF(AtomValue),
DAMASTERHANDLERREF(int16_t), DAMASTERHANDLERREF(int32_t),
DAMASTERHANDLERREF(int64_t), DAMASTERHANDLERREF(int8_t),
DAMASTERHANDLERREF(long_double), DAMASTERHANDLERREF(std__string),
DAMASTERHANDLERREF(uint16_t), DAMASTERHANDLERREF(uint32_t),
DAMASTERHANDLERREF(uint64_t), DAMASTERHANDLERREF(uint8_t),
DAMASTERHANDLERREF(unit_t), DAMASTERHANDLERREF(bool),
DAMASTERHANDLERREF(double), DAMASTERHANDLERREF(float)),
ExecutionPolicy(DeluxeExecutionPolicy::decimation(1)), OutputType(T::TT),
NumberOfInputs(sizeof...(As)), MasterInputType(MT::TT),
NumberOfMasterOutputs(NumberOfInputs), InputTypes({As::TT...}),
InputNextPos(NumberOfInputs, 0), InputChanged(NumberOfInputs, false),
- InputStorageOffsets(storageOffsets<As...>(seq_t<sizeof...(As)>())),
- InputValues(new typename TokenizedStorageForTypeList<
- typename TypeListUnwrapDeluxeTuple<TypeList<As...>>::Type>::
- Type()),
- MasterInputNextPos(0), MasterInputChanged(false),
+ InputValues(makeInputStorages<As...>()), MasterInputNextPos(0),
+ MasterInputChanged(false),
MasterInputValue(new typename TokenizedStorageForTypeList<
typename UnwrapDeluxeTuple<MT>::Type>::Type()),
MasterOutputTypes({Ts::TT...}),
FP(triggerHandlerFromProcessingFunctions(std::move(MF), std::move(F),
seq_t<MT::Length>())),
Slaves(NumberOfInputs) {
ASSERT(Kind == atoms::AgentKind);
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " is created." << std::endl;
ASSERT(inv());
}
template <typename... Ts, size_t... S0>
void DeluxeAgent::sendToMaster(const DeluxeTuple<Ts...> &Value,
Seq<S0...>) noexcept {
STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments");
ASSERT(inv() && OutputType == TypeToken<Ts...>::Value);
// The assert must hold if \p this object was successfuuly constructed.
ASSERT((true && ... &&
(static_cast<size_t>(static_cast<token_size_t>(S0)) == S0)));
// Create a static constant array for these indices to be available as lvalue
// references when creating messages below. \c S0... when used directly in a
// fold expression is a temporary value, which would result in \c
// rosa::Message instances being created with rvalue references. Further, all
// other values would to copied into a temporary variable for making them
/// available as rvalue references (they are constant lvalue references here).
static constexpr std::array<token_size_t, sizeof...(S0)> Indices{{S0...}};
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id
<< ") sends to master ("
<< static_cast<bool>(Master && *Master) << "): " << Value
<< " (" << sizeof...(S0) << ")" << std::endl;
// There is a handle and the referred *master* is in a valid state.
if (Master && *Master) {
// Handle each element of the tuple in a fold expression.
(Master->sendMessage(Message::create(atoms::Slave::Value, Id, Indices[S0],
std::get<S0>(Value))),
...);
}
ASSERT(inv());
}
template <typename... Ts, size_t... S0>
void DeluxeAgent::sendToSlave(const size_t Pos, const DeluxeTuple<Ts...> &Value,
Seq<S0...>) noexcept {
STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments");
ASSERT(inv() && Pos < NumberOfMasterOutputs &&
MasterOutputTypes[Pos] == TypeToken<Ts...>::Value);
// The assert must hold if \p this object was successfuuly constructed.
ASSERT((true && ... &&
(static_cast<size_t>(static_cast<token_size_t>(S0)) == S0)));
// Create a static constant array for these indices to be available as lvalue
// references when creating messages below. \c S0... when used directly in a
// fold expression is a temporary value, which would result in \c
// rosa::Message instances being created with rvalue references. Further, all
// other values would to copied into a temporary variable for making them
/// available as rvalue references (they are constant lvalue references here).
static constexpr std::array<token_size_t, sizeof...(S0)> Indices{{S0...}};
// There is a handle and the referred *slave* is in a valid state.
auto Slave = Slaves[Pos];
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id
<< ") sends to slave (" << static_cast<bool>(Slave && *Slave)
<< ") at position " << Pos << ": " << Value << " ("
<< sizeof...(S0) << ")" << std::endl;
if (Slave && *Slave) {
// Handle each element of the tuple in a fold expression.
(Slave->sendMessage(Message::create(atoms::Master::Value, Id, Indices[S0],
std::get<S0>(Value))),
...);
}
}
template <typename T>
void DeluxeAgent::saveInput(id_t Id, token_size_t Pos, T Value) noexcept {
ASSERT(inv() && SlaveIds.find(Id) != SlaveIds.end() &&
Pos == InputNextPos[SlaveIds.find(Id)->second] &&
typeAtPositionOfToken(InputTypes[SlaveIds.find(Id)->second], Pos) ==
TypeNumberOf<T>::Value);
- size_t SlavePos = SlaveIds.at(Id);
+ const 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;
+ *static_cast<T *>(InputValues[SlavePos]->pointerTo(Pos)) = Value;
// Update position of next value.
if (++InputNextPos[SlavePos] == lengthOfToken(InputTypes[SlavePos])) {
InputNextPos[SlavePos] = 0;
}
// Set flag.
InputChanged[SlavePos] = true;
ASSERT(inv());
}
template <typename T>
void DeluxeAgent::saveMasterInput(id_t Id, token_size_t Pos, T Value) noexcept {
ASSERT(inv() && Master && masterId() == Id && Pos == MasterInputNextPos &&
typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf<T>::Value);
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id
<< ") saves value from master: (" << static_cast<size_t>(Pos)
<< ") " << Value << std::endl;
// Save value.
*static_cast<T *>(MasterInputValue->pointerTo(Pos)) = Value;
// Update position of next value.
if (++MasterInputNextPos == lengthOfToken(MasterInputType)) {
MasterInputNextPos = 0;
}
// Set flag.
MasterInputChanged = true;
ASSERT(inv());
}
} // End namespace deluxe
} // End namespace rosa
#undef DASLAVEHANDLEREF
#undef DAMASTERHANDLEREF
#undef DASLAVEHANDLEDEF
#undef DAMASTERHANDLEDEF
#undef DASLAVEHANDLEDEFN
#undef DAMASTERHANDLEDEFN
#undef DASLAVEHANDLENAME
#undef DAMASTERHANDLENAME
#endif // ROSA_DELUXE_DELUXEAGENT_HPP
diff --git a/include/rosa/deluxe/DeluxeContext.hpp b/include/rosa/deluxe/DeluxeContext.hpp
index a54a2da..9363c41 100644
--- a/include/rosa/deluxe/DeluxeContext.hpp
+++ b/include/rosa/deluxe/DeluxeContext.hpp
@@ -1,932 +1,935 @@
//===-- rosa/deluxe/DeluxeContext.hpp ---------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/deluxe/DeluxeContext.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2019
///
/// \brief Public interface for the *deluxe interface* for working with agent
/// systems.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_DELUXE_DELUXECONTEXT_HPP
#define ROSA_DELUXE_DELUXECONTEXT_HPP
#include "rosa/deluxe/DeluxeSystem.hpp"
#include "rosa/support/types.hpp"
#include <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 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,
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;
/// Connects a *sensor* to an *agent* in the context of \p this object.
///
/// \param Agent the *agent* to connect to
/// \param Pos the index of slot of \p Agent to connect \p Sensor to
/// \param Sensor the *sensor* to connect
/// \param Description optional textual description of the connection
///
/// \return how successfull connecting \p Sensor to \p Agent at slot
/// index \p Pos was
///
/// \note The function may return the following
/// \c rosa::deluxe::DeluxeContext::ErrorCode values:
/// `ErrorCode` | Comment
/// ----------- | -------
/// `NoError` | Success
/// `NotAgent` | Referred \p Agent is not \c rosa::deluxe::DeluxeAgent
/// `NotSensor` | Referred \p Sensor is not \c rosa::deluxe::DeluxeSensor
/// `WrongPosition` | \p Pos is not a valid input position of \p Agent
/// `TypeMismatch` | Expected input type at position \p Pos of \p Agent is other thanthe output type of \p Sensor or expected master-input of \p Sensor is other than master-output at position \p Pos of \p Agent if any
/// `AlreadyHasSlave` | \p Agent at position \p Pos already has a *slave* registered
/// `AlreadyHasMaster` | \p Sensor already has a *master* registered
ErrorCode connectSensor(AgentHandle Agent, const size_t Pos,
AgentHandle Sensor,
const std::string &Description = "") noexcept;
/// Connectes two *agents* in the context of \p this object.
///
/// \param Master the *agent* to connect to
/// \param Pos the index of slot of \p Master to connect \p Slave to
/// \param Slave the *agent* to connect
/// \param Description optional textual description of the connection
///
/// \return how succesfull connecting \p Slave to \p Master at slot
/// index \p Pos was
///
/// \note The function may return the following
/// \c rosa::deluxe::DeluxeContext::ErrorCode values:
/// `ErrorCode` | Comment
/// ----------- | -------
/// `NoError` | Success
/// `NotAgent` | Referred \p Master or \p Slave is not \c rosa::deluxe::DeluxeAgent
/// `WrongPosition` | \p Pos is not a valid input position of \p Master
/// `TypeMismatch` | Expected input type at position \p Pos of \p Master is other than the output type of \p Slave or expected master-input of \p Slave is other than master-output at position \p Pos of \p Master if any
/// `AlreadyHasSlave` | \p Master at position \p Pos already has a *slave* registered
/// `AlreadyHasMaster` | \p Slave already has a *master* registered
ErrorCode connectAgents(AgentHandle Master, const size_t Pos,
AgentHandle Slave,
const std::string &Description = "") noexcept;
/// Initializes \c this object and others managed by \p this object
/// for setting up and performing simulation.
///
/// \see \c rosa::deluxe::DeluxeContext::registerSensorValues,
/// \c rosa::deluxe::DeluxeContext::simulate
///
/// Need to clear simulation data sources from all the *sensors*.
void initializeSimulation(void) noexcept;
public:
/// Registers a stream providing values for a *sensor* during
/// simulation.
///
/// \tparam Iterator type of iterator providing values for \p Sensor
- /// \tparam T type of values \p Sensor is operating on, always use
- /// default!
+ /// \tparam T type that can be matched to values \p Sensor is operating on,
+ /// always use default!
///
/// \note Instantiation fails if type argument \p T is neither a built-in type
- /// nor an instance of \c rosa::deluxe::DeluxeTuple with at least one element.
+ /// nor a tuple (i.e., can be converted to \c rosa::deluxe::DeluxeTuple).
///
/// \param Sensor the *sensor* to register values for
/// \param Start provides values for \p Sensor
/// \param End denotes the end of stream of values
/// \param Default value to be used when input stream is depleted
/// during simulation
///
/// \return how successful registering \p Source for \p Sensor
///
/// \note The function may return the following
/// \c rosa::deluxe::DeluxeContext::ErrorCode values:
/// `ErrorCode` | Comment
/// ----------- | -------
/// `NoError` | Success
- /// `TypeMismatch` | \p Sensor generates values of a type other than
- /// \p T `NotSensor` | Referred \p Sensor is not \c
- /// rosa::deluxe::DeluxeSensor `AlreadyHasValueStream` | \p Sensor already has
- /// simulation data source set
- template <
- typename Iterator, typename T = typename Iterator::value_type,
- typename = std::enable_if_t<TypeListContains<BuiltinTypes, T>::Value ||
- (IsDeluxeTuple<T>::Value &&
- !std::is_same<T, EmptyDeluxeTuple>::value)>>
+ /// `TypeMismatch` | \p T does not match the type of values
+ /// generated by \p Sensor
+ /// `NotSensor` | Referred \p Sensor is not \c
+ /// rosa::deluxe::DeluxeSensor
+ /// `AlreadyHasValueStream` | \p Sensor already has simulation data source set
+ template <typename Iterator, typename T = typename Iterator::value_type,
+ typename = std::enable_if_t<
+ TypeListContains<BuiltinTypes, T>::Value || IsTuple<T>::Value>>
ErrorCode registerSensorValues(AgentHandle Sensor, Iterator &&Start,
const Iterator &End, T Default = {}) noexcept;
/// Performs the system contained by \p this object.
///
/// The function performs \p NumCycles cycle of simulation. In each
/// cycle, all the *agents* and *sensors* registered in \c
/// rosa::deluxe::DeluxeContext::DeluxeUnits are trigged for
/// execution.
///
/// \param NumCycles number of cycles to perform
///
/// \pre All the *sensors* in the system contained by \p this object
/// generate their output from simulation data sources.
void simulate(const size_t NumCycles) const noexcept;
};
/// Anonymous namespace with helper features for implementing
/// \c rosa::deluxe::DeluxeContext, consider it private.
namespace {
/// Maps any type \p T to \c rosa::deluxe::EmptyDeluxeTuple.
template <typename T> struct MapToEmptyDeluxeTuple {
using Type = EmptyDeluxeTuple;
};
/// Convenience template alias for \c MapToEmptyDeluxeTuple.
template <typename T>
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>
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 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->simulationDataSourceIsSet()) {
DCRETERROR(ErrorCode::AlreadyHasValueStream);
}
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) {
+ } else if constexpr (IsTuple<T>::Value) {
+
+ using TT = matching_deluxe_tuple_t<T>;
+ if (std::is_same<T, EmptyDeluxeTuple>::value || S->OutputType != TT::TT) {
DCRETERROR(ErrorCode::TypeMismatch);
}
// Register input stream.
// \note Need to capture parameters by value so having local copies.
S->registerSimulationDataSource(
- std::function<T(void)>([=](void) mutable noexcept->T {
+ std::function<TT(void)>([=](void) mutable noexcept->TT {
if (Start != End) {
+ const TT DV(*Start++);
LOG_TRACE_STREAM << "Reading next value for sensor '" << S->FullName
- << "': " << *Start << '\n';
- return *Start++;
+ << "': " << DV << '\n';
+ return DV;
} else {
+ static const TT DD(Default);
LOG_TRACE_STREAM << "Providing default value for sensor '"
- << S->FullName << "': " << Default << '\n';
- return Default;
+ << S->FullName << "': " << DD << '\n';
+ return DD;
}
}));
} else {
ASSERT(false && "Unexpected type argument");
}
return ErrorCode::NoError;
}
} // End namespace deluxe
} // End namespace rosa
// Undef local macro if not used in the corresponding implementation.
#ifndef ROSA_LIB_DELUXE_DELUXECONTEXT_CPP
#undef DCRETERROR
#endif
#endif // ROSA_DELUXE_DELUXECONTEXT_HPP
diff --git a/include/rosa/deluxe/DeluxeTuple.hpp b/include/rosa/deluxe/DeluxeTuple.hpp
index e57d29b..1e6b717 100644
--- a/include/rosa/deluxe/DeluxeTuple.hpp
+++ b/include/rosa/deluxe/DeluxeTuple.hpp
@@ -1,359 +1,419 @@
//===-- 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.
+ // are too many 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)...) {}
+ /// Contructor, initializes the underlying \c std::tuple from another matching
+ /// \c std::tuple.
+ DeluxeTuple(const std::tuple<Ts...> &Args) : std::tuple<Ts...>(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)...);
}
+/// Creates a \c rosa::deluxe::DeluxeTuple instance from the given \c std::tuple
+/// reference.
+///
+/// \tparam Ts types of elements of the tuple
+///
+/// \see \c rosa::deluxe::DeluxeTuple
+///
+/// \param Args values to store in the tuple
+///
+/// \return an instance of \c rosa::deluxe::DeluxeTuple<Ts...> with \p Args as
+/// elements
+template <typename... Ts>
+inline DeluxeTuple<Ts...>
+make_deluxe_tuple(const std::tuple<Ts...> &Args) noexcept {
+ return DeluxeTuple<Ts...>(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
+///@}
+
+/// \defgroup IsTuple Implementation of \c rosa::deluxe::IsTuple
///
-/// \brief Unwraps element types from instances of \c
-/// rosa::deluxe::DeluxeTuple in a \c rosa::TypeList.
+/// \brief Tells if a type is a tuple as in it can be converted to \c
+/// rosa::deluxe::DeluxeTuple.
///
-/// Types can be unwrapped from \c rosa::deluxe::DeluxeTuple instances as \code
-/// typename TypeListUnwrapDeluxeTuple<List>::Type
-/// \endcode
+/// \see \c rosa::deluxe::MatchingDeluxeTuple
///
-/// 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
+/// Whether a type \c T is a tuple can be checked as \code
+/// IsTuple<T>::Value
/// \endcode
///@{
/// Declaration of the template.
///
-/// \tparam List \c rosa::TypeList to check
-template <typename List> struct TypeListUnwrapDeluxeTuple;
+/// \tparam T type to check
+template <typename T> struct IsTuple;
-/// Specialization for \c rosa::EmptyTypeList.
-template <> struct TypeListUnwrapDeluxeTuple<EmptyTypeList> {
- using Type = EmptyTypeList;
+/// Specialization for the case when the type is an instance of \c std::tuple.
+template <typename... Ts> struct IsTuple<std::tuple<Ts...>> {
+ static constexpr bool Value = true;
};
-/// 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;
+/// Specialization for the case when the type is an instance of \c std::tuple.
+template <typename... Ts> struct IsTuple<DeluxeTuple<Ts...>> {
+ static constexpr bool Value = true;
};
-/// 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;
-};
+/// Implementation for a general case of type \p T.
+template <typename T> struct IsTuple { static constexpr bool Value = false; };
///@}
/// \defgroup IsDeluxeTuple Implementation of \c rosa::deluxe::IsDeluxeTuple
///
/// \brief Tells if a type is an instance of \c rosa::deluxe::DeluxeTuple.
///
/// Whether a type \c T is an instance of \c rosa::deluxe::DeluxeTuple can be
/// checked as \code
/// IsDeluxeTuple<T>::Value
/// \endcode
+///
+/// \note `!IsDeluxeTuple<T>::Value || IsTuple<T>::Value`
///@{
/// 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 MatchingDeluxeTuple Implementation of \c
+/// rosa::deluxe::MatchingDeluxeTuple
+///
+/// \brief Gives the \c rosa::deluxe::DeluxeTuple type that matches a given
+/// tuple type.
+///
+/// The matching \c rosa::deluxe::DeluxeTuple type for a tuple type \p T can be
+/// obtained as \code
+/// typename MatchingDeluxeTuple<T>::Type
+/// \endcode
+/// If \p T is \c rosa::deluxe::DeluxeTuple, the matching type is \p T itself.
+/// If \p T is \c std::tuple, the matching type if \c rosa::deluxe::DeluxeTuple
+/// with the same type parameters as \p T.
+/// \c rosa::deluxe::MatchingDeluxeTuple is not defined for other type
+/// parameters.
+///
+/// \note The template is defined for type \p T only if
+/// `rosa::deluxe::IsTuple<T>::Value`. Values of such types can be used to
+/// construct a new instance of the class \c rosa::deluxe::DeluxeTuple.
+///
+///\see \c rosa::deluxe::IsTuple
+///@{
+
+/// Declaration of the template.
+///
+/// \tparam T type to check
+template <typename T> struct MatchingDeluxeTuple;
+
+/// Specialization for the case when the type is an instance of \c
+/// rosa::deluxe::DeluxeTuple.
+template <typename... Ts> struct MatchingDeluxeTuple<DeluxeTuple<Ts...>> {
+ using Type = DeluxeTuple<Ts...>;
+};
+
+/// Specialization for the case when the type is an instance of \c
+/// std::tuple.
+template <typename... Ts> struct MatchingDeluxeTuple<std::tuple<Ts...>> {
+ using Type = DeluxeTuple<Ts...>;
+};
+
+///@}
+
+/// Convenience template type alias for easy use of \c
+/// rosa::deluxe::MatchingDeluxeTuple.
+///
+/// Converts a tuple type to the matching \c rosa::deluxe::DeluxeTuple type.
+///
+/// \tparam Tuple type to convert
+template <typename Tuple>
+using matching_deluxe_tuple_t = typename MatchingDeluxeTuple<Tuple>::Type;
+
/// \defgroup TypeListAllDeluxeTuple Implementation of
/// \c rosa::deluxe::TypeListAllDeluxeTuple
///
/// \brief Tells if all types in a \c rosa::TypeList is an instance of \c
/// rosa::deluxe::DeluxeTuple.
///
/// Whether a \c rosa::TypeList \c List contains instances of \c
/// rosa::deluxe::DeluxeTuple only can be checked as \code
/// TypeListAllDeluxeTuple<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/csv/CSVReader.hpp b/include/rosa/support/csv/CSVReader.hpp
index 0491c17..5402018 100755
--- a/include/rosa/support/csv/CSVReader.hpp
+++ b/include/rosa/support/csv/CSVReader.hpp
@@ -1,382 +1,486 @@
//===-- rosa/support/csv/CSVReader.hpp --------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/support/csv/CSVReader.hpp
///
-/// \author David Juhasz (david.juhasz@tuwien.ac.at)
+/// \authors David Juhasz (david.juhasz@tuwien.ac.at), Edwin Willegger (edwin.willegger@tuwien.ac.at)
///
/// \date 2017-2019
///
/// \brief Facitilities to read CSV files.
///
/// \note The implementation is based on the solution at
/// https://stackoverflow.com/a/1120224
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_SUPPORT_CSV_CSVREADER_HPP
#define ROSA_SUPPORT_CSV_CSVREADER_HPP
#include "rosa/support/debug.hpp"
+#include "rosa/support/sequence.hpp"
#include <istream>
#include <sstream>
#include <vector>
+#include <map>
+#include <algorithm>
+#include <set>
namespace rosa {
namespace csv {
+/// Indicating it the CSV file contains any header or not
+enum class HeaderInformation {
+ HasHeader,
+ HasNoHeader
+};
+
/// Anonymous namespace providing implementation details for
/// \c rosa::csv::CSVIterator, consider it private.
namespace {
-/// Provides facility for parsing values from one row CSV data.
+/// Provides facility for parsing one value from a string.
///
-/// \tparam T type of values to parse from the line
+/// \tparam T type of value to parse
/// \tparam IsSignedInt if \p T is a signed integral type, always use default
/// \tparam IsUnsignedInt if \p T is an unsigned integral type, always use
/// default
/// \tparam IsFloat if \p T is a floating-point type, always use default
/// \tparam IsString if \p T is \c std::string, always use default
///
-/// \note Specializations of this `struct` are provided for arithmentic types
+/// \note Specializations of this struct are provided for arithmentic types
/// and \c std::string.
-template <typename T, bool IsSignedInt = (std::is_integral<T>::value &&
- std::is_signed<T>::value),
+template <typename T,
+ bool IsSignedInt =
+ (std::is_integral<T>::value && std::is_signed<T>::value),
bool IsUnsignedInt =
(std::is_integral<T>::value && std::is_unsigned<T>::value),
bool IsFloat = std::is_floating_point<T>::value,
bool IsString = std::is_same<T, std::string>::value>
-struct CSVRowParser;
+struct ValueParser {
-/// Specialization for signed integral types.
-///
-/// \tparam T type of values to parse from the line
-///
-/// \pre \p T is a signed integral type:\code
-/// std::is_integral<T>::value && std::is_signed<T>::value
-/// \endcode
-template <typename T> struct CSVRowParser<T, true, false, false, false> {
- STATIC_ASSERT((std::is_integral<T>::value && std::is_signed<T>::value),
- "wrong type"); // Sanity check.
-
- /// Parses a given row of CSV data into a given container.
///
- /// \p Data is cleared and then filled with values parsed from \p LineStream.
- /// Entries in the line are to be separated by commas, the character `,`. A
- /// trailing comma results in an empty entry at the end of the line. No empty
- /// entry should be present otherwise.
///
- /// \note Parsed values are silently converted to type \p T.
+ /// \param Cell the \c std::string to parse
///
- /// \param [in,out] LineStream the line to parse
- /// \param [in,out] Data the container to store the parsed values
- static void parse(std::stringstream &LineStream, std::vector<T> &Data) {
- std::string Cell;
- Data.clear();
- while (std::getline(LineStream, Cell, ',')) {
- Data.push_back(static_cast<T>(std::stoll(Cell)));
- }
- // This checks for a trailing comma with no data after it.
- if (!LineStream && Cell.empty()) {
- // If there was a trailing comma then add an empty element.
- Data.push_back(0);
- }
+ /// \return the parsed value
+ ///
+ /// \note The function silently fails if cannot parse \p Cell for type \p T.
+ static T parse(const std::string &Cell) noexcept;
+};
+
+template <typename T>
+struct ValueParser<T, true, false, false, false> {
+ STATIC_ASSERT((std::is_integral<T>::value && std::is_signed<T>::value),
+ "wrong type"); // Sanity check.
+ static T parse(const std::string &Cell) noexcept {
+ return static_cast<T>(std::stoll(Cell));
}
};
-/// Specialization for unsigned integral types.
-///
-/// \tparam T type of values to parse from the line
-///
-/// \pre \p T is an unsigned integral type:\code
-/// std::is_integral<T>::value && std::is_unsigned<T>::value
-/// \endcode
-template <typename T> struct CSVRowParser<T, false, true, false, false> {
+template <typename T>
+struct ValueParser<T, false, true, false, false> {
STATIC_ASSERT((std::is_integral<T>::value && std::is_unsigned<T>::value),
"wrong type"); // Sanity check.
-
- /// Parses a given row of CSV data into a given container.
- ///
- /// \p Data is cleared and then filled with values parsed from \p LineStream.
- /// Entries in the line are to be separated by commas, the character `,`. A
- /// trailing comma results in an empty entry at the end of the line. No empty
- /// entry should be present otherwise.
- ///
- /// \note Parsed values are silently converted to type \p T.
- ///
- /// \param [in,out] LineStream the line to parse
- /// \param [in,out] Data the container to store the parsed values
- static void parse(std::stringstream &LineStream, std::vector<T> &Data) {
- std::string Cell;
- Data.clear();
- while (std::getline(LineStream, Cell, ',')) {
- Data.push_back(static_cast<T>(std::stoull(Cell)));
- }
- // This checks for a trailing comma with no data after it.
- if (!LineStream && Cell.empty()) {
- // If there was a trailing comma then add an empty element.
- Data.push_back(0);
- }
+ static T parse(const std::string &Cell) noexcept {
+ return static_cast<T>(std::stoull(Cell));
}
};
-/// Specialization for floating-point types.
-///
-/// \tparam T type of values to parse from the line
-///
-/// \pre \p T is a floating-point type:\code
-/// std::is_floating_point<T>::value
-/// \endcode
-template <typename T> struct CSVRowParser<T, false, false, true, false> {
+template <typename T>
+struct ValueParser<T, false, false, true, false> {
STATIC_ASSERT((std::is_floating_point<T>::value),
"wrong type"); // Sanity check.
-
- /// Parses a given row of CSV data into a given container.
- ///
- /// \p Data is cleared and then filled with values parsed from \p LineStream.
- /// Entries in the line are to be separated by commas, the character `,`. A
- /// trailing comma results in an empty entry at the end of the line. No empty
- /// entry should be present otherwise.
- ///
- /// \note Parsed values are silently converted to type \p T.
- ///
- /// \param [in,out] LineStream the line to parse
- /// \param [in,out] Data the container to store the parsed values
- static void parse(std::stringstream &LineStream, std::vector<T> &Data) {
- std::string Cell;
- Data.clear();
- while (std::getline(LineStream, Cell, ',')) {
- Data.push_back(static_cast<T>(std::stold(Cell)));
- }
- // This checks for a trailing comma with no data after it.
- if (!LineStream && Cell.empty()) {
- // If there was a trailing comma then add an empty element.
- Data.push_back(0);
- }
+ static T parse(const std::string &Cell) noexcept {
+ return static_cast<T>(std::stold(Cell));
}
};
-/// Specialization for \c std::string.
-///
-/// \tparam T type of values to parse from the line
-///
-/// \pre \p T is \c std::string:\code
-/// std::is_same<T, std::string>::value
-/// \endcode
-template <typename T> struct CSVRowParser<T, false, false, false, true> {
+template <typename T>
+struct ValueParser<T, false, false, false, true> {
STATIC_ASSERT((std::is_same<T, std::string>::value),
"wrong type"); // Sanity check.
-
- /// Parses a given row of CSV data into a given container.
- ///
- /// \p Data is cleared and then filled with values parsed from \p LineStream.
- /// Entries in the line are to be separated by commas, the character `,`. A
- /// trailing comma results in an empty entry at the end of the line. No empty
- /// entry should be present otherwise.
- ///
- /// \param [in,out] LineStream the line to parse
- /// \param [in,out] Data the container to store the parsed values
- static void parse(std::stringstream &LineStream, std::vector<T> &Data) {
- std::string Cell;
- Data.clear();
- while (std::getline(LineStream, Cell, ',')) {
- Data.push_back(Cell);
- }
- // This checks for a trailing comma with no data after it.
- if (!LineStream && Cell.empty()) {
- // If there was a trailing comma then add an empty element.
- Data.push_back("");
- }
- }
+ static T parse(const std::string &Cell) noexcept { return Cell; }
};
/// Parses and stores entries from a row of CSV data.
///
-/// \tparam T type of values to parse and store, i.e. entries in the row
+/// \tparam Ts types of values to parse and store, i.e. entries in the row
///
/// \note The implementation relies on \c rosa::csv::CSVRowParser, which is
-/// implemented only for `arithmetic` types -- signed and unsigned integral and
-/// floating-point types -- and for \c std::string. Those are the valid values
-/// for \p T.
-template <typename T>
-class CSVRow {
-public:
- /// Gives a constant reference for an entry at a given position of the row.
+/// implemented only for `arithmetic` types -- signed and unsigned integral
+/// and floating-point types -- and for \c std::string. Those are the valid
+/// values for \p Ts.
+template <typename... Ts> class CSVRow {
+private:
+ /// Parses a given row of CSV data into \c CSVRow::Data.
///
- /// \note No bounds checking is performed.
+ /// \ CSVRow::Data is filled with values parsed from \p LineStream. Entries
+ /// in the line are to be separated by commas, the character `,`.
///
- /// \param Index the position of the entry
+ /// \note Parsed values are silently converted to types \p Ts.
+ ///
+ /// \note Parsing silently fails if values do not match \p Ts.
+ ///
+ /// \tparam S0 indices to access tuple elements.
+ ///
+ /// \param [in,out] LineStream the line to parse
///
- /// \return constant reference for the stored entry at position \p Index
- const T &operator[](const size_t Index) const noexcept { return Data[Index]; }
+ /// \note The last argument is used only to get \p S0, the actual value of
+ /// the parameter is ignored.
+ template <size_t... S0>
+ void parseRow(std::stringstream &LineStream, char Delimeter, Seq<S0...>) {
+ STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0),
+ "Not matching template arguments.");
+ std::string Cell;
+ // Get fields and parse the values into the proper element of the tuple
+ // one by one in a fold expression.
+ ((std::getline(LineStream, Cell, Delimeter),
+ std::get<S0>(Data) = ValueParser<Ts>::parse(Cell)),
+ ...);
+
+ }
+
+public:
- /// Tells the number of entries stored in the row.
+ /// Constructor with all possible parameters
+ ///
+ /// The function creates an instance of an CSVRow object and sets the attributes of the
+ /// object to the values of the parameters.
///
- /// \return number of stored entries.
- size_t size(void) const noexcept { return Data.size(); }
+ /// \param SkipRows the number of data rows to skip, not taking header into account.
+ /// \param HeaderInfo is the first line of the file a header row or not.
+ /// \param Delimeter to seperate between the data entries within one row.
+ CSVRow(const size_t SkipRows = 0,
+ const HeaderInformation HeaderInfo = HeaderInformation::HasHeader,
+ const char Delimeter = ',') :
+ SkipRows(SkipRows), HeaderInfo(HeaderInfo), Delimeter(Delimeter),
+ RowNumber(0), IsHeaderRead(false) {
+ }
+
+
/// Parses and stores one row of CSV data.
///
/// The function reads one line from \p Str and parses it into
/// \c rosa::csv::CSVRow::Data using \c rosa::csv::CSVRowParser.
///
/// \param [in,out] Str input stream of a CSV file
- void readNextRow(std::istream &Str) {
+ void readNextRow(std::istream &Str) noexcept {
std::string Line;
+
std::getline(Str, Line);
- std::stringstream LineStream(Line);
- CSVRowParser<T>::parse(LineStream, Data);
+
+ if(Line.size() > 0){
+ std::stringstream LineStream(Line);
+ parseRow(LineStream, Delimeter, seq_t<sizeof...(Ts)>());
+
+ RowNumber = RowNumber + 1;
+ }
+
+ }
+
+ /// Read header row and stores it as \p std::string.
+ ///
+ /// The function reads the first line of the csv file and stores the entries
+ /// in a vector.
+ ///
+ /// \param [in,out] Str input stream of a CSV file
+ void readHeader(std::istream &Str) noexcept {
+ std::string Line;
+ std::getline(Str, Line);
+ std::stringstream LineStream(Line);
+ std::string Value;
+
+ while( getline(LineStream, Value, Delimeter) ){
+ Header.push_back(Value);
+ }
+
+ IsHeaderRead = true;
+ }
+
+ /// The number of rows to skip once.
+ ///
+ /// This function returns the number of data rows to skip
+ /// at the beginning of the file.
+ ///
+ /// \return The number of rows to skip at the beginning of a csv file.
+ inline size_t SkipNumRows() const noexcept {
+ return this->SkipRows;
+ }
+
+ /// The current row number within the csv file.
+ ///
+ /// This function returns the current row number. The header
+ /// row is not counted as a row.
+ ///
+ /// \returns the current row number within the csv file.
+ inline size_t CurRow() const noexcept {
+ return this->RowNumber;
+ }
+
+ /// Indiciates if the header was already read.
+ ///
+ /// This function returns true, if the header of a csv file which contains
+ /// a header file is already read.
+ /// The user has to pass in the attribute HeaderInfo the information if the
+ /// file has in the first row the header row or not.
+ ///
+ /// \return if the header of a file is already read.
+ inline bool IsHeaderReadDone() const noexcept{
+ return this->IsHeaderRead;
+ }
+
+
+ /// Indicates if the file contains a header row in the first row.
+ ///
+ /// This function returns if the file contains a header row.
+ /// The information if the file contains a header row or not, has to be passed by the user.
+ /// The standard value is HeaderInformation::HasHeader
+ ///
+ /// \return if the csv file contains a header row in the first line of the file.
+ inline HeaderInformation HasFileHeader() const noexcept {
+ return this->HeaderInfo;
+ }
+
+ /// Set the number of rows to skip.
+ ///
+ /// This function sets the number of rows to skip at the beginning of
+ /// the reading of the file.
+ ///
+ /// \param SkipRows the number of rows you want to skip at the beginning of the file.
+ inline void SetSkipRows(const size_t SkipRows) noexcept {
+ this->SkipRows = SkipRows;
+ }
+
+ /// Is the first row a header row or not.
+ ///
+ /// This function sets the information, if the first row of the csv file
+ /// is a header line or not.
+ ///
+ /// \param HeaderInfo if the first row is a header row or not.
+ inline void SetHeaderInfo(const HeaderInformation HeaderInfo) noexcept {
+ this->HeaderInfo = HeaderInfo;
+ }
+
+ /// Set the seperator between data entries.
+ ///
+ /// This funcction sets the separator between the data entries of the csv file.
+ ///
+ /// \param Delimeter the character that separates the data values.
+ inline void SetDelimeter(char Delimeter) {
+ this->Delimeter = Delimeter;
}
+
+
+
+ /// Gives a constant references for the \c std::tuple containing the values
+ /// read by \p this object.
+ ///
+ /// \return \c CSVRow::Data
+ const std::tuple<Ts...> &tuple(void) const noexcept { return Data; }
+
private:
- std::vector<T> Data; ///< Stores parsed entries
+ std::tuple<Ts...> Data; ///< Stores parsed entries
+ size_t SkipRows; ///< The number of rows to skip at the very beginning of the file.
+ ///< This number only applies on the number of data rows.
+ ///< If your file contains a header row and data rows, the skiping
+ ///< of the header row is not taken into account.
+ HeaderInformation HeaderInfo; ///< If the file contains a header row or not.
+ char Delimeter; ///< The seperator between the data entries.
+ size_t RowNumber; ///< Current row number, counts all row numbers including the header row.
+ bool IsHeaderRead; ///< Was the header read or not.
+ std::vector<std::string> Header; ///< The content of the header row.
+
};
/// Reads a row of CSV data into \c rosa::csv::CSVRow.
///
/// The next line is read from \p Str by calling
-/// \c rosa::csv::CSVRow::readNextRow on \p Data.
+/// \c rosa::csv::CSVRow::readNextRow on \p Data until all lines are
+/// skipped.
///
-/// \note A CSV file should contain no empty lines.
+/// If the function is called for the first time and the file contains
+/// a header than is the header and the first data row read in after the
+/// number of rows that the user wants to skip.
+///
+/// \tparam Ts type of values to read from the row
+///
+/// \note The CSV file should contain a line with fields matching \p Ts...
///
/// \param [in,out] Str input stream of a CSV file
/// \param [in,out] Data object to read the next line into
///
/// \return \p Str after reading one line from it
-template <typename T>
-std::istream &operator>>(std::istream &Str, CSVRow<T> &Data) {
+template <typename... Ts>
+std::istream &operator>>(std::istream &Str, CSVRow<Ts...> &Data) {
+
+ if( Data.HasFileHeader() == HeaderInformation::HasHeader && !Data.IsHeaderReadDone() ) {
+ Data.readHeader(Str);
+ }
+
+ while(Data.CurRow() < (Data.SkipNumRows())){
+ Data.readNextRow(Str);
+ }
+
+ //read the lines after you skipped the number of rows you want to skip
Data.readNextRow(Str);
+
return Str;
}
} // End namespace
-/// Provides `InputIterator` features for iterating over a CSV file in a
-/// flat way.
+/// Provides `InputIterator` features for iterating over a CSV file.
///
-/// The iterator hides rows of the CSV file, and iterates over the entries
-/// row-by-row.
+/// The iterator parses rows into `std::tuple` values and iterates over the
+/// file row by row.
///
-/// \note A CSV file should contain no empty lines.
+/// \tparam Ts types of values stored in one row of the CSV file
///
-/// \tparam T type of values to iterate over, i.e. entries in the CSV file.
+/// \note The iterator expects each row to consists of fields matching \p Ts.
///
/// \note The implementation relies on \c rosa::csv::CSVRow, which in turn
/// relies on \c rosa::csv::CSVRowParser, which is implemented only for
/// `arithmetic` types -- signed and unsigned integral types and floating-point
-/// types -- and for \c std::string. Those are the valid values for \p T.
-template <typename T>
-class CSVFlatIterator {
+/// types -- and for \c std::string. Those are the valid values for \p Ts
+template <typename... Ts> class CSVIterator {
public:
- /// \defgroup CSVFlatIteratorTypedefs Typedefs of rosa::csv::CSVFlatIterator
+ /// \defgroup CSVIteratorTypedefs Typedefs of rosa::csv::CSVIterator
///
/// Standard `typedef`s for iterators.
///
///@{
typedef std::input_iterator_tag
- iterator_category; ///< Category of the iterator.
- typedef T value_type; ///< Type of values iterated over.
- typedef std::size_t difference_type; ///< Type to identify distance.
- typedef T *pointer; ///< Pointer to the type iterated over.
- typedef T &reference; ///< Reference to the type iterated over.
+ iterator_category; ///< Category of the iterator.
+ typedef std::tuple<Ts...> value_type; ///< Type of values iterated over.
+ typedef std::size_t difference_type; ///< Type to identify distance.
+ typedef std::tuple<Ts...> *pointer; ///< Pointer to the type iterated over.
+ typedef std::tuple<Ts...>
+ &reference; ///< Reference to the type iterated over.
///@}
/// Creates a new instance.
///
/// \param [in,out] S input stream to iterate over
- CSVFlatIterator(std::istream &S)
- : Str(S.good() ? &S : nullptr), Pos((size_t)(-1)) {
- // \c rosa::csv::CSVFlatIterator::Pos is initialized to `-1` so the first
- // incrementation here will set it properly.
+ /// \param SkipRows the number of rows you want to skip only once at the beginning of the file.
+ /// If you have an header in the file, it is supposed to be the first row, and it will be always read out.
+ /// But after this header the next number of Rows will be skipped.
+ /// \param HeaderInfo is used to know wheter the file contains an header row or not.
+ /// The header has to be in the first row.
+ /// \param Delimeter is the separator between the differnt values of the csv file.
+ CSVIterator(std::istream &S, const size_t SkipRows = 0,
+ const HeaderInformation HeaderInfo = HeaderInformation::HasHeader,
+ const char Delimeter = ',') : Str(S.good() ? &S : nullptr),
+ SkipRows(SkipRows), HeaderInfo(HeaderInfo), Delimeter(Delimeter), Row(){
+
+ Row.SetSkipRows(SkipRows);
+ Row.SetHeaderInfo(HeaderInfo);
+ Row.SetDelimeter(Delimeter);
+
+ // \c rosa::csv::CSVIterator::Row is initialized empty so the first
+ // incrementation here will read the first row.
++(*this);
}
/// Creates an empty new instance.
- CSVFlatIterator(void) noexcept : Str(nullptr) {}
+ CSVIterator(void) noexcept : Str(nullptr) {}
/// Pre-increment operator.
///
- /// The implementation moves over the entries in the current row and advances
- /// to the next row when the end of the current row is reached. If the end of
- /// the input stream is reached, the operator becomes empty and has no
- /// further effect.
+ /// The implementation reads the next row. If the end of the input stream is
+ /// reached, the operator becomes empty and has no further effect.
///
/// \return \p this object after incrementing it.
- CSVFlatIterator &operator++() {
+ CSVIterator &operator++() {
if (Str) {
- ++Pos;
- if (Pos == Row.size()) {
- if (!((*Str) >> Row)) {
- Str = nullptr;
- --Pos; // Stay on the last entry forever.
- } else {
- Pos = 0;
- }
+ if (!((*Str) >> Row)) {
+ Str = nullptr;
}
}
return *this;
}
/// Post-increment operator.
///
/// The implementation uses the pre-increment operator and returns a copy of
/// the original state of \p this object.
///
/// \return \p this object before incrementing it.
- CSVFlatIterator operator++(int) {
- CSVFlatIterator Tmp(*this);
+ CSVIterator operator++(int) {
+ CSVIterator Tmp(*this);
++(*this);
return Tmp;
}
/// Returns a constant reference to the current entry.
///
/// \note Should not dereference the iterator when it is empty.
///
/// \return constant reference to the current entry.
- const T &operator*(void)const noexcept { return Row[Pos]; }
+ const std::tuple<Ts...> &operator*(void)const noexcept { return Row.tuple(); }
/// Returns a constant pointer to the current entry.
///
/// \note Should not dereference the iterator when it is empty.
///
/// \return constant pointer to the current entry.
- const T *operator->(void)const noexcept { return &Row[Pos]; }
+ const std::tuple<Ts...> *operator->(void)const noexcept {
+ return &Row.tuple();
+ }
/// Tells if \p this object is equal to another one.
///
/// Two \c rosa::csv::CSVReader instances are equal if and only if they are
/// the same or both are empty.
///
/// \param RHS other object to compare to
///
/// \return whether \p this object is equal with \p RHS
- bool operator==(const CSVFlatIterator &RHS) const noexcept {
+ bool operator==(const CSVIterator &RHS) const noexcept {
return ((this == &RHS) || ((this->Str == nullptr) && (RHS.Str == nullptr)));
}
/// Tells if \p this object is not equal to another one.
///
/// \see rosa::csv::CSVReader::operator==
///
/// \param RHS other object to compare to
///
/// \return whether \p this object is not equal with \p RHS.
- bool operator!=(const CSVFlatIterator &RHS) const noexcept {
+ bool operator!=(const CSVIterator &RHS) const noexcept {
return !((*this) == RHS);
}
+ /// Set the delimeter used in the csv file.
+ /// \param Delimeter the character which separates the values in the csv file.
+ inline void setDelimeter(char Delimeter) noexcept {
+ this->Delimeter = Delimeter;
+ }
+
+ /// get the delimeter currently set to separate the values in the csv file.
+ /// \return the current character, which is used to separte teh values in the csv file.
+ inline char getDelimeter() const noexcept {
+ return this->Delimeter;
+ }
+
private:
- std::istream *Str; ///< Input stream of a CSV file to iterate over.
- CSVRow<T> Row; ///< Content of the current row iterating over.
- size_t Pos; ///< Current position within the current row.
+ std::istream *Str; ///< Input stream of a CSV file to iterate over.
+ size_t SkipRows; ///< Number of Rows to skip only once at the beginning of the file.
+ HeaderInformation HeaderInfo; ///< does the csv file contain a header or not, if this information is
+ ///< not given correclty, the reading of the header would result in
+ ///< in an error.
+ char Delimeter; ///< Delimeter between the entries in the csv file.
+ CSVRow<Ts...> Row; ///< Content of the current row
+
};
} // End namespace csv
} // End namespace rosa
#endif // ROSA_SUPPORT_CSV_CSVREADER_HPP
diff --git a/include/rosa/support/csv/CSVWriter.hpp b/include/rosa/support/csv/CSVWriter.hpp
index b67de6a..a077e3d 100755
--- a/include/rosa/support/csv/CSVWriter.hpp
+++ b/include/rosa/support/csv/CSVWriter.hpp
@@ -1,99 +1,221 @@
//===-- rosa/support/csv/CSVWriter.hpp --------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/support/csv/CSVWriter.hpp
///
-/// \author David Juhasz (david.juhasz@tuwien.ac.at)
+/// \authors David Juhasz (david.juhasz@tuwien.ac.at)
+/// Edwin Willegger (edwin.willegger@tuwien.ac.at)
///
-/// \date 2017
+/// \date 2017-2019
///
/// \brief Facitilities to write CSV files.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_SUPPORT_CSV_CSVWRITER_HPP
#define ROSA_SUPPORT_CSV_CSVWRITER_HPP
+#include <iostream>
#include <ostream>
+#include <tuple>
+#include <vector>
+#include <array>
+
+#include "rosa/support/log.h"
+
namespace rosa {
namespace csv {
/// Provides facilities to write values into a CSV file.
///
/// The writer emits a comma, the character `,`, between each written values.
/// The resulted stream is a flat CSV file as it consists of onlyone row, no new
/// line is emitted.
///
/// \tparam T type of values to write
template <typename T>
class CSVWriter {
public:
/// Creates a new instance.
///
/// \param [in,out] S output stream to write to
///
/// \note The writer operates on non-binary outputs as long as \p S is in
/// good state.
CSVWriter(std::ostream &S)
: Str(S.good() && !(S.flags() & std::ios::binary) ? &S : nullptr),
IsFirst(true) {}
/// Tells if the last operation was successful.
///
/// \return if the last operation was successful
bool good(void) const noexcept {
return Str != nullptr;
}
/// Writes an entry to the output stream.
///
/// The implementation does anything only if the last operation was
/// successful. If so, \p V is written to \c rosa::csv::CSVWriter::Str.
/// The emitted value is preceded with a comma if the actual call is not the
/// first one for \p this object. Success of the operation is checked at the
/// end.
///
/// \param V value to write
void write(const T &V) {
if (Str) {
if (!IsFirst) {
*Str << ',';
} else {
IsFirst = false;
}
*Str << V;
if (!Str->good()) {
Str = nullptr;
}
}
}
private:
std::ostream *Str; ///< Output stream to write to.
bool IsFirst; ///< Denotes if the next write would be the first one.
};
+/// Writes a tuple of values into a CSV file
+///
+/// \tparam Ts types of values to write
+template <typename... Ts> class CSVTupleWriter {
+
+public:
+
+// typedef <Ts...> value_type ; ///< Type of values written.
+ typedef std::tuple<Ts...> value_type;
+
+ /// Creates a new instance.
+ ///
+ /// \param [in,out] S output stream to write to
+ ///
+ /// \note The writer operates on non-binary outputs as long as \p S is in
+ /// good state.
+ CSVTupleWriter(std::ostream &S)
+ : Str(S.good() && !(S.flags() & std::ios::binary) ? &S : nullptr),
+ IsHeaderWritten(false), IsDataWritten(false) {}
+
+ /// Tells if the last operation was successful.
+ ///
+ /// \return if the last operation was successful
+ bool good(void) const noexcept {
+ return Str != nullptr;
+ }
+
+
+ /// Write the values of a tuple to a CSV file with \c rosa::csv::CSVTupleWriter.
+ ///
+ /// \see rosa::csv::CSVTupleWriter
+ ///
+ ///
+ /// \param [in,out] values tuple, which values are written in a recusive fashion into a stream.
+ template<size_t i = 0>
+ void write(const std::tuple<Ts...> &values) {
+ constexpr size_t size = sizeof...(Ts);
+
+ LOG_TRACE_STREAM << "Writing tuple values into file \n";
+ LOG_TRACE_STREAM << " Tuple has " << std::to_string(size) << " elements. \n";
+
+ LOG_TRACE_STREAM << " Value is " << std::get<i>(values);
+
+ if(Str){
+ /// Write the current element of the tuple into the stream and add a separtor after it,
+ /// and call the function for the next element in the tuple.
+ if constexpr(i+1 != sizeof...(Ts)){
+ *Str << std::get<i>(values) << ", ";
+ write<i+1>(values);
+ /// If the last element is written into the stream than begin a new line.
+ }else if constexpr(i + 1 == sizeof...(Ts)){
+ *Str << std::get<i>(values) << '\n';
+ /// every time the last data value of a line is written, the flag indicates that data was already written into the file.
+ IsDataWritten = true;
+ }
+ }
+ }
+
+ /// Write the header values to a CSV file with \c rosa::csv::CSVTupleWriter.
+ ///
+ /// \note The function has no effect if anything has already been written
+ /// to the output stream either by \c
+ /// rosa::csv::CSVTupleWriter::writeHeader() or \c
+ /// rosa::csv::CSVTupleWriter::write().
+ ///
+ /// \see rosa::csv::CSVTupleWriter
+ ///
+ /// \param header the content of the header line.
+ void writeHeader(const std::array<std::string, sizeof...(Ts)> &header){
+ size_t index = 0;
+ /// write into the stream only, if it is not a nullptr, and if no data and no header was already written into it.
+ if(Str && IsDataWritten == false && IsHeaderWritten == false){
+ index = 0;
+ for (auto i = header.begin(); i != header.end(); ++i){
+ index = index + 1;
+ /// write into the stream every entry with a delimeter, in this case ", " until
+ /// the last entry
+ if(index != header.size()){
+ *Str << *i << ", ";
+ /// write the last entry into the stream, without any delimeter
+ }else {
+ *Str << *i;
+ }
+ }
+ /// finish the header line and start a new line.
+ *Str << '\n';
+ /// now it is not possible to write additional header lines.
+ IsHeaderWritten = true;
+ }
+ }
+
+private:
+ std::ostream *Str; ///< Output stream to write to.
+ bool IsHeaderWritten; ///< If an header line was already written into the stream. If set than no additional header could be written.
+ bool IsDataWritten; ///< If one line of data has already been written into the stream, than no headerline could be added.
+};
+
+/// Writes all values of a tuple to a CSV file with \c rosa::csv::CSVTupleWriter.
+///
+/// \see rosa::csv::CSVTupleWriter
+///
+/// \tparam Ts types of values to write
+///
+/// \param [in,out] W object to write with
+/// \param V values to write
+///
+/// \return \p W after writing \p V with it
+template <typename... Ts>
+CSVTupleWriter<Ts...> &operator<<(CSVTupleWriter<Ts...> &W, const std::tuple<Ts...> &V)
+{
+ W.write(V);
+ return W;
+}
+
/// Writes a value to a CSV file with \c rosa::csv::CSVWriter.
///
/// \see rosa::csv::CSVWriter
///
/// \tparam T type of value to write
///
/// \param [in,out] W object to write with
/// \param V value to write
///
/// \return \p W after writing \p V with it
template <typename T>
CSVWriter<T> &operator<<(CSVWriter<T> &W, const T& V) {
W.write(V);
return W;
}
} // End namespace csv
} // End namespace rosa
#endif // ROSA_SUPPORT_CSV_CSVWRITER_HPP
diff --git a/include/rosa/support/iterator/namespace.h b/include/rosa/support/iterator/namespace.h
new file mode 100644
index 0000000..027978d
--- /dev/null
+++ b/include/rosa/support/iterator/namespace.h
@@ -0,0 +1,25 @@
+//===-- rosa/support/iterator/namespace.h -----------------------*- C++ -*-===/
+//
+// The RoSA Framework
+//
+//===----------------------------------------------------------------------===/
+///
+/// \file rosa/support/iterator/namespace.h
+///
+/// \author David Juhasz (david.juhasz@tuwien.ac.at)
+///
+/// \date 2019
+///
+/// \brief Documentation for the namespace \c rosa::iterator.
+///
+//===----------------------------------------------------------------------===/
+
+#ifndef ROSA_SUPPORT_ITERATOR_NAMESPACE_H
+#define ROSA_SUPPORT_ITERATOR_NAMESPACE_H
+
+namespace rosa {
+/// Provides facilities to work with iterators.
+namespace iterator {}
+} // End namespace rosa
+
+#endif // ROSA_SUPPORT_ITERATOR_NAMESPACE_H
diff --git a/include/rosa/support/iterator/split_tuple_iterator.hpp b/include/rosa/support/iterator/split_tuple_iterator.hpp
new file mode 100644
index 0000000..f5af821
--- /dev/null
+++ b/include/rosa/support/iterator/split_tuple_iterator.hpp
@@ -0,0 +1,469 @@
+//===-- rosa/support/iterator/split_tuple_iterator.hpp ----------*- C++ -*-===/
+//
+// The RoSA Framework
+//
+//===----------------------------------------------------------------------===/
+///
+/// \file rosa/support/iterator/split_tuple_iterator.hpp
+///
+/// \authors David Juhasz (david.juhasz@tuwien.ac.at)
+///
+/// \date 2019
+///
+/// \brief Facilities to split iterators of \c std::tuple into iterators of
+/// their elements.
+///
+//===----------------------------------------------------------------------===/
+
+#ifndef ROSA_SUPPORT_ITERATOR_SPLIT_TUPLE_ITERATOR_HPP
+#define ROSA_SUPPORT_ITERATOR_SPLIT_TUPLE_ITERATOR_HPP
+
+#include "rosa/support/debug.hpp"
+#include "rosa/support/sequence.hpp"
+
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <queue>
+
+namespace rosa {
+namespace iterator {
+
+/// Anonymous namespace providing implementation for splitting tuple iterators;
+/// consider it private.
+namespace {
+
+/// Iterator of values provided by a buffer based on an index
+///
+/// \tparam TB buffer providing values to iterate
+/// \tparam I index of the values to iterator from \p TB
+///
+/// \note \p TB is expected to implemnet an interface matching that of \c
+/// TupleIteratorBuffer.
+template <typename TB, size_t I>
+class SplittedElementIterator {
+public:
+ /// Type alias for the values the iterator iterates.
+ using T = typename TB::template element_type<I>;
+
+ /// \defgroup SplittedElementIteratorTypedefs Typedefs of
+ /// \c SplittedElementIterator
+ ///
+ /// Standard `typedef`s for iterators.
+ ///
+ ///@{
+ typedef std::input_iterator_tag
+ iterator_category; ///< Category of the iterator.
+ typedef T value_type; ///< Type of values iterated over.
+ typedef std::size_t difference_type; ///< Type to identify distance.
+ typedef T *pointer; ///< Pointer to the type iterated over.
+ typedef T &reference; ///< Reference to the type iterated ver.
+ ///@}
+
+ /// Creates a new instance.
+ ///
+ /// \param Buffer buffer providing values for \p this object
+ ///
+ /// \note \p Buffer is captured as a \c std::shared_ptr because it is shared
+ /// among iterators for its element positions.
+ SplittedElementIterator(std::shared_ptr<TB> Buffer)
+ : Buffer(Buffer), Value() {
+ // \c Value is initialized empty so the first incrementation here will read
+ // the first value.
+ ++(*this);
+ }
+
+ /// Creates an empty new instance.
+ SplittedElementIterator(void) noexcept : Buffer(), Value() {}
+
+ /// Pre-increment operator.
+ ///
+ /// The implementation reads the next value from \c Buffer. If no more values
+ /// can be provided by \c Buffer, \p this object becomes empty and the
+ /// operator has no further effect.
+ ///
+ /// \return \p this object after incrementing it.
+ SplittedElementIterator &operator++() {
+ if (Buffer->template hasMore<I>()) {
+ Value = Buffer->template next<I>();
+ } else {
+ // Reached end of range, remove reference of \c Buffer.
+ Buffer.reset();
+ }
+ return *this;
+ }
+
+ /// Post-increment operator.
+ ///
+ /// The implementation uses the pre-increment operator and returns a copy of
+ /// the original state of \p this object.
+ ///
+ /// \return \p this object before incrementing it.
+ SplittedElementIterator operator++(int) {
+ SplittedElementIterator<TB, I> Tmp(*this);
+ ++(*this);
+ return Tmp;
+ }
+
+ /// Returns a constant reference to the current value.
+ ///
+ /// \note Should not dereference the iterator when it is empty.
+ ///
+ /// \return constant reference to the current value.
+ const T &operator*(void)const noexcept { return Value; }
+
+ /// Returns a constant pointer to the current value.
+ ///
+ /// \note Should not dereference the iterator when it is empty.
+ ///
+ /// \return constant pointer to the current value.
+ const T *operator->(void)const noexcept { return &Value; }
+
+ /// Tells if \p this object is equal to another one.
+ ///
+ /// Two \c SplittedElementIterator instances are equal if and only if they are
+ /// the same or both are empty.
+ ///
+ /// \param RHS other object to compare to
+ ///
+ /// \return whether \p this object is equal with \p RHS
+ bool operator==(const SplittedElementIterator &RHS) const noexcept {
+ return ((this == &RHS) || (!Buffer && !RHS.Buffer));
+ }
+
+ /// Tells if \p this object is not equal to another one.
+ ///
+ /// \see SplittedElementIterator::operator==
+ ///
+ /// \param RHS other object to compare to
+ ///
+ /// \return whether \p this object is not equal with \p RHS.
+ bool operator!=(const SplittedElementIterator &RHS) const noexcept {
+ return !((*this) == RHS);
+ }
+
+private:
+ std::shared_ptr<TB> Buffer; ///< Buffer providing values for \p this object
+ T Value; ///< The current value of the iterator
+};
+
+///\defgroup TupleBufferContainer Type converter turning a \c std::tuple of
+///types into a \c std::tuple of container of those types.
+///
+/// The new type is used for buffering elements of tuples separately.
+///
+///@{
+
+/// Template declaration.
+///
+/// \tparam T type to convert
+///
+/// \note The template is defined only when \p T is a \c std::tuple.
+///
+/// Usage for a type \c Tuple as:\code
+/// typename TupleBufferContainer<Tuple>::Type
+/// \endcode
+template <typename T> struct TupleBufferContainer;
+
+/// Template definition for \c std::tuple.
+template <typename... Ts> struct TupleBufferContainer<std::tuple<Ts...>> {
+ /// The converted type.
+ using Type = std::tuple<std::queue<Ts>...>;
+};
+
+///@}
+
+/// Convenience template type alias for easy use of \c TupleBufferContainer.
+///
+/// Converts a \c std::tuple of types into a \c std::tuple of container of those
+/// types.
+///
+/// \tparam Tuple type to convert
+template <typename Tuple>
+using tuple_buffer_container_t = typename TupleBufferContainer<Tuple>::Type;
+
+/// Buffer for element values for iterator of \c std::tuple.
+///
+/// The class can be instantiated with a range of iterator of \c std::tuple and
+/// provides an interface for accessing elements of the iterated tuples one by
+/// one.
+///
+/// \note The class is utilized by \c SplittedElementIterator.
+///
+/// \tparam TupleIterator the iterator type that provides values of \c
+/// std::tuple
+///
+/// \todo Consider thread safety of the class.
+template <typename TupleIterator>
+class TupleIteratorBuffer {
+public:
+ /// Type alias for the value type of \p TupleIterator.
+ /// \note The type is expected to be \c std::tuple.
+ using iterator_value_type = typename TupleIterator::value_type;
+
+ /// The number of elements of \c iterator_value_type.
+ static constexpr size_t iterator_value_size =
+ std::tuple_size_v<iterator_value_type>;
+
+ /// Template type alias to get element types of \c iterator_value_type by
+ /// index.
+ /// \tparam I the index of the element
+ template <size_t I>
+ using element_type =
+ typename std::tuple_element<I, iterator_value_type>::type;
+
+ /// Type alias for index sequence for accessing elements of \c
+ /// iterator_value_type.
+ using element_idx_seq_t = seq_t<iterator_value_size>;
+
+ /// Creates a new instance.
+ ///
+ /// \param Begin the beginning of the iterator range to buffer
+ /// \param End the end of the iterator range to buffer
+ TupleIteratorBuffer(TupleIterator &&Begin, const TupleIterator &End) noexcept
+ : Iterator(Begin), End(End), Buffer() {}
+
+ /// Tells whether there is any more value in the handled range for index \p I.
+ ///
+ /// \tparam I the index of the element to check
+ ///
+ /// \return whether there is more value for index \p I
+ template <size_t I> bool hasMore(void) const noexcept {
+ const auto &ElementBuffer = std::get<I>(Buffer);
+ // There is more value if some has already been buffered or the end of the
+ // handled range is not reached yet.
+ return !ElementBuffer.empty() || Iterator != End;
+ }
+
+ /// Gives the next value from the handled range for index \p I.
+ ///
+ /// \note The function should not be called if there is no more value for
+ /// index \p I (i.e., \c hasMore<I>() returns \c false). If it is called in
+ /// that situation, it returns the default value of \c element_type<I>.
+ ///
+ /// \tparam I the index of the element to fetch next value for
+ ///
+ /// \return the next value for index \p I
+ template <size_t I> element_type<I> next(void) noexcept {
+ auto &ElementBuffer = std::get<I>(Buffer);
+ if (ElementBuffer.empty()) {
+ // This position needs a new value from Iterator if there is more.
+ if (Iterator != End) {
+ // Push next value for all elements.
+ push(*Iterator++, element_idx_seq_t());
+ } else {
+ // No more values; called when hasMore<I>() is false.
+ // Return default value.
+ return element_type<I>();
+ }
+ }
+ // Buffer is not empty here, return the next value.
+ ASSERT(!ElementBuffer.empty() && "Unexpected condition");
+ // Get the next value and also pop it from the buffer.
+ const element_type<I> Elem = ElementBuffer.front();
+ ElementBuffer.pop();
+ return Elem;
+ }
+
+private:
+ /// Buffers next tuple value elementwise into element containers.
+ ///
+ /// \tparam S0 indices for accessing elements of \c std::tuple
+ ///
+ /// \param Tuple the values to buffer
+ ///
+ /// \note The second parameter provides indices as template parameter \p S0
+ /// and its actual value is ignored.
+ template <size_t... S0>
+ void push(const iterator_value_type &Tuple, Seq<S0...>) noexcept {
+ (std::get<S0>(Buffer).push(std::get<S0>(Tuple)), ...);
+ }
+
+ TupleIterator Iterator; ///< Current input iterator
+ const TupleIterator End; ///< End of iterator range to handle
+ tuple_buffer_container_t<iterator_value_type>
+ Buffer; ///< Container for elementwise buffering
+};
+
+/// Template type alias for iterator of an element based on buffered iterator of
+/// \c std::tuple.
+///
+/// The alias utilizes \c SplittedElementIterator for iterating over the values
+/// provided by \p TB.
+///
+/// \tparam TB buffer providing values to iterate
+/// \tparam I index of the values to iterator from \p TB
+template <typename TB, size_t I>
+using element_iterator_t = SplittedElementIterator<TB, I>;
+
+/// Template type alias for iterator-range of an element based on buffered
+/// iterator of \c std::tuple.
+///
+/// \tparam TB buffer providing values to iterate
+/// \tparam I index of the values to iterator from \p TB
+template <typename TB, size_t I>
+using element_iterator_range_t =
+ std::pair<element_iterator_t<TB, I>, element_iterator_t<TB, I>>;
+
+///\defgroup ElementIteratorRanges Type converter turning a buffer of \c
+/// std::tuple into a \c std::tuple of corresponding \c
+/// element_iterator_range_t.
+///
+///@{
+
+/// Template declaration.
+///
+/// \tparam TB buffer providing values
+/// \tparam S type providing indices for accessing elements of \c std::tuple
+///
+/// \note \p TB is expected to implement an interface matching that of \c
+/// TupleIteratorBuffer.
+///
+/// \note The template is defined only when \p S is a \c rosa::Seq.
+///
+/// Usage for a proper buffer type \c TB:\code
+/// typename ElementIteratorRanges<TB, typename TB::element_idx_seq_t>::Type
+/// \endcode
+template <typename TB, typename S> struct ElementIteratorRanges;
+
+/// Template definition.
+template <typename TB, size_t... S0>
+struct ElementIteratorRanges<TB, Seq<S0...>> {
+ /// The converted type.
+ using Type = std::tuple<element_iterator_range_t<TB, S0>...>;
+};
+
+///@}
+
+/// Convenience template type alias for easy use of \c ElementIteratorRanges.
+///
+/// Converts a buffer of \c std::tuple into a \c std::tuple of corresponding \c
+/// element_iterator_range_t.
+///
+/// \tparam TB buffer providing values
+///
+/// \note \p TB is expected to implement an interface matching that of \c
+/// TupleIteratorBuffer.
+template <typename TB>
+using element_iterator_ranges_t =
+ typename ElementIteratorRanges<TB, typename TB::element_idx_seq_t>::Type;
+
+/// Template type alias for turning an iterator of \c std::tuple into
+/// corresponding \c element_iterator_ranges_t.
+///
+/// The alias utilizes \c TupleIteratorBuffer for buffering the values provided
+/// by \p TupleIterator. The elementwise iterators are \c
+/// SplittedElementIterator.
+///
+/// \tparam TupleIterator iterator of \c std::tuple
+template <typename TupleIterator>
+using splitted_tuple_iterator_ranges_t =
+ element_iterator_ranges_t<TupleIteratorBuffer<TupleIterator>>;
+
+/// Creates elementwise iterator ranges for an iterator range of \c std::tuple.
+///
+/// \note Implementation for \c rosa::iterator::splitTupleIterator.
+///
+/// \tparam TupleIterator iterator of \c std::tuple
+/// \tparam S0 indices for accessing elements of \c std::tuple
+///
+/// \param Begin the beginning of the iterator range to handle
+/// \param End the end of the iterator range to handle
+///
+/// \note The last parameter provides indices as template parameter \p S0 and
+/// its actual value is ignored.
+///
+/// \return \c std::tuple of elementwise iterator ranges corresponding to \p
+/// Begin and \p End
+template <typename TupleIterator, size_t... S0>
+splitted_tuple_iterator_ranges_t<TupleIterator>
+splitTupleIteratorImpl(TupleIterator &&Begin, const TupleIterator &End,
+ Seq<S0...>) noexcept {
+ using TB = TupleIteratorBuffer<TupleIterator>;
+ // Create a buffer in shared pointer. The buffer will be destructed once
+ // all corresponding element iterators (created below) have reached the end of
+ // the handled range or have been destructed.
+ auto Buffer = std::make_shared<TB>(std::move(Begin), End);
+ return {std::make_pair(element_iterator_t<TB, S0>(Buffer),
+ element_iterator_t<TB, S0>())...};
+}
+
+} // End namespace
+
+/// Gives the beginning of a range created by \c splitTupleIterator().
+///
+/// \see rosa::iterator::splitTupleIterator()
+///
+/// \note The signature of the template function uses implementation details.
+///
+/// \tparam TB buffer providing values to iterate
+/// \tparam I index of the values to iterator from \p TB
+///
+/// \param Range range to get the beginning of
+///
+/// \return beginning of \p Range
+template <typename TB, size_t I>
+element_iterator_t<TB, I> &begin(element_iterator_range_t<TB, I> &Range) {
+ return Range.first;
+}
+
+/// Gives the end of a range created by \c splitTupleIterator().
+///
+/// \see rosa::iterator::splitTupleIterator()
+///
+/// \note The signature of the template function uses implementation details.
+///
+/// \tparam TB buffer providing values to iterate
+/// \tparam I index of the values to iterator from \p TB
+///
+/// \param Range range to get the end of
+///
+/// \return end of \p Range
+template <typename TB, size_t I>
+element_iterator_t<TB, I> &end(element_iterator_range_t<TB, I> &Range) {
+ return Range.second;
+}
+
+/// Creates elementwise iterator ranges for an iterator range of \c std::tuple.
+///
+/// \note The implementation utilizes \c splitTupleIteratorImpl.
+///
+/// Obtain element iterator ranges for an iterator range \c Begin and \c End of
+/// iterator type \c TupleIterator of \c std::tuple<T1, T2, T3> as \code
+/// auto [T1Range, T2Range, T3Range] = splitTupleIterator(std::move(Begin), End);
+/// auto [T1Begin, T1End] = T1Range;
+/// auto [T2Begin, T2End] = T2Range;
+/// auto [T3Begin, T3End] = T3Range;
+/// \endcode
+/// One range can also be dissected by dedicated functions like \code
+/// auto T1Begin = begin(T1Range);
+/// auto T1End = end(T1Range);
+/// \endcode
+///
+/// The function returns a tuple of ranges (i.e., \c std::pair of iterators),
+/// which can be assigned to variables using structured binding. The ranges can
+/// be dissected into begin and end iterators by separate structured bindings.
+///
+/// The function moves its first argument (i.e., \p Begin cannot be used
+/// directly after splitting it). If the first argument is a variable, it needs
+/// to be moved explicitly by \c std::move() as in the example.
+///
+/// \tparam TupleIterator iterator of \c std::tuple
+///
+/// \param Begin the beginning of the iterator range to handle
+/// \param End the end of the iterator range to handle
+///
+/// \return \c std::tuple of elementwise iterator ranges corresponding to \p
+/// Begin and \p End
+template <typename TupleIterator>
+splitted_tuple_iterator_ranges_t<TupleIterator>
+splitTupleIterator(TupleIterator &&Begin, const TupleIterator &End) noexcept {
+ return splitTupleIteratorImpl(
+ std::move(Begin), End,
+ seq_t<std::tuple_size_v<typename TupleIterator::value_type>>());
+}
+
+} // End namespace iterator
+} // End namespace rosa
+
+#endif // ROSA_SUPPORT_ITERATOR_SPLIT_TUPLE_ITERATOR_HPP
diff --git a/include/rosa/support/sequence.hpp b/include/rosa/support/sequence.hpp
index 62b8408..bf61487 100755
--- a/include/rosa/support/sequence.hpp
+++ b/include/rosa/support/sequence.hpp
@@ -1,57 +1,56 @@
//===-- rosa/support/sequence.hpp -------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/support/sequence.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \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;
-
+ /// 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/writer/namespace.h b/include/rosa/support/writer/namespace.h
new file mode 100644
index 0000000..3349a93
--- /dev/null
+++ b/include/rosa/support/writer/namespace.h
@@ -0,0 +1,25 @@
+//===-- rosa/support/writer/namespace.h --------------------------*- C++ -*-===/
+//
+// The RoSA Framework
+//
+//===----------------------------------------------------------------------===/
+///
+/// \file rosa/support/writer/namespace.h
+///
+/// \author David Juhasz (david.juhasz@tuwien.ac.at)
+///
+/// \date 2019
+///
+/// \brief Documentation for the namespace \c rosa::writer.
+///
+//===----------------------------------------------------------------------===/
+
+#ifndef ROSA_SUPPORT_WRITER_NAMESPACE_H
+#define ROSA_SUPPORT_WRITER_NAMESPACE_H
+
+namespace rosa {
+/// Provides facilities to work with writers.
+namespace writer {}
+} // End namespace rosa
+
+#endif // ROSA_SUPPORT_WRITER_NAMESPACE_H
diff --git a/include/rosa/support/writer/split_tuple_writer.hpp b/include/rosa/support/writer/split_tuple_writer.hpp
new file mode 100644
index 0000000..d98ace2
--- /dev/null
+++ b/include/rosa/support/writer/split_tuple_writer.hpp
@@ -0,0 +1,562 @@
+//===-- rosa/support/writer/split_tuple_writer.hpp ---------------*- C++ -*-===/
+//
+// The RoSA Framework
+//
+//===----------------------------------------------------------------------===/
+///
+/// \file rosa/support/writer/split_tuple_writer.hpp
+///
+/// \authors David Juhasz (david.juhasz@tuwien.ac.at)
+///
+/// \date 2019
+///
+/// \brief Facilities to split writers of \c std::tuple into writers of
+/// their elements.
+///
+//===----------------------------------------------------------------------===/
+
+#ifndef ROSA_SUPPORT_WRITER_SPLIT_TUPLE_WRITER_HPP
+#define ROSA_SUPPORT_WRITER_SPLIT_TUPLE_WRITER_HPP
+
+#include "rosa/support/debug.hpp"
+#include "rosa/support/sequence.hpp"
+
+#include <memory>
+#include <tuple>
+#include <queue>
+
+namespace rosa {
+namespace writer {
+
+/// What to do when splitted element writers got destructed so that some
+/// incomplete tuples remain in the buffer.
+enum IncompleteTuplePolicy {
+ Ignore, ///< ignore incomplete tuples, data discarded
+ Flush, ///< flush incomplete tuples, use default value for missing elements
+#ifndef NDEBUG
+ DbgFail ///< fail with an assertion, available only in debug build
+#endif // !defined NDEBUG
+};
+
+/// Anonymous namespace providing implementation for splitting tuple writers;
+/// consider it private.
+namespace {
+
+/// Writes values into a buffer based on an index.
+///
+/// The values are first buffered and flushed out later once a corresponding
+/// tuple gets complete (i.e., other elementwise writers put the corresponding
+/// values into the buffer). Since the underlying buffer waits for tuples to
+/// become complete, the elementwise writers are expected to provide the same
+/// number of values for the buffer (i.e., completing all tuples that have an
+/// element). That is, however, not enforced in any ways.
+///
+/// Once all elementwise writers are destructed, the underlying buffer gets
+/// destructed as well. Should the buffer contain some values, which are
+/// elements of incomplete tuples, the destructor of the buffer handles the
+/// situation according to the \c rosa::writer::IncompleteTuplePolicy set when
+/// the elementwise writers were created by \c rosa::writer::splitTupleWriter().
+///
+/// \tparam TB buffer to write into
+/// \tparam I index of the values to write to \p TB
+///
+/// \note \p TB is expected to implemnet an interface matching that of \c
+/// TupleWriterBuffer.
+///
+/// \note the
+template <typename TB, size_t I>
+class SplittedElementWriter {
+public:
+ /// Type alias for the values the writer writes.
+ using T = typename TB::template element_type<I>;
+
+ /// \defgroup SplittedElemenWriterTypedefs Typedefs of
+ /// \c SplittedElementWriter
+ ///
+ /// Useful `typedef`s for writers.
+ ///
+ ///@{
+ typedef T value_type; ///< Type of values written.
+ typedef T &reference; ///< Reference to the type written
+ ///@}
+
+ /// Creates a new instance.
+ ///
+ /// \param Buffer buffer \p this object writes values into
+ ///
+ /// \note \p Buffer is captured as a \c std::shared_ptr because it is shared
+ /// among writers for its element positions.
+ SplittedElementWriter(std::shared_ptr<TB> Buffer) : Buffer(Buffer) {}
+
+ /// Tells how many values are still in the buffer.
+ ///
+ /// Values are buffered as long as all elements of their corresponding tuples
+ /// are written into the buffer by other elementwise writers and in turn
+ /// complete tuples written by the buffer itself. This function tells how many
+ /// values are still in the buffer waiting for their containing tuples to be
+ /// complete and written out from the buffer.
+ ///
+ /// \note The function simply calles the corresponding function of \p TB.
+ ///
+ /// \return how many values are still in the buffer.
+ size_t buffered(void) const noexcept {
+ return Buffer->template buffered<I>();
+ }
+
+ /// Tells if the last write operation of the buffer was successful.
+ ///
+ /// Values are buffered until their corresponding tuples gets complete. The
+ /// buffer writes complete tuples by the underlying writer. This function
+ /// tells if the buffer successfully has written the latest complete tuple by
+ /// the underlying writer.
+ ///
+ /// Once this function returns \c false, further \c write() calls has no
+ /// effect and the last \c buffered() values will not be written to the
+ /// underlying writer of \c std::tuple.
+ ///
+ /// \note The function simply calles the corresponding function of \p TB.
+ ///
+ /// \return if the last write operation was successful
+ bool good(void) const noexcept { return Buffer->good(); }
+
+ /// Writes an entry to the buffer.
+ ///
+ /// The function has no effect if an earlier write operation of the buffer has
+ /// failed, that is \c good() returns \c false.
+ ///
+ /// \note The function simply calles the corresponding function of \p TB.
+ ///
+ /// \param V value to write
+ void write(const T &V) { Buffer->template write<I>(V); }
+
+private:
+ std::shared_ptr<TB> Buffer; ///< Buffer \p this object writes into
+};
+
+/// Writes an element of a tuple.
+///
+/// \see SplittedElementWriter
+///
+/// \tparam TB type of the buffer \p W writes into
+/// \tparam I index of the values \p W write to \p TB
+///
+/// \param [in,out] W object to write with
+/// \param V value to write
+///
+/// \return \p W after writing \p V with it
+template <typename TB, size_t I>
+SplittedElementWriter<TB, I> &
+operator<<(SplittedElementWriter<TB, I> &W,
+ const typename SplittedElementWriter<TB, I>::value_type &V) {
+ W.write(V);
+ return W;
+}
+
+///\defgroup TupleBufferContainer Type converter turning a \c std::tuple of
+///types into a \c std::tuple of container of those types.
+///
+/// The new type is used for buffering elements of tuples separately.
+///
+///@{
+
+/// Template declaration.
+///
+/// \tparam T type to convert
+///
+/// \note The template is defined only when \p T is a \c std::tuple.
+///
+/// Usage for a type \c Tuple as:\code
+/// typename TupleBufferContainer<Tuple>::Type
+/// \endcode
+template <typename T> struct TupleBufferContainer;
+
+/// Template definition for \c std::tuple.
+template <typename... Ts> struct TupleBufferContainer<std::tuple<Ts...>> {
+ /// The converted type.
+ using Type = std::tuple<std::queue<Ts>...>;
+};
+
+///@}
+
+/// Convenience template type alias for easy use of \c TupleBufferContainer.
+///
+/// Converts a \c std::tuple of types into a \c std::tuple of container of those
+/// types.
+///
+/// \tparam Tuple type to convert
+template <typename Tuple>
+using tuple_buffer_container_t = typename TupleBufferContainer<Tuple>::Type;
+
+/// Buffer for element values for writer of \c std::tuple.
+///
+/// The class can be instantiated with a writer of \c std::tuple and provides
+/// an interface for writing elements into tuples one by one.
+///
+/// \note The class is utilized by \c SplittedElementWriter.
+///
+/// \tparam TupleWriter the writer type that handles values of \c std::tuple
+///
+/// \note The elements of the written \c std::tuple need to be default
+/// constructible because of \c rosa::writer::IncompleteTuplePolicy::Flush.
+///
+/// \todo Consider thread safety of the class.
+template <typename TupleWriter>
+class TupleWriterBuffer {
+public:
+ /// Type alias for the value type of \p TupleWriter.
+ /// \note The type is expected to be \c std::tuple.
+ using writer_value_type = typename TupleWriter::value_type;
+
+ /// The number of elements of \c writer_value_type.
+ static constexpr size_t writer_value_size =
+ std::tuple_size_v<writer_value_type>;
+
+ /// Template type alias to get element types of \c writer_value_type by
+ /// index.
+ /// \tparam I the index of the element
+ template <size_t I>
+ using element_type =
+ typename std::tuple_element<I, writer_value_type>::type;
+
+ /// Type alias for index sequence for accessing elements of \c
+ /// writer_value_size.
+ using element_idx_seq_t = seq_t<writer_value_size>;
+
+ /// Creates a new instance.
+ ///
+ /// \param Writer the writer \p this object uses to write tuples
+ /// \param Policy what to do with incomplete tuples when destructing \p
+ /// this object
+ TupleWriterBuffer(TupleWriter &&Writer,
+ const IncompleteTuplePolicy Policy) noexcept
+ : Writer(Writer), Policy(Policy), Buffer() {}
+
+ /// Destructor.
+ ///
+ /// Should \p this object has some values in \c Buffer (i.e., incomplete
+ /// tuples), the destructor takes care of them according to \c Policy.
+ ///
+ /// \note The destructor may flush the buffered values if the last write
+ /// operation was successful, that is \c good() returns \c true.
+ ~TupleWriterBuffer(void) noexcept {
+ if (good()) {
+ switch (Policy) {
+ default:
+ ASSERT(false && "Unexpected Policy");
+ case Ignore:
+ // Do not care about incomplete tuples in the buffer.
+ break;
+ case Flush:
+ // Whatever we have in the buffer, flush it out.
+ flush();
+ break;
+#ifndef NDEBUG
+ case DbgFail:
+ ASSERT(empty(element_idx_seq_t()) && "Non-empty buffer");
+ break;
+#endif // !defined NDEBUG
+ }
+ }
+ }
+
+ /// Tells how many values are in the buffer for index \p I.
+ ///
+ /// \tparam I the index of the element to check
+ ///
+ /// \return the number of values in buffer for index \p I
+ template <size_t I> size_t buffered(void) const noexcept {
+ return std::get<I>(Buffer).size();
+ }
+
+ /// Tells if the last write operation was successful.
+ ///
+ /// Values are buffered until their corresponding tuples gets complete.
+ /// Once a tuple becomes complete, it is written by \c Writer. This function
+ /// tells if writing the latest complete tuple was successful.
+ ///
+ /// Once this function returns \c false, further \c write<I>() calls has no
+ /// effect and the last \c buffered<I>() values will not be written to the
+ /// underlying writer of \c std::tuple.
+ ///
+ /// \return if the last write operation was successful
+ bool good(void) const noexcept { return Writer.good(); }
+
+ /// Writes an entry to the buffer for index \p I.
+ ///
+ /// The function has no effect if an earlier write operation of the buffer has
+ /// failed, that is \c good() returns \c false.
+ ///
+ /// The value is buffered first and written by \p Writer once its
+ /// corresponding tuple becomes complete (i.e., all other elements of the
+ /// tuple are written into \p this object by corresponding elementwise
+ /// writers).
+ ///
+ /// \tparam I the index of the element to write
+ ///
+ /// \param V value to write
+ template <size_t I> void write(const element_type<I> &V) {
+ ASSERT(!hasCompleteTuple(element_idx_seq_t()));
+ if (good()) {
+ std::get<I>(Buffer).push(V);
+ if (hasCompleteTuple(element_idx_seq_t())) {
+ writeTuple(false);
+ }
+ }
+ ASSERT(!hasCompleteTuple(element_idx_seq_t()));
+ }
+
+private:
+ /// Tells whether \c Buffer is completely empty, all elements of it are empty.
+ ///
+ /// \tparam S0 indices for accessing elements of \c std::tuple
+ ///
+ /// \note The parameter provides indices as template parameter \p S0 and its
+ /// actual value is ignored.
+ ///
+ /// \return whether all elements of \c Buffer are empty
+ template <size_t... S0> bool empty(Seq<S0...>) const noexcept {
+ return (true && ... && std::get<S0>(Buffer).empty());
+ }
+
+ /// Tells whether \c Buffer contains at least one complete tuple.
+ ///
+ /// \tparam S0 indices for accessing elements of \c std::tuple
+ ///
+ /// \note The parameter provides indices as template parameter \p S0 and its
+ /// actual value is ignored.
+ ///
+ /// \return whether there is at least one complete tuple in \c Buffer
+ template <size_t... S0> bool hasCompleteTuple(Seq<S0...>) const noexcept {
+ return (true && ... && !std::get<S0>(Buffer).empty());
+ }
+
+ /// Makes sure \c Buffer has one complete tuple in front.
+ ///
+ /// If the tuple in front of \c Buffer is not complete, missing elements are
+ /// default constructed in \c Buffer to complete the tuple.
+ ///
+ /// \tparam S0 indices for accessing elements of \c std::tuple
+ ///
+ /// \note The parameter provides indices as template parameter \p S0 and its
+ /// actual value is ignored.
+ template <size_t... S0> void makeCompleteTuple(Seq<S0...>) noexcept {
+ auto addDefaultIfEmpty = [](auto &Queue) {
+ if (Queue.empty()) {
+ Queue.emplace(); // default construct a value in the empty queue.
+ }
+ };
+ (addDefaultIfEmpty(std::get<S0>(Buffer)), ...);
+ }
+
+ /// Gives the first complete tuple from \c Buffer.
+ ///
+ /// \tparam S0 indices for accessing elements of \c std::tuple
+ ///
+ /// \note The parameter provides indices as template parameter \p S0 and its
+ /// actual value is ignored.
+ ///
+ /// \return the first complete tuple from \c Buffer
+ ///
+ /// \pre There is at least one complete tuple in \c Buffer:\code
+ /// hasCompleteTuple(element_idx_seq_t())
+ /// \endcode
+ template <size_t... S0> writer_value_type front(Seq<S0...>) const noexcept {
+ ASSERT(hasCompleteTuple(element_idx_seq_t()));
+ return {std::get<S0>(Buffer).front()...};
+ }
+
+ /// Removes the first complete tuple from \c Buffer.
+ ///
+ /// \tparam S0 indices for accessing elements of \c std::tuple
+ ///
+ /// \note The parameter provides indices as template parameter \p S0 and its
+ /// actual value is ignored.
+ ///
+ /// \pre There is at least one complete tuple in \c Buffer:\code
+ /// hasCompleteTuple(element_idx_seq_t())
+ /// \endcode
+ template <size_t... S0> void pop(Seq<S0...>) noexcept {
+ ASSERT(hasCompleteTuple(element_idx_seq_t()));
+ (std::get<S0>(Buffer).pop(), ...);
+ }
+
+ /// Takes the first tuple from \c Buffer and writes it with \c Writer.
+ ///
+ /// \c Buffer need to contain a complete tuple to write it with \c Writer. The
+ /// function makes sure that the tuple in front of \c Buffer is complete if \p
+ /// MakeComplete is true. The tuple in fron of \c Buffer are expected to be
+ /// complete otherwise.
+ ///
+ /// \param MakeComplete whether to make the first tuple complete
+ ///
+ /// \pre There is at least one complete tuple in \c Buffer if not \p
+ /// MakeComplete: \code
+ /// MakeComplete || hasCompleteTuple(element_idx_seq_t())
+ /// \endcode
+ void writeTuple(const bool MakeComplete) noexcept {
+ if (MakeComplete) {
+ makeCompleteTuple(element_idx_seq_t());
+ }
+ ASSERT(hasCompleteTuple(element_idx_seq_t()));
+ Writer.write(front(element_idx_seq_t()));
+ pop(element_idx_seq_t());
+ }
+
+ /// Empties \c Buffer by writing out all tuples from it so that missing
+ /// elements of incomplete tuples are extended with default constructed
+ /// values.
+ void flush(void) noexcept {
+ while (!empty(element_idx_seq_t())) {
+ ASSERT(!hasCompleteTuple(element_idx_seq_t())); // Sanity check.
+ writeTuple(true);
+ }
+ }
+
+ TupleWriter Writer; ///< The splitted writer
+ const IncompleteTuplePolicy Policy; ///< what to do with incomplete tuples
+ ///< when destructing \p this object
+ tuple_buffer_container_t<writer_value_type>
+ Buffer; ///< Container for elementwise buffering
+};
+
+/// Template type alias for writer of an element based on buffered writer of
+/// \c std::tuple.
+///
+/// The alias utilizes \c SplittedElementWriter for writing element values by
+/// \p TB.
+///
+/// \tparam TB buffer to write into
+/// \tparam I index of the values to write to \p TB
+template <typename TB, size_t I>
+using element_writer_t = SplittedElementWriter<TB, I>;
+
+///\defgroup ElementWriters Type converter turning a buffer of \c std::tuple
+///into a \c std::tuple of corresponding \c element_writer_t.
+///
+///@{
+
+/// Template declaration.
+///
+/// \tparam TB buffer to write into
+/// \tparam S type providing indices for accessing elements of \c std::tuple
+///
+/// \note \p TB is expected to implement an interface matching that of \c
+/// TupleWriterBuffer.
+///
+/// \note The template is defined only when \p S is a \c rosa::Seq.
+///
+/// Usage for a proper buffer type \c TB:\code
+/// typename ElementIteratorWriters<TB, typename TB::element_idx_seq_t>::Type
+/// \endcode
+template <typename TB, typename S> struct ElementWriters;
+
+/// Template definition.
+template <typename TB, size_t... S0>
+struct ElementWriters<TB, Seq<S0...>> {
+ /// The converted type.
+ using Type = std::tuple<element_writer_t<TB, S0>...>;
+};
+
+///@}
+
+/// Convenience template type alias for easy use of \c ElementIteratorWriters.
+///
+/// Converts a buffer of \c std::tuple into a \c std::tuple of corresponding \c
+/// element_writer_t.
+///
+/// \tparam TB buffer to write into
+///
+/// \note \p TB is expected to implement an interface matching that of \c
+/// TupleWriterBuffer.
+template <typename TB>
+using element_writers_t =
+ typename ElementWriters<TB, typename TB::element_idx_seq_t>::Type;
+
+/// Template type alias for turning a writer of \c std::tuple into
+/// corresponding \c element_writers_t.
+///
+/// The alias utilizes \c TupleWriterBuffer for buffering values before writing
+/// them by \p TupleWriter. The elementwise writers are \c
+/// SplittedElementWriter.
+///
+/// \tparam TupleWriter writer of \c std::tuple
+template <typename TupleWriter>
+using splitted_tuple_writers_t =
+ element_writers_t<TupleWriterBuffer<TupleWriter>>;
+
+/// Creates elementwise writers for a writer of \c std::tuple.
+///
+/// \note Implementation for \c rosa::iterator::splitTupleWriter.
+///
+/// \tparam TupleWriter writer of \c std::tuple
+/// \tparam S0 indices for accessing elements of \c std::tuple
+///
+/// \param Writer the writer to write tuples with
+/// \param Policy how to handle incomplete tuples when destructing the
+/// underlying buffer
+///
+/// \note The last parameter provides indices as template parameter \p S0 and
+/// its actual value is ignored.
+///
+/// \return \c std::tuple of elementwise writers corresponding to \p Writer
+template <typename TupleWriter, size_t... S0>
+splitted_tuple_writers_t<TupleWriter>
+splitTupleWriterImpl(TupleWriter &&Writer, const IncompleteTuplePolicy Policy,
+ Seq<S0...>) noexcept {
+ using TB = TupleWriterBuffer<TupleWriter>;
+ // Create a buffer in shared pointer. The buffer will be destructed once
+ // all corresponding element writers (created below) have been destructed.
+ auto Buffer = std::make_shared<TB>(std::move(Writer), Policy);
+ return {element_writer_t<TB, S0>(Buffer)...};
+}
+
+} // End namespace
+
+/// Creates elementwise writers for a writer of \c std::tuple.
+///
+/// \note The implementation utilizes \c splitTupleWriterImpl.
+///
+/// Obtain elementwise writers for a writer \p Writer of type \c TupleWriter of
+/// \c std::tuple<T1, T2, T3> as \code
+/// auto [T1Writer, T2Writer, T3Writer] = splitTupleWriter(std::move(Writer),
+/// Ignore);
+/// \endcode
+///
+/// The function returns a tuple of writers, which can be assigned to variables
+/// using structured binding.
+///
+/// The function moves its first argument (i.e., \p Writer cannot be used
+/// directly after splitting it). If the first argument is a variable, it needs
+/// to be moved explicitly by \c std::move() as in the example.
+///
+/// While a tuple could be written with \p Writer directly as \code
+/// Writer << std::make_tuple(T1(), T2(), T3());
+/// \endcode The same tuple is written with splitted writers as \code
+/// T1Writer << T1();
+/// T2Writer << T2();
+/// T3Writer << T3();
+/// \endcode
+///
+/// \tparam TupleWriter writer of \c std::tuple
+///
+/// \note The elements of the written \c std::tuple need to be default
+/// constructible because of \c rosa::writer::IncompleteTuplePolicy::Flush.
+///
+/// \param Writer the writer to write tuples with
+/// \param Policy what to do with incomplete tuples when destructing the
+/// underlying buffer
+///
+/// \return \c std::tuple of elementwise writers corresponding to \p Writer
+template <typename TupleWriter>
+splitted_tuple_writers_t<TupleWriter>
+splitTupleWriter(TupleWriter &&Writer,
+ const IncompleteTuplePolicy Policy) noexcept {
+ return splitTupleWriterImpl(
+ std::move(Writer), Policy,
+ seq_t<std::tuple_size_v<typename TupleWriter::value_type>>());
+}
+
+} // End namespace writer
+} // End namespace rosa
+
+#endif // ROSA_SUPPORT_WRITER_SPLIT_TUPLE_WRITER_HPP
diff --git a/lib/deluxe/DeluxeAgent.cpp b/lib/deluxe/DeluxeAgent.cpp
index 8f6669e..3deb5ea 100644
--- a/lib/deluxe/DeluxeAgent.cpp
+++ b/lib/deluxe/DeluxeAgent.cpp
@@ -1,313 +1,298 @@
//===-- 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 &&
- InputStorageOffsets.size() == NumberOfInputs &&
- InputValues->size() ==
- InputStorageOffsets[NumberOfInputs - 1] +
- lengthOfToken(InputTypes[NumberOfInputs - 1]) &&
+ InputValues.size() == NumberOfInputs &&
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 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 size of storage at position \c I.
+ if (InputValues[I]->size() != TL) {
+ return false;
+ }
// 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)) !=
+ if (InputValues[I]->typeAt(static_cast<token_size_t>(TI)) !=
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` and `MasterInputType` of the registered *slave*.
const auto &A = unwrapAgent(*Slave);
if (!((A.Kind == atoms::SensorKind &&
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 &&
(!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_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());
}
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] &&
(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] &&
(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/support/CMakeLists.txt b/lib/support/CMakeLists.txt
index 6e191ef..318486d 100644
--- a/lib/support/CMakeLists.txt
+++ b/lib/support/CMakeLists.txt
@@ -1,38 +1,46 @@
set(LIB_INCLUDE_DIR ${ROSA_MAIN_INCLUDE_DIR}/rosa/support)
add_library(ROSASupport
${LIB_INCLUDE_DIR}/debug.hpp
debug.cpp
${LIB_INCLUDE_DIR}/terminal_colors.h
terminal_colors.cpp
${LIB_INCLUDE_DIR}/log.h
log.cpp
${LIB_INCLUDE_DIR}/math.hpp
math.cpp
${LIB_INCLUDE_DIR}/type_helper.hpp
type_helper.cpp
${LIB_INCLUDE_DIR}/types.hpp
types.cpp
${LIB_INCLUDE_DIR}/atom.hpp
atom.cpp
${LIB_INCLUDE_DIR}/type_pair.hpp
type_pair.cpp
${LIB_INCLUDE_DIR}/type_list.hpp
type_list.cpp
${LIB_INCLUDE_DIR}/squashed_int.hpp
squashed_int.cpp
${LIB_INCLUDE_DIR}/type_numbers.hpp
type_numbers.cpp
${LIB_INCLUDE_DIR}/type_token.hpp
type_token.cpp
${LIB_INCLUDE_DIR}/tokenized_storages.hpp
tokenized_storages.cpp
${LIB_INCLUDE_DIR}/sequence.hpp
sequence.cpp
${LIB_INCLUDE_DIR}/csv/namespace.h
csv/namespace.cpp
${LIB_INCLUDE_DIR}/csv/CSVReader.hpp
csv/CSVReader.cpp
${LIB_INCLUDE_DIR}/csv/CSVWriter.hpp
csv/CSVWriter.cpp
+ ${LIB_INCLUDE_DIR}/iterator/namespace.h
+ iterator/namespace.cpp
+ ${LIB_INCLUDE_DIR}/iterator/split_tuple_iterator.hpp
+ iterator/split_tuple_iterator.cpp
+ ${LIB_INCLUDE_DIR}/writer/namespace.h
+ writer/namespace.cpp
+ ${LIB_INCLUDE_DIR}/writer/split_tuple_writer.hpp
+ writer/split_tuple_writer.cpp
)
diff --git a/lib/support/iterator/namespace.cpp b/lib/support/iterator/namespace.cpp
new file mode 100644
index 0000000..57fe49f
--- /dev/null
+++ b/lib/support/iterator/namespace.cpp
@@ -0,0 +1,20 @@
+//===-- support/iterator/namespace.cpp --------------------------*- C++ -*-===/
+//
+// The RoSA Framework
+//
+//===----------------------------------------------------------------------===/
+///
+/// \file support/iterator/namespace.cpp
+///
+/// \author David Juhasz (david.juhasz@tuwien.ac.at)
+///
+/// \date 2019
+///
+/// \brief Placeholder for rosa/support/iterator/namespace.h.
+///
+/// \note Empty implementation, source file here to have a compile database
+/// entry for rosa/support/iterator/namespace.h.
+///
+//===----------------------------------------------------------------------===/
+
+#include "rosa/support/iterator/namespace.h"
diff --git a/lib/support/iterator/split_tuple_iterator.cpp b/lib/support/iterator/split_tuple_iterator.cpp
new file mode 100644
index 0000000..72f4f1f
--- /dev/null
+++ b/lib/support/iterator/split_tuple_iterator.cpp
@@ -0,0 +1,20 @@
+//===-- support/iterator/split_tuple_iterator.cpp ---------------*- C++ -*-===/
+//
+// The RoSA Framework
+//
+//===----------------------------------------------------------------------===/
+///
+/// \file support/iterator/split_tuple_iterator.cpp
+///
+/// \author David Juhasz (david.juhasz@tuwien.ac.at)
+///
+/// \date 2019
+///
+/// \brief Implementation for rosa/support/iterator/split_tuple_iterator.hpp.
+///
+/// \note Empty implementation, source file here to have a compile database
+/// entry for rosa/support/iterator/split_tuple_iterator.hpp.
+///
+//===----------------------------------------------------------------------===/
+
+#include "rosa/support/iterator/split_tuple_iterator.hpp"
diff --git a/lib/support/writer/namespace.cpp b/lib/support/writer/namespace.cpp
new file mode 100644
index 0000000..b659e2d
--- /dev/null
+++ b/lib/support/writer/namespace.cpp
@@ -0,0 +1,20 @@
+//===-- support/writer/namespace.cpp -----------------------------*- C++ -*-===/
+//
+// The RoSA Framework
+//
+//===----------------------------------------------------------------------===/
+///
+/// \file support/writer/namespace.cpp
+///
+/// \author David Juhasz (david.juhasz@tuwien.ac.at)
+///
+/// \date 2019
+///
+/// \brief Placeholder for rosa/support/iterator/namespace.h.
+///
+/// \note Empty implementation, source file here to have a compile database
+/// entry for rosa/support/writer/namespace.h.
+///
+//===----------------------------------------------------------------------===/
+
+#include "rosa/support/writer/namespace.h"
diff --git a/lib/support/writer/split_tuple_writer.cpp b/lib/support/writer/split_tuple_writer.cpp
new file mode 100644
index 0000000..651e012
--- /dev/null
+++ b/lib/support/writer/split_tuple_writer.cpp
@@ -0,0 +1,20 @@
+//===-- support/writer/split_tuple_writer.cpp --------------------*- C++ -*-===/
+//
+// The RoSA Framework
+//
+//===----------------------------------------------------------------------===/
+///
+/// \file support/writer/split_tuple_writer.cpp
+///
+/// \author David Juhasz (david.juhasz@tuwien.ac.at)
+///
+/// \date 2019
+///
+/// \brief Implementation for rosa/support/writer/split_tuple_writer.hpp.
+///
+/// \note Empty implementation, source file here to have a compile database
+/// entry for rosa/support/writer/split_tuple_writer.hpp.
+///
+//===----------------------------------------------------------------------===/
+
+#include "rosa/support/writer/split_tuple_writer.hpp"

File Metadata

Mime Type
text/x-diff
Expires
Fri, Mar 14, 7:34 PM (5 h, 52 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
128952
Default Alt Text
(234 KB)

Event Timeline