Page MenuHomePhorge

No OneTemporary

Size
161 KB
Referenced Files
None
Subscribers
None
diff --git a/apps/ccam/ccam.cpp b/apps/ccam/ccam.cpp
index e6f615d..98aa787 100644
--- a/apps/ccam/ccam.cpp
+++ b/apps/ccam/ccam.cpp
@@ -1,279 +1,279 @@
//===-- apps/ccam/ccam.cpp --------------------------------------*- C++ -*-===//
//
// The RoSA Framework -- Application CCAM
//
//===----------------------------------------------------------------------===//
///
/// \file apps/ccam/ccam.cpp
///
/// \author Maximilian Goetzinger (maximilian.goetzinger@tuwien.ac.at)
/// \author Benedikt Tutzer (benedikt.tutzer@tuwien.ac.at)
///
/// \date 2019
///
/// \brief The application CCAM implements the case study from the paper:
/// M. Goetzinger, N. TaheriNejad, H. A. Kholerdi, A. Jantsch, E. Willegger,
/// T. Glatzl, A.M. Rahmani, T.Sauter, P. Liljeberg: Model - Free Condition
/// Monitoring with Confidence
//===----------------------------------------------------------------------===//
#include "rosa/agent/Abstraction.hpp"
#include "rosa/agent/Confidence.hpp"
#include "rosa/agent/FunctionAbstractions.hpp"
#include <iostream>
#include "rosa/config/version.h"
#include "rosa/agent/SignalStateDetector.hpp"
#include "rosa/agent/SystemStateDetector.hpp"
#include "rosa/deluxe/DeluxeContext.hpp"
#include "rosa/support/csv/CSVReader.hpp"
#include "rosa/support/csv/CSVWriter.hpp"
#include <fstream>
#include <limits>
#include <memory>
#include <streambuf>
#include "configuration.h"
#include "statehandlerutils.h"
using namespace rosa;
using namespace rosa::agent;
using namespace rosa::deluxe;
using namespace rosa::terminal;
const std::string AppName = "CCAM";
int main(int argc, char **argv) {
LOG_INFO_STREAM << '\n'
<< library_string() << " -- " << Color::Red << AppName
<< "app" << Color::Default << '\n';
if (argc < 2) {
LOG_ERROR("Specify config File!\nUsage:\n\tccam config.json");
return 1;
}
std::string ConfigPath = argv[1];
if (!readConfigFile(ConfigPath)) {
LOG_ERROR_STREAM << "Could not read config from \"" << ConfigPath << "\"\n";
return 2;
}
std::string InputFilePath, OutputFilePath;
LOG_INFO("Creating Context");
std::unique_ptr<DeluxeContext> C = DeluxeContext::create(AppName);
- std::shared_ptr<PartialFunction<float, float>> BrokenDelayFunction(
- new PartialFunction<float, float>(
+ std::shared_ptr<PartialFunction<uint32_t, float>> BrokenDelayFunction(
+ new PartialFunction<uint32_t, float>(
{{{0, AppConfig.BrokenCounter},
- std::make_shared<LinearFunction<float, float>>(
+ std::make_shared<LinearFunction<uint32_t, float>>(
0, 0.f, AppConfig.BrokenCounter, 1.f)},
- {{AppConfig.BrokenCounter, std::numeric_limits<float>::max()},
- std::make_shared<LinearFunction<float, float>>(1.f, 0.f)}},
- 0));
+ {{AppConfig.BrokenCounter, std::numeric_limits<uint32_t>::max()},
+ std::make_shared<LinearFunction<uint32_t, float>>(1.f, 0.f)}},
+ 0.f));
- std::shared_ptr<PartialFunction<float, float>> OkDelayFunction(
- new PartialFunction<float, float>(
+ std::shared_ptr<PartialFunction<uint32_t, float>> OkDelayFunction(
+ new PartialFunction<uint32_t, float>(
{{{0, AppConfig.BrokenCounter},
- std::make_shared<LinearFunction<float, float>>(
+ std::make_shared<LinearFunction<uint32_t, float>>(
0, 1.f, AppConfig.BrokenCounter, 0.f)},
- {{AppConfig.BrokenCounter, std::numeric_limits<float>::max()},
- std::make_shared<LinearFunction<float, float>>(0.f, 0.f)}},
- 1));
+ {{AppConfig.BrokenCounter, std::numeric_limits<uint32_t>::max()},
+ std::make_shared<LinearFunction<uint32_t, float>>(0.f, 0.f)}},
+ 1.f));
//
// Create a DeluxeAgent with SystemStateDetector functionality.
//
LOG_INFO("Create SystemStateDetector agent.");
AgentHandle SystemStateDetectorAgent = createSystemStateDetectorAgent(
C, "SystemStateDetector", AppConfig.SignalConfigurations.size(),
BrokenDelayFunction, OkDelayFunction);
LOG_INFO("Creating sensors, SignalStateDetector functionalities and their "
"Abstractions.");
std::vector<AgentHandle> Sensors;
std::vector<std::shared_ptr<PartialFunction<float, float>>>
SampleMatchesFunctions;
std::vector<std::shared_ptr<PartialFunction<float, float>>>
SampleMismatchesFunctions;
std::vector<std::shared_ptr<PartialFunction<float, float>>>
SignalIsStableFunctions;
std::vector<std::shared_ptr<PartialFunction<float, float>>>
SignalIsDriftingFunctions;
std::vector<std::shared_ptr<StepFunction<float, float>>>
NumOfSamplesMatchFunctions;
std::vector<std::shared_ptr<StepFunction<float, float>>>
NumOfSamplesMismatchFunctions;
std::vector<std::shared_ptr<
SignalStateDetector<float, float, float, HistoryPolicy::FIFO>>>
SignalStateDetectors;
std::vector<AgentHandle> SignalStateDetectorAgents;
for (auto SignalConfiguration : AppConfig.SignalConfigurations) {
//
// Create deluxe sensors.
//
Sensors.emplace_back(C->createSensor<float>(SignalConfiguration.Name));
//
// Create functionalities for SignalStateDetector.
//
SampleMatchesFunctions.emplace_back(new PartialFunction<float, float>(
{
{{-SignalConfiguration.OuterBound, -SignalConfiguration.InnerBound},
std::make_shared<LinearFunction<float, float>>(
-SignalConfiguration.OuterBound, 0.f,
-SignalConfiguration.InnerBound, 1.f)},
{{-SignalConfiguration.InnerBound, SignalConfiguration.InnerBound},
std::make_shared<LinearFunction<float, float>>(1.f, 0.f)},
{{SignalConfiguration.InnerBound, SignalConfiguration.OuterBound},
std::make_shared<LinearFunction<float, float>>(
SignalConfiguration.InnerBound, 1.f,
SignalConfiguration.OuterBound, 0.f)},
},
0));
SampleMismatchesFunctions.emplace_back(new PartialFunction<float, float>(
{
{{-SignalConfiguration.OuterBound, -SignalConfiguration.InnerBound},
std::make_shared<LinearFunction<float, float>>(
-SignalConfiguration.OuterBound, 1.f,
-SignalConfiguration.InnerBound, 0.f)},
{{-SignalConfiguration.InnerBound, SignalConfiguration.InnerBound},
std::make_shared<LinearFunction<float, float>>(0.f, 0.f)},
{{SignalConfiguration.InnerBound, SignalConfiguration.OuterBound},
std::make_shared<LinearFunction<float, float>>(
SignalConfiguration.InnerBound, 0.f,
SignalConfiguration.OuterBound, 1.f)},
},
1));
SignalIsStableFunctions.emplace_back(new PartialFunction<float, float>(
{
{{-SignalConfiguration.OuterBoundDrift,
-SignalConfiguration.InnerBoundDrift},
std::make_shared<LinearFunction<float, float>>(
-SignalConfiguration.OuterBoundDrift, 0.f,
-SignalConfiguration.InnerBoundDrift, 1.f)},
{{-SignalConfiguration.InnerBoundDrift,
SignalConfiguration.InnerBoundDrift},
std::make_shared<LinearFunction<float, float>>(1.f, 0.f)},
{{SignalConfiguration.InnerBoundDrift,
SignalConfiguration.OuterBoundDrift},
std::make_shared<LinearFunction<float, float>>(
SignalConfiguration.InnerBoundDrift, 1.f,
SignalConfiguration.OuterBoundDrift, 0.f)},
},
0));
SignalIsDriftingFunctions.emplace_back(new PartialFunction<float, float>(
{
{{-SignalConfiguration.OuterBoundDrift,
-SignalConfiguration.InnerBoundDrift},
std::make_shared<LinearFunction<float, float>>(
-SignalConfiguration.OuterBoundDrift, 1.f,
-SignalConfiguration.InnerBoundDrift, 0.f)},
{{-SignalConfiguration.InnerBoundDrift,
SignalConfiguration.InnerBoundDrift},
std::make_shared<LinearFunction<float, float>>(0.f, 0.f)},
{{SignalConfiguration.InnerBoundDrift,
SignalConfiguration.OuterBoundDrift},
std::make_shared<LinearFunction<float, float>>(
SignalConfiguration.InnerBoundDrift, 0.f,
SignalConfiguration.OuterBoundDrift, 1.f)},
},
1));
NumOfSamplesMatchFunctions.emplace_back(new StepFunction<float, float>(
1.0f / SignalConfiguration.SampleHistorySize, StepDirection::StepUp));
NumOfSamplesMismatchFunctions.emplace_back(new StepFunction<float, float>(
1.0f / SignalConfiguration.SampleHistorySize, StepDirection::StepDown));
//
// Create SignalStateDetector functionality
//
SignalStateDetectors.emplace_back(
new SignalStateDetector<float, float, float, HistoryPolicy::FIFO>(
SignalConfiguration.Output ? SignalProperties::OUTPUT
: SignalProperties::INPUT,
std::numeric_limits<int>::max(), SampleMatchesFunctions.back(),
SampleMismatchesFunctions.back(), NumOfSamplesMatchFunctions.back(),
NumOfSamplesMismatchFunctions.back(),
SignalIsDriftingFunctions.back(), SignalIsStableFunctions.back(),
SignalConfiguration.SampleHistorySize, SignalConfiguration.DABSize,
SignalConfiguration.DABHistorySize));
//
// Create low-level deluxe agents
//
SignalStateDetectorAgents.push_back(createSignalStateDetectorAgent(
C, SignalConfiguration.Name, SignalStateDetectors.back()));
//
// Connect sensors to low-level agents.
//
LOG_INFO("Connect sensors to their corresponding low-level agents.");
C->connectSensor(SignalStateDetectorAgents.back(), 0, Sensors.back(),
SignalConfiguration.Name);
C->connectAgents(SystemStateDetectorAgent, SignalStateDetectors.size() - 1,
SignalStateDetectorAgents.back(),
SignalConfiguration.Name);
}
//
// 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 OutputCSV(AppConfig.OutputFilePath);
// The agent writes each new input value into a CSV file and produces nothing.
using Input = std::pair<SystemStateTuple, bool>;
using Result = Optional<DeluxeTuple<unit_t>>;
using Handler = std::function<Result(Input)>;
std::string Name = "Logger Agent";
AgentHandle LoggerAgent =
C->createAgent("Logger Agent", Handler([&OutputCSV](Input I) -> Result {
OutputCSV << std::get<0>(I.first) << std::endl;
return Result();
}));
//
// Connect the high-level agent to the logger agent.
//
LOG_INFO("Connect the high-level agent to the logger agent.");
C->connectAgents(LoggerAgent, 0, SystemStateDetectorAgent,
"SystemStateDetector 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.
//
//
// Simulate.
//
C->simulate(AppConfig.NumberOfSimulationCycles);
return 0;
}
diff --git a/apps/ccam/statehandlerutils.h b/apps/ccam/statehandlerutils.h
index 477bafe..b19eac4 100644
--- a/apps/ccam/statehandlerutils.h
+++ b/apps/ccam/statehandlerutils.h
@@ -1,182 +1,179 @@
#ifndef STATEHANDLERUTILS_H
#define STATEHANDLERUTILS_H
#include "rosa/agent/Abstraction.hpp"
#include "rosa/agent/Confidence.hpp"
#include "rosa/agent/FunctionAbstractions.hpp"
#include <functional>
#include <iostream>
#include <tuple>
#include <vector>
#include "rosa/config/version.h"
#include "rosa/agent/SignalStateDetector.hpp"
#include "rosa/agent/SystemStateDetector.hpp"
#include "rosa/deluxe/DeluxeContext.hpp"
#include "rosa/support/csv/CSVReader.hpp"
#include "rosa/support/csv/CSVWriter.hpp"
#include <fstream>
#include <limits>
#include <memory>
#include <streambuf>
using namespace rosa;
using namespace rosa::agent;
using namespace rosa::deluxe;
using namespace rosa::terminal;
// Signal State
using SignalStateTuple = DeluxeTuple<float, uint32_t, uint8_t, float, uint8_t,
uint32_t, bool, bool, bool>;
AgentHandle createSignalStateDetectorAgent(
std::unique_ptr<DeluxeContext> &C, const std::string &Name,
std::shared_ptr<
SignalStateDetector<float, float, float, HistoryPolicy::FIFO>>
SigSD) {
using Input = std::pair<DeluxeTuple<float>, bool>;
using Result = Optional<SignalStateTuple>;
using Handler = std::function<Result(Input)>;
return C->createAgent(
Name, Handler([&Name, &SigSD](Input I) -> Result {
LOG_INFO_STREAM << "\n******\n"
<< Name << " " << (I.second ? "<New>" : "<Old>")
<< " value: " << std::get<0>(I.first) << "\n******\n";
auto StateInfo = SigSD->detectSignalState(std::get<0>(I.first));
if (I.second) {
SignalStateTuple Res = {
std::get<0>(I.first),
StateInfo.SignalStateID,
StateInfo.SignalProperty,
StateInfo.SignalStateConfidence,
StateInfo.SignalStateCondition,
StateInfo.NumberOfInsertedSamplesAfterEntrance,
StateInfo.SignalStateIsValid,
StateInfo.SignalStateJustGotValid,
StateInfo.SignalStateIsValidAfterReentrance};
return Result(Res);
}
return Result();
}));
}
// System State
using SystemStateTuple = DeluxeTuple<std::string, uint32_t, float, uint8_t,
uint32_t, bool, bool, bool, bool>;
template <std::size_t size, typename ret, typename functype, typename... A>
struct Handler_helper;
template <typename B, typename func, typename A, typename... As>
struct function_helper {
static_assert(std::conjunction_v<std::is_same<A, As>...>,
"All types need to be identical");
static B function(A valA, As... valAs) {
std::vector<A> ar({valA, valAs...});
return func()(ar);
}
};
template <typename ret, typename typeA, typename functype, typename... B>
struct Handler_helper<0, ret, functype, typeA, B...> {
using handler = function_helper<ret, functype, B...>;
};
template <std::size_t size, typename ret, typename typeA, typename functype,
typename... B>
struct Handler_helper<size, ret, functype, typeA, B...> {
using handler =
typename Handler_helper<size - 1, ret, functype, typeA,
std::pair<typeA, bool>, B...>::handler;
};
template <std::size_t size, typename ret, typename functype, typename typeA>
using Handler = typename Handler_helper<size, ret, functype, typeA>::handler;
// todo: state-detector durschleifen
template <typename ret, typename A> struct function {
ret operator()(A a) {
// std::vector<SystemStateInformation> out;
for (auto tmp1 : a) {
// convert tuple to info struct out.push_back({});
(void)tmp1;
LOG_INFO_STREAM << "new SignalStateTuple!\n";
}
// feed state detector
// return result
return ret();
}
};
using arr = std::vector<std::pair<SignalStateTuple, bool>>;
-auto HandlerFunction = Handler<4, Optional<SystemStateTuple>,
- function<Optional<SystemStateTuple>, arr>,
- SignalStateTuple>::function;
-
template <size_t NumOfSlaves>
AgentHandle createSystemStateDetectorAgent(
std::unique_ptr<DeluxeContext> &C, const std::string &Name,
- std::shared_ptr<PartialFunction<float, float>> BrokenDelayFunction,
- std::shared_ptr<PartialFunction<float, float>> OkDelayFunction) {
+ std::shared_ptr<PartialFunction<uint32_t, float>> BrokenDelayFunction,
+ std::shared_ptr<PartialFunction<uint32_t, float>> OkDelayFunction) {
LOG_TRACE("Creating fixed SystemStateDetectorAgent");
using Input = SignalStateTuple;
using Result = Optional<SystemStateTuple>;
auto HandlerFunction =
Handler<NumOfSlaves, Result, function<Optional<SystemStateTuple>, arr>,
Input>::function;
- std::shared_ptr<SystemStateDetector<float, float, float, HistoryPolicy::FIFO>>
- SysSD(new SystemStateDetector<float, float, float, HistoryPolicy::FIFO>(
+ std::shared_ptr<
+ SystemStateDetector<uint32_t, float, float, HistoryPolicy::FIFO>>
+ SysSD(new SystemStateDetector<uint32_t, float, float, HistoryPolicy::FIFO>(
std::numeric_limits<uint32_t>::max(), BrokenDelayFunction,
OkDelayFunction));
return C->createAgent(Name, std::function(HandlerFunction));
}
AgentHandle createSystemStateDetectorAgent(
std::unique_ptr<DeluxeContext> &C, const std::string &Name,
- uint8_t NumOfSlaves,
- std::shared_ptr<PartialFunction<float, float>> BrokenDelayFunction,
- std::shared_ptr<PartialFunction<float, float>> OkDelayFunction) {
+ size_t NumOfSlaves,
+ std::shared_ptr<PartialFunction<uint32_t, float>> BrokenDelayFunction,
+ std::shared_ptr<PartialFunction<uint32_t, float>> OkDelayFunction) {
LOG_TRACE("Creating dynamic SystemStateDetectorAgent");
switch (NumOfSlaves) {
- // clang-format off
+ // clang-format off
case 2: return createSystemStateDetectorAgent< 2>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 3: return createSystemStateDetectorAgent< 3>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 4: return createSystemStateDetectorAgent< 4>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 5: return createSystemStateDetectorAgent< 5>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 6: return createSystemStateDetectorAgent< 6>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 7: return createSystemStateDetectorAgent< 7>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 8: return createSystemStateDetectorAgent< 8>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 9: return createSystemStateDetectorAgent< 9>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 10: return createSystemStateDetectorAgent<10>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 11: return createSystemStateDetectorAgent<11>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 12: return createSystemStateDetectorAgent<12>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 13: return createSystemStateDetectorAgent<13>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 14: return createSystemStateDetectorAgent<14>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 15: return createSystemStateDetectorAgent<15>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 16: return createSystemStateDetectorAgent<16>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 17: return createSystemStateDetectorAgent<17>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 18: return createSystemStateDetectorAgent<18>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 19: return createSystemStateDetectorAgent<19>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 20: return createSystemStateDetectorAgent<20>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 21: return createSystemStateDetectorAgent<21>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 22: return createSystemStateDetectorAgent<22>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 23: return createSystemStateDetectorAgent<23>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 24: return createSystemStateDetectorAgent<24>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 25: return createSystemStateDetectorAgent<25>(C, Name, BrokenDelayFunction, OkDelayFunction);
case 1:
default: return createSystemStateDetectorAgent<1>(C, Name, BrokenDelayFunction, OkDelayFunction);
// clang-format on
}
}
#endif // STATEHANDLERUTILS_H
diff --git a/examples/CSVFiles/main.cpp b/examples/CSVFiles/main.cpp
index b2c43ce..20df4cf 100644
--- a/examples/CSVFiles/main.cpp
+++ b/examples/CSVFiles/main.cpp
@@ -1,352 +1,373 @@
//===-- 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.
+/// \c rosa::writer classes. Focus is on the tuple impementations.
///
//===----------------------------------------------------------------------===//
-#include <cstdint>
+#include <algorithm>
#include <cstddef>
+#include <cstdint>
+#include <fstream>
#include <iostream>
#include <istream>
-#include <sstream>
-#include <fstream>
+#include <map>
#include <ostream>
+#include <sstream>
#include <string>
-#include <vector>
-#include <typeinfo>
-#include <map>
-#include <algorithm>
#include <tuple>
#include <type_traits>
-#include <unistd.h>
+#include <typeinfo>
+#include <vector>
+//#include <unistd.h>
-//includes for an complete example to read and write
-//with sensors and agents.
+// 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.
+// 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 csvPath = "../examples/CSVFiles/";
const std::string csvFileWithHeader = csvPath + "HR-New.csv";
-const std::string csvFileNoHeader = csvPath + "HR.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";
+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.
+/// 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);
+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.
+/// 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);
-
+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.3f, "hallo");
+ std::tuple<int, float, std::string> values2(3, 8.3f, "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.
+/// 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);
+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.
+/// 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);
-
+// NOTE (Maxi): I had to add an "f" to some numbers to declare them as float,
+// otherwise: "C:\Users\maxgot\Source\SoC_RoSA\examples\CSVFiles\main.cpp:314:
+// warning: C4305: 'argument': truncation from 'double' to 'const float'"
+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.6f, "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.004f);
+
+ // 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.4f);
+ T0Writer << (3);
+ T2Writer << "again";
+ T0Writer << (4);
+ T1Writer << (-0.1f);
+ T1Writer << (-0.2f);
+ T2Writer << "and";
+ T1Writer << (-0.3f);
+ T2Writer << "splitting";
+ T2Writer << "once again";
+
+ // again writing data of one tuple entry to the different writers in a random
+ // fashion.
+ T1Writer << (-0.004f);
+ T2Writer << "splitting is cool";
+ T0Writer << (-30);
}
-int main(void){
+int main(void) {
LOG_INFO_STREAM << library_string() << " -- " << terminal::Color::Red
- << ExampleName << " example" << terminal::Color::Default << '\n';
+ << 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.");
-
+ LOG_INFO("All tests finished.");
return 0;
}
-
diff --git a/include/rosa/agent/SystemState.hpp b/include/rosa/agent/SystemState.hpp
index e1fa090..d1b8920 100644
--- a/include/rosa/agent/SystemState.hpp
+++ b/include/rosa/agent/SystemState.hpp
@@ -1,156 +1,173 @@
//===-- rosa/agent/SystemState.hpp ------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/agent/SystemState.hpp
///
/// \author Maximilian Götzinger (maximilian.goetzinger@tuwien.ac.at)
///
/// \date 2019
///
/// \brief Definition of *system state* *functionality*.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_AGENT_SYSTEMSTATE_HPP
#define ROSA_AGENT_SYSTEMSTATE_HPP
#include "rosa/agent/Functionality.h"
#include "rosa/agent/SignalState.hpp"
#include "rosa/support/debug.hpp"
#include <vector>
namespace rosa {
namespace agent {
// System state conditions defining how the condition of a \c
/// rosa::agent::SystemState is saved in \c rosa::agent::SystemStateInformation.
enum class SystemStateCondition : uint8_t {
STABLE = 0, ///< The system state is stable
DRIFTING = 1, ///< The system state is drifting
MALFUNCTIONING = 2, ///< The system state is malfunctioning
UNKNOWN = 3 ///< The system state is unknown
};
/// TODO: write description
template <typename CONFDATATYPE> struct SystemStateInformation {
// Make sure the actual type arguments are matching our expectations.
STATIC_ASSERT((std::is_arithmetic<CONFDATATYPE>::value),
"confidence type is not to arithmetic");
/// The system state ID saved as an uint32_teger number
uint32_t SystemStateID;
/// The SystemStateConfidence shows the overall confidence value of the system
/// state.
CONFDATATYPE OverallDetectionConfidence;
/// The SystemStateCondition shows the condition of a system state (stable,
/// drifting, malfunctioning, or unknown)
SystemStateCondition SystemStateCondition;
/// The SystemStateIsValid saves the number of samples which have been
/// inserted into the state after entering it.
uint32_t NumberOfInsertedSamplesAfterEntrance;
/// The SystemStateIsValid shows whether a state is valid or invalid.
/// In this context, valid means that enough samples which are in close
/// proximitry have been inserted into the state.
bool SystemStateIsValid;
/// The SystemStateJustGotValid shows whether a system state got valid
/// (toggled from invalid to valid) during the current inserted sample.
bool SystemStateJustGotValid;
/// The SystemStateIsValidAfterReentrance shows whether a system state is
/// valid after the variable changed back to it again.
bool SystemStateIsValidAfterReentrance;
};
// todo: do we need PROCDATATYPE?
/// TODO TEXT
template <typename INDATATYPE, typename CONFDATATYPE, typename PROCDATATYPE>
class SystemState : public Functionality {
// Make sure the actual type arguments are matching our expectations.
STATIC_ASSERT(std::is_arithmetic<INDATATYPE>::value,
"input data type is not to arithmetic");
STATIC_ASSERT(std::is_arithmetic<CONFDATATYPE>::value,
"confidence abstraction type is not to arithmetic");
STATIC_ASSERT(std::is_arithmetic<PROCDATATYPE>::value,
"process data type is not to arithmetic");
private:
SystemStateInformation<CONFDATATYPE> SystemStateInfo;
std::vector<SignalStateInformation<CONFDATATYPE>> Signals;
uint32_t NumberOfSignals;
public:
- /// TODO write
- SystemState(uint32_t SignalStateID, uint32_t NumberOfSignals) noexcept
- : SystemStateInfo{SignalStateID,
- 0,
- SystemStateCondition::UNKNOWN,
- 0,
- false,
- false,
- false},
+ /// TODO: write description
+ SystemState(uint32_t StateID, uint32_t NumberOfSignals) noexcept
+ : SystemStateInfo{StateID, 0, SystemStateCondition::UNKNOWN, 0, false,
+ false, false},
NumberOfSignals(NumberOfSignals) {
+ //@benedikt: there is now possibility to not doing the resize within these
+ //{}-brackets, right?
Signals.resize(NumberOfSignals);
}
/// Destroys \p this object.
~SystemState(void) = default;
+ /// TODO: write description
template <std::size_t size>
- void
- insertSignalStateInfos(const std::array<SystemStateInformation<CONFDATATYPE>,
- size> &Data) noexcept {
+ void insertSignalStateInformation(
+ const std::array<SystemStateInformation<CONFDATATYPE>, size>
+ &Data) noexcept {
+ //@Daniel: is it possible to check with a ASSERT/STATIC_ASSERT whether the
+ // number of parameter is the same as NumberOfSignals (which is the length
+ // of the vectror)?
ASSERT(size <= NumberOfSignals);
std::size_t counter = 0;
for (auto tmp : Data) {
Signals.at(counter) = tmp;
counter++;
}
}
+ /// TODO: write description
template <typename... Types>
std::enable_if_t<std::conjunction_v<std::is_same<
Types, SystemStateInformation<CONFDATATYPE>>...>,
void>
- insertSignalStateInfos(Types... Data) {
+ insertSignalStateInformation(Types... Data) {
+ //@Daniel: is it possible to check with a ASSERT/STATIC_ASSERT whether the
+ // number of parameter is the same as NumberOfSignals (which is the length
+ // of the vectror)?
+ //
+ // TODO (future): think about saving the state information in a history
insertSignalStateInfos(
std::array<SystemStateInformation<CONFDATATYPE>, sizeof...(Data)>(
{Data...}));
}
// returns true if they are identical
+ /// TODO: write description
template <std::size_t size>
- bool
- compareSignalStateInfos(const std::array<SystemStateInformation<CONFDATATYPE>,
- size> &Data) noexcept {
+ bool compareSignalStateInformation(
+ const std::array<SystemStateInformation<CONFDATATYPE>, size>
+ &Data) noexcept {
+ //@Daniel: is it possible to check with a ASSERT/STATIC_ASSERT whether the
+ // number of parameter is the same as NumberOfSignals (which is the length
+ // of the vectror)?
+ //
+ // TODO (future): think about saving the state information in a history
std::size_t counter = 0;
for (auto tmp : Data) {
if (Signals.at(counter) != tmp)
return false;
counter++;
}
return true;
}
// checks only the given amount
+ /// TODO: write description
template <typename... Types>
std::enable_if_t<std::conjunction_v<std::is_same<
Types, SystemStateInformation<CONFDATATYPE>>...>,
bool>
- compareSignalStateInfos(Types... Data) {
+ compareSignalStateInformation(Types... Data) {
+ //@Daniel: is it possible to check with a ASSERT/STATIC_ASSERT whether the
+ // number of parameter is the same as NumberOfSignals (which is the length
+ // of the vectror)?
return compareSignalStateInfos(
std::array<SystemStateInformation<CONFDATATYPE>, sizeof...(Data)>(
{Data...}));
}
};
} // End namespace agent
} // End namespace rosa
#endif // ROSA_AGENT_SYSTEMSTATE_HPP
diff --git a/include/rosa/agent/SystemStateDetector.hpp b/include/rosa/agent/SystemStateDetector.hpp
index d18607c..b3fc125 100644
--- a/include/rosa/agent/SystemStateDetector.hpp
+++ b/include/rosa/agent/SystemStateDetector.hpp
@@ -1,104 +1,114 @@
//===-- rosa/agent/SystemStateDetector.hpp ----------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/agent/SystemStateDetector.hpp
///
/// \author Maximilian Götzinger (maximilian.goetzinger@tuwien.ac.at)
///
/// \date 2019
///
/// \brief Definition of *system state detector* *functionality*.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_AGENT_SYSTEMSTATEDETECTOR_HPP
#define ROSA_AGENT_SYSTEMSTATEDETECTOR_HPP
#include "rosa/agent/Functionality.h"
#include "rosa/agent/StateDetector.hpp"
#include "rosa/agent/SystemState.hpp"
#include "rosa/support/debug.hpp"
namespace rosa {
namespace agent {
// todo: löschen , std::size_t NUMOFINPUTSIGNALS, std::size_t NUMOFOUTPUTSIGNALS
/// TODO: write description
template <typename INDATATYPE, typename CONFDATATYPE, typename PROCDATATYPE,
HistoryPolicy HP>
class SystemStateDetector
: public StateDetector<INDATATYPE, CONFDATATYPE, PROCDATATYPE, HP> {
- //@maxi added them to make it compilable is this what you wanted?
using StateDetector =
StateDetector<INDATATYPE, CONFDATATYPE, PROCDATATYPE, HP>;
using PartFuncPointer = typename StateDetector::PartFuncPointer;
private:
// For the convinience to write a shorter data type name
using SystemStatePtr =
std::shared_ptr<SystemState<INDATATYPE, CONFDATATYPE, PROCDATATYPE>>;
- /// The NextSystemStateID is a counter variable which stores the ID which
- /// the
- /// next system state shall have.
- uint32_t NextSystemStateID;
-
- /// The SystemStateHasChanged is a flag that show whether the observed
- /// system
- /// has changed its state.
- bool SystemStateHasChanged;
-
/// The CurrentSystemState is a pointer to the (saved) system state in which
/// the actual state of the observed system is.
SystemStatePtr CurrentSystemState;
/// The DetectedSystemStates is a history in that all detected system states
/// are saved.
DynamicLengthHistory<SystemStatePtr, HP> DetectedSystemStates;
/// The FuzzyFunctionDelayTimeToGetBroken is the fuzzy function that gives
/// the confidence whether the system is Broken because of an input change
/// without an output change or vice versa. A small time gap between the two
/// shall be allowed.
PartFuncPointer FuzzyFunctionDelayTimeToGetBroken;
/// The FuzzyFunctionDelayTimeToBeWorking is the fuzzy function that gives
/// the
/// confidence whether the system is still OK allthough an input change
/// without an output change or vice versa.
PartFuncPointer FuzzyFunctionDelayTimeToBeWorking;
public:
// todo zwei parameter für variablen anzahl
/// TODO: write description
SystemStateDetector(
uint32_t MaximumNumberOfSystemStates,
PartFuncPointer FuzzyFunctionDelayTimeToGetBroken,
PartFuncPointer FuzzyFunctionDelayTimeToBeWorking) noexcept
- : NextSystemStateID(1), SystemStateHasChanged(false),
- CurrentSystemState(nullptr),
+ : CurrentSystemState(nullptr),
DetectedSystemStates(MaximumNumberOfSystemStates),
FuzzyFunctionDelayTimeToGetBroken(FuzzyFunctionDelayTimeToGetBroken),
- FuzzyFunctionDelayTimeToBeWorking(FuzzyFunctionDelayTimeToBeWorking) {}
+ FuzzyFunctionDelayTimeToBeWorking(FuzzyFunctionDelayTimeToBeWorking) {
+ //@Benedikt: if I write "NextStateID(1), StateHasChanged(false)" before the
+ //{}-brackets, the compiler tells me: "SystemStateDetector.hpp:72:9: error:
+ // member initializer 'NextStateID'/'StateHasChanged' does not name a
+ // non-static data member or base class"
+ this->NextStateID = 1;
+ this->StateHasChanged = false;
+ }
/// Destroys \p this object.
~SystemStateDetector(void) = default;
/// TODO: write description
SystemStateInformation<CONFDATATYPE>
detectSystemState(INDATATYPE Sample) noexcept {
// dummy line
Sample = 1;
}
+
+private:
+ /// Creates a new signal state and adds it to the signal state vector in which
+ /// all known states are saved.
+ ///
+ /// \return a pointer to the newly created signal state or NULL if no state
+ /// could be created.
+ SystemStatePtr createNewSignalState(void) noexcept {
+ SystemStatePtr S(new SignalState<INDATATYPE, CONFDATATYPE, PROCDATATYPE>(
+ this->NextStateID, this->NumberOfSignals));
+
+ DetectedSystemStates.addEntry(S);
+
+ return S;
+ }
};
} // End namespace agent
} // End namespace rosa
#endif // ROSA_AGENT_SYSTEMSTATEDETECTOR_HPP
diff --git a/include/rosa/deluxe/DeluxeAgent.hpp b/include/rosa/deluxe/DeluxeAgent.hpp
index fa328a2..c523494 100644
--- a/include/rosa/deluxe/DeluxeAgent.hpp
+++ b/include/rosa/deluxe/DeluxeAgent.hpp
@@ -1,1456 +1,1457 @@
//===-- 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;
/// 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 \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
+ /// 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 {
/// 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::InputValues. That is due to not being able to use
/// an initializer list directly; the initializer list always copies but \c
/// std::unique_ptr is not copyable.
///
/// \tparam Ts types to create storages for
///
/// \note Instantiation fails if any of the type arguments \p Ts... is not an
/// instance of \c rosa::deluxe::DeluxeTuple.
///
/// \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: \code
/// TypeListAllDeluxeTuple<TypeList<Ts...>>::Value
/// \endcode
template <typename... Ts>
std::vector<std::unique_ptr<AbstractTokenizedStorage>>
makeInputStorages(void) noexcept {
std::vector<std::unique_ptr<AbstractTokenizedStorage>> InputStorages;
(InputStorages.push_back(
std::make_unique<typename TokenizedStorageForTypeList<
typename 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...> {
+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 {
+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]);
// 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)));
+ STATIC_ASSERT((true && ... &&
+ (static_cast<size_t>(static_cast<token_size_t>(S0)) == S0)),
+ "Should not happen");
const auto &SlaveInput = InputValues[Pos];
// Get all elements of the tuple in a fold expression.
return T(*static_cast<const Ts *>(
SlaveInput->pointerTo(static_cast<token_size_t>(S0)))...);
}
template <typename... As, size_t... S0>
std::tuple<std::pair<As, bool>...>
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>());
+ if
+ constexpr(!std::is_same<MOT, EmptyDeluxeTuple>::value) {
+ if (Value) {
+ sendToSlave(Pos, *Value, seq_t<MOT::Length>());
+ }
}
- } else {
+ 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 successfully constructed.
STATIC_ASSERT((true && ... && (static_cast<size_t>(
static_cast<token_size_t>(S0)) == S0)),
"Unexpected error");
const auto MasterInputArg = std::make_pair(
// Get all elements of the tuple in a fold expression.
MT(*static_cast<const MTs *>(
MasterInputValue->pointerTo(static_cast<token_size_t>(S0)))...),
MasterInputChanged);
MasterInputChanged = false;
const std::tuple<Optional<Ts>...> MasterOutput = MF(MasterInputArg);
handleMasterOutputs<0>(MasterOutput, SlaveIndices());
}
// Handle inputs.
// Call the processing function only if \p ExecutionPolicy allows.
if (ExecutionPolicy->shouldProcess(InputChanged)) {
LOG_TRACE_STREAM << "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),
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 successfully constructed.
STATIC_ASSERT((true && ... &&
(static_cast<size_t>(static_cast<token_size_t>(S0)) == S0)),
"Unexpected error");
// Create a static constant array for these indices to be available as lvalue
// references when creating messages below. \c S0... when used directly in a
// fold expression is a temporary value, which would result in \c
// rosa::Message instances being created with rvalue references. Further, all
// other values would to copied into a temporary variable for making them
/// available as rvalue references (they are constant lvalue references here).
static constexpr std::array<token_size_t, sizeof...(S0)> Indices{{S0...}};
LOG_TRACE_STREAM << "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 successfully constructed.
STATIC_ASSERT((true && ... &&
(static_cast<size_t>(static_cast<token_size_t>(S0)) == S0)),
"Unexpected error");
// Create a static constant array for these indices to be available as lvalue
// references when creating messages below. \c S0... when used directly in a
// fold expression is a temporary value, which would result in \c
// rosa::Message instances being created with rvalue references. Further, all
// other values would to copied into a temporary variable for making them
/// available as rvalue references (they are constant lvalue references here).
static constexpr std::array<token_size_t, sizeof...(S0)> Indices{{S0...}};
// There is a handle and the referred *slave* is in a valid state.
auto Slave = Slaves[Pos];
LOG_TRACE_STREAM << "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);
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.
*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/support/csv/CSVReader.hpp b/include/rosa/support/csv/CSVReader.hpp
index 5402018..c5678ef 100755
--- a/include/rosa/support/csv/CSVReader.hpp
+++ b/include/rosa/support/csv/CSVReader.hpp
@@ -1,486 +1,484 @@
//===-- rosa/support/csv/CSVReader.hpp --------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/support/csv/CSVReader.hpp
///
-/// \authors David Juhasz (david.juhasz@tuwien.ac.at), Edwin Willegger (edwin.willegger@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 <algorithm>
#include <istream>
-#include <sstream>
-#include <vector>
#include <map>
-#include <algorithm>
#include <set>
+#include <sstream>
+#include <vector>
namespace rosa {
namespace csv {
/// Indicating it the CSV file contains any header or not
-enum class HeaderInformation {
- HasHeader,
- HasNoHeader
-};
+enum class HeaderInformation { HasHeader, HasNoHeader };
/// Anonymous namespace providing implementation details for
/// \c rosa::csv::CSVIterator, consider it private.
namespace {
/// Provides facility for parsing one value from a string.
///
/// \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
/// and \c std::string.
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 ValueParser {
///
///
/// \param Cell the \c std::string to parse
///
/// \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> {
+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));
}
};
-template <typename T>
-struct ValueParser<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.
static T parse(const std::string &Cell) noexcept {
return static_cast<T>(std::stoull(Cell));
}
};
-template <typename T>
-struct ValueParser<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.
static T parse(const std::string &Cell) noexcept {
return static_cast<T>(std::stold(Cell));
}
};
-template <typename T>
-struct ValueParser<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.
static T parse(const std::string &Cell) noexcept { return Cell; }
};
/// Parses and stores entries from a row of CSV data.
///
/// \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 Ts.
template <typename... Ts> class CSVRow {
private:
/// Parses a given row of CSV data into \c CSVRow::Data.
///
/// \ CSVRow::Data is filled with values parsed from \p LineStream. Entries
/// in the line are to be separated by commas, the character `,`.
///
/// \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
///
/// \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:
-
/// Constructor with all possible parameters
///
- /// The function creates an instance of an CSVRow object and sets the attributes of the
+ /// The function creates an instance of an CSVRow object and sets the
+ /// attributes of the
/// object to the values of the parameters.
///
- /// \param SkipRows the number of data rows to skip, not taking header into account.
+ /// \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) {
- }
-
-
+ 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) noexcept {
std::string Line;
std::getline(Str, Line);
- if(Line.size() > 0){
- std::stringstream LineStream(Line);
- parseRow(LineStream, Delimeter, seq_t<sizeof...(Ts)>());
+ if (Line.size() > 0) {
+ std::stringstream LineStream(Line);
+ parseRow(LineStream, Delimeter, seq_t<sizeof...(Ts)>());
- RowNumber = RowNumber + 1;
+ 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;
+ std::string Line;
+ std::getline(Str, Line);
+ std::stringstream LineStream(Line);
+ std::string Value;
- while( getline(LineStream, Value, Delimeter) ){
- Header.push_back(Value);
- }
+ while (getline(LineStream, Value, Delimeter)) {
+ Header.push_back(Value);
+ }
- IsHeaderRead = true;
+ 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;
- }
+ 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;
- }
+ 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;
- }
-
+ 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 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.
+ /// \return if the csv file contains a header row in the first line of the
+ /// file.
inline HeaderInformation HasFileHeader() const noexcept {
- return this->HeaderInfo;
+ 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;
+ /// \param SkipRows the number of rows you want to skip at the beginning of
+ /// the file.
+ // NOTE (Maxi): I had to change "SkipRows" to "_SkipRows" because otherwise:
+ // "warning: C4458: declaration of 'SkipRows' hides class member"
+ 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;
+ // NOTE (Maxi): I had to change "HeaderInfo" to "_HeaderInfo", otherwise:
+ // "warning: C4458: declaration of 'HeaderInfo' hides class member"
+ 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.
+ /// 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;
- }
-
-
-
+ // NOTE (Maxi): I had to change "Delimeter" to "_Delimeter" because otherwise:
+ // "warning: C4458: declaration of 'Delimeter' hides class member"
+ 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::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.
-
+ 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 until all lines are
/// skipped.
///
/// 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... Ts>
std::istream &operator>>(std::istream &Str, CSVRow<Ts...> &Data) {
- if( Data.HasFileHeader() == HeaderInformation::HasHeader && !Data.IsHeaderReadDone() ) {
+ if (Data.HasFileHeader() == HeaderInformation::HasHeader &&
+ !Data.IsHeaderReadDone()) {
Data.readHeader(Str);
}
- while(Data.CurRow() < (Data.SkipNumRows())){
+ while (Data.CurRow() < (Data.SkipNumRows())) {
Data.readNextRow(Str);
}
- //read the lines after you skipped the number of rows you want to skip
+ // 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.
///
/// The iterator parses rows into `std::tuple` values and iterates over the
/// file row by row.
///
/// \tparam Ts types of values stored in one row of 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 Ts
template <typename... Ts> class CSVIterator {
public:
/// \defgroup CSVIteratorTypedefs Typedefs of rosa::csv::CSVIterator
///
/// Standard `typedef`s for iterators.
///
///@{
typedef std::input_iterator_tag
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
- /// \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.
+ /// \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.
+ /// \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(){
+ 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.
CSVIterator(void) noexcept : Str(nullptr) {}
/// Pre-increment operator.
///
/// 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.
CSVIterator &operator++() {
if (Str) {
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.
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 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 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 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 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;
+ // NOTE (Maxi): I had to change "Delimeter" to "_Delimeter" because otherwise:
+ // "warning: C4458: declaration of 'Delimeter' hides class member"
+ 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;
- }
+ /// \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.
- 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
-
+ 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/writer/split_tuple_writer.hpp b/include/rosa/support/writer/split_tuple_writer.hpp
index d98ace2..f8701b3 100644
--- a/include/rosa/support/writer/split_tuple_writer.hpp
+++ b/include/rosa/support/writer/split_tuple_writer.hpp
@@ -1,562 +1,559 @@
//===-- 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/log.h"
#include "rosa/support/sequence.hpp"
#include <memory>
-#include <tuple>
#include <queue>
+#include <tuple>
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
+#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 {
+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
+ /// \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.
+/// 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 {
+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;
+ 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");
+ LOG_ERROR("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.
+/// 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...>> {
+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

File Metadata

Mime Type
text/x-diff
Expires
Mon, Oct 20, 1:31 PM (16 h, 26 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
199730
Default Alt Text
(161 KB)

Event Timeline