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 #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 #include #include #include #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 C = DeluxeContext::create(AppName); - std::shared_ptr> BrokenDelayFunction( - new PartialFunction( + std::shared_ptr> BrokenDelayFunction( + new PartialFunction( {{{0, AppConfig.BrokenCounter}, - std::make_shared>( + std::make_shared>( 0, 0.f, AppConfig.BrokenCounter, 1.f)}, - {{AppConfig.BrokenCounter, std::numeric_limits::max()}, - std::make_shared>(1.f, 0.f)}}, - 0)); + {{AppConfig.BrokenCounter, std::numeric_limits::max()}, + std::make_shared>(1.f, 0.f)}}, + 0.f)); - std::shared_ptr> OkDelayFunction( - new PartialFunction( + std::shared_ptr> OkDelayFunction( + new PartialFunction( {{{0, AppConfig.BrokenCounter}, - std::make_shared>( + std::make_shared>( 0, 1.f, AppConfig.BrokenCounter, 0.f)}, - {{AppConfig.BrokenCounter, std::numeric_limits::max()}, - std::make_shared>(0.f, 0.f)}}, - 1)); + {{AppConfig.BrokenCounter, std::numeric_limits::max()}, + std::make_shared>(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 Sensors; std::vector>> SampleMatchesFunctions; std::vector>> SampleMismatchesFunctions; std::vector>> SignalIsStableFunctions; std::vector>> SignalIsDriftingFunctions; std::vector>> NumOfSamplesMatchFunctions; std::vector>> NumOfSamplesMismatchFunctions; std::vector>> SignalStateDetectors; std::vector SignalStateDetectorAgents; for (auto SignalConfiguration : AppConfig.SignalConfigurations) { // // Create deluxe sensors. // Sensors.emplace_back(C->createSensor(SignalConfiguration.Name)); // // Create functionalities for SignalStateDetector. // SampleMatchesFunctions.emplace_back(new PartialFunction( { {{-SignalConfiguration.OuterBound, -SignalConfiguration.InnerBound}, std::make_shared>( -SignalConfiguration.OuterBound, 0.f, -SignalConfiguration.InnerBound, 1.f)}, {{-SignalConfiguration.InnerBound, SignalConfiguration.InnerBound}, std::make_shared>(1.f, 0.f)}, {{SignalConfiguration.InnerBound, SignalConfiguration.OuterBound}, std::make_shared>( SignalConfiguration.InnerBound, 1.f, SignalConfiguration.OuterBound, 0.f)}, }, 0)); SampleMismatchesFunctions.emplace_back(new PartialFunction( { {{-SignalConfiguration.OuterBound, -SignalConfiguration.InnerBound}, std::make_shared>( -SignalConfiguration.OuterBound, 1.f, -SignalConfiguration.InnerBound, 0.f)}, {{-SignalConfiguration.InnerBound, SignalConfiguration.InnerBound}, std::make_shared>(0.f, 0.f)}, {{SignalConfiguration.InnerBound, SignalConfiguration.OuterBound}, std::make_shared>( SignalConfiguration.InnerBound, 0.f, SignalConfiguration.OuterBound, 1.f)}, }, 1)); SignalIsStableFunctions.emplace_back(new PartialFunction( { {{-SignalConfiguration.OuterBoundDrift, -SignalConfiguration.InnerBoundDrift}, std::make_shared>( -SignalConfiguration.OuterBoundDrift, 0.f, -SignalConfiguration.InnerBoundDrift, 1.f)}, {{-SignalConfiguration.InnerBoundDrift, SignalConfiguration.InnerBoundDrift}, std::make_shared>(1.f, 0.f)}, {{SignalConfiguration.InnerBoundDrift, SignalConfiguration.OuterBoundDrift}, std::make_shared>( SignalConfiguration.InnerBoundDrift, 1.f, SignalConfiguration.OuterBoundDrift, 0.f)}, }, 0)); SignalIsDriftingFunctions.emplace_back(new PartialFunction( { {{-SignalConfiguration.OuterBoundDrift, -SignalConfiguration.InnerBoundDrift}, std::make_shared>( -SignalConfiguration.OuterBoundDrift, 1.f, -SignalConfiguration.InnerBoundDrift, 0.f)}, {{-SignalConfiguration.InnerBoundDrift, SignalConfiguration.InnerBoundDrift}, std::make_shared>(0.f, 0.f)}, {{SignalConfiguration.InnerBoundDrift, SignalConfiguration.OuterBoundDrift}, std::make_shared>( SignalConfiguration.InnerBoundDrift, 0.f, SignalConfiguration.OuterBoundDrift, 1.f)}, }, 1)); NumOfSamplesMatchFunctions.emplace_back(new StepFunction( 1.0f / SignalConfiguration.SampleHistorySize, StepDirection::StepUp)); NumOfSamplesMismatchFunctions.emplace_back(new StepFunction( 1.0f / SignalConfiguration.SampleHistorySize, StepDirection::StepDown)); // // Create SignalStateDetector functionality // SignalStateDetectors.emplace_back( new SignalStateDetector( SignalConfiguration.Output ? SignalProperties::OUTPUT : SignalProperties::INPUT, std::numeric_limits::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; using Result = Optional>; using Handler = std::function; 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 #include #include #include #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 #include #include #include using namespace rosa; using namespace rosa::agent; using namespace rosa::deluxe; using namespace rosa::terminal; // Signal State using SignalStateTuple = DeluxeTuple; AgentHandle createSignalStateDetectorAgent( std::unique_ptr &C, const std::string &Name, std::shared_ptr< SignalStateDetector> SigSD) { using Input = std::pair, bool>; using Result = Optional; using Handler = std::function; return C->createAgent( Name, Handler([&Name, &SigSD](Input I) -> Result { LOG_INFO_STREAM << "\n******\n" << Name << " " << (I.second ? "" : "") << " 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; template struct Handler_helper; template struct function_helper { static_assert(std::conjunction_v...>, "All types need to be identical"); static B function(A valA, As... valAs) { std::vector ar({valA, valAs...}); return func()(ar); } }; template struct Handler_helper<0, ret, functype, typeA, B...> { using handler = function_helper; }; template struct Handler_helper { using handler = typename Handler_helper, B...>::handler; }; template using Handler = typename Handler_helper::handler; // todo: state-detector durschleifen template struct function { ret operator()(A a) { // std::vector 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>; -auto HandlerFunction = Handler<4, Optional, - function, arr>, - SignalStateTuple>::function; - template AgentHandle createSystemStateDetectorAgent( std::unique_ptr &C, const std::string &Name, - std::shared_ptr> BrokenDelayFunction, - std::shared_ptr> OkDelayFunction) { + std::shared_ptr> BrokenDelayFunction, + std::shared_ptr> OkDelayFunction) { LOG_TRACE("Creating fixed SystemStateDetectorAgent"); using Input = SignalStateTuple; using Result = Optional; auto HandlerFunction = Handler, arr>, Input>::function; - std::shared_ptr> - SysSD(new SystemStateDetector( + std::shared_ptr< + SystemStateDetector> + SysSD(new SystemStateDetector( std::numeric_limits::max(), BrokenDelayFunction, OkDelayFunction)); return C->createAgent(Name, std::function(HandlerFunction)); } AgentHandle createSystemStateDetectorAgent( std::unique_ptr &C, const std::string &Name, - uint8_t NumOfSlaves, - std::shared_ptr> BrokenDelayFunction, - std::shared_ptr> OkDelayFunction) { + size_t NumOfSlaves, + std::shared_ptr> BrokenDelayFunction, + std::shared_ptr> 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 +#include #include +#include +#include #include #include -#include -#include +#include #include +#include #include -#include -#include -#include -#include #include #include -#include +#include +#include +//#include -//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 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 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> it2(file, 1); - - //try to skip a valid number of lines after the header - csv::CSVIterator 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 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 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 it2_3(file2, 1, csv::HeaderInformation::HasNoHeader); - - //try to skip a valid number of lines after the header, but with different delimeter - csv::CSVIterator it2_4(file3, 2, csv::HeaderInformation::HasHeader, ';'); - - // if you skip more lines than valid, you generate an infinte loop - //csv::CSVIterator 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 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 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 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> it2(file, 1); + + // try to skip a valid number of lines after the header + csv::CSVIterator 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 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 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 it2_3( + file2, 1, csv::HeaderInformation::HasNoHeader); + + // try to skip a valid number of lines after the header, but with different + // delimeter + csv::CSVIterator it2_4( + file3, 2, csv::HeaderInformation::HasHeader, ';'); + + // if you skip more lines than valid, you generate an infinte loop + // csv::CSVIterator 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 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 wri(file_header_out); - - // - // Create test tuples - // - std::tuple values(5, 8.3, "hallo"); - std::tuple values2(3, 8.3, "end"); - - // - // Create test header lines for the test tuples - // - std::array header {"zero column", "first column", "second column"}; - - std::array headerWrong {"zero column", "first column", "second column", "third column"}; - - std::array 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 wri(file_header_out); + + // + // Create test tuples + // + std::tuple values(5, 8.3f, "hallo"); + std::tuple values2(3, 8.3f, "end"); + + // + // Create test header lines for the test tuples + // + std::array header{"zero column", "first column", + "second column"}; + + std::array headerWrong{"zero column", "first column", + "second column", "third column"}; + + std::array 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 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("Element1 Sensor"); - AgentHandle Elem1Sensor = C->createSensor("Element2 Sensor"); - AgentHandle Elem2Sensor = C->createSensor("Element3 Sensor"); - - - // - // Initialize deluxe context for simulation. - // - C->initializeSimulation(); - - // Type aliases for iterators - using Iterator = rosa::csv::CSVIterator; - using IteratorValue = std::tuple; - - static_assert (std::is_same::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 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("Element1 Sensor"); + AgentHandle Elem1Sensor = C->createSensor("Element2 Sensor"); + AgentHandle Elem2Sensor = C->createSensor("Element3 Sensor"); + // + // Initialize deluxe context for simulation. + // + C->initializeSimulation(); + + // Type aliases for iterators + using Iterator = rosa::csv::CSVIterator; + using IteratorValue = std::tuple; + + static_assert( + std::is_same::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 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(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 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(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 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 struct SystemStateInformation { // Make sure the actual type arguments are matching our expectations. STATIC_ASSERT((std::is_arithmetic::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 class SystemState : public Functionality { // Make sure the actual type arguments are matching our expectations. STATIC_ASSERT(std::is_arithmetic::value, "input data type is not to arithmetic"); STATIC_ASSERT(std::is_arithmetic::value, "confidence abstraction type is not to arithmetic"); STATIC_ASSERT(std::is_arithmetic::value, "process data type is not to arithmetic"); private: SystemStateInformation SystemStateInfo; std::vector> 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 - void - insertSignalStateInfos(const std::array, - size> &Data) noexcept { + void insertSignalStateInformation( + const std::array, 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 std::enable_if_t>...>, 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, sizeof...(Data)>( {Data...})); } // returns true if they are identical + /// TODO: write description template - bool - compareSignalStateInfos(const std::array, - size> &Data) noexcept { + bool compareSignalStateInformation( + const std::array, 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 std::enable_if_t>...>, 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, 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 class SystemStateDetector : public StateDetector { - //@maxi added them to make it compilable is this what you wanted? using StateDetector = StateDetector; using PartFuncPointer = typename StateDetector::PartFuncPointer; private: // For the convinience to write a shorter data type name using SystemStatePtr = std::shared_ptr>; - /// 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 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 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( + 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 /// 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 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 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 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 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> 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 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 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; /// 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 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> Slaves; /// Associates \c rosa::id_t values to corresponding indices of registered /// *slaves*. /// /// \see \c rosa::deluxe::DeluxeAgent::Slaves std::map 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 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 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::TT == InputTypes[Pos] /// \endcode template DeluxeTuple prepareInputValueAtPos(TypeList, Seq) 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>::Value && /// sizeof...(As) == sizeof...(S0) /// \endcode Dynamically, type arguments \p As... match the input types of \p /// this object: \code /// inputTypesMatch>() /// \endcode template std::tuple...> prepareCurrentInputs(Seq) 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 static std::tuple, Optional...> invokeWithTuple(std::function, Optional...>( std::pair...)> F, const std::tuple...> Args, Seq) 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::TT == MasterOutputTypes[Pos] /// \endcode template void handleMasterOutputAtPos(const Optional> &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>::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>::Type>() && /// sizeof...(S0) == NumberOfMasterOutputs /// \endcode template void handleMasterOutputs(const std::tuple...> &Output, Seq) 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>::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::TT && OutputType == T::TT && /// inputTypesMatch>() && /// masterOutputTypesMatch>() /// \endcode template H triggerHandlerFromProcessingFunctions( std::function...>( std::pair, bool>)> &&MF, std::function< std::tuple, Optional...>(std::pair...)> &&F, Seq) 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::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 >::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::pair)> &&MF, std::function, Optional...>( std::pair...)> &&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 &&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 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 _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 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(unwrapAgent(*Slave)).OutputType == /// InputTypes[Pos] && /// (emptyToken(MasterOutputTypes[Pos]) || /// static_cast(unwrapAgent(*Slave)).MasterInputType /// == MasterOutputTypes[Pos])) || /// (unwrapAgent(*Slave).Kind == rosa::deluxe::atoms::AgentKind && /// static_cast(unwrapAgent(*Slave)).OutputType == /// InputTypes[Pos] && /// (emptyToken(MasterOutputTypes[Pos]) || /// static_cast(unwrapAgent(*Slave)).MasterInputType == /// MasterOutputTypes[Pos]))) /// \endcode void registerSlave(const size_t Pos, const Optional 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::Value /// \endcode template void sendToMaster(const DeluxeTuple &Value, Seq) 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::Value /// \endcode template void sendToSlave(const size_t Pos, const DeluxeTuple &Value, Seq) 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::Value /// \endcode template 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::Value /// \endcode template 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>::Value /// \endcode template std::vector> makeInputStorages(void) noexcept { std::vector> InputStorages; (InputStorages.push_back( std::make_unique::Type>::Type>()), ...); return InputStorages; } /// Template \c struct whose specializations provide a recursive implementation /// for \c TypesMatchList. /// /// \tparam As types to match template 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 struct TypesMatchImpl, As...> { /// Tells whether types \c rosa::deluxe::DeluxeTuple 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 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 and \p As... match \c /// rosa::Token values stored in \p Tokens starting at position \p Pos static bool f(const std::vector &Tokens, size_t Pos) noexcept { return Pos < Tokens.size() && TypeToken::Value == Tokens[Pos] && TypesMatchImpl::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 -struct TypesMatchImpl { +template struct TypesMatchImpl { /// 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 &, 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 &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::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 struct TypesMatchList; /// Template specialization implementing the feature. /// /// \tparam As types to match template struct TypesMatchList> { /// 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 &Tokens) noexcept { return TypesMatchImpl::f(Tokens, 0); } }; } // End namespace -template -bool DeluxeAgent::inputTypesMatch(void) const noexcept { +template bool DeluxeAgent::inputTypesMatch(void) const noexcept { return TypesMatchList::f(InputTypes); } template bool DeluxeAgent::masterOutputTypesMatch(void) const noexcept { return TypesMatchList::f(MasterOutputTypes); } template DeluxeTuple DeluxeAgent::prepareInputValueAtPos(TypeList, Seq) const noexcept { using T = DeluxeTuple; 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(static_cast(S0)) == S0))); + STATIC_ASSERT((true && ... && + (static_cast(static_cast(S0)) == S0)), + "Should not happen"); const auto &SlaveInput = InputValues[Pos]; // Get all elements of the tuple in a fold expression. return T(*static_cast( SlaveInput->pointerTo(static_cast(S0)))...); } template std::tuple...> DeluxeAgent::prepareCurrentInputs(Seq) const noexcept { STATIC_ASSERT(TypeListAllDeluxeTuple>::Value, "not tuple types"); STATIC_ASSERT(sizeof...(As) == sizeof...(S0), "inconsistent type arguments"); ASSERT(inv() && inputTypesMatch>()); return std::make_tuple(std::make_pair( prepareInputValueAtPos(typename UnwrapDeluxeTuple::Type(), seq_t()), InputChanged[S0])...); } template std::tuple, Optional...> DeluxeAgent::invokeWithTuple( std::function< std::tuple, Optional...>(std::pair...)> F, const std::tuple...> Args, Seq) noexcept { STATIC_ASSERT(sizeof...(As) == sizeof...(S0), "wrong number of type parameters"); return F(std::get(Args)...); } template void DeluxeAgent::handleMasterOutputAtPos( const Optional> &Value) noexcept { using MOT = DeluxeTuple; 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::value) { - if (Value) { - sendToSlave(Pos, *Value, seq_t()); + if + constexpr(!std::is_same::value) { + if (Value) { + sendToSlave(Pos, *Value, seq_t()); + } } - } else { + else { (void)Value; } ASSERT(inv()); } template void DeluxeAgent::handleMasterOutputs(const std::tuple...> &Output, Seq) noexcept { using MOTs = typename TypeListDrop>::Type; STATIC_ASSERT(TypeListAllDeluxeTuple::Value, "not tuple type arguments"); STATIC_ASSERT(sizeof...(Ts) == Offset + sizeof...(S0), "inconsistent arguments"); ASSERT(inv() && masterOutputTypesMatch() && sizeof...(S0) == NumberOfMasterOutputs); // Handle each master-output position in a fold expression. (handleMasterOutputAtPos(std::get(Output)), ...); ASSERT(inv()); } template DeluxeAgent::H DeluxeAgent::triggerHandlerFromProcessingFunctions( std::function< std::tuple...>(std::pair, bool>)> &&MF, std::function< std::tuple, Optional...>(std::pair...)> &&F, Seq) noexcept { using MT = DeluxeTuple; STATIC_ASSERT((TypeListAllDeluxeTuple>::Value), "not tuple type arguments"); STATIC_ASSERT(sizeof...(MTs) == sizeof...(S0), "inconsistent arguments"); ASSERT(MasterInputType == MT::TT && OutputType == T::TT && inputTypesMatch>() && masterOutputTypesMatch>()); return [ this, MF, F ]() noexcept { // \note These indices work for both inputs and master-outputs. using SlaveIndices = seq_t; // Handle master-input. // Do not do anything for master-input type \c // rosa::deluxe::EmptyDeluxeTuple. if (!std::is_same::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( static_cast(S0)) == S0)), "Unexpected error"); const auto MasterInputArg = std::make_pair( // Get all elements of the tuple in a fold expression. MT(*static_cast( MasterInputValue->pointerTo(static_cast(S0)))...), MasterInputChanged); MasterInputChanged = false; const std::tuple...> 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(SlaveIndices()); std::fill(InputChanged.begin(), InputChanged.end(), false); const std::tuple, Optional...> Output = invokeWithTuple(F, InputArgs, SlaveIndices()); const auto OutputToMaster = std::get<0>(Output); if (OutputToMaster) { sendToMaster(*OutputToMaster, seq_t()); } handleMasterOutputs<1>(Output, SlaveIndices()); } else { LOG_TRACE_STREAM << "DeluxeAgent " << Name << " skips input." << std::endl; } }; } template DeluxeAgent::DeluxeAgent( const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&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()), MasterInputNextPos(0), MasterInputChanged(false), MasterInputValue(new typename TokenizedStorageForTypeList< typename UnwrapDeluxeTuple::Type>::Type()), MasterOutputTypes({Ts::TT...}), FP(triggerHandlerFromProcessingFunctions(std::move(MF), std::move(F), seq_t())), Slaves(NumberOfInputs) { ASSERT(Kind == atoms::AgentKind); LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " is created." << std::endl; ASSERT(inv()); } template void DeluxeAgent::sendToMaster(const DeluxeTuple &Value, Seq) noexcept { STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments"); ASSERT(inv() && OutputType == TypeToken::Value); // The assert must hold if \p this object was successfully constructed. STATIC_ASSERT((true && ... && (static_cast(static_cast(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 Indices{{S0...}}; LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id << ") sends to master (" << static_cast(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(Value))), ...); } ASSERT(inv()); } template void DeluxeAgent::sendToSlave(const size_t Pos, const DeluxeTuple &Value, Seq) noexcept { STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments"); ASSERT(inv() && Pos < NumberOfMasterOutputs && MasterOutputTypes[Pos] == TypeToken::Value); // The assert must hold if \p this object was successfully constructed. STATIC_ASSERT((true && ... && (static_cast(static_cast(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 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(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(Value))), ...); } } template 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::Value); const size_t SlavePos = SlaveIds.at(Id); LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id << ") saves value from slave at position " << SlavePos << ": (" << static_cast(Pos) << ") " << Value << std::endl; // Save value. *static_cast(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 void DeluxeAgent::saveMasterInput(id_t Id, token_size_t Pos, T Value) noexcept { ASSERT(inv() && Master && masterId() == Id && Pos == MasterInputNextPos && typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf::Value); LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id << ") saves value from master: (" << static_cast(Pos) << ") " << Value << std::endl; // Save value. *static_cast(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 #include -#include -#include #include -#include #include +#include +#include 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 ::value && std::is_signed::value), bool IsUnsignedInt = (std::is_integral::value && std::is_unsigned::value), bool IsFloat = std::is_floating_point::value, bool IsString = std::is_same::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 -struct ValueParser { +template struct ValueParser { STATIC_ASSERT((std::is_integral::value && std::is_signed::value), "wrong type"); // Sanity check. static T parse(const std::string &Cell) noexcept { return static_cast(std::stoll(Cell)); } }; -template -struct ValueParser { +template struct ValueParser { STATIC_ASSERT((std::is_integral::value && std::is_unsigned::value), "wrong type"); // Sanity check. static T parse(const std::string &Cell) noexcept { return static_cast(std::stoull(Cell)); } }; -template -struct ValueParser { +template struct ValueParser { STATIC_ASSERT((std::is_floating_point::value), "wrong type"); // Sanity check. static T parse(const std::string &Cell) noexcept { return static_cast(std::stold(Cell)); } }; -template -struct ValueParser { +template struct ValueParser { STATIC_ASSERT((std::is_same::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 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 void parseRow(std::stringstream &LineStream, char Delimeter, Seq) { 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(Data) = ValueParser::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()); + if (Line.size() > 0) { + std::stringstream LineStream(Line); + parseRow(LineStream, Delimeter, seq_t()); - 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 &tuple(void) const noexcept { return Data; } private: - std::tuple 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 Header; ///< The content of the header row. - + std::tuple 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 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 std::istream &operator>>(std::istream &Str, CSVRow &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 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 value_type; ///< Type of values iterated over. typedef std::size_t difference_type; ///< Type to identify distance. typedef std::tuple *pointer; ///< Pointer to the type iterated over. typedef std::tuple &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 &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 *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 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 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 -#include #include +#include 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 -class SplittedElementWriter { +template class SplittedElementWriter { public: /// Type alias for the values the writer writes. using T = typename TB::template element_type; - /// \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 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(); } /// 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(V); } private: std::shared_ptr 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 SplittedElementWriter & operator<<(SplittedElementWriter &W, const typename SplittedElementWriter::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::Type /// \endcode template struct TupleBufferContainer; /// Template definition for \c std::tuple. template struct TupleBufferContainer> { /// The converted type. using Type = std::tuple...>; }; ///@} /// 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 using tuple_buffer_container_t = typename TupleBufferContainer::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 -class TupleWriterBuffer { +template 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; /// Template type alias to get element types of \c writer_value_type by /// index. /// \tparam I the index of the element template - using element_type = - typename std::tuple_element::type; + using element_type = typename std::tuple_element::type; /// Type alias for index sequence for accessing elements of \c /// writer_value_size. using element_idx_seq_t = seq_t; /// 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 buffered(void) const noexcept { return std::get(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() calls has no /// effect and the last \c buffered() 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 void write(const element_type &V) { ASSERT(!hasCompleteTuple(element_idx_seq_t())); if (good()) { std::get(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 bool empty(Seq) const noexcept { return (true && ... && std::get(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 bool hasCompleteTuple(Seq) const noexcept { return (true && ... && !std::get(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 void makeCompleteTuple(Seq) noexcept { auto addDefaultIfEmpty = [](auto &Queue) { if (Queue.empty()) { Queue.emplace(); // default construct a value in the empty queue. } }; (addDefaultIfEmpty(std::get(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 writer_value_type front(Seq) const noexcept { ASSERT(hasCompleteTuple(element_idx_seq_t())); return {std::get(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 void pop(Seq) noexcept { ASSERT(hasCompleteTuple(element_idx_seq_t())); (std::get(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 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 using element_writer_t = SplittedElementWriter; ///\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::Type /// \endcode template struct ElementWriters; /// Template definition. -template -struct ElementWriters> { +template struct ElementWriters> { /// The converted type. using Type = std::tuple...>; }; ///@} /// 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 using element_writers_t = typename ElementWriters::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 using splitted_tuple_writers_t = element_writers_t>; /// 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 splitted_tuple_writers_t splitTupleWriterImpl(TupleWriter &&Writer, const IncompleteTuplePolicy Policy, Seq) noexcept { using TB = TupleWriterBuffer; // 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(std::move(Writer), Policy); return {element_writer_t(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 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 splitted_tuple_writers_t splitTupleWriter(TupleWriter &&Writer, const IncompleteTuplePolicy Policy) noexcept { return splitTupleWriterImpl( std::move(Writer), Policy, seq_t>()); } } // End namespace writer } // End namespace rosa #endif // ROSA_SUPPORT_WRITER_SPLIT_TUPLE_WRITER_HPP