diff --git a/.gitignore b/.gitignore index 386d354..cd73404 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ .vscode -.vs - +build* +CMakeLists.txt.user diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..36b7cd9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/README.txt b/README.txt index 2240886..4cb7e4f 100644 --- a/README.txt +++ b/README.txt @@ -1,6 +1,7 @@ ==================================== Research on Self-Awareness Framework ==================================== For documentation of the RoSA framework, please refer to the docs directory. +For licensing information, see the file LICENSE. diff --git a/apps/sa-ews1/sa-ews1.cpp b/apps/sa-ews1/sa-ews1.cpp index 836e3d1..e137d8e 100644 --- a/apps/sa-ews1/sa-ews1.cpp +++ b/apps/sa-ews1/sa-ews1.cpp @@ -1,318 +1,324 @@ //===-- apps/sa-ews1/sa-ews1.cpp --------------------------------*- C++ -*-===// // // The RoSA Framework -- Application SA-EWS1 // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file apps/sa-ews1/sa-ews1.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief The application SA-EWS1 implements the case study from the paper: /// M. Götzinger, N. Taherinejad, A. M. Rahmani, P. Liljeberg, A. Jantsch, and /// H. Tenhunen: Enhancing the Early Warning Score System Using Data Confidence /// DOI: 10.1007/978-3-319-58877-3_12 //===----------------------------------------------------------------------===// #include "rosa/agent/Abstraction.hpp" #include "rosa/agent/Confidence.hpp" #include "rosa/config/version.h" #include "rosa/deluxe/DeluxeContext.hpp" #include "rosa/support/csv/CSVReader.hpp" #include "rosa/support/csv/CSVWriter.hpp" #include using namespace rosa; using namespace rosa::agent; using namespace rosa::deluxe; using namespace rosa::terminal; const std::string AppName = "SA-EWS1"; /// Paths for the CSV files for simulation. /// ///@{ const std::string HRCSVPath = "HR.csv"; const std::string BRCSVPath = "BR.csv"; const std::string SpO2CSVPath = "SpO2.csv"; const std::string BPSysCSVPath = "BPSys.csv"; const std::string BodyTempCSVPath = "BodyTemp.csv"; const std::string ScoreCSVPath = "Score.csv"; ///@} /// How many cycles of simulation to perform. const size_t NumberOfSimulationCycles = 16; /// Warning levels for abstraction. enum WarningScore { No = 0, Low = 1, High = 2, Emergency = 3 }; /// Helper function creating a deluxe agent for pre-processing sensory values. /// /// Received values are first validated for confidence. Values which the /// validator does not mark confident are ignored. Confident values are /// abstracted into a \c WarningScore value, which is the result of the /// processing function. /// /// \note The result, \c WarningScore, is returned as \c uint32_t because /// enumeration types are not integrated into built-in types. Hence, a master /// to these agents receives its input as \c uint32_t values, and may cast them /// to \c WarningScore explicitly. /// /// \tparam T type of values to receive from the sensor /// /// \param C the deluxe context to create the agent in /// \param Name name of the new agent /// \param CC confidence validator to use /// \param A abstraction to use /// /// \return handle for the new agent template AgentHandle createLowLevelAgent(std::unique_ptr &C, const std::string &Name, const Confidence &CC, const Abstraction &A) { using handler = std::function(std::pair)>; using result = Optional; return C->createAgent( Name, handler([&, Name](std::pair I) -> result { LOG_INFO_STREAM << "\n******\n" << Name << " " << (I.second ? "" : "") << " value: " << I.first << "\n******\n"; return (I.second && CC(I.first)) ? result(A(I.first)) : result(); })); } int main(void) { LOG_INFO_STREAM << '\n' << library_string() << " -- " << Color::Red << AppName << "app" << Color::Default << '\n' << Color::Yellow << "CSV files are read from and written to the current working directory." << Color::Default << '\n'; std::unique_ptr C = DeluxeContext::create(AppName); // // Create deluxe sensors. // LOG_INFO("Creating sensors."); // All sensors are created without defining a normal generator function, but // with the default value of the second argument. That, however, requires the // data type to be explicitly defined. This is good for simulation only. AgentHandle HRSensor = C->createSensor("HR Sensor"); AgentHandle BRSensor = C->createSensor("BR Sensor"); AgentHandle SpO2Sensor = C->createSensor("SpO2 Sensor"); AgentHandle BPSysSensor = C->createSensor("BPSys Sensor"); AgentHandle BodyTempSensor = C->createSensor("BodyTemp Sensor"); // // Create functionalities. // LOG_INFO("Creating Functionalities for Agents."); // // Define confidence validators. // // Lower bounds are inclusive and upper bounds are exclusive. Confidence HRConfidence(0, 501); Confidence BRConfidence(0, 301); Confidence SpO2Confidence(0, 101); Confidence BPSysConfidence(0,501); Confidence BodyTempConfidence(-60, nextRepresentableFloatingPoint(50.0f)); // // Define abstractions. // RangeAbstraction HRAbstraction( {{{0, 40}, Emergency}, {{40, 51}, High}, {{51, 60}, Low}, {{60, 100}, No}, {{100, 110}, Low}, {{110, 129}, High}, {{129, 200}, Emergency}}, Emergency); RangeAbstraction BRAbstraction({{{0, 9}, High}, {{9, 14}, No}, {{14, 20}, Low}, {{20, 29}, High}, {{29, 50}, Emergency}}, Emergency); RangeAbstraction SpO2Abstraction({{{1, 85}, Emergency}, {{85, 90}, High}, {{90, 95}, Low}, {{95, 100}, No}}, Emergency); RangeAbstraction BPSysAbstraction( {{{0, 70}, Emergency}, {{70, 81}, High}, {{81, 101}, Low}, {{101, 149}, No}, {{149, 169}, Low}, {{169, 179}, High}, {{179, 200}, Emergency}}, Emergency); RangeAbstraction BodyTempAbstraction( {{{0.f, 28.f}, Emergency}, {{28.f, 32.f}, High}, {{32.f, 35.f}, Low}, {{35.f, 38.f}, No}, {{38.f, 39.5f}, High}, {{39.5f, 100.f}, Emergency}}, Emergency); // // Create low-level deluxe agents with \c createLowLevelAgent. // LOG_INFO("Creating low-level agents."); AgentHandle HRAgent = createLowLevelAgent(C, "HR Agent", HRConfidence, HRAbstraction); AgentHandle BRAgent = createLowLevelAgent(C, "BR Agent", BRConfidence, BRAbstraction); AgentHandle SpO2Agent = createLowLevelAgent(C, "SpO2 Agent", SpO2Confidence, SpO2Abstraction); AgentHandle BPSysAgent = createLowLevelAgent(C, "BPSys Agent", BPSysConfidence, BPSysAbstraction); AgentHandle BodyTempAgent = createLowLevelAgent( C, "BodyTemp Agent", BodyTempConfidence, BodyTempAbstraction); // // Connect sensors to low-level agents. // LOG_INFO("Connect sensors to their corresponding low-level agents."); C->connectSensor(HRAgent, 0, HRSensor, "HR Sensor Channel"); C->connectSensor(BRAgent, 0, BRSensor, "BR Sensor Channel"); C->connectSensor(SpO2Agent, 0, SpO2Sensor, "SpO2 Sensor Channel"); C->connectSensor(BPSysAgent, 0, BPSysSensor, "BPSys Sensor Channel"); C->connectSensor(BodyTempAgent, 0, BodyTempSensor, "BodyTemp Sensor Channel"); // // Create a high-level deluxe agent. // LOG_INFO("Create high-level agent."); // The new agent logs its input values and results in the the sum of them. AgentHandle BodyAgent = C->createAgent( "Body Agent", std::function( std::pair, std::pair, std::pair, std::pair, std::pair)>( [](std::pair HR, std::pair BR, std::pair SpO2, std::pair BPSys, std::pair BodyTemp) -> Optional { LOG_INFO_STREAM << "\n*******\nBody Agent trigged with values:\n" << (HR.second ? "" : "") << " HR warning score: " << HR.first << "\n" << (BR.second ? "" : "") << " BR warning score: " << BR.first << "\n" << (SpO2.second ? "" : "") << " SpO2 warning score: " << SpO2.first << "\n" << (BPSys.second ? "" : "") << " BPSys warning score: " << BPSys.first << "\n" << (BodyTemp.second ? "" : "") << " BodyTemp warning score: " << BodyTemp.first << "\n******\n"; return {HR.first + BR.first + SpO2.first + BPSys.first + BodyTemp.first}; })); // // Connect low-level agents to the high-level agent. // LOG_INFO("Connect low-level agents to the high-level agent."); C->connectAgents(BodyAgent, 0, HRAgent, "HR Agent Channel"); C->connectAgents(BodyAgent, 1, BRAgent, "BR Agent Channel"); C->connectAgents(BodyAgent, 2, SpO2Agent, "SpO2 Agent Channel"); C->connectAgents(BodyAgent, 3, BPSysAgent, "BPSys Agent Channel"); C->connectAgents(BodyAgent, 4, BodyTempAgent, "BodyTemp Agent Channel"); // // For simulation output, create a logger agent writing the output of the // high-level agent into a CSV file. // LOG_INFO("Create a logger agent."); // Create CSV writer. std::ofstream ScoreCSV(ScoreCSVPath); csv::CSVWriter ScoreWriter(ScoreCSV); // The agent writes each new input value into a CSV file and produces nothing. AgentHandle LoggerAgent = C->createAgent( "Logger Agent", std::function(std::pair)>( [&ScoreWriter](std::pair Score) -> Optional { if (Score.second) { // The state of \p ScoreWriter is not checked, expecting good. ScoreWriter << Score.first; } return {}; })); // // Connect the high-level agent to the logger agent. // LOG_INFO("Connect the high-level agent to the logger agent."); C->connectAgents(LoggerAgent, 0, BodyAgent, "Body Agent Channel"); // // Do simulation. // LOG_INFO("Setting up and performing simulation."); // // Initialize deluxe context for simulation. // C->initializeSimulation(); // // Open CSV files and register them for their corresponding sensors. // // Type aliases for iterators. - using CSVInt = csv::CSVFlatIterator; - using CSVFloat = csv::CSVFlatIterator; + using CSVInt = csv::CSVIterator; + using CSVFloat = csv::CSVIterator; std::ifstream HRCSV(HRCSVPath); C->registerSensorValues(HRSensor, CSVInt(HRCSV), CSVInt()); std::ifstream BRCSV(BRCSVPath); C->registerSensorValues(BRSensor, CSVInt(BRCSV), CSVInt()); std::ifstream SpO2CSV(SpO2CSVPath); C->registerSensorValues(SpO2Sensor, CSVInt(SpO2CSV), CSVInt()); std::ifstream BPSysCSV(BPSysCSVPath); C->registerSensorValues(BPSysSensor, CSVInt(BPSysCSV), CSVInt()); std::ifstream BodyTempCSV(BodyTempCSVPath); C->registerSensorValues(BodyTempSensor, CSVFloat(BodyTempCSV), CSVFloat()); // // Simulate. // C->simulate(NumberOfSimulationCycles); return 0; } diff --git a/docs/Issues.rst b/docs/Issues.rst index b3f6fd0..c873116 100755 --- a/docs/Issues.rst +++ b/docs/Issues.rst @@ -1,53 +1,52 @@ ================================================================== Known Issues with the Current Implementation of the RoSA Framework ================================================================== .. contents:: :local: TODO ==== * Project logo - `docs/_themes/rosa-theme/static/logo.png` -* License? * Packaging with `CPack `_. * What about design documentation on the basics of RoSA? * What about testing the framework? Known Issues ============ The issues that are listed below are described assuming :ref:`minimal build dependencies ` are met. * C++ * The noexcept-specification is part of the function type since C++17 but `std::function` is not defined for noexcept template arguments. So we cannot enforce the expected noexcept semantics for functions stored in `std::function`. See affected functions `rosa::Invoker::wrap()`, `rosa::deluxe::DeluxeAgent::DeluxeAgent()`, `rosa::deluxe::DeluxeSensor::DeluxeSensor()`, and `rosa::deluxe::DeluxeSensor::registerSimulationDataSource()`. * MSVC * While :ref:`RoSA does not use exceptions `, we cannot disable unwind semantics with MSVC because some included header files define functions that use exceptions. * Doxygen * "Potential recursive class relation" is detected for `rosa::GenSeq`, which is true if one ignores the template specialization for the terminal case. It would be nice not to have this pointless warning. * clang-tidy * Clang-tidy reports warnings about `noexcept` marking for the move constructor and move assignment operator of `rosa::Optional` in some situations when the template with the non-specialized argument list is used -- for example, in the file `example/deluxe-interface/deluxe-interface.cpp`. However, the condition for the `noexcept` marking should be met and the warning is pointless. diff --git a/docs/index.rst b/docs/index.rst index 6a9b649..e42d88a 100755 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,71 +1,83 @@ ================== The RoSA Framework ================== .. contents:: :local: Overview ======== The RoSA (Research on Self-Awareness) Framework is intended to be used as a tool for experimenting and evaluating new ways of implementing self-aware applications. This documentation was generated for RoSA version |release| on |today|. Documentation ============= .. toctree:: :hidden: Changelog Use Build Dev Issues Release Plan CommandGuide/index :doc:`Use` Describes how to create your own software based on RoSA. :doc:`Build` Describes how to build RoSA. :doc:`Dev` Provides basic information for developers on how to contribute to RoSA. :doc:`Issues` Known issues and things to do with the current implementation of RoSA. :doc:`Release` Describes for maintainers how to make a new release of RoSA. :doc:`Plan` Discusses future work and possible ways to develop RoSA. :doc:`CommandGuide/index` Documentation for RoSA applications and tools. Changes ======= Refer to :doc:`Changelog`. .. _API_Documentation: API Documentation ================= For details of the provided interface, refer to our `API documentation`_. .. _API documentation: ../doxygen/html/index.html +License +======= + +RoSA is distributed under the terms and conditions of the +`Boost Software License 1.0 `_: + +.. literalinclude:: ../LICENSE + :language: none + +.. note:: + 3rd party externals in the directory `modules` are not part of the RoSA source base and are licensed separately. + Indices and Tables ================== * :ref:`genindex` * :ref:`search` diff --git a/examples/CSVFiles/CMakeLists.txt b/examples/CSVFiles/CMakeLists.txt new file mode 100644 index 0000000..cfc92e7 --- /dev/null +++ b/examples/CSVFiles/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(csvfiles main.cpp) +ROSA_add_library_dependencies(csvfiles ROSAConfig) +ROSA_add_library_dependencies(csvfiles ROSADeluxe) diff --git a/examples/CSVFiles/main.cpp b/examples/CSVFiles/main.cpp new file mode 100644 index 0000000..5fc6075 --- /dev/null +++ b/examples/CSVFiles/main.cpp @@ -0,0 +1,360 @@ +//===-- examples/CSVFiles/main.cpp ------------------*- C++ -*-===// +// +// The RoSA Framework +// +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// +//===----------------------------------------------------------------------===// +/// +/// \file examples/basic-system/basic-system.cpp +/// +/// \author Edwin Willegger (edwin.willegger@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief A simple example on the basic \c rosa::csv, \c rosa::iterator and +/// \c rosa::writer classes. Focus is on the tuple impementations. +/// +//===----------------------------------------------------------------------===// +#include +#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. +#include "rosa/deluxe/DeluxeContext.hpp" + +#include "rosa/config/version.h" + +//includes to test the basic functionality +//to read and write tuples. +#include "rosa/support/csv/CSVReader.hpp" +#include "rosa/support/csv/CSVWriter.hpp" +#include "rosa/support/iterator/split_tuple_iterator.hpp" +#include "rosa/support/writer/split_tuple_writer.hpp" + +/// the name of the example +const std::string ExampleName = "csvfiles"; + +/// How many cycles of simulation to perform. +const size_t NumberOfSimulationCycles = 10; + +/// Paths for the CSV files for simulation. +/// input csv files +const std::string csvPath = "../examples/CSVFiles/"; +const std::string csvFileWithHeader = csvPath + "HR-New.csv"; +const std::string csvFileNoHeader = csvPath + "HR.csv"; +const std::string csvFileHeaderSemi = csvPath + "HR-New-Semicolon.csv"; +/// output csv files +const std::string csvFileWriteHea = csvPath + "csvwriter_noheader.csv"; +const std::string csvFileWriteNoHeaSplit = csvPath + "csvSplitwriter_noheader.csv"; + +using namespace rosa; + +/// +/// This function tests the basic CSVIterator capablities, and shows you +/// how you could work with this class. +/// +void testtupleCSVReader(void){ + + //different streams to get the csv data out of the files + //file contains header and valid data entries, delimter = ',' + std::ifstream file_header_data(csvFileWithHeader); + //file contains header and valid data entries, delimter = ',' + std::ifstream file_header_data_2(csvFileWithHeader); + //file contains header and valid data entries, delimter = ',' + std::ifstream file_header_data_3(csvFileWithHeader); + //file contains header and valid data entries, delimter = ',' + std::ifstream file_header_data_4(csvFileWithHeader); + //file contains header and valid data entries, delimter = ',' + std::ifstream file_header_data_5(csvFileWithHeader); + //file contains header and valid data entries, delimter = ',' + std::ifstream file_header_data_6(csvFileWithHeader); + //file contains no header an valid data entries, delimter = ',' + std::ifstream file2(csvFileNoHeader); + //file contains header and valid data entries, delimter = ';' + std::ifstream file3(csvFileHeaderSemi); + + csv::CSVIterator it(file_header_data); + + it.setDelimeter(','); + + (void)++it; + (void)++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. + (void)++it; + (void)++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. +/// +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. +/// +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. +/// +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){ + LOG_INFO_STREAM << library_string() << " -- " << terminal::Color::Red + << ExampleName << " example" << terminal::Color::Default << '\n'; + + // + // Testing CSVWriter. + // + LOG_INFO("Testing CSVWriter CSVTupleItrator implementation: "); + + testtupleCSVWriter(); + + // + // Testing CSVReader. + // + LOG_INFO("Testing CSVReader CSVTupleIterator implementation: "); + + testtupleCSVReader(); + + // + // Testing SplitTupleIterator. + // + LOG_INFO("Testing SplitTupleIterator: "); + + testsplitTupleIterator(); + + // + // Testing SplitTupleWriter. + // + LOG_INFO("Testing SplitTupleWriter: "); + testsplitTupleWriter(); + + + // + // info that user knows programm has finished. + // + LOG_INFO( "All tests finished."); + + + return 0; +} + diff --git a/examples/agent-functionalities/basic-agent-functionalities/agent-functionalities.cpp b/examples/agent-functionalities/basic-agent-functionalities/agent-functionalities.cpp index 0266565..d6e5ede 100644 --- a/examples/agent-functionalities/basic-agent-functionalities/agent-functionalities.cpp +++ b/examples/agent-functionalities/basic-agent-functionalities/agent-functionalities.cpp @@ -1,190 +1,194 @@ //===-- examples/agent-functionalities/agent-functionalities.cpp *-- C++-*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file examples/agent-functionalities/agent-functionalities.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief A simple example on defining \c rosa::Agent instances using /// \c rosa::agent::Functionality object as components. /// //===----------------------------------------------------------------------===// // Make sure M_PI is available, needed for _WIN32 #define _USE_MATH_DEFINES #include #include "rosa/agent/Abstraction.hpp" +#include "rosa/agent/Confidence.hpp" #include "rosa/agent/FunctionAbstractions.hpp" #include "rosa/agent/RangeConfidence.hpp" -#include "rosa/agent/Confidence.hpp" #include "rosa/config/version.h" #include "rosa/core/Agent.hpp" #include "rosa/core/MessagingSystem.hpp" #include "rosa/support/log.h" #include "rosa/support/terminal_colors.h" #include using namespace rosa; using namespace rosa::agent; using namespace rosa::terminal; // We use pi as float rather than double, which M_PI is. constexpr float Pi = (float) M_PI; /// A dummy wrapper for testing \c rosa::MessagingSystem. /// /// \note Since we test \c rosa::MessagingSystem directly here, we need to get /// access to its protected members. That we do by imitating to be a decent /// subclass of \c rosa::MessagingSystem, while calling protected member /// functions on an object of a type from which we actually don't inherit. struct SystemTester : protected MessagingSystem { template static AgentHandle createMyAgent(MessagingSystem *S, const std::string &Name, Funs &&... Fs) { return ((SystemTester *)S)->createAgent(Name, std::move(Fs)...); } static void destroyMyAgent(MessagingSystem *S, const AgentHandle &H) { ((SystemTester *)S)->destroyUnit(unwrapAgent(H)); } }; /// A special \c rosa::Agent with its own state. class MyAgent : public Agent { public: using Tick = AtomConstant; private: enum class Categories { Bad, Normal, Good }; static const std::map CategoryNames; - History H; + StaticLengthHistory H; Confidence C; RangeAbstraction A; PartialFunction L; RangeConfidence RCL; RangeConfidence RCS; public: void handler(Tick, uint8_t V) noexcept { // Record \p V to the \c rosa::agent::History, then print state info. H << V; ASSERT(H.entry() == V); // Sanity check. LOG_INFO_STREAM << "\nNext value: " << PRINTABLE(V) << ", confidence: " << C(H) << ", category: " << CategoryNames.at(A(H.entry())) << ", partial: " << int(L(H.entry())) << ", range-confidence-linear: "; - std::map res_lin = RCL(H.entry()); - for (auto i : res_lin){ - LOG_INFO_STREAM << " " << CategoryNames.at(i.first) - << " " << i.second << "," ; + std::map ResLin = RCL(H.entry()); + for (auto Con : ResLin) { + LOG_INFO_STREAM << " " << CategoryNames.at(Con.first) << " " << Con.second + << ","; } LOG_INFO_STREAM << " range-confidence-sine: "; - std::map res_sine = RCS(H.entry()); - for (auto i : res_sine){ - LOG_INFO_STREAM << " " << CategoryNames.at(i.first) - << " " << i.second << "," ; + std::map ResSine = RCS(H.entry()); + for (auto Con : ResSine) { + LOG_INFO_STREAM << " " << CategoryNames.at(Con.first) << " " << Con.second + << ","; } LOG_INFO_STREAM << '\n'; - } MyAgent(const AtomValue Kind, const rosa::id_t Id, const std::string &Name, MessagingSystem &S) : Agent(Kind, Id, Name, S, THISMEMBER(handler)), H(), C(5, 20, 1), A({{{(uint8_t)10, (uint8_t)14}, Categories::Normal}, {{(uint8_t)15, (uint8_t)17}, Categories::Good}, {{(uint8_t)18, (uint8_t)19}, Categories::Normal}}, Categories::Bad), L({{{0, 2}, std::make_shared>(0, 1)}, {{2, 4}, std::make_shared>(2, 0)}, {{4, 6}, std::make_shared>(6, -1)}}, 0), RCL({ {Categories::Bad, PartialFunction({ {{0.f, 3.f}, std::make_shared> (0.f, 1.f/3)}, {{3.f, 6.f}, std::make_shared> (1.f, 0.f)}, {{6.f, 9.f}, std::make_shared> (3.f, -1.f/3)}, },0)}, {Categories::Normal, PartialFunction({ {{6.f, 9.f}, std::make_shared> (-2.f, 1.f/3)}, {{9.f, 12.f}, std::make_shared> (1.f, 0.f)}, {{12.f, 15.f}, std::make_shared> (5.f, -1.f/3)}, },0)}, {Categories::Good, PartialFunction({ {{12.f, 15.f}, std::make_shared> (-4.f, 1.f/3)}, {{15.f, 18.f}, std::make_shared> (1.f, 0.f)}, {{18.f, 21.f}, std::make_shared> (7.f, -1.f/3)}, },0)} }), RCS({ {Categories::Bad, PartialFunction({ {{0.f, 3.f}, std::make_shared> (Pi/3, 0.5f, -Pi/2, 0.5f)}, {{3.f, 6.f}, std::make_shared>(1.f, 0.f)}, {{6.f, 9.f}, std::make_shared> (Pi/3, 0.5f, -Pi/2 + 3, 0.5f)}, },0)}, {Categories::Normal, PartialFunction({ {{6.f, 9.f}, std::make_shared> (Pi/3, 0.5f, -Pi/2, 0.5f)}, {{9.f, 12.f}, std::make_shared>(1.f, 0.f)}, {{12.f, 15.f}, std::make_shared> (Pi/3, 0.5f, -Pi/2 + 3, 0.5f)}, },0)}, {Categories::Good, PartialFunction({ {{12.f, 15.f}, std::make_shared> (Pi/3, 0.5f, -Pi/2, 0.5f)}, {{15.f, 18.f}, std::make_shared>(1.f, 0.f)}, {{18.f, 21.f}, std::make_shared> (Pi/3, 0.5f, -Pi/2 + 3, 0.5f)}, },0)} }, true){} }; const std::map MyAgent::CategoryNames{ {Categories::Bad, "Bad"}, {Categories::Normal, "Normal"}, {Categories::Good, "Good"}}; int main(void) { - LOG_INFO_STREAM << library_string() << " -- " << Color::Red - << "agent-functionalities example" << Color::Default - << '\n'; + LOG_INFO_STREAM << library_string() << " -- " << Color::Red + << "agent-functionalities example" << Color::Default << '\n'; std::unique_ptr S = MessagingSystem::createSystem("Sys"); MessagingSystem *SP = S.get(); AgentHandle A = SystemTester::createMyAgent(SP, "MyAgent"); - std::vector Vs{0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 13, - 15, 14, 15, 16, 19, 20, 21}; + std::vector Vs{0, 1, 2, 3, 4, 5, 6, 7, 9, 10, + 11, 13, 15, 14, 15, 16, 19, 20, 21}; for (auto I = Vs.begin(); I != Vs.end(); ++I) { A.send(MyAgent::Tick::Value, *I); } SystemTester::destroyMyAgent(SP, A); return 0; } diff --git a/examples/basic-system/basic-system.cpp b/examples/basic-system/basic-system.cpp index a8d013a..39a8229 100644 --- a/examples/basic-system/basic-system.cpp +++ b/examples/basic-system/basic-system.cpp @@ -1,70 +1,76 @@ //===-- examples/basic-system/basic-system.cpp ------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file examples/basic-system/basic-system.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief A simple example on the basic \c rosa::System and \c rosa::Unit /// classes. /// //===----------------------------------------------------------------------===// #include "rosa/config/version.h" #include "rosa/core/System.hpp" #include "rosa/core/Unit.h" #include "rosa/support/log.h" #include "rosa/support/terminal_colors.h" using namespace rosa; using namespace rosa::terminal; /// A dummy wrapper for testing \c rosa::System. /// /// \note Since we test \c rosa::System directly here, we need to get access to /// its protected members. That we do by imitating to be a decent subclass of /// \c rosa::System, while calling protected member functions on an object of a /// type from which we actually don't inherit. struct SystemTester : protected System { static constexpr AtomValue UnitKind = atom("unit"); static Unit &createMyUnit(System *S, const std::string &Name = std::string()) { return ((SystemTester *)S) ->createUnit([&Name](const rosa::id_t Id, System &S) noexcept { const std::string N( Name.empty() ? "Unit_" + std::to_string(S.numberOfConstructedUnits()) : Name); return new Unit(UnitKind, Id, N, S); }); } static void destroyMyUnit(System *S, Unit &U) { ((SystemTester *)S)->destroyUnit(U); } }; int main(void) { LOG_INFO_STREAM << library_string() << " -- " << Color::Red << "basic-system example" << Color::Default << '\n'; std::unique_ptr S = System::createSystem("Sys"); System *SP = S.get(); Unit &Unit1 = SystemTester::createMyUnit(SP), &Unit2 = SystemTester::createMyUnit(SP, "Second"), &Unit3 = SystemTester::createMyUnit(SP); SystemTester::destroyMyUnit(SP, Unit1); SystemTester::destroyMyUnit(SP, Unit3); LOG_INFO_STREAM << "Dumping Unit2\n" << Unit2 << '\n'; SystemTester::destroyMyUnit(SP, Unit2); return 0; } diff --git a/examples/deluxe-interface/deluxe-interface.cpp b/examples/deluxe-interface/deluxe-interface.cpp index 78638ff..3795e86 100644 --- a/examples/deluxe-interface/deluxe-interface.cpp +++ b/examples/deluxe-interface/deluxe-interface.cpp @@ -1,338 +1,344 @@ //===-- examples/deluxe-interface/deluxe-interface.cpp ----------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file examples/deluxe-interface/deluxe-interface.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief A simple example on the \c rosa::deluxe::DeluxeContext and related /// classes. //===----------------------------------------------------------------------===// #include "rosa/config/version.h" #include "rosa/deluxe/DeluxeContext.hpp" #include #include #include using namespace rosa; using namespace rosa::deluxe; using namespace rosa::terminal; /// How many cycles of simulation to perform. const size_t NumberOfSimulationCycles = 16; int main(void) { LOG_INFO_STREAM << '\n' << library_string() << " -- " << Color::Red << "deluxe-interface example" << Color::Default << '\n'; std::unique_ptr C = DeluxeContext::create("Deluxe"); // // Create deluxe sensors. // LOG_INFO("Creating sensors."); // All sensors are created without defining a normal generator function, but // with the default value of the last argument. That, however, requires the // data type to be explicitly defined. This is good for simulation only. // The first and second sensors do not receive master-input. AgentHandle BoolSensor = C->createSensor("BoolSensor"); AgentHandle IntSensor = C->createSensor("IntSensor"); // This sensor receives master-input and dumps it to \c LOG_INFO_STREAM. const std::string FloatSensorName = "FloatSensor"; AgentHandle FloatSensor = C->createSensor( FloatSensorName, [&FloatSensorName](std::pair I) { LOG_INFO_STREAM << "\n******\n" << FloatSensorName << " master-input " << (I.second ? "" : "") << " value: " << I.first << "\n******\n"; }); // This sensor do not receive master-input but produces tuples. using TupleType = DeluxeTuple; AgentHandle TupleSensor = C->createSensor("TupleSensor"); // // Check and set execution policy for sensors. // LOG_INFO("Execution policies for sensors."); LOG_INFO(std::to_string(*C->getExecutionPolicy(IntSensor))); C->setExecutionPolicy(IntSensor, DeluxeExecutionPolicy::decimation(2)); C->setExecutionPolicy(FloatSensor, DeluxeExecutionPolicy::decimation(2)); LOG_INFO(std::to_string(*C->getExecutionPolicy(IntSensor))); // // Create low-level deluxe agents with \c createLowLevelAgent. // LOG_INFO("Creating low-level agents."); // All agents below dump their received values to \c LOG_INFO_STREAM on each // triggering. // This agent does not receive master-input and does not produce // master-output. It results in the value it received. const std::string BoolAgentName = "BoolAgent"; using BoolResult = Optional; using BoolHandler = std::function)>; AgentHandle BoolAgent = C->createAgent( BoolAgentName, BoolHandler([&BoolAgentName](std::pair I) -> BoolResult { LOG_INFO_STREAM << "\n******\n" << BoolAgentName << " " << (I.second ? "" : "") << " value: " << I.first << "\n******\n"; return {I.first}; })); // This agent receives master-input but does not produce master-output. The // agent maintains a state in \c IntAgentOffset. The master-input handler // updates \c IntAgentOffset according to each received (new) value from its // master. The slave-input handler results in the sum of the received value // and the actual value of \c IntAgentOffset. const std::string IntAgentName = "IntAgent"; using IntMasterHandler = std::function)>; using IntResult = Optional; using IntHandler = std::function)>; uint32_t IntAgentOffset = 0; AgentHandle IntAgent = C->createAgent( IntAgentName, // Master-input handler. IntMasterHandler([&IntAgentName, &IntAgentOffset](std::pair I) { LOG_INFO_STREAM << "\n******\n" << IntAgentName << " master-input " << (I.second ? "" : "") << " value: " << I.first << "\n******\n"; if (I.second) { IntAgentOffset = I.first; } }), // Slave-input handler. IntHandler([&IntAgentName, &IntAgentOffset](std::pair I) -> IntResult { LOG_INFO_STREAM << "\n******\n" << IntAgentName << " " << (I.second ? "" : "") << " value: " << I.first << "\n******\n"; return {I.first + IntAgentOffset}; })); // This agent receives master-input and produces master-output. The // master-input handler propagaates each received (new) value to its slave as // master-output. The slave-input handler results in the value it received and // produces no actual master-output. const std::string FloatAgentName = "FloatAgent"; using FloatMasterResult = std::tuple>; using FloatMasterHandler = std::function)>; using FloatResult = std::tuple, Optional>; using FloatHandler = std::function)>; AgentHandle FloatAgent = C->createAgent( FloatAgentName, // Master-input handler. FloatMasterHandler([&FloatAgentName]( std::pair I) -> FloatMasterResult { LOG_INFO_STREAM << "\n******\n" << FloatAgentName << " master-input " << (I.second ? "" : "") << " value: " << I.first << "\n******\n"; const auto Output = I.second ? Optional(I.first) : Optional(); return {Output}; }), // Slave-input handler. FloatHandler([&FloatAgentName](std::pair I) -> FloatResult { LOG_INFO_STREAM << "\n******\n" << FloatAgentName << " " << (I.second ? "" : "") << " value: " << I.first << "\n******\n"; return {{I.first}, {}}; })); // This agent does not receive master-input and does not produce // master-output. It results in the sum of the values it receives in a tuple. const std::string TupleAgentName = "TupleAgent"; using TupleSumResult = Optional>; using TupleHandler = std::function)>; AgentHandle TupleAgent = C->createAgent( TupleAgentName, TupleHandler( [&TupleAgentName](std::pair I) -> TupleSumResult { LOG_INFO_STREAM << "\n******\n" << TupleAgentName << " " << (I.second ? "" : "") << " value: " << I.first << "\n******\n"; return {std::get<0>(I.first) + std::get<1>(I.first)}; })); // // Set execution policies for low-level agents. // LOG_INFO("Setting Execution policies for low-level agents."); C->setExecutionPolicy(IntAgent, DeluxeExecutionPolicy::awaitAll({0})); C->setExecutionPolicy(FloatAgent, DeluxeExecutionPolicy::awaitAll({0})); // // Connect sensors to low-level agents. // LOG_INFO("Connect sensors to their corresponding low-level agents."); C->connectSensor(BoolAgent, 0, BoolSensor, "Bool Sensor Channel"); C->connectSensor(IntAgent, 0, IntSensor, "Int Sensor Channel"); C->connectSensor(FloatAgent, 0, FloatSensor, "Float Sensor Channel"); C->connectSensor(TupleAgent, 0, TupleSensor, "Tuple Sensor Channel"); // // Create a high-level deluxe agent. // LOG_INFO("Create high-level agent."); using SingleDoubleOutputType = Optional>; using SingleUInt32OutputType = Optional>; using NoOutputType = Optional; // This agent does not receive master-input but produces master-output for its // slaves at positions `1` and `2` but not for that at position `0`. The agent // maintains a state in \c SumAgentState. The handler increments \c // SumAgentState upon each received (new) `true` value from its slave at // position `0`. Whenever \c SumAgentState has been updated, it is sent to the // slaves at positions `1` and `2`. The handler results in the sum of the // values received from slaves at positions `1`, `2`, and `3`. using SumResult = std::tuple; using SumHandler = std::function, bool>, std::pair, bool>, std::pair, bool>, std::pair, bool>)>; uint32_t SumAgentState = 0; AgentHandle SumAgent = C->createAgent( "Sum Agent", SumHandler([&SumAgentState]( std::pair, bool> I0, std::pair, bool> I1, std::pair, bool> I2, std::pair, bool> I3) -> SumResult { const auto V0 = std::get<0>(I0.first); const auto V1 = std::get<0>(I1.first); const auto V2 = std::get<0>(I2.first); const auto V3 = std::get<0>(I3.first); LOG_INFO_STREAM << "\n*******\nSum Agent triggered with values:\n" << (I0.second ? "" : "") << " bool value: " << V0 << "\n" << (I1.second ? "" : "") << " int value: " << V1 << "\n" << (I2.second ? "" : "") << " float value: " << V2 << "\n" << (I3.second ? "" : "") << " double value: " << V3 << "\n******\n"; if (I0.second && V0) { ++SumAgentState; } const SingleUInt32OutputType MasterOutput = I0.second && V0 ? SingleUInt32OutputType(DeluxeTuple(SumAgentState)) : SingleUInt32OutputType(); const DeluxeTuple Output = {V1 + V2 + V3}; return {{Output}, {}, {MasterOutput}, {MasterOutput}, {}}; })); // // Connect low-level agents to the high-level agent. // LOG_INFO("Connect low-level agents to the high-level agent."); C->connectAgents(SumAgent, 0, BoolAgent, "Bool Agent Channel"); C->connectAgents(SumAgent, 1, IntAgent, "Int Agent Channel"); C->connectAgents(SumAgent, 2, FloatAgent, "Float Agent Channel"); C->connectAgents(SumAgent, 3, TupleAgent, "Tuple Agent Channel"); // // For simulation output, create a logger agent writing the output of the // high-level agent into a log stream. // LOG_INFO("Create a logger agent."); // The agent dumps each received (new) value to \c LOG_INFO_STREAM and // produces nothing; does not receive mater-input and does not produce // master-output. AgentHandle LoggerAgent = C->createAgent("Logger Agent", std::function(std::pair)>( [](std::pair Sum) -> Optional { if (Sum.second) { LOG_INFO_STREAM << "Result: " << Sum.first << "\n"; } return {}; })); // // Connect the high-level agent to the logger agent. // LOG_INFO("Connect the high-level agent to the logger agent."); C->connectAgents(LoggerAgent, 0, SumAgent, "Sum Agent Channel"); // // Do simulation. // LOG_INFO("Setting up and performing simulation."); // // Initialize deluxe context for simulation. // C->initializeSimulation(); // // Create some vectors and register them for their corresponding sensors. // std::vector BoolValues(NumberOfSimulationCycles); std::generate(BoolValues.begin(), BoolValues.end(), [i = 0](void) mutable -> bool { return (++i % 4) == 0; }); C->registerSensorValues(BoolSensor, BoolValues.begin(), BoolValues.end()); std::vector IntValues(NumberOfSimulationCycles); std::generate(IntValues.begin(), IntValues.end(), [i = 0](void) mutable { return ++i; }); C->registerSensorValues(IntSensor, IntValues.begin(), IntValues.end()); std::vector FloatValues(NumberOfSimulationCycles); std::generate(FloatValues.begin(), FloatValues.end(), [f = 0.5f](void) mutable { f += 0.3f; return std::floor(f) + 0.5f; }); C->registerSensorValues(FloatSensor, FloatValues.begin(), FloatValues.end()); std::vector TupleValues(NumberOfSimulationCycles); std::generate(TupleValues.begin(), TupleValues.end(), [f1 = 0.f, f2 = 3.14f](void) mutable -> TupleType { f1 += f2; f2 -= f1; return {f1, f2}; }); C->registerSensorValues(TupleSensor, TupleValues.begin(), TupleValues.end()); // // Simulate. // C->simulate(NumberOfSimulationCycles); return 0; } diff --git a/examples/messaging-system/messaging-system.cpp b/examples/messaging-system/messaging-system.cpp index e7817cc..868cf25 100644 --- a/examples/messaging-system/messaging-system.cpp +++ b/examples/messaging-system/messaging-system.cpp @@ -1,136 +1,142 @@ //===-- examples/messaging-system/messaging-system.cpp ----------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file examples/messaging-system/messaging-system.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief A simple example on the \c rosa::MessagingSystem and \c rosa::Agent /// classes. //===----------------------------------------------------------------------===// #include "rosa/config/version.h" #include "rosa/core/Agent.hpp" #include "rosa/core/MessagingSystem.hpp" #include "rosa/support/log.h" #include "rosa/support/terminal_colors.h" using namespace rosa; using namespace rosa::terminal; /// A dummy wrapper for testing \c rosa::MessagingSystem. /// /// \note Since we test \c rosa::MessagingSystem directly here, we need to get /// access to its protected members. That we do by imitating to be a decent /// subclass of \c rosa::MessagingSystem, while calling protected member /// functions on an object of a type from which we actually don't inherit. struct SystemTester : protected MessagingSystem { template static AgentHandle createMyAgent(MessagingSystem *S, const std::string &Name, Funs &&... Fs) { return ((SystemTester *)S)->createAgent(Name, std::move(Fs)...); } static void destroyMyAgent(MessagingSystem *S, const AgentHandle &H) { ((SystemTester *)S)->destroyUnit(unwrapAgent(H)); } }; /// A special \c rosa::Agent subclass with its own state. class MyAgent : public Agent { public: using Tick = AtomConstant; using Report = AtomConstant; private: size_t Counter; public: void handler(Tick) noexcept { LOG_INFO_STREAM << "MyAgent Tick count: " << ++Counter << '\n'; } MyAgent(const AtomValue Kind, const rosa::id_t Id, const std::string &Name, MessagingSystem &S) : Agent(Kind, Id, Name, S, Invoker::F([this](Report) noexcept { LOG_INFO_STREAM << "MyAgent count: " << Counter << '\n'; }), THISMEMBER(handler)), Counter(0) {} }; int main(void) { LOG_INFO_STREAM << library_string() << " -- " << Color::Red << "messaging-system example" << Color::Default << '\n'; std::unique_ptr S = MessagingSystem::createSystem("Sys"); MessagingSystem *SP = S.get(); LOG_INFO("\n\n** Stateless Agents\n"); AgentHandle Agent1 = SystemTester::createMyAgent( SP, "Agent1", Invoker::F([](const std::string &M) noexcept { LOG_INFO("Agent1: " + M); })); using Print = AtomConstant; using Forward = AtomConstant; AgentHandle Agent2 = SystemTester::createMyAgent( SP, "Agent2", Invoker::F([](Print, uint8_t N) noexcept { LOG_INFO("Agent2: " + std::to_string(N)); }), Invoker::F([&Agent1](Forward, uint8_t N) noexcept { if (Agent1) { Agent1.send(std::to_string(N)); } else { LOG_INFO("Agent2 cannot forward: Agent1 is not valid"); } })); LOG_INFO_STREAM << "\nAgent1 is valid: " << bool(Agent1) << "\nAgent2 is valid: " << bool(Agent2) << '\n'; LOG_INFO("Sending a print-message to Agent2..."); SP->send(Agent2, Print::Value, 42); LOG_INFO("Sending a forward-message to Agent2..."); SP->send(Agent2, Forward::Value, 42); LOG_INFO("Sending an unexpected message to Agent2..."); SP->send(Agent2, unit); SystemTester::destroyMyAgent(SP, Agent1); LOG_INFO_STREAM << "\nAgent1 is valid: " << bool(Agent1) << "\nAgent2 is valid: " << bool(Agent2) << '\n'; LOG_INFO("Sending a forward-message to Agent2..."); SP->send(Agent2, Forward::Value, 42); SystemTester::destroyMyAgent(SP, Agent2); LOG_INFO("\n\n** Stateful Agents\n"); AgentHandle Agent3 = SystemTester::createMyAgent(SP, "Agent3"); for (size_t I = 0; I < 2; ++I) { LOG_INFO("Sending report-message to Agent3..."); Agent3.send(MyAgent::Report::Value); LOG_INFO("Sending tick-message to Agent3..."); Agent3.send(MyAgent::Tick::Value); } SystemTester::destroyMyAgent(SP, Agent3); LOG_INFO("\n"); return 0; } diff --git a/examples/messaging/messaging.cpp b/examples/messaging/messaging.cpp index 640a2d1..ed453d6 100644 --- a/examples/messaging/messaging.cpp +++ b/examples/messaging/messaging.cpp @@ -1,127 +1,133 @@ //===-- examples/messaging/messaging.cpp ------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file examples/messaging/messaging.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief An example showcasing features related to the \c rosa::Message class. /// //===----------------------------------------------------------------------===// #include "rosa/config/version.h" #include "rosa/core/MessageHandler.hpp" #include "rosa/support/log.h" #include "rosa/support/terminal_colors.h" using namespace rosa; using namespace rosa::terminal; int main(void) { LOG_INFO_STREAM << library_string() << " -- " << Color::Red << "messaging" << Color::Default << '\n'; auto &Log = LOG_INFO_STREAM << '\n'; // Message interface. auto PMsg = Message::create(1, 2); auto &Msg = *PMsg; Log << "Checking on a 'Message with TypeList<>':" << "\n Size: " << Msg.Size << "\n Pos 0 is uint8_t: " << Msg.isTypeAt(0) << "\n Pos 1 is uint16_t: " << Msg.isTypeAt(1) << "\n Pos 2 is uint32_t: " << Msg.isTypeAt(1) << "\n Value at pos 0: " << PRINTABLE(Msg.valueAt(0)) << "\n Value at pos 1: " << Msg.valueAt(1) << "\n\n"; // MessageMatcher. using MyMatcher = MsgMatcher; Log << "Matching against 'TypeList':" << "\n matching: " << MyMatcher::doesStronglyMatch(Msg); auto Vs = MyMatcher::extractedValues(Msg); Log << "\n value: '(" << PRINTABLE(std::get<0>(Vs)) << ", " << std::get<1>(Vs) << ")'\n\n"; using MyWrongMatcher = MsgMatcher; Log << "Matching against 'TypeList':" << "\n matching: " << MyWrongMatcher::doesStronglyMatch(Msg) << "\n\n"; using MyAtom = AtomConstant; const MyAtom &A = MyAtom::Value; using MyNAtom = AtomConstant; auto PAMsg = Message::create(A); auto &AMsg = *PAMsg; Log << "Checking on a 'Message with TypeList>':" << "\n Size: " << AMsg.Size << "\n Pos 0 is 'AtomValue': " << AMsg.isTypeAt(0) << "\n Pos 0 is 'AtomConstant': " << AMsg.isTypeAt(0) << "\n\n"; using MyAtomMatcher = MsgMatcher; Log << "Matching against 'TypeList>':" << "\n matching: " << MyAtomMatcher::doesStronglyMatch(AMsg) << "\n value: '(" << std::to_string(std::get<0>(MyAtomMatcher::extractedValues(AMsg))) << ")'" << "\n\n"; using MyWrongAtomMatcher = MsgMatcher; Log << "Matching against 'TypeList>':" << "\n matching: " << MyWrongAtomMatcher::doesStronglyMatch(AMsg) << "\n\n"; // Invoker. auto IP = Invoker::wrap(Invoker::F([&Log](MyAtom) noexcept->void { Log << "** Handling 'Message with " "TypeList>'.\n"; })); auto &I = *IP; // Get a reference from the pointer. Log << "Invoking a function of signature 'void(AtomConstant) " "noexcept':" << "\n with 'Message with TypeList'" << "\n does Message match Invoker: " << I.match(Msg) << "\n invoking..."; I(Msg); Log << "\n with 'Message with TypeList>'..." << "\n does Message match Invoker: " << I.match(AMsg) << "\n invoking..."; I(AMsg); Log << "\n\n"; // MessageHandler. MessageHandler Handler{ Invoker::F([&Log](uint8_t, uint16_t) { Log << "** Handling 'Message with TypeList'\n"; }), Invoker::F([&Log](MyAtom) { Log << "** Handling 'Message with " "TypeList>'\n"; })}; auto PANMsg = Message::create(MyNAtom::Value); auto &ANMsg = *PANMsg; Log << "Handling Messages with 'MessageHandler " "{ Invoker::F, " "Invoker::F> }':" << "\n 'Message with TypeList'" << "\n can handle: " << Handler.canHandle(Msg) << "\n handling..."; Handler(Msg); Log << "\n 'Message with TypeList>'" << "\n can handle: " << Handler.canHandle(AMsg) << "\n handling..."; Handler(AMsg); Log << "\n 'Message with TypeList>'" << "\n can handle: " << Handler.canHandle(ANMsg) << "\n handling..."; Handler(ANMsg); Log << "\n\n"; Log << "Terminating, destroying automatic variables.\n"; return 0; } diff --git a/examples/type-facilities/type-facilities.cpp b/examples/type-facilities/type-facilities.cpp index b4a3ade..696679e 100644 --- a/examples/type-facilities/type-facilities.cpp +++ b/examples/type-facilities/type-facilities.cpp @@ -1,88 +1,94 @@ //===-- examples/type-facilities/type-facilities.cpp ------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file examples/type-facilities/type-facilities.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief An example showcasing various type-related support facilities. /// //===----------------------------------------------------------------------===// #include "rosa/config/version.h" #include "rosa/support/log.h" #include "rosa/support/terminal_colors.h" #include "rosa/support/type_token.hpp" using namespace rosa; using namespace rosa::terminal; int main(void) { LOG_INFO_STREAM << library_string() << " -- " << Color::Red << "type facilities" << Color::Default << '\n'; auto &Log = LOG_TRACE_STREAM; Log << "\nNumberOfBuiltinTypes: " << NumberOfBuiltinTypes << "\nTokenBits: " << token::TokenBits << "\nRepresentationBits: " << token::RepresentationBits << "\nMaxTokenizableListSize: " << token::MaxTokenizableListSize << "\n\n"; Log << "Type number information on 'uint8_t':"; constexpr TypeNumber TN = TypeNumberOf::Value; Log << "\n type number: " << PRINTABLE_TN(TN) << "\n size: " << TypeForNumber::Size << "\n name: " << TypeForNumber::Name << "\n\n"; Log << "Type number information on 'std::string':"; constexpr TypeNumber TNS = TypeNumberOf::Value; Log << "\n type number: " << PRINTABLE_TN(TNS) << "\n size: " << TypeForNumber::Size << "\n name: " << TypeForNumber::Name << "\n\n"; Log << "Type number information of AtomConstants:"; using Atom1 = AtomConstant; using Atom2 = AtomConstant; Log << "\n std::is_same::value: " << std::is_same::value << "\n TypeNumberOf::Value: " << PRINTABLE_TN(TypeNumberOf::Value) << "\n TypeNumberOf::Value: " << PRINTABLE_TN(TypeNumberOf::Value) << "\n name: " << TypeForNumber::Value>::Name << "\n\n"; Log << "Type token information on 'TypeList':"; // \c rosa::Token is generated statically. constexpr Token T = TypeToken::Value; STATIC_ASSERT( (T == TypeListToken>::Value), "alias template definition is wrong"); Token T_ = T; // We need a non-const value for dropping head later. // Iterate over encoded entries in \c T_. while (!emptyToken(T_)) { Log << "\n token: " << PRINTABLE_TOKEN(T_) << "\n valid: " << validToken(T_) << "\n empty: " << emptyToken(T_) << "\n length: " << static_cast(lengthOfToken(T_)) << "\n full size: " << sizeOfValuesOfToken(T_) << "\n head type number: " << PRINTABLE_TN(headOfToken(T_)) << "\n size of head: " << sizeOfHeadOfToken(T_) << "\n name of head: " << nameOfHeadOfToken(T_) << "\n is head uint8_t: " << isHeadOfTokenTheSameType(T_) << "\n is head uint16_t: " << isHeadOfTokenTheSameType(T_) << "\n is head std::string: " << isHeadOfTokenTheSameType(T_) << "\nDropping head..."; dropHeadOfToken(T_); } // Here when Token became empty. Log << "\n token: " << PRINTABLE_TOKEN(T_) << "\n empty: " << emptyToken(T_) << '\n'; return 0; } diff --git a/include/rosa/agent/Abstraction.hpp b/include/rosa/agent/Abstraction.hpp index b44b2de..6afa7fd 100644 --- a/include/rosa/agent/Abstraction.hpp +++ b/include/rosa/agent/Abstraction.hpp @@ -1,238 +1,244 @@ //===-- rosa/agent/Abstraction.hpp ------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/agent/Abstraction.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Definition of *abstraction* *functionality*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_ABSTRACTION_HPP #define ROSA_AGENT_ABSTRACTION_HPP #include "rosa/agent/Functionality.h" #include "rosa/support/debug.hpp" #include #include namespace rosa { namespace agent { /// Abstracts values from a type to another one. /// /// \tparam T type to abstract from /// \tparam A type to abstract to template class Abstraction : public Functionality { protected: /// Value to abstract to by default. const A Default; public: /// Creates an instance. /// /// \param Default value to abstract to by default Abstraction(const A Default) noexcept : Default(Default) {} /// Destroys \p this object. ~Abstraction(void) = default; /// Checks wether the Abstraction evaluates to default at the given position /// /// \param V the value at which to check if the function falls back to it's /// default value. /// \return true, the default implementation always falls back to the default /// value virtual bool isDefaultAt(const T &V) const noexcept{ (void)V; return true; } /// Abstracts a value from type \p T to type \p A. /// /// \note The default implementation always returns /// \c rosa::agent::Abstraction::Default, hence the actual argument is /// ignored. /// /// \return the abstracted value virtual A operator()(const T &) const noexcept { return Default; } }; /// Implements \c rosa::agent::Abstraction as a \c std::map from a type to /// another one. /// /// \note This implementation is supposed to be used to abstract between /// enumeration types, which is statically enforced. /// /// \tparam T type to abstract from /// \tparam A type to abstract to template class MapAbstraction : public Abstraction, private std::map { // Make sure the actual type arguments are enumerations. STATIC_ASSERT((std::is_enum::value && std::is_enum::value), "mapping not enumerations"); // Bringing into scope inherited members. using Abstraction::Default; using std::map::end; using std::map::find; public: /// Creates an instance by initializing the underlying \c std::map. /// /// \param Map the mapping to do abstraction according to /// \param Default value to abstract to by default MapAbstraction(const std::map &Map, const A Default) noexcept : Abstraction(Default), std::map(Map) {} /// Destroys \p this object. ~MapAbstraction(void) = default; /// Checks wether the Abstraction evaluates to default at the given position /// /// \param V the value at which to check if the function falls back to it's /// default value. /// \return true if the Abstraction falls back to the default value bool isDefaultAt(const T &V) const noexcept override { const auto I = find(V); return I == end() ? true : false; } /// Abstracts a value from type \p T to type \p A based on the set mapping. /// /// Results in the value associated by the set mapping to the argument, or /// \c rosa::agent::MapAbstraction::Default if the actual argument is not /// associated with anything by the set mapping. /// /// \param V value to abstract /// /// \return the abstracted value based on the set mapping A operator()(const T &V) const noexcept override { const auto I = find(V); return I == end() ? Default : *I; } }; /// Implements \c rosa::agent::Abstraction as a \c std::map from ranges of a /// type to values of another type. /// /// \note This implementation is supposed to be used to abstract ranges of /// arithmetic types into enumerations, which is statically enforced. /// /// \invariant The keys in the underlying \c std::map define valid ranges /// such that `first <= second` and there are no overlapping ranges defined by /// the keys. /// /// \tparam T type to abstract from /// \tparam A type to abstract to template class RangeAbstraction : public Abstraction, private std::map, A> { // Make sure the actual type arguments are matching our expectations. STATIC_ASSERT((std::is_arithmetic::value), "abstracting not arithmetic"); /// \todo check if this compiles with the definition of abstractions as /// self-aware properties //STATIC_ASSERT((std::is_enum::value), "abstracting not to enumeration"); // Bringing into scope inherited members. using Abstraction::Default; using std::map, A>::begin; using std::map, A>::end; using std::map, A>::find; public: /// Creates an instance by Initializing the unserlying \c std::map. /// /// \param Map the mapping to do abstraction according to /// \param Default value to abstract to by default /// /// \pre Each key defines a valid range such that `first <= second` and /// there are no overlapping ranges defined by the keys. RangeAbstraction(const std::map, A> &Map, const A &Default) : Abstraction(Default), std::map, A>(Map) { // Sanity check. ASSERT(std::all_of( begin(), end(), [this](const std::pair, A> &P) { return P.first.first <= P.first.second && std::all_of(++find(P.first), end(), [&P](const std::pair, A> &R) { // \note Values in \c Map are sorted. return P.first.first < P.first.second && P.first.second <= R.first.first || P.first.first == P.first.second && P.first.second < R.first.first; }); })); } /// Destroys \p this object. ~RangeAbstraction(void) = default; /// Checks wether the Abstraction evaluates to default at the given position /// /// \param V the value at which to check if the function falls back to it's /// default value. /// \return true if the Abstraction falls back to the default value bool isDefaultAt(const T &V) const noexcept override { auto I = begin(); bool Found = false; // Indicates if \c I refers to a matching range. bool Failed = false; // Indicates if it is pointless to continue searching. while (!Found && !Failed && I != end()) { if (V < I->first.first) { // No match so far and \p V is below the next range, never will match. // \note Keys are sorted in the map. return true; } else if (I->first.first <= V && V < I->first.second) { // Matching range found. return false; } else { // Cannot conclude in this step, move to the next range. ++I; } } return true; } /// Abstracts a value from type \p T to type \p A based on the set mapping. /// /// Results in the value associated by the set mapping to the argument, or /// \c rosa::agent::RangeAbstraction::Default if the actual argument is not /// included in any of the ranges in the set mapping. /// /// \param V value to abstract /// /// \return the abstracted value based on the set mapping A operator()(const T &V) const noexcept override { auto I = begin(); bool Found = false; // Indicates if \c I refers to a matching range. bool Failed = false; // Indicates if it is pointless to continue searching. while (!Found && !Failed && I != end()) { if (V < I->first.first) { // No match so far and \p V is below the next range, never will match. // \note Keys are sorted in the map. Failed = true; } else if (I->first.first <= V && V < I->first.second) { // Matching range found. Found = true; } else { // Cannot conclude in this step, move to the next range. ++I; } } ASSERT(!Found || I != end()); return Found ? I->second : Default; } }; } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_ABSTRACTION_HPP diff --git a/include/rosa/agent/Confidence.hpp b/include/rosa/agent/Confidence.hpp index 3d07606..3ec78ae 100644 --- a/include/rosa/agent/Confidence.hpp +++ b/include/rosa/agent/Confidence.hpp @@ -1,204 +1,208 @@ //===-- rosa/agent/Confidence.hpp -------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/agent/Confidence.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Definition of *confidence* *functionality*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_CONFIDENCE_HPP #define ROSA_AGENT_CONFIDENCE_HPP #include "rosa/agent/History.hpp" #include "rosa/support/debug.hpp" #include namespace rosa { namespace agent { /// Confidence validator. /// /// Checks the plausibility of given values by validating if a valid region /// contains them. It also capable of checking consistency by validating the /// rate of change recorded by a \c rosa::agent::History object against a /// maximal absolute valid rate of change. /// /// \tparam T type of values to validate /// /// \note The template is defined only for arithmetic types. /// /// \note The lower bound is inclusive and the upper bound is exclusive. /// /// \invariant The bounds are defined in a meaningful way:\code /// LowerBound <= UpperBound /// \endcode template class Confidence : public Functionality { // Make sure the actual type argument is an arithmetic type. STATIC_ASSERT(std::is_arithmetic::value, "not arithmetic Confidence"); public: /// Unsigned type corresponding to \p T. using UT = unsigned_t; /// The minimal value of type \p T. /// \note Not exist \c V of type \p T such that `V < Min`. static constexpr T Min = std::is_integral::value ? std::numeric_limits::min() : std::numeric_limits::lowest(); /// The maximal value of type \p T. /// \note Not exist \c V of type \p T such that `V > Max`. static constexpr T Max = (std::is_integral::value || !std::numeric_limits::has_infinity) ? std::numeric_limits::max() : std::numeric_limits::infinity(); /// The maximal value of type \c UT. /// \note Not exist \c V of type \c UT such that `V > UnsignedMax`. static constexpr UT UnsignedMax = (std::is_integral::value || !std::numeric_limits::has_infinity) ? std::numeric_limits::max() : std::numeric_limits::infinity(); private: /// The inclusive lower bound for plausibility check. T LowerBound; /// The exclusive upper bound for plausibility check. T UpperBound; /// The maximal absolute rate of change for consistency check. UT ChangeRate; public: /// Creates an instance by setting the validator variables. /// /// \param LowerBound the lower bound for plausability check /// \param UpperBound the upper bound for plausability check /// \param ChangeRate maximal absolute rate of change for consistency check /// /// \pre The bounds are defined in a meaningful way:\code /// LowerBound <= UpperBound /// \endcode Confidence(const T LowerBound = Min, const T UpperBound = Max, const UT ChangeRate = UnsignedMax) noexcept - : LowerBound(LowerBound), - UpperBound(UpperBound), - ChangeRate(ChangeRate) { + : LowerBound(LowerBound), UpperBound(UpperBound), ChangeRate(ChangeRate) { // Make sure Confidence is created in a valid state. if (LowerBound > UpperBound) { ROSA_CRITICAL("Confidence with LowerBound higher than UpperBound"); } } /// Destroys \p this object. ~Confidence(void) = default; /// Gives a snapshot of the current state of the validator variables. /// /// \param [out] LowerBound to copy \c rosa::agent::Confidence::LowerBound /// into /// \param [out] UpperBound to copy \c rosa::agent::Confidence::UpperBound /// into /// \param [out] ChangeRate to copy \c rosa::agent::Confidence::ChangeRate /// into void getParameters(T &LowerBound, T &UpperBound, UT &ChangeRate) const noexcept { // Copy members to the given references. LowerBound = this->LowerBound; UpperBound = this->UpperBound; ChangeRate = this->ChangeRate; } /// Sets the lower bound for plausability check. /// /// Beyond setting the lower bound, the function also adjusts the upper bound /// to the given lower bound if the new lower bound would be higher than the /// upper bound. /// /// \param LowerBound the new lower bound to set void setLowerBound(const T LowerBound) noexcept { // Adjust UpperBound if necessary, then set LowerBound. if (UpperBound < LowerBound) { UpperBound = LowerBound; } this->LowerBound = LowerBound; } /// Sets the upper bound for plausability check. /// /// Beyond setting the upper bound, the function also adjusts the lower bound /// to the given upper bound if the new upper bound would be lower than the /// lower bound. /// /// \param UpperBound the new upper bound to set void setUpperBound(const T UpperBound) noexcept { // Adjust LowerBound if necessary, then set UpperBound. if (UpperBound < LowerBound) { LowerBound = UpperBound; } this->UpperBound = UpperBound; } /// Sets the maximal rate of change for consistency check. /// /// \param ChangeRate the new rate of change to set void setChangeRate(const UT ChangeRate) noexcept { // Set ChangeRate. this->ChangeRate = ChangeRate; } /// Tells the binary confidence on the plausibility of a value. /// /// \param V value to check /// /// \return whether \c V is within the range defined by /// \c rosa::agent::Confidence::LowerBound and /// \c rosa::agent::Confidence::UpperBound. bool operator()(const T V) const noexcept { // Return if \c V is plausible. return LowerBound <= V && V < UpperBound; } /// Tells the binary confidence on the plausibility and consistency of the /// last value recorded by a \c rosa::agent::History instance. /// /// Consistency of the last value is checked by validating the difference with /// its preceding entry. /// /// \note The \c rosa::agent::History instance needs to store values of type /// \p T. /// /// \note An empty \c rosa::agent::History instance results in full /// confidence. /// /// \tparam N number of values \p H is able to store /// \tparam P retention policy followed by \p H when capacity is reached /// /// \param H *history* whose last entry to check template - bool operator()(const History &H) const noexcept { + bool operator()(const StaticLengthHistory &H) const noexcept { if (H.empty()) { // No entry to validate. return true; } else { // Validate the last entry and the one step average absolute difference. return (*this)(H.entry()) && H.averageAbsDiff(1) <= ChangeRate; } } }; } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_CONFIDENCE_HPP diff --git a/include/rosa/agent/FunctionAbstractions.hpp b/include/rosa/agent/FunctionAbstractions.hpp index 9a7127a..2fa6912 100644 --- a/include/rosa/agent/FunctionAbstractions.hpp +++ b/include/rosa/agent/FunctionAbstractions.hpp @@ -1,224 +1,364 @@ //===-- rosa/agent/FunctionAbstractions.hpp ---------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/agent/FunctionAbstractions.hpp /// /// \author Benedikt Tutzer (benedikt.tutzer@tuwien.ac.at) /// /// \date 2019 /// /// \brief Definition of *FunctionAbstractions* *functionality*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_FUNCTIONABSTRACTIONS_HPP #define ROSA_AGENT_FUNCTIONABSTRACTIONS_HPP -#include "rosa/agent/Functionality.h" #include "rosa/agent/Abstraction.hpp" +#include "rosa/agent/Functionality.h" #include "rosa/support/debug.hpp" #include -#include #include #include +#include namespace rosa { namespace agent { /// Implements \c rosa::agent::Abstraction as a linear function, /// y = Coefficient * X + Intercept. /// /// \note This implementation is supposed to be used to represent a linear /// function from an arithmetic domain to an arithmetic range. This is enforced /// statically. /// /// \tparam D type of the functions domain /// \tparam R type of the functions range -template class LinearFunction : - public Abstraction{ +template +class LinearFunction : public Abstraction { // Make sure the actual type arguments are matching our expectations. STATIC_ASSERT((std::is_arithmetic::value), - "LinearFunction not arithmetic T"); + "LinearFunction not arithmetic T"); STATIC_ASSERT((std::is_arithmetic::value), - "LinearFunction not to arithmetic"); + "LinearFunction not to arithmetic"); + protected: /// The Intercept of the linear function const D Intercept; /// The Coefficient of the linear function const D Coefficient; public: - /// Creates an instance. + /// Creates an instance given the intercept and the coefficient of a linear + /// function. /// /// \param Intercept the intercept of the linear function /// \param Coefficient the coefficient of the linear function LinearFunction(D Intercept, D Coefficient) noexcept - : Abstraction(Intercept), - Intercept(Intercept), + : Abstraction(Intercept), Intercept(Intercept), Coefficient(Coefficient) {} + /// Creates an instance given the two points on a linear function. + /// + /// \param x1 The x-value of the first point + /// \param y1 The x-value of the first point + /// \param x2 The y-value of the second point + /// \param y2 The y-value of the second point + LinearFunction(D x1, R y1, D x2, R y2) noexcept + : Abstraction(y1 - x1 * (y1 - y2) / (x1 - x2), + (y1 - y2) / (x1 - x2)) {} + + /// Creates an instance given the two points on a linear function. + /// + /// \param p1 The coordinates of the first point + /// \param p2 The coordinates of the second point + LinearFunction(std::pair p1, std::pair p2) noexcept + : LinearFunction(p1.first, p1.second, p2.first, p2.second) {} + /// Destroys \p this object. ~LinearFunction(void) = default; /// Checks wether the Abstraction evaluates to default at the given position /// As LinearFunctions can be evaluated everythwere, this is always false /// /// \param V the value at which to check if the function falls back to it's /// default value. /// /// \return false bool isDefaultAt(const D &V) const noexcept override { (void)V; return false; } + /// Getter for member variable Intercept + /// + /// \return Intercept + D getIntercept() const { return Intercept; } + + /// Setter for member variable Intercept + /// + /// \param Intercept the new Intercept + void setIntercept(const D &Intercept) { this->Intercept = Intercept; } + + /// Getter for member variable Coefficient + /// + /// \return Coefficient + D getCoefficient() const { return Coefficient; } + + /// Setter for member variable Coefficient + /// + /// \param Coefficient the new Intercept + void setCoefficient(const D &Coefficient) { this->Coefficient = Coefficient; } + + /// Set Intercept and Coefficient from two points on the linear function + /// + /// \param x1 The x-value of the first point + /// \param y1 The x-value of the first point + /// \param x2 The y-value of the second point + /// \param y2 The y-value of the second point + void setFromPoints(D x1, R y1, D x2, R y2) { + Coefficient = (y1 - y2) / (x1 - x2); + Intercept = y1 - Coefficient * x1; + } + + /// Set Intercept and Coefficient from two points on the linear function + /// + /// \param p1 The coordinates of the first point + /// \param p2 The coordinates of the second point + inline void setFromPoints(std::pair p1, std::pair p2) { + setFromPoints(p1.first, p1.second, p2.first, p2.second); + } + /// Evaluates the linear function /// /// \param X the value at which to evaluate the function /// /// \return Coefficient*X + Intercept virtual R operator()(const D &X) const noexcept override { - return Intercept + X*Coefficient; + return Intercept + X * Coefficient; } }; /// Implements \c rosa::agent::Abstraction as a sine function, /// y = Amplitude * sin(Frequency * X + Phase) + Average. /// /// \note This implementation is supposed to be used to represent a sine /// function from an arithmetic domain to an arithmetic range. This is enforced /// statically. /// /// \tparam D type of the functions domain /// \tparam R type of the functions range -template class SineFunction : - public Abstraction{ +template +class SineFunction : public Abstraction { // Make sure the actual type arguments are matching our expectations. STATIC_ASSERT((std::is_arithmetic::value), - "SineFunction not arithmetic T"); + "SineFunction not arithmetic T"); STATIC_ASSERT((std::is_arithmetic::value), - "SineFunction not to arithmetic"); + "SineFunction not to arithmetic"); + protected: /// The frequency of the sine wave const D Frequency; /// The Ampiltude of the sine wave const D Amplitude; /// The Phase-shift of the sine wave const D Phase; /// The y-shift of the sine wave const D Average; public: /// Creates an instance. /// /// \param Frequency the frequency of the sine wave /// \param Amplitude the amplitude of the sine wave /// \param Phase the phase of the sine wave /// \param Average the average of the sine wave SineFunction(D Frequency, D Amplitude, D Phase, D Average) noexcept - : Abstraction(Average), - Frequency(Frequency), - Amplitude(Amplitude), - Phase(Phase), - Average(Average) {} + : Abstraction(Average), Frequency(Frequency), Amplitude(Amplitude), + Phase(Phase), Average(Average) {} /// Destroys \p this object. ~SineFunction(void) = default; /// Checks wether the Abstraction evaluates to default at the given position /// As SineFunctions can be evaluated everythwere, this is always false /// /// \param V the value at which to check if the function falls back to it's /// default value. /// /// \return false bool isDefaultAt(const D &V) const noexcept override { (void)V; return false; } /// Evaluates the sine function /// /// \param X the value at which to evaluate the function /// \return the value of the sine-function at X virtual R operator()(const D &X) const noexcept override { - return Amplitude*sin(Frequency * X + Phase) + Average; + return Amplitude * sin(Frequency * X + Phase) + Average; + } +}; + +enum StepDirection { StepUp, StepDown }; + +/// Implements \c rosa::agent::PartialFunction as a step function from 0 to 1 +/// with a ramp in between +/// +/// \tparam D type of the functions domain +/// \tparam R type of the functions range +template +class StepFunction : public Abstraction { + // Make sure the actual type arguments are matching our expectations. + STATIC_ASSERT((std::is_arithmetic::value), "abstracting not arithmetic"); + STATIC_ASSERT((std::is_arithmetic::value), + "abstracting not to arithmetic"); + +private: + D Coefficient; + D RightLimit; + StepDirection Direction; + +public: + /// Creates an instance by Initializing the underlying \c Abstraction. + /// + /// \param Coefficient Coefficient of the ramp + /// \param Direction wether to step up or down + /// + /// \pre Coefficient > 0 + StepFunction(D Coefficient, StepDirection Direction = StepUp) + : Abstraction(0), Coefficient(Coefficient), + RightLimit(1.0f / Coefficient), Direction(Direction) { + ASSERT(Coefficient > 0); + } + + /// Destroys \p this object. + ~StepFunction(void) = default; + + /// Setter for Coefficient + /// + /// \param Coefficient the new Coefficient + void setCoefficient(const D &Coefficient) { + ASSERT(Coefficient > 0); + this->Coefficient = Coefficient; + this->RightLimit = 1 / Coefficient; + } + + /// Setter for RightLimit + /// + /// \param RightLimit the new RightLimit + void setRightLimit(const D &RightLimit) { + ASSERT(RightLimit > 0); + this->RightLimit = RightLimit; + this->Coefficient = 1 / RightLimit; + } + + /// Checks wether the Abstraction evaluates to default at the given position + /// + /// \param V the value at which to check if the function falls back to it's + /// default value. + /// + /// \return false if the is negative, true otherwise + bool isDefaultAt(const D &V) const noexcept override { return V > 0; } + + /// Executes the Abstraction + /// + /// \param V value to abstract + /// + /// \return the abstracted value + R operator()(const D &V) const noexcept override { + R ret = 0; + if (V <= 0) + ret = 0; + else if (V >= RightLimit) + ret = 1; + else + ret = V * Coefficient; + return Direction == StepDirection::StepUp ? ret : 1 - ret; } }; + /// Implements \c rosa::agent::Abstraction as a partial function from a domain -// /to a range. +/// to a range. /// /// \note This implementation is supposed to be used to represent a partial /// function from an arithmetic domain to an arithmetic range. This is enforced /// statically. /// /// A partial function is defined as a list of abstractions, where each /// abstraction is associated a range in which it is defined. These ranges must /// be mutually exclusive. /// /// \tparam D type of the functions domain /// \tparam R type of the functions range template class PartialFunction : public Abstraction { // Make sure the actual type arguments are matching our expectations. STATIC_ASSERT((std::is_arithmetic::value), "abstracting not arithmetic"); STATIC_ASSERT((std::is_arithmetic::value), - "abstracting not to arithmetic"); + "abstracting not to arithmetic"); private: /// A \c rosa::agent::RangeAbstraction RA is used to represent the association /// from ranges to Abstractions. /// This returns the Abstraction that is defined for any given value, or /// a default Abstraction if no Abstraction is defined for that value. RangeAbstraction>> RA; public: /// Creates an instance by Initializing the underlying \c Abstraction. /// /// \param Map the mapping to do abstraction according to /// \param Default abstraction to abstract to by default /// /// \pre Each key defines a valid range such that `first <= second` and /// there are no overlapping ranges defined by the keys. - PartialFunction(const std::map, - std::shared_ptr>> &Map, - const R Default) + PartialFunction( + const std::map, std::shared_ptr>> &Map, + const R Default) : Abstraction(Default), - RA(Map, std::shared_ptr> - (new Abstraction(Default))) { + RA(Map, + std::shared_ptr>(new Abstraction(Default))) { } /// Destroys \p this object. ~PartialFunction(void) = default; /// Checks wether the Abstraction evaluates to default at the given position /// /// \param V the value at which to check if the function falls back to it's /// default value. /// /// \return false if the value falls into a defined range and the Abstraction /// defined for that range does not fall back to it's default value. bool isDefaultAt(const D &V) const noexcept override { return RA.isDefaultAt(V) ? true : RA(V)->isDefaultAt(V); } /// Searches for an Abstraction for the given value and executes it for that /// value, if such an Abstraction is found. The default Abstraction is /// evaluated otherwise. /// /// \param V value to abstract /// /// \return the abstracted value based on the set mapping R operator()(const D &V) const noexcept override { return RA(V)->operator()(V); } }; } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_FUNCTIONABSTRACTIONS_HPP diff --git a/include/rosa/agent/Functionality.h b/include/rosa/agent/Functionality.h index 5825529..051dae9 100644 --- a/include/rosa/agent/Functionality.h +++ b/include/rosa/agent/Functionality.h @@ -1,34 +1,40 @@ //===-- rosa/agent/Functionality.h ------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/agent/Functionality.h /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Declaration of `rosa::Functionality` base-class. /// //===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_FUNCTIONALITY_H #define ROSA_AGENT_FUNCTIONALITY_H namespace rosa { namespace agent { /// Base class for actual *functionalities* that implement various concepts of /// self-awareness and are supposed to be used in implementing *agents*. class Functionality { public: Functionality(void) noexcept = default; virtual ~Functionality(void) = default; }; } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_FUNCTIONALITY_H diff --git a/include/rosa/agent/History.hpp b/include/rosa/agent/History.hpp index 1097ad0..f164fae 100644 --- a/include/rosa/agent/History.hpp +++ b/include/rosa/agent/History.hpp @@ -1,292 +1,534 @@ //===-- rosa/agent/History.hpp ----------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/agent/History.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Definition of *history* *functionality*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_HISTORY_HPP #define ROSA_AGENT_HISTORY_HPP #include "rosa/agent/Functionality.h" #include "rosa/config/config.h" #include "rosa/support/debug.hpp" #include "rosa/support/type_helper.hpp" #include +#include namespace rosa { namespace agent { /// Retention policies defining what a \c rosa::agent::History instance should /// do when the number of recorded entries reached its capacity. enum class HistoryPolicy { SRWF, ///< Stop Recording When Full -- no new entry is recorded when full - FIFO ///< First In First Out -- overwrite the earliest entry with a new one + FIFO, ///< First In First Out -- overwrite the earliest entry with a new one + LIFO ///< Last In First Out -- overwrite the latest entry with a new one }; -/// Implements *history* by recording and storing values. -/// -/// \note Not thread-safe implementation, which should not be a problem as any -/// instance of \c rosa::agent::Functionality is an internal component of a -/// \c rosa::Agent, which is the basic unit of concurrency. -/// -/// \tparam T type of values to store -/// \tparam N number of values to store at most -/// \tparam P retention policy to follow when capacity is reached -/// -/// \invariant The size of the underlying \c std::array is `N + 1`:\code -/// max_size() == N + 1 && N == max_size() - 1 -/// \endcode -template -class History : public Functionality, private std::array { - - // Bring into scope inherited functions that are used. - using std::array::max_size; - using std::array::operator[]; - - /// The index of the first data element in the circular buffer. - size_t Data; - - /// The index of the first empty slot in the circular buffer. - size_t Space; +template class History : public Functionality { public: - /// Creates an instances by initializing the indices for the circular buffer. - History(void) noexcept : Data(0), Space(0) {} + History(void) noexcept {} /// Destroys \p this object. - ~History(void) = default; + virtual ~History(void) = default; /// Tells the retention policy applied to \p this object. /// /// \return \c rosa::agent::History::P - static constexpr HistoryPolicy policyOfHistory(void) noexcept { return P; } + static constexpr HistoryPolicy policy(void) noexcept { return P; } /// Tells how many entries may be recorded by \c this object. /// /// \note The number of entries that are actually recorded may be smaller. /// - /// \return \c rosa::agent::History::N - static constexpr size_t lengthOfHistory(void) noexcept { return N; } + /// \return The max number of entries that may be recorded + virtual size_t maxLength(void) const noexcept = 0; /// Tells how many entries are currently recorded by \p this object. /// /// \return number of entries currently recorded by \p this object. /// /// \post The returned value cannot be larger than the capacity of \p this /// object:\code /// 0 <= numberOfEntries() && numberOfEntries <= lengthOfHistory() /// \endcode - size_t numberOfEntries(void) const noexcept { - return Data <= Space ? Space - Data : max_size() - Data + Space; - } + virtual size_t numberOfEntries(void) const noexcept = 0; /// Tells if \p this object has not recorded anything yet. /// /// \return if \p this object has no entries recorded bool empty(void) const noexcept { return numberOfEntries() == 0; } + /// Tells if the history reached it's maximum length + /// + /// \return if the history reached it's maximum length. + bool full(void) const noexcept { return numberOfEntries() == maxLength(); } + /// Gives a constant lvalue reference to an entry stored in \p this object. /// /// \note The recorded entries are indexed starting from the latest one. /// /// \param I the index at which the stored entry to take from /// /// \pre \p I is a valid index:\code - /// 0 <= I && I <= numberOfEntries() + /// 0 <= I && I < numberOfEntries() /// \endcode - const T &entry(const size_t I = 0) const noexcept { - ASSERT(0 <= I && I < numberOfEntries()); // Boundary check. - // Position counted back from the last recorded entry. - typename std::make_signed::type Pos = Space - (1 + I); - // Actual index wrapped around to the end of the buffer if negative. - return (*this)[Pos >= 0 ? Pos : max_size() + Pos]; - } + virtual const T &entry(const size_t I = 0) const noexcept = 0; + + /// Removes all entries recorded in \p this object. + virtual void clear() noexcept = 0; private: - /// Tells if the circular buffer is full. + /// Pushes a new entry into the history. /// - /// \return if the circular buffer is full. - bool full(void) const noexcept { return numberOfEntries() == N; } - - /// Pushes a new entry into the circular buffer. + /// \note The earliest entry gets overwritten if the history is full. /// - /// \note The earliest entry gets overwritten if the buffer is full. + /// \param V value to push into the history + virtual void pushBack(const T &V) noexcept = 0; + + /// Replaces the most recent entry in the history. /// - /// \param V value to push into the buffer - void pushBack(const T &V) noexcept { - // Store value to the first empty slot and step Space index. - (*this)[Space] = V; - Space = (Space + 1) % max_size(); - if (Data == Space) { - // Buffer was full, step Data index. - Data = (Data + 1) % max_size(); - } - } + /// \param V value to replace the most current value with + virtual void replaceFront(const T &V) noexcept = 0; public: /// Adds a new entry to \p this object and tells if the operation was /// successful. /// /// \note Success of the operation depends on the actual policy. /// /// \param V value to store /// /// \return if \p V was successfully stored bool addEntry(const T &V) noexcept { switch (P) { default: ROSA_CRITICAL("unkown HistoryPolicy"); + case HistoryPolicy::LIFO: + if (full()) { + replaceFront(V); + return true; + } case HistoryPolicy::SRWF: if (full()) { return false; } // \note Fall through to FIFO which unconditionally pushes the new entry. case HistoryPolicy::FIFO: // FIFO and SRWF not full. pushBack(V); return true; } } /// Tells the trend set by the entries recorded by \p this object. /// /// The number of steps to go back when calculating the trend is defined as /// argument to the function. /// /// \note The number of steps that can be made is limited by the number of /// entries recorded by \p this object. /// /// \note The function is made a template only to be able to use /// \c std::enable_if. /// /// \tparam X always use the default! /// /// \param D number of steps to go back in *history* /// /// \return trend set by analyzed entries /// /// \pre Statically, \p this object stores signed arithmetic values:\code /// std::is_arithmetic::value && std::is_signed::value /// \endcode Dynamically, \p D is a valid number of steps to take:\code - /// 0 <= D && D < N + /// 0 <= D && D < lengthOfHistory() /// \endcode template typename std::enable_if< std::is_arithmetic::value && std::is_signed::value, X>::type - trend(const size_t D = N - 1) const noexcept { + trend(const size_t D) const noexcept { STATIC_ASSERT((std::is_same::value), "not default template arg"); - ASSERT(0 <= D && D < N); // Boundary check. + ASSERT(0 <= D && D < maxLength()); // Boundary check. if (numberOfEntries() < 2 || D < 1) { // No entries for computing trend. return {}; // Zero element of \p T } else { // Here at least two entries. // \c S is the number of steps that can be done. const size_t S = std::min(numberOfEntries() - 1, D); size_t I = S; // Compute trend with linear regression. size_t SumIndices = 0; T SumEntries = {}; T SumSquareEntries = {}; T SumProduct = {}; while (I > 0) { // \note Indexing for the regression starts in the past. const size_t Index = S - I; const T Entry = entry(--I); SumIndices += Index; SumEntries += Entry; SumSquareEntries += Entry * Entry; SumProduct += Entry * Index; } return (SumProduct * S - SumEntries * SumIndices) / (SumSquareEntries * S - SumEntries * SumEntries); } } /// Tells the average absolute difference between consecutive entries recorded /// by \p this object /// The number of steps to go back when calculating the average is defined as /// argument to the function. /// /// \note The number of steps that can be made is limited by the number of /// entries recorded by \p this object. /// /// \note The function is made a template only to be able to use /// \c std::enable_if. /// /// \tparam X always use the default! /// /// \param D number of steps to go back in *history* /// /// \pre Statically, \p this object stores arithmetic values:\code /// std::is_arithmetic::value /// \endcode Dynamically, \p D is a valid number of steps to take:\code - /// 0 <= D && D < N + /// 0 <= D && D < lengthOfHistory() /// \endcode template typename std::enable_if::value, size_t>::type - averageAbsDiff(const size_t D = N - 1) const noexcept { + averageAbsDiff(const size_t D) const noexcept { STATIC_ASSERT((std::is_same::value), "not default template arg"); - ASSERT(0 <= D && D < N); // Boundary check. + ASSERT(0 <= D && D < maxLength()); // Boundary check. if (numberOfEntries() < 2 || D < 1) { // No difference to average. return {}; // Zero element of \p T } else { // Here at least two entries. // \c S is the number of steps that can be done. const size_t S = std::min(numberOfEntries() - 1, D); // Sum up differences as non-negative values only, hence using an // unsigned variable for that. size_t Diffs = {}; // Init to zero. // Count down entry indices and sum up all the absolute differences. size_t I = S; T Last = entry(I); while (I > 0) { T Next = entry(--I); Diffs += Last < Next ? Next - Last : Last - Next; Last = Next; } // Return the average of the summed differences. return Diffs / S; } } + + /// Tells the average of all entries recorded by \p this object + /// + /// \tparam R type of the result + template R average() const noexcept { + R Average = 0; + for (size_t I = 0; I < numberOfEntries(); I++) { + Average += entry(I); + } + Average /= numberOfEntries(); + return Average; + } +}; + +/// Implements *history* by recording and storing values. +/// The length of the underlying std::array is static and must be set at +/// compile-time +/// +/// \note Not thread-safe implementation, which should not be a problem as any +/// instance of \c rosa::agent::Functionality is an internal component of a +/// \c rosa::Agent, which is the basic unit of concurrency. +/// +/// \tparam T type of values to store +/// \tparam N number of values to store at most +/// \tparam P retention policy to follow when capacity is reached +/// +/// \invariant The size of the underlying \c std::array is `N + 1`:\code +/// max_size() == N + 1 && N == max_size() - 1 +/// \endcode +template +class StaticLengthHistory : public History, private std::array { + + // Bring into scope inherited functions that are used. + using std::array::max_size; + using std::array::operator[]; + + /// The index of the first data element in the circular buffer. + size_t Data; + + /// The index of the first empty slot in the circular buffer. + size_t Space; + +public: + using History::policy; + using History::empty; + using History::full; + using History::addEntry; + using History::trend; + using History::averageAbsDiff; + + /// Creates an instances by initializing the indices for the circular buffer. + StaticLengthHistory(void) noexcept : Data(0), Space(0) {} + + /// Destroys \p this object. + ~StaticLengthHistory(void) override = default; + + /// Tells how many entries may be recorded by \c this object. + /// + /// \note The number of entries that are actually recorded may be smaller. + /// + /// \return \c rosa::agent::History::N + size_t maxLength(void) const noexcept override { return N; } + + /// Tells how many entries are currently recorded by \p this object. + /// + /// \return number of entries currently recorded by \p this object. + /// + /// \post The returned value cannot be larger than the capacity of \p this + /// object:\code + /// 0 <= numberOfEntries() && numberOfEntries <= lengthOfHistory() + /// \endcode + size_t numberOfEntries(void) const noexcept override { + return Data <= Space ? Space - Data : max_size() - Data + Space; + } + + /// Gives a constant lvalue reference to an entry stored in \p this object. + /// + /// \note The recorded entries are indexed starting from the latest one. + /// + /// \param I the index at which the stored entry to take from + /// + /// \pre \p I is a valid index:\code + /// 0 <= I && I < numberOfEntries() + /// \endcode + const T &entry(const size_t I = 0) const noexcept override { + ASSERT(0 <= I && I < numberOfEntries()); // Boundary check. + // Position counted back from the last recorded entry. + typename std::make_signed::type Pos = Space - (1 + I); + // Actual index wrapped around to the end of the buffer if negative. + return (*this)[Pos >= 0 ? Pos : max_size() + Pos]; + } + + /// Removes all entries recorded in \p this object. + void clear() noexcept override { + Data = 0; + Space = 0; + } + +private: + /// Pushes a new entry into the circular buffer. + /// + /// \note The earliest entry gets overwritten if the buffer is full. + /// + /// \param V value to push into the buffer + void pushBack(const T &V) noexcept override { + // Store value to the first empty slot and step Space index. + (*this)[Space] = V; + Space = (Space + 1) % max_size(); + if (Data == Space) { + // Buffer was full, step Data index. + Data = (Data + 1) % max_size(); + } + } + + /// Replaces the most recent entry in the history. + /// + /// \param V value to replace the most current value with + void replaceFront(const T &V) noexcept override { + (*this)[(Space - 1) % max_size()] = V; + } }; /// Adds a new entry to a \c rosa::agent::History instance. /// /// \note The result of \c rosa::agent::History::addEntry is ignored. /// /// \tparam T type of values stored in \p H /// \tparam N number of values \p H is able to store /// \tparam P retention policy followed by \p H when capacity is reached /// /// \param H to add a new entry to /// \param V value to add to \p H /// /// \return \p H after adding \p V to it template -History &operator<<(History &H, const T &V) noexcept { +StaticLengthHistory &operator<<(StaticLengthHistory &H, + const T &V) noexcept { H.addEntry(V); return H; } +/// Implements *DynamicLengthHistory* by recording and storing values. +/// +/// \note Not thread-safe implementation, which should not be a problem as any +/// instance of \c rosa::agent::Functionality is an internal component of a +/// \c rosa::Agent, which is the basic unit of concurrency. +/// +/// \tparam T type of values to store +/// \tparam P retention policy to follow when capacity is reached +template +class DynamicLengthHistory : public History, private std::vector { + + // Bring into scope inherited functions that are used. + using std::vector::erase; + using std::vector::begin; + using std::vector::end; + using std::vector::size; + using std::vector::max_size; + using std::vector::resize; + using std::vector::push_back; + using std::vector::pop_back; + using std::vector::operator[]; + + /// The current length of the DynamicLengthHistory. + size_t Length; + +public: + using History::policy; + using History::empty; + using History::full; + using History::addEntry; + using History::trend; + using History::averageAbsDiff; + + /// Creates an instances by setting an initial length + DynamicLengthHistory(size_t Length) noexcept : Length(Length) { + this->resize(Length); + } + + /// Destroys \p this object. + ~DynamicLengthHistory(void) override = default; + + /// Tells how many entries may be recorded by \c this object. + /// + /// \note The number of entries that are actually recorded may be smaller. + /// + /// \return \c rosa::agent::DynamicLengthHistory::N + size_t maxLength(void) const noexcept override { return Length; } + + /// Tells how many entries are currently recorded by \p this object. + /// + /// \return number of entries currently recorded by \p this object. + /// + /// \post The returned value cannot be larger than the capacity of \p this + /// object:\code + /// 0 <= numberOfEntries() && numberOfEntries <= + /// lengthOfHistory() \endcode + size_t numberOfEntries(void) const noexcept { return size(); } + + /// Gives a constant lvalue reference to an entry stored in \p this object. + /// + /// \note The recorded entries are indexed starting from the latest one. + /// + /// \param I the index at which the stored entry to take from + /// + /// \pre \p I is a valid index:\code + /// 0 <= I && I < numberOfEntries() + /// \endcode + const T &entry(const size_t I = 0) const noexcept override { + ASSERT(0 <= I && I < numberOfEntries()); // Boundary check. + return this->operator[](size() - I - 1); + } + + /// Removes all entries recorded in \p this object. + void clear() noexcept override { erase(begin(), end()); } + +private: + /// Pushes a new entry into the circular buffer. + /// + /// \note The earliest entry gets overwritten if the buffer is full. + /// + /// \param V value to push into the buffer + void pushBack(const T &V) noexcept override { + if (full()) { + erase(begin()); + } + push_back(V); + } + + /// Replaces the most recent entry in the history. + /// + /// \param V value to replace the most current value with + void replaceFront(const T &V) noexcept override { + (void)pop_back(); + push_back(V); + } + +public: + /// Resizes the History length. If the new length is smaller than the number + /// of currently stored values, values are deleted according to the + /// HistoryPolicy. + /// + /// @param NewLength The new Length of the History. + void setLength(size_t NewLength) noexcept { + Length = NewLength; + if (NewLength < numberOfEntries()) { + switch (P) { + default: + ROSA_CRITICAL("unkown HistoryPolicy"); + case HistoryPolicy::LIFO: + case HistoryPolicy::SRWF: + // Delete last numberOfEntries() - NewLength items from the back + erase(begin() + NewLength, end()); + break; + case HistoryPolicy::FIFO: + // Delete last numberOfEntries() - NewLength items from the front + erase(begin(), begin() + (numberOfEntries() - NewLength)); + break; + } + } + this->resize(Length); + } +}; + +/// Adds a new entry to a \c rosa::agent::DynamicLengthHistory instance. +/// +/// \note The result of \c rosa::agent::DynamicLengthHistory::addEntry is +/// ignored. +/// +/// \tparam T type of values stored in \p H +/// \tparam P retention policy followed by \p H when capacity is reached +/// +/// \param H to add a new entry to +/// \param V value to add to \p H +/// +/// \return \p H after adding \p V to it +template +DynamicLengthHistory &operator<<(DynamicLengthHistory &H, + const T &V) noexcept { + H.addEntry(V); + return H; +} } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_HISTORY_HPP diff --git a/include/rosa/agent/RangeConfidence.hpp b/include/rosa/agent/RangeConfidence.hpp index 0d8732e..f441972 100644 --- a/include/rosa/agent/RangeConfidence.hpp +++ b/include/rosa/agent/RangeConfidence.hpp @@ -1,109 +1,114 @@ //===-- rosa/agent/RangeConfidence.hpp --------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/agent/RangeConfidence.hpp /// /// \author Benedikt Tutzer (benedikt.tutzer@tuwien.ac.at) /// /// \date 2019 /// /// \brief Definition of *RangeConfidence* *functionality*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_RANGECONFIDENCE_HPP #define ROSA_AGENT_RANGECONFIDENCE_HPP -#include "rosa/agent/Functionality.h" #include "rosa/agent/Abstraction.hpp" #include "rosa/agent/FunctionAbstractions.hpp" +#include "rosa/agent/Functionality.h" #include "rosa/support/debug.hpp" #include -#include #include #include +#include namespace rosa { namespace agent { /// Evaluates a map of ID's to Abstractions at a given value and returns the /// results as a map from ID's to results of the corresponding Abstraction /// /// \note This implementation is supposed to be used to abstract ranges of /// arithmetic types into maps whose values are of another arithmetic type, /// which is statically enforced. /// /// \tparam D type to abstract from /// \tparam I type the type of the ID's /// \tparam R type of the range template class RangeConfidence : protected Abstraction>, - private std::map>{ + private std::map> { // Make sure the actual type arguments are matching our expectations. STATIC_ASSERT((std::is_arithmetic::value), "abstracting not arithmetic"); STATIC_ASSERT((std::is_arithmetic::value), - "abstracting not to arithmetic"); + "abstracting not to arithmetic"); private: /// Wether to include default results in the result-map or not bool IgnoreDefaults; public: /// Creates an instance by Initializing the underlying \c Abstraction and /// \c std::map. /// /// \param Abstractions the Abstractions to be evaluated /// \param IgnoreDefaults wether to include default results in the result-map /// or not (defaults to false). RangeConfidence(const std::map> &Abstractions, - bool IgnoreDefaults = false) - : Abstraction>({}), - std::map>(Abstractions), - IgnoreDefaults(IgnoreDefaults){ - } + bool IgnoreDefaults = false) + : Abstraction>({}), std::map>( + Abstractions), + IgnoreDefaults(IgnoreDefaults) {} /// Destroys \p this object. ~RangeConfidence(void) = default; /// Checks wether all Abstractions evaluate to default at the given position /// /// \param V the value at which to check if the functions falls back to it's /// default value. /// /// \return true, if all Abstractions evaluate to default bool isDefaultAt(const D &V) const noexcept override { - for (auto const& p : ((std::map>)*this)){ - if(!p.second.isDefaultAt(V)) - return false; - } + for (auto const &P : ((std::map>)*this)) { + if (!P.second.isDefaultAt(V)) + return false; + } return true; } /// All Abstractions stored in the underlying \c std::map are evaluated for /// the given value. Their results are stored in another map, with /// corresponding keys. /// If IgnoreDefaults is set, Abstractions that default for that value are not /// evaluated and inserted into the resulting \c std::map /// /// \param V value to abstract /// /// \return a \c std::map containing the results of the stored Abstractions, /// indexable by the key's the Abstractions are associated with std::map operator()(const D &V) const noexcept override { - std::map ret; - for (auto const& p : ((std::map>)*this)){ - if(!IgnoreDefaults || !p.second.isDefaultAt(V)) - ret.insert(std::pair(p.first, p.second(V))); + std::map Ret; + for (auto const &P : ((std::map>)*this)) { + if (!IgnoreDefaults || !P.second.isDefaultAt(V)) + Ret.insert(std::pair(P.first, P.second(V))); } - return ret; + return Ret; } }; } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_RANGECONFIDENCE_HPP diff --git a/include/rosa/agent/namespace.h b/include/rosa/agent/namespace.h index 2cd458c..70a305b 100755 --- a/include/rosa/agent/namespace.h +++ b/include/rosa/agent/namespace.h @@ -1,28 +1,34 @@ //===-- rosa/agent/namespace.h ----------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/agent/namespace.h /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Documentation for namespace \c rosa::agent. /// //===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_NAMESPACE_H #define ROSA_AGENT_NAMESPACE_H namespace rosa { /// Contains functionalities that are supposed to be useful for implementing /// *agents*. namespace agent {} } // End namespace rosa #endif // ROSA_AGENT_NAMESPACE_H diff --git a/include/rosa/config/config.h b/include/rosa/config/config.h index 89794fa..6090667 100644 --- a/include/rosa/config/config.h +++ b/include/rosa/config/config.h @@ -1,72 +1,86 @@ //===-- rosa/config/config.h ------------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/config/config.h /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \brief Configuration information on the build of the library. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CONFIG_CONFIG_H #define ROSA_CONFIG_CONFIG_H #include "rosa/config/rosa_config.h" #include // This OS-specific block defines one of the following: // - ROSA_LINUX // - ROSA_WINDOWS // It also defines ROSA_POSIX for POSIX-compatible systems. #if defined(__linux__) #define ROSA_LINUX #elif defined(WIN32) || defined(_WIN32) #define ROSA_WINDOWS #else #error Platform and/or compiler not supported #endif #if defined(ROSA_LINUX) #define ROSA_POSIX #endif // Defining filenames in a project-relative way based on absolute paths. #include "rosa/config/project_path.hpp" /// The project-relative path of the current source file. #define __FILENAME__ (__FILE__ + project_relative_path_index(__FILE__)) // Convenience macros. /// No-op. #define ROSA_VOID_STMT static_cast(0) -/// Ignors anything. +/// Ignors an expression. +/// +/// \param x expression +#define ROSA_IGNORE_UNUSED_EXPR(x) static_cast(x) + +/// Ignors a statement (not an expression). /// -/// \param x anything -#define ROSA_IGNORE_UNUSED(x) static_cast(x) +/// \param s statement +#define ROSA_IGNORE_UNUSED_STMT(s) \ + while (false) { \ + s; \ + } \ /// Prints an error message and aborts execution. /// /// \param error the error message to print #define ROSA_CRITICAL(error) \ do { \ std::cerr << "[FATAL] " << __func__ << "@" << __FILENAME__ << ":" \ << __LINE__ << ": critical error: '" << (error) << "'" \ << std::endl; \ ::abort(); \ } while (false) /// Raises a runtime error in the program. /// /// \param msg message describing the error /// /// \throws std::runtime_error #define ROSA_RAISE_ERROR(msg) throw std::runtime_error(msg) #endif // ROSA_CONFIG_CONFIG_H diff --git a/include/rosa/config/namespace.h b/include/rosa/config/namespace.h index ce87454..d2006ad 100755 --- a/include/rosa/config/namespace.h +++ b/include/rosa/config/namespace.h @@ -1,23 +1,29 @@ //===-- rosa/config/namespace.h ---------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/config/namespace.h /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Documentation for the namespace \c rosa. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CONFIG_NAMESPACE_H #define ROSA_CONFIG_NAMESPACE_H /// Base namespace used by the RoSA framework. namespace rosa {} #endif // ROSA_CONFIG_NAMESPACE_H diff --git a/include/rosa/config/project_path.hpp b/include/rosa/config/project_path.hpp index b4849ae..a719fae 100644 --- a/include/rosa/config/project_path.hpp +++ b/include/rosa/config/project_path.hpp @@ -1,66 +1,72 @@ //===-- rosa/config/project_path.hpp ----------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/config/project_path.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Facility for compile-time manipulation of paths. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CONFIG_PROJECT_PATH_HPP #define ROSA_CONFIG_PROJECT_PATH_HPP #include "rosa/config/rosa_config.h" #include namespace rosa { /// Nested namespace with implementation of the provided features, /// consider it private. namespace { /// Tells the index of the project-relative part of an absolute path. /// /// \param path absolute path to check /// \param project the absolute path of the project /// \param index number of leading characters already checked and found /// matching /// /// \return index of the part relative to \c project in \p path; \c 0 if \p path /// is not under \p project constexpr size_t project_relative_path_index_impl(const char *const path, const char *const project = ROSA_SRC_DIR, const size_t index = 0) { return project[index] == '\0' ? index // Found it. : (path[index] == '\0' || project[index] != path[index]) ? 0 // Path is not under project. : project_relative_path_index_impl( path, project, index + 1); // Continue searching... } } // End namespace /// Tells the index of the project-relative part of an absolute path. /// /// \param path absolute path to check /// /// \return index of the project-relative part of \p path; \c 0 if \p path is /// not under the RoSA siource directory. constexpr size_t project_relative_path_index(const char *const path) { return project_relative_path_index_impl(path); } } // End namespace rosa #endif // ROSA_CONFIG_PROJECT_PATH_HPP diff --git a/include/rosa/config/rosa_config.h.cmake b/include/rosa/config/rosa_config.h.cmake index 933d260..5d9ce0a 100644 --- a/include/rosa/config/rosa_config.h.cmake +++ b/include/rosa/config/rosa_config.h.cmake @@ -1,57 +1,63 @@ //===-- rosa/config/rosa_config.h -------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// // // The file enumerates variables from the RoSA configurations so that they can // be in exported headers. // //===----------------------------------------------------------------------===// #ifndef ROSA_CONFIG_ROSA_CONFIG_H #define ROSA_CONFIG_ROSA_CONFIG_H // clang-format off #define CMAKE_SYSTEM "${CMAKE_SYSTEM}" #define CMAKE_GENERATOR "${CMAKE_GENERATOR}" #define CMAKE_C_COMPILER_ID "${CMAKE_C_COMPILER_ID}" #define CMAKE_C_COMPILER_VERSION "${CMAKE_C_COMPILER_VERSION}" #define CMAKE_CXX_COMPILER_ID "${CMAKE_CXX_COMPILER_ID}" #define CMAKE_CXX_COMPILER_VERSION "${CMAKE_CXX_COMPILER_VERSION}" #define ROSA_VERSION_MAJOR ${ROSA_VERSION_MAJOR} #define ROSA_VERSION_MINOR ${ROSA_VERSION_MINOR} #define ROSA_VERSION_PATCH ${ROSA_VERSION_PATCH} #define PACKAGE_NAME "${PACKAGE_NAME}" #define PACKAGE_STRING "${PACKAGE_STRING}" #define PACKAGE_VERSION "${PACKAGE_VERSION}" #define PACKAGE_BUGREPORT "${PACKAGE_BUGREPORT}" #define BUILD_DATE __DATE__ " " __TIME__ #define ROSA_SRC_DIR "${ROSA_MAIN_SRC_DIR}" #if ${ROSA_LOG_LEVEL_INT} != -1 #define ROSA_LOG_LEVEL ${ROSA_LOG_LEVEL} #endif #if ${ROSA_ENABLE_ASSERTIONS_INT} != -1 #define ROSA_ENABLE_ASSERTIONS #endif // clang-format on #endif // ROSA_CONFIG_ROSA_CONFIG_H diff --git a/include/rosa/config/version.h b/include/rosa/config/version.h index 3583d92..502b41f 100644 --- a/include/rosa/config/version.h +++ b/include/rosa/config/version.h @@ -1,37 +1,43 @@ //===-- rosa/config/version.h -----------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/config/version.h /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Version information about the build of the library. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CONFIG_VERSION_H #define ROSA_CONFIG_VERSION_H #include "rosa/config/rosa_config.h" #include // NOLINT namespace rosa { /// Returns a string containing the name of the library followed by its version. std::string library_string(void); /// Returns a string containing the version number of the library. std::string version(void); /// Returns a multi-line string containing verbose information on the library. std::string verbose_version(void); } // End namespace rosa #endif // ROSA_CONFIG_VERSION_H diff --git a/include/rosa/core/AbstractAgent.hpp b/include/rosa/core/AbstractAgent.hpp index bbb1a52..8396c37 100644 --- a/include/rosa/core/AbstractAgent.hpp +++ b/include/rosa/core/AbstractAgent.hpp @@ -1,150 +1,156 @@ //===-- rosa/core/AbstractAgent.hpp -----------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/core/AbstractAgent.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Declaration of an abstract interface for *Agents*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_ABSTRACTAGENT_HPP #define ROSA_CORE_ABSTRACTAGENT_HPP #include "rosa/core/Message.hpp" #include "rosa/core/forward_declarations.h" #include "rosa/support/debug.hpp" #include namespace rosa { /// Abstract class declaring an interface for *Agents*. /// /// \tparam Ref type of the derived class implementing \c rosa::AbstractAgent /// for referencing \p this object /// /// \note \p Ref is reference for \c rosa::AbstractAgent, whose actual value /// must be a class derived from \c rosa::AbstractAgent. /// /// \note It can be statically checked if \p Ref is derived from /// \c rosa::AbstractAgent, but the static assertion cannot be defined /// directly in the class body. That is because a class \c C derived from /// \c rosa::AbstractAgent is not complete when the static assertion in the /// definition of \c rosa::AbstractAgent would be evaluated. Thus, the static /// assertion is placed in the constructor of \c rosa::AbstractAgent. template class AbstractAgent { protected: /// Creates a new instance of \c rosa::AbstractAgent. /// /// \note The constructor is protected, thus restricting class instantiation /// for derived classes only. /// /// \pre \p Ref is derived from \c rosa::AbstractAgent:\code /// std::is_base_of, Ref>::value /// \endcode AbstractAgent(void) noexcept; public: /// Destroys \p this object. virtual ~AbstractAgent(void) = default; /// Tells if \p this object is in a valid state. /// /// \return if \p this object is in a valid state virtual operator bool(void) const noexcept = 0; /// Tells if a given reference refers to \p this object. /// /// \param R reference to another object /// /// \return if \p R refers to \p this object virtual bool operator==(const Ref &R) const noexcept = 0; /// Compares \p this object to a given reference. /// /// The operator can be used to sort references. Standard containers storing /// entries in an ordered way use \c std::less for comparing entries, which /// utilizes this operator. /// /// \param R reference to another object /// /// \return if \p this object compares less to \p R virtual bool operator<(const Ref &R) const noexcept = 0; /// Returns a reference to \p this object. /// /// \return a reference to \p this object virtual Ref self(void) noexcept = 0; /// Sends a \c rosa::message_t instance to \p this object. /// /// \param M message to send /// /// \pre \p this object is in a valid state:\code /// bool(*this) /// \endcode virtual void sendMessage(message_t &&M) noexcept = 0; /// Sends a message -- created from given constant lvalue references -- to /// \p this object. /// /// \note The message must consists of at least one value. /// /// \tparam Type type of the first mandatory value /// \tparam Types types of any further values /// /// \param T the first value to include in the message /// \param Ts optional further values to include in the message /// /// \pre \p this object is in a valid state:\code /// bool(*this) /// \endcode template void send(const Type &T, const Types &... Ts) noexcept; /// Sends a message -- created from given rvalue references -- to \p this /// object. /// /// \note The message must consists of at least one value. /// /// \tparam Type type of the first mandatory value /// \tparam Types types of any further values /// /// \param T the first value to include in the message /// \param Ts optional further values to include in the message /// /// \pre \p this object is in a valid state:\code /// bool(*this) /// \endcode template void send(Type &&T, Types &&... Ts) noexcept; }; template AbstractAgent::AbstractAgent(void) noexcept { STATIC_ASSERT((std::is_base_of, Ref>::value), "not derived Agent"); // Sanity check. } template template void AbstractAgent::send(const Type &T, const Types &... Ts) noexcept { sendMessage(Message::create(T, Ts...)); } template template void AbstractAgent::send(Type &&T, Types &&... Ts) noexcept { sendMessage(Message::create(std::move(T), std::move(Ts)...)); } } // End namespace rosa #endif // ROSA_CORE_ABSTRACTAGENT_HPP diff --git a/include/rosa/core/Agent.hpp b/include/rosa/core/Agent.hpp index 07524fb..bb32cd5 100644 --- a/include/rosa/core/Agent.hpp +++ b/include/rosa/core/Agent.hpp @@ -1,146 +1,152 @@ //===-- rosa/core/Agent.hpp -------------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/core/Agent.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Declaration of the base \c rosa::Agent class. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_AGENT_HPP #define ROSA_CORE_AGENT_HPP #include "rosa/core/AgentHandle.hpp" #include "rosa/core/MessageHandler.hpp" #include "rosa/core/MessagingSystem.hpp" #include "rosa/core/Unit.h" #include "rosa/support/log.h" namespace rosa { /// Implements an *Agent* that is a special \c rosa::Unit owned by a /// \c rosa::MessagingSystem, capable of handling \c rosa::Message instances /// as \c rosa::MessageHandler, and provides the \c rosa::AbstractAgent /// interface with \c rosa::AgentHandle as reference type. class Agent : public Unit, public MessageHandler, public AbstractAgent { friend class AgentHandle; ///< \c rosa::AgentHandle is our friend. protected: /// A handle for \p this object. const AgentHandle Self; public: /// Creates a new instance by instantiating all the base-classes. /// /// \tparam Fun type of the first mandatory function for handling messages /// \tparam Funs types of any further functions for handling messages /// /// \param Kind kind of the new \c rosa::Unit instance /// \param Id unique identifier of the new \c rosa::Unit instance /// \param Name name of the new \c rosa::Unit instance /// \param S \c rosa::MessagingSystem owning the new instance /// \param F the first mandatory function for handling messages /// \param Fs optional further functions for handling messages template Agent(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, Fun &&F, Funs &&... Fs); /// Destroys \p this object. ~Agent(void); /// Tells if \p this object is in a valid state. /// /// \note A \c rosa::Agent instance is always valid. /// /// \return if \p this object is in a valid state operator bool(void) const noexcept override; /// Tells if a given reference refers to \p this object. /// /// \param H reference to another object /// /// \return if \p H refers to \p this object bool operator==(const AgentHandle &H) const noexcept override; /// Comapres \p this object to a given reference. /// /// The comparison is based on the memory addresses. /// /// \param H reference to another object /// /// \return if \p this object compares less to \p H bool operator<(const AgentHandle &H) const noexcept override; /// Returns a reference to \p this object. /// /// \return a reference to \p this object AgentHandle self(void) noexcept override; /// Sends a given \c rosa::message_t instance to \p this object. /// /// \param M message to send /// /// \note Since a \c rosa::Agent instance is always valid, there is no /// precondition for this function. /// \see \c rosa::AbstractAgent::sendMessage and /// `rosa::Agent::operator bool() const` void sendMessage(message_t &&M) noexcept override; /// Dumps \p this object into a \c std::string for tracing purposes. /// /// \return \c std::string representing the state of \p this object std::string dump(void) const noexcept override; protected: /// Returns a reference to the \c rosa::MessagingSystem owning \p this object. /// /// \return reference of \c rosa::Unit::S MessagingSystem &system(void) const noexcept override; /// Gives the referenced \c rosa::Agent instance for a \c rosa::AgentHandle. /// /// \note Intended for derived classes to be able to inspect /// \c rosa::AgentHandle instances. /// /// \param H \c rosa::AgentHandle to take the referenced \c rosa::Agent from /// /// \return reference to the \c rosa::Agent instance from \p H static Agent &unwrapAgent(const AgentHandle &H) noexcept { return H.A; } /// Gives the owning \c rosa::MessagingSystem of a \c rosa::Agent instance /// for a \c rosa::AgentHandle. /// /// \note Intended for for derived classes to be able to inspect /// \c rosa::AgentHandle instances. /// /// \param H \c rosa::AgentHandle to take the owning /// \c rosa::MessagingSystem from /// /// \return reference to the \c rosa::MessagingSystem owning the /// \c rosa::Agent instance from \p H static MessagingSystem &unwrapSystem(const AgentHandle &H) noexcept { return H.S; } }; template Agent::Agent(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, Fun &&F, Funs &&... Fs) : Unit(Kind, Id, Name, S), MessageHandler(std::move(F), std::move(Fs)...), Self(*this, /*valid*/ true) { LOG_TRACE("Agent is created."); } } // End namespace rosa #endif // ROSA_CORE_AGENT_HPP diff --git a/include/rosa/core/AgentHandle.hpp b/include/rosa/core/AgentHandle.hpp index 0a57953..13f4cd3 100644 --- a/include/rosa/core/AgentHandle.hpp +++ b/include/rosa/core/AgentHandle.hpp @@ -1,313 +1,319 @@ //===-- rosa/core/AgentHandle.hpp -------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/core/AgentHandle.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Declaration of a handle for \c rosa::Agent. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_AGENTHANDLE_HPP #define ROSA_CORE_AGENTHANDLE_HPP #include "rosa/core/AbstractAgent.hpp" namespace rosa { /// Wraps an actual \c rosa::Agent to decouple its public interface. /// \note Such decoupling might be necessary when operating with remote /// *systems*, sometime in the future. class AgentHandle : public AbstractAgent { /// \c rosa::Agent and \c rosa::MessagingSystem are our friends, they may /// inspect the private member fields of the class. ///@{ friend class Agent; friend class MessagingSystem; ///@} /// The wrapped \c rosa::Agent instance. Agent &A; /// The \c rosa::MessagingSystem owning \c A. MessagingSystem &S; /// Creates a new instance without validating the state of the wrapped /// \c rosa::Agent. /// /// \note Used by a \c rosa::Agent instance to create a reference to itself /// during construction, when its state is not valid yet. /// /// \param A \c rosa::Agent to wrap /// /// \note There a second argument, which is ignored, that is only present to /// separate this constructor from the public constructor taking only a /// \c rosa::Agent to wrap. AgentHandle(Agent &A, bool) noexcept; public: /// Creates a new instance validating the state of the wrapped \p rosa::Agent. /// /// \note The wrapped \c rosa::Agent must be in a valid state to instantiate /// \c rosa::AgentHandle with this constructor. /// /// \param A \c rosa::Agent to wrap /// /// \pre \p A is registered in its owning *system*:\code /// A.system().isUnitRegistered(A) /// \endcode AgentHandle(Agent &A); /// Destroys \p this object. /// /// The destructor has nothing to take care of. ~AgentHandle(void) = default; /// Tells if the wrapped \c rosa::Agent is in a valid state. /// /// \note A \c rosa::AgentHandler belongs to a \c rosa::MessagingSystem. /// Working with a \c rosa::AgentHandler whose originating /// \c rosa::MessagingSystem has already been destroyed results in *undefined* /// behavior. /// /// \return if the wrapped \c rosa::Agent is in a valid state operator bool(void) const noexcept override; /// Tells if another \c rosa::AgentHandle wraps the same \c rosa::Agent as /// \p this object. /// /// \param H \c rosa::AgentHandle whose wrapped \c rosa::Agent to check /// /// \return if \p H wraps \c A like \p this object bool operator==(const AgentHandle &H) const noexcept override; /// Compares \p this object to another \c rosa::AgentHandle instance. /// /// The comparison is based on the memory addresses of the wrapped /// \c rosa::Agent instances. /// /// \param H \c rosa::AgentHandle to compare to /// /// \return if \p this object a \c rosa::Agent instance whose address is less /// than that of \p H bool operator<(const AgentHandle &H) const noexcept override; /// Returns a reference to the wrapped \c rosa::Agent. /// /// \return a reference to \c A AgentHandle self(void) noexcept override; /// Sends a given \c rosa::message_t instance to the wrapped \c rosa::Agent. /// /// \param M message to send /// /// \pre The wrapped \c rosa::Agent instance is in a valid state:\code /// bool(*this) /// \endcode void sendMessage(message_t &&M) noexcept override; }; /// Template specialization for optionally storing \c rosa::AgentHandle /// instances. /// /// \ingroup Optional /// /// Due to \c rosa::AgentHandle not supporting copying and moving of instances, /// the member functions in this class fall back to destroying the old stored /// object and creating a new one whenever the stored value is to be changed. template <> class Optional { public: using Type = AgentHandle; /// Creates an instance without value. /// /// \note Use it with its default parameter. Optional(const none_t & = none) : Valid(false) {} /// Creates a valid instance with value. /// /// \param X value to store in the object Optional(AgentHandle X) : Valid(false) { cr(std::move(X)); } /// Creates an instance as a copy of another one. /// /// \param Other the instance whose state to copy Optional(const Optional &Other) : Valid(false) { if (Other.Valid) { cr(Other.Value); } } /// Creates an instance as a copy of another one. /// /// \param Other the instance whose state to obtain Optional(Optional &&Other) noexcept : Valid(false) { if (Other.Valid) { cr(std::move(Other.Value)); } } /// Destroys \p this object. ~Optional(void) { destroy(); } /// Updates \p this object by copying the state of another one. /// /// \param Other the instance whose state to copy /// /// \return reference of the updated instance Optional &operator=(const Optional &Other) { if (Valid) { destroy(); } if (Other.Valid) { cr(Other.Value); } return *this; } /// Updates \p this object by copying the state of another one. /// /// \param Other the instance whose state to obtain /// /// \return reference of the updated instance Optional &operator=(Optional &&Other) noexcept { if (Valid) { destroy(); } if (Other.Valid) { cr(std::move(Other.Value)); } return *this; } /// Checks whether \p this object contains a value. /// /// \return if \p this object contains a value explicit operator bool(void) const { return Valid; } /// Checks whether \p this object does not contain a value. /// /// \return if \p this object does not contain a value bool operator!(void)const { return !Valid; } /// Returns the value stored in \p this object. /// /// \return reference of the stored value /// /// \pre \p this object contains a value AgentHandle &operator*(void) { ASSERT(Valid); return Value; } /// Returns the value stored in \p this object. /// /// \return reference of the stored value /// /// \pre \p this object contains a value const AgentHandle &operator*(void)const { ASSERT(Valid); return Value; } /// Returns the value stored in \p this object. /// /// \return pointer to the stored value /// /// \pre \p this object contains a value const AgentHandle *operator->(void)const { ASSERT(Valid); return &Value; } /// Returns the value stored in \p this object. /// /// \return pointer of the stored value /// /// \pre \p this object contains a value AgentHandle *operator->(void) { ASSERT(Valid); return &Value; } /// Returns the value stored in \p this object. /// /// \return reference of the stored value /// /// \pre \p this object contains a value AgentHandle &value(void) { ASSERT(Valid); return Value; } /// Returns the value stored in \p this object. /// /// \return reference of the stored value /// /// \pre \p this object contains a value const AgentHandle &value(void) const { ASSERT(Valid); return Value; } /// Returns the stored value or a default. /// /// If \p this object contains a value, then the stored value is returned. A /// given default value is returned otherwise. /// /// \param DefaultValue the value to return if \p this object does not contain /// a value /// /// \return reference to either the stored value or \p DefaultValue if \p this /// object does not contain a value const AgentHandle &valueOr(const AgentHandle &DefaultValue) const { return Valid ? Value : DefaultValue; } private: /// Deallocates the stored value if any. void destroy(void) { if (Valid) { Value.~AgentHandle(); Valid = false; } } /// Updates the state of \p this object by copying a value into it. /// /// \tparam V type of \p X /// /// \param X value to copy /// /// \pre \p this object does not contain a value template void cr(V &&X) { ASSERT(!Valid); Valid = true; new (&Value) AgentHandle(std::forward(X)); } /// Denotes if \p this object contains a value. bool Valid; /// Holds the stored value if any. union { AgentHandle Value; ///< The stored value. }; }; } // End namespace rosa #endif // ROSA_CORE_AGENTHANDLE_HPP diff --git a/include/rosa/core/Invoker.hpp b/include/rosa/core/Invoker.hpp index 365e133..63ab835 100644 --- a/include/rosa/core/Invoker.hpp +++ b/include/rosa/core/Invoker.hpp @@ -1,257 +1,264 @@ //===-- rosa/core/Invoker.hpp -----------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/core/Invoker.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Facilities for providing actual arguments for functions as /// \c rosa::Messageobjects. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_INVOKER_HPP #define ROSA_CORE_INVOKER_HPP #include "rosa/core/MessageMatcher.hpp" #include "rosa/support/log.h" #include "rosa/support/sequence.hpp" #include #include namespace rosa { /// Wraps a function and provides a simple interface to invoke the stored /// function by passing actual arguments as a \c rosa::Message object. /// /// \note A \c rosa::Invoker instance is supposed to be owned by a /// \c rosa::MessageHandler instance, and not being used directly from user /// code. class Invoker { protected: /// Creates an instance. /// /// \note Protected constructor restricts instantiation to derived classes. Invoker(void) noexcept; public: /// Destroys \p this object. virtual ~Invoker(void); /// Possible results of an invocation. enum class Result { NoMatch, ///< The wrapped function could not be invoked Invoked ///< The wrapped function has been invoked }; /// Type alias for a smart-pointer for \c rosa::Invoker. using invoker_t = std::unique_ptr; /// Type alias for \c rosa::Invoker::Result. using result_t = Result; /// Tells if a \c rosa::Message object can be used to invoke the function /// wrapped in \p this object. /// /// \param Msg \c rosa::Message to check /// /// \return whether \p Msg can be used to invoke the wrapped function virtual bool match(const Message &Msg) const noexcept = 0; /// Tries to invoke the wrapped function with a \c rosa::Message object. /// /// The wrapped function is invoked if the actual \c rosa::Message object can /// be used to invoke it. /// /// \param Msg \c rosa::Message to try to invoke the wrapped function with /// /// \return whether the wrapped function could be invoked with \p Msg virtual result_t operator()(const Message &Msg) const noexcept = 0; /// Instantiates an implementation of \c rosa::Invoker with the given /// function. /// /// \note As there is no empty \c rosa::Message, no \c rosa::Invoker wraps a /// function without any argument. /// /// \todo Enforce F does not potentially throw exception. /// /// \tparam T type of the first mandatory argument /// \tparam Ts types of any further arguments /// /// \param F function to wrap /// /// \return new \c rosa::Invoker::invoker_t object created from the given /// function template static invoker_t wrap(std::function &&F) noexcept; /// Convenience template alias for casting callable stuff to function objects /// for wrapping. /// /// \tparam Ts types of arguments /// /// \todo Should make it possible to avoid using an explicit conversion for /// the arguments of wrap. template using F = std::function; /// Convenience template for preparing non-static member functions into /// function objects for wrapping. /// /// \tparam C type whose non-static member the function is /// \tparam Ts types of arguments /// /// \see \c THISMEMBER template static inline F M(C *O, void (C::*Fun)(Ts...) noexcept) noexcept; }; /// Convenience preprocessor macro for the typical use of \c rosa::Invoker::M. /// It can be used inside a class to turn a non-static member function into a /// function object capturing this pointer, so using the actual object when /// handling a \c rosa::Message. /// /// \param FUN the non-static member function to wrap /// /// \note Inside the class \c MyClass, use\code /// THISMEMBER(fun) /// \endcode instead of\code /// Invoker::M(this, &MyClass::fun) /// \endcode #define THISMEMBER(FUN) \ Invoker::M(this, &std::decay::type::FUN) /// Nested namespace with implementation of \c rosa::Invoker and helper /// templates, consider it private. namespace { /// \defgroup InvokerImpl Implementation for rosa::Invoker /// /// Implements the \c rosa::Invoker interface for functions with different /// signatures. /// ///@{ /// Declaration of \c rosa::InvokerImpl implementing \c rosa::Invoker. /// /// \tparam Fun function to wrap template class InvokerImpl; /// Implementation of \c rosa::InvokerImpl for \c std::function. /// /// \tparam T type of the first mandatory argument /// \tparam Ts types of further arguments /// /// \note As there is no empty \c rosa::Message, no \c rosa::Invoker wraps a /// function without any argument, i.e., no /// \c std::function. template class InvokerImpl> final : public Invoker { /// Type alias for the stored function. using function_t = std::function; /// Type alias for correctly typed argument-tuples as obtained from /// \c rosa::Message. using args_t = std::tuple; /// Alias for \c rosa::MessageMatcher for the arguments of the stored /// function. using Matcher = MsgMatcher; /// The wrapped function. const function_t F; /// Invokes \c InvokerImpl::F by unpacking arguments from a \c std::tuple with /// the help of the actual template arguments. /// /// \tparam S sequence of numbers indexing \c std::tuple for arguments /// /// \param Args arguments to invoke \c InvokerImpl::F with /// /// \pre the length of \p S and size of \p Args are matching:\code /// sizeof...(S) == std::tuple_size::value /// \endcode template inline void invokeFunction(Seq, const args_t &Args) const noexcept; public: /// Creates an instance. /// /// \param F function to wrap /// /// \pre \p F is valid:\code /// bool(F) /// \endcode InvokerImpl(function_t &&F) noexcept : F(F) { ASSERT(bool(F)); // Sanity check. } /// Destroys \p this object. ~InvokerImpl(void) = default; /// Tells if a \c rosa::Message object can be used to invoke the function /// wrapped in \p this object. /// /// \param Msg \c rosa::Message to check /// /// \return whether \p Msg can be used to invoke the wrapped function bool match(const Message &Msg) const noexcept override { return Matcher::doesStronglyMatch(Msg); - }; + } /// Tries to invoke the wrapped function with a \c rosa::Message object. /// /// The wrapped function is invoked if the actual \c rosa::Message object can /// be used to invoke it. /// /// \param Msg \c rosa::Message to try to invoke the wrapped function with /// /// \return whether the wrapped function could be invoked with \p Msg result_t operator()(const Message &Msg) const noexcept override { if (match(Msg)) { LOG_TRACE("Invoking with matching arguments"); invokeFunction(seq_t(), Matcher::extractedValues(Msg)); return result_t::Invoked; } else { LOG_TRACE("Tried to invoke with non-matching arguments"); return result_t::NoMatch; } } }; template template void InvokerImpl>::invokeFunction( Seq, const args_t &Args) const noexcept { - ASSERT(sizeof...(S) == std::tuple_size::value); // Sanity check. + STATIC_ASSERT(sizeof...(S) == std::tuple_size::value, + "wrong number of type parameters"); F(std::get(Args)...); } ///@} } // End namespace template Invoker::invoker_t Invoker::wrap(std::function &&F) noexcept { return std::unique_ptr( new InvokerImpl>(std::move(F))); } template Invoker::F Invoker::M(C *O, void (C::*Fun)(Ts...) noexcept) noexcept { return [ O, Fun ](Ts... Vs) noexcept->void { (O->*Fun)(Vs...); }; } } // End namespace rosa #endif // ROSA_CORE_INVOKER_HPP diff --git a/include/rosa/core/Message.hpp b/include/rosa/core/Message.hpp index 7a1fb74..4c7e788 100644 --- a/include/rosa/core/Message.hpp +++ b/include/rosa/core/Message.hpp @@ -1,259 +1,265 @@ //===-- rosa/core/Message.hpp -----------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/core/Message.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Declaration of \c rosa::Message base-class. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_MESSAGE_HPP #define ROSA_CORE_MESSAGE_HPP #include "rosa/support/log.h" #include "rosa/support/tokenized_storages.hpp" #include "rosa/core/forward_declarations.h" namespace rosa { /// *Message* interface. /// /// The interface provides means to check the type of the stored values, but /// actual data is to be managed by derived implementations. /// /// A \c rosa::Message instance is an immutable data object that obtains its /// data upon creation and provides only constant references for the stored /// values. /// /// \note Any reference obtained from a \c rosa::Message instance remains valid /// only as long as the owning \c rosa::Message object is not destroyed. /// /// \todo Some member functions of \c rosa::Message duplicate member functions /// of \c rosa::TokenizedStorage, which cannot be easily factored out into a /// common base class due to eventual diamond inheritance issues in derived /// classes. Could this duplication be avoided? class Message { protected: /// Creates a new instance. /// /// \note No implementation for empty list. /// /// \tparam Type type of the mandatory first argument /// \tparam Types types of any further arguments /// /// \note the actual arguments are ignored by the constructor it is only /// their type that matters. The actual values are supposed to be handled by /// any implementation derived from \c rosa::Message. /// /// \pre \p Type and \p Types are all built-in types and the number of stored /// values does not exceed \c rosa::token::MaxTokenizableListSize. template Message(const Type &, const Types &...) noexcept; /// No copying and moving of \c rosa::Message instances. ///@{ Message(const Message &) = delete; Message(Message &&) = delete; Message &operator=(const Message &) = delete; Message &operator=(Message &&) = delete; ///@} public: /// Creates a \c rosa::message_t object from constant lvalue references. /// /// \tparam Type type of the mandatory first argument /// \tparam Types types of any further arguments /// /// \param T the first value to include in the \c rosa::Message /// \param Ts optional further values to include in the \c rosa::Message /// /// \return new \c rosa::message_t object created from the given arguments template static message_t create(const Type &T, const Types &... Ts) noexcept; /// Creates a \c rosa::message_t object from rvalue references. /// /// \tparam Type type of the mandatory first argument /// \tparam Types types of any further arguments /// /// \param T the first value to include in the \c rosa::Message /// \param Ts optional further values to include in the \c rosa::Message /// /// \return new \c rosa::message_t object created from the given arguments template static message_t create(Type &&T, Types &&... Ts) noexcept; /// Represents the types of the values stored in \p this object. /// /// A valid, non-empty \c rosa::Token representing the types of the values /// stored in \p this object. const Token T; /// The number of values stored in \p this object. /// /// That is the number of types encoded in \c rosa::Message::T. const token_size_t Size; /// Destroys \p this object. virtual ~Message(void); /// Tells if the value stored at a given index is of a given type. /// /// \note Any \c rosa::AtomConstant is encoded in \c rosa::Token as /// the \c rosa::AtomValue wrapped into it. /// /// \tparam Type type to match against /// /// \param Pos index the type of the value at is to be matched against \p Type /// /// \return if the value at index \p Pos of type \p Type /// /// \pre \p Pos is a valid index:\code /// Pos < Size /// \endcode template bool isTypeAt(const token_size_t Pos) const noexcept; /// Gives a constant reference of a value of a given type stored at a given /// index. /// /// \tparam Type type to give a reference of /// /// \param Pos index to set the reference for /// /// \return constant reference of \p Type for the value stored at index \p Pos /// /// \pre \p Pos is a valid index and the value at index \p Pos is of type /// \p Type: /// \code /// Pos < Size && isTypeAt(Pos) /// \endcode template const Type &valueAt(const token_size_t Pos) const noexcept; protected: /// Provides an untyped pointer for the value at a given index. /// /// \param Pos index to take a pointer for /// /// \return untyped pointer for the value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code /// Pos < Size /// \endcode virtual const void *pointerTo(const token_size_t Pos) const noexcept = 0; }; /// Nested namespace with implementation for \c rosa::Message, consider it /// private. namespace { /// Template class for an implementation of \c rosa::Message. /// /// \tparam Types types whose values are to be stored template class LocalMessage; /// Implementation of the template \c rosa::LocalMessage providing facilities /// for storing values as a \c rosa::Message object. /// /// \tparam Type type of the first mandatory value of the \c rosa::Message /// \tparam Types of any further values template class LocalMessage final : public Message, private TokenizedStorage { public: /// Creates an instance from constant lvalue references. /// /// \param T the mandatory first value to store in the \c rosa::Message object /// \param Ts optional further values to store in the \c rosa::Message object LocalMessage(const Type &T, const Types &... Ts) noexcept : Message(T, Ts...), TokenizedStorage(T, Ts...) { ASSERT(this->T == this->ST && Size == this->size()); // Sanity check. } /// Creates an instance from rvalue references. /// /// \param T the mandatory first value to store in the \c rosa::Message object /// \param Ts optional further values to store in the \c rosa::Message object LocalMessage(Type &&T, Types &&... Ts) noexcept : Message(T, Ts...), TokenizedStorage(std::move(T), std::move(Ts)...) { ASSERT(this->T == this->ST && Size == this->size()); // Sanity check. } /// Provides an untyped pointer for the constant value stored at a position. /// /// \param Pos the index of the value to return an untyped pointer for /// /// \return untyped pointer for the constant value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code /// Pos < Size /// \endcode const void *pointerTo(const token_size_t Pos) const noexcept override { ASSERT(Pos < Size); return TokenizedStorage::pointerTo(Pos); } /// Aborts the program! /// /// Since \c rosa::Message instances are supposed to be immutable, the /// non-const inherited function is overridden so that it aborts execution. void *pointerTo(const token_size_t) noexcept override { ROSA_CRITICAL("Unallowed operation of rosa::LocalMessage"); } }; } // End namespace template Message::Message(const Type &, const Types &...) noexcept : T(TypeToken::type, typename std::decay::type...>::Value), Size(lengthOfToken(T)) { ASSERT(validToken(T) && lengthOfToken(T) == (1 + sizeof...(Types))); // Sanity check. LOG_TRACE("Creating Message with Token(" + std::to_string(T) + ")"); } /// \note The implementation instantiates a private local template class /// \c LocalMessage. template message_t Message::create(const Type &T, const Types &... Ts) noexcept { return message_t(new LocalMessage(T, Ts...)); } /// \note The implementation instantiates a private local template class /// \c LocalMessage. template message_t Message::create(Type &&T, Types &&... Ts) noexcept { return message_t( new LocalMessage(std::move(T), std::move(Ts)...)); } template bool Message::isTypeAt(const token_size_t Pos) const noexcept { ASSERT(Pos < Size); Token TT = T; dropNOfToken(TT, Pos); return isHeadOfTokenTheSameType(TT); } template const Type &Message::valueAt(const token_size_t Pos) const noexcept { ASSERT(Pos < Size && isTypeAt(Pos)); return *static_cast(pointerTo(Pos)); } } // End namespace rosa #endif // ROSA_CORE_MESSAGE_HPP diff --git a/include/rosa/core/MessageHandler.hpp b/include/rosa/core/MessageHandler.hpp index 3a19c57..a770b32 100644 --- a/include/rosa/core/MessageHandler.hpp +++ b/include/rosa/core/MessageHandler.hpp @@ -1,169 +1,175 @@ //===-- rosa/core/MessageHandler.hpp ----------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/core/MessageHandler.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Facility for combining \c rosa::Invoker instances and applying /// \c rosa::Message intances to them. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_MESSAGEHANDLER_HPP #define ROSA_CORE_MESSAGEHANDLER_HPP #include "rosa/core/Invoker.hpp" #include "rosa/support/log.h" #include namespace rosa { /// Handles \c rosa::Message instances. /// /// A \c rosa::MessageHandler stores \c rosa::Invoker instances and tries to /// apply \c rosa::Message objects to them in the order of definition.The first /// matching \c rosa::Invoker instance is invoked with the \c rosa::Message /// object, after which handling of that \c rosa::Message object is completed. /// /// For example, consider the following snippet: \code /// rosa::MessageHandler { /// rosa::Invoker::F([](uint8_t) { /* ... */ }), /// rosa::Invoker::F([](uint8_t) { /* Never invoked */ }) /// }; /// \endcode /// Applying a \c rosa::Message with \c rosa::TypeList invokes the /// first function, and the second function would never be invoked because any /// matching \c rosa::Message object had already been handled by the first one. class MessageHandler { /// Type alias to bring \c rosa::Invoker::invoker_t to the local scope. using invoker_t = Invoker::invoker_t; /// Type alias for a \c std::vector storing \c rosa::Invoker instances. using invokers_t = std::vector; /// Stores \c rosa::Invoker instances. const invokers_t Invokers; /// Creates a container with \c rosa::Invoker instances from functions. /// /// \tparam Fun type of the mandatory first function /// \tparam Funs types of further functions /// /// \param F the mandatory first function /// \param Fs optional further functions /// /// \return \c rosa::MessageHandler::invokers_t object storing /// \c rosa::Invoker instances created from the \p F and \p Fs... template static inline invokers_t createInvokers(Fun &&F, Funs &&... Fs) noexcept; /// Updates an \c rosa::MessageHandler::invokers_t object with a new /// \c rosa::Invoker instance and handles further functions recursively. /// /// \tparam Fun type of the first function /// \tparam Funs types of further functions /// /// \param I \c rosa::MessageHandler::invokers_t to update /// \param Pos index at which to store the new \c rosa::Invoker instance /// \param F function to wrap and store into \p I at index \p Pos /// \param Fs further functions to handle later /// /// \pre \p Pos is a valid index:\code /// Pos < I.size() /// \endcode template static inline void wrapFun(invokers_t &I, const size_t Pos, Fun &&F, Funs &&... Fs) noexcept; /// Terminal case for the template \c rosa::MessageHandler::wrapFun. /// /// \param I \c rosa::MessageHandler::invokers_t which is now complete /// \param Pos size of \p I /// /// \pre \p Pos is the size of \p I:\code /// Pos == I.size(); /// \endcode static inline void wrapFun(invokers_t &I, const size_t Pos) noexcept; public: /// Creates an instance. /// /// The constructor stores the given functions into the new /// \c rosa::MessageHandler instance. /// /// \tparam Fun type of the mandatory first function /// \tparam Funs types of further functions /// /// \param F the first function to store /// \param Fs optional further functions to store template MessageHandler(Fun &&F, Funs &&... Fs) noexcept; /// Destroys \p this object. virtual ~MessageHandler(void); /// Tells if a \c rosa::Message object can be handled by \p this object. /// /// \param Msg \c rosa::Message to check /// /// \return whether \p this object stores a \c rosa::Invoker instance that can /// handle \p Msg bool canHandle(const Message &Msg) const noexcept; /// Applies a \c rosa::Message object to the first stored \c rosa::Invoker /// that can handle it, and tells if there was any. /// /// \note This operator finds the first applicable \c rosa::Invoker and /// invokes it with the given \c rosa::Message object, while the member /// function \c rosa::MessageHandler::canHandle only checks if there is any /// \c rosa::Invoker that can be invoked with a given \c rosa::Message object. /// /// \param Msg \c rosa::Message to use in invoking a matching \c rosa::Invoker /// /// \return whether there was a matching \c rosa::Invoker found and invoked /// with \p Msg bool operator()(const Message &Msg) const noexcept; }; template MessageHandler::MessageHandler(Fun &&F, Funs &&... Fs) noexcept : Invokers(createInvokers(std::move(F), std::move(Fs)...)) { LOG_TRACE("MessageHandler is created"); } template MessageHandler::invokers_t MessageHandler::createInvokers(Fun &&F, Funs &&... Fs) noexcept { // Create a container with the required size and get all the functions // wrapped. invokers_t I(1 + sizeof...(Funs)); wrapFun(I, 0, std::move(F), std::move(Fs)...); return I; } template void MessageHandler::wrapFun(invokers_t &I, const size_t Pos, Fun &&F, Funs &&... Fs) noexcept { ASSERT(Pos < I.size()); // Sanity check. // Wrap the current function and continue with the rest. I[Pos] = Invoker::wrap(std::move(F)); wrapFun(I, Pos + 1, std::move(Fs)...); } void MessageHandler::wrapFun(invokers_t &I, const size_t Pos) noexcept { ASSERT(Pos == I.size()); // Sanity check. // Nothing to do here. } } // End namespace rosa #endif // ROSA_CORE_MESSAGEHANDLER_HPP diff --git a/include/rosa/core/MessageMatcher.hpp b/include/rosa/core/MessageMatcher.hpp index 0092cb9..e94b257 100644 --- a/include/rosa/core/MessageMatcher.hpp +++ b/include/rosa/core/MessageMatcher.hpp @@ -1,201 +1,207 @@ //===-- rosa/core/MessageMatcher.hpp ----------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/core/MessageMatcher.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Facilities for checking and matching types of values stored in /// \c rosa::Message instances. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_MESSAGEMATCHER_HPP #define ROSA_CORE_MESSAGEMATCHER_HPP #include "rosa/core/Message.hpp" #include namespace rosa { /// Provides features to type-check a \c rosa::Message instance and extract /// stored values from it into an \c std::tuple instance with matching type /// arguments. /// /// \tparam List \c rosa::TypeList to check the stored values against template struct MessageMatcher; /// Definition of \c rosa::MessageMatcher for non-empty lists of types, like /// \c rosa::Message itself. /// /// \tparam Type first mandatory type /// \tparam Types any further types template struct MessageMatcher> { /// \c rosa::Token associated to the given \c rosa::TypeList. static constexpr Token T = TypeToken::Value; /// Tells if the values stored in a \c rosa::Message instance are matching /// types given as \c rosa::TypeList, considering /// \c rosa::AtomConstant instead of \c rosa::AtomValue. /// /// \param Msg \c rosa::Message to match /// /// \return whether the types of values stored in \p Msg matches /// \c rosa::TypeList static inline bool doesStronglyMatch(const Message &Msg) noexcept; /// Gives a \c std::tuple with references to the values stored in a /// type-matching instance of \c rosa::Message. /// /// \param Msg \c rosa::Message to extract values from /// /// \return \c std::tuple with references to the values stored in \p Msg /// /// \pre Types of the values stored in \p Msg matches /// \c rosa::TypeList:\code /// doesStronglyMatch(Msg) /// \endcode static inline std::tuple extractedValues(const Message &Msg) noexcept; }; /// Turns a list of types into a \c rosa::TypeList for \c rosa::MessageMatcher. template using MsgMatcher = MessageMatcher>; /// Nested namespace with implementation for features of /// \c rosa::MessageMatcher, consider it private. namespace { /// \defgroup MessageMatcherImpl Implementation for rosa::MessageMatcher /// /// An implementation of type-checking and value extraction for /// \c rosa::MessageMatcher. /// ///@{ /// Template declaration of \c MessageMatcherImpl. /// /// \tparam List \c rosa::TypeList to match against template struct MessageMatcherImpl; /// Specialization for \c rosa::EmptyTypeList. template <> struct MessageMatcherImpl { static bool doesStronglyMatchFrom(const Message &Msg, const token_size_t Pos) noexcept { // Matching EmptyTypeList only if reached the end of the stored types. return Pos == Msg.Size; } static std::tuple<> extractedValuesFrom(const Message &Msg, const token_size_t Pos) noexcept { // It is valid to extract an empty list only if we reached the end of // stored values. ASSERT(doesStronglyMatchFrom(Msg, Pos)); return std::tie(); } }; /// Specialization for \c rosa::AtomValue in the head. template struct MessageMatcherImpl, Ts...>> { static bool doesHeadStronglyMatchAt(const Message &Msg, const token_size_t Pos) noexcept { // Matching a \c rosa::AtomConstant in the head if there is a type stored at // \p Pos, the stored type is \c rosa::AtomValue, and the corresponding // value matches the \c rosa::AtomValue \p V. return Pos < Msg.Size && Msg.isTypeAt(Pos) && Msg.valueAt(Pos) == V; } static bool doesStronglyMatchFrom(const Message &Msg, const token_size_t Pos) noexcept { // Matching a non-empty list if the head is matching and the rest of the // list is matching. return doesHeadStronglyMatchAt(Msg, Pos) && MessageMatcherImpl>::doesStronglyMatchFrom(Msg, Pos + 1); } static std::tuple &, const Ts &...> extractedValuesFrom(const Message &Msg, const token_size_t Pos) noexcept { // Extracting for a non-empty list with a matching \c rosa::AtomConstant in // the head by getting the encoded \c rosa::AtomConstant and concatenating // it with values extracted for the rest of the list. ASSERT(doesHeadStronglyMatchAt(Msg, Pos)); return std::tuple_cat( std::tie(AtomConstant::Value), MessageMatcherImpl>::extractedValuesFrom(Msg, Pos + 1)); } }; /// Definition for the general case when a regular built-in type (not a /// \c rosa::AtomConstant) is in the head. template struct MessageMatcherImpl> { static bool doesHeadStronglyMatchAt(const Message &Msg, const token_size_t Pos) noexcept { // Matching the head if there is a type stored at \p Pos, and the stored // type is \p T. return Pos < Msg.Size && Msg.isTypeAt(Pos); } static bool doesStronglyMatchFrom(const Message &Msg, const token_size_t Pos) noexcept { // Matching a non-empty list if the head is matching and the rest of the // list is matching. return doesHeadStronglyMatchAt(Msg, Pos) && MessageMatcherImpl>::doesStronglyMatchFrom(Msg, Pos + 1); } static std::tuple extractedValuesFrom(const Message &Msg, const token_size_t Pos) noexcept { // Extracting for a non-empty list with a matching head by getting the // value for the head and concatenating it with values extracted for the // rest of the list. ASSERT(doesHeadStronglyMatchAt(Msg, Pos)); return std::tuple_cat( std::tie(Msg.valueAt(Pos)), MessageMatcherImpl>::extractedValuesFrom(Msg, Pos + 1)); } }; ///@} } // End namespace template bool MessageMatcher>::doesStronglyMatch( const Message &Msg) noexcept { // \note Fail quick on \c rosa::MessageMatcher::T, then match against list // with squashed integers the way \c rosa::Token is generated. return T == Msg.T && MessageMatcherImpl>::Type>::doesStronglyMatchFrom(Msg, 0); } template std::tuple MessageMatcher>::extractedValues( const Message &Msg) noexcept { ASSERT(doesStronglyMatch(Msg)); // \note Match against a list with squashed integers as \c rosa::Token is // generated. return MessageMatcherImpl>::Type>::extractedValuesFrom(Msg, 0); } } // End namespace rosa #endif // ROSA_CORE_MESSAGEMATCHER_HPP diff --git a/include/rosa/core/MessagingSystem.hpp b/include/rosa/core/MessagingSystem.hpp index 7e9b3cf..6ddd4cd 100644 --- a/include/rosa/core/MessagingSystem.hpp +++ b/include/rosa/core/MessagingSystem.hpp @@ -1,201 +1,207 @@ //===-- rosa/core/MessagingSystem.hpp ---------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/core/MessagingSystem.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Declaration of an interface extending \c rosa::System with messaging. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_MESSAGINGSYSTEM_HPP #define ROSA_CORE_MESSAGINGSYSTEM_HPP #include "rosa/core/AgentHandle.hpp" #include "rosa/core/System.hpp" #include "rosa/support/atom.hpp" namespace rosa { /// Extends the \c rosa::System interface with features to create \c rosa::Agent /// instancess and register \c rosa::Message objects for them. class MessagingSystem : public System { friend class AgentHandle; ///< \c rosa::AgentHandle is our friend. public: /// Returns an object implementing the \c rosa::MessagingSystem interface. /// /// \param Name name of the new instance /// /// \return \c std::unique_ptr for the new instance of /// \c rosa::MessagingSystem static std::unique_ptr createSystem(const std::string &Name) noexcept; private: /// Kind for categorizing \c rosa::Unit instances as *agents*. static constexpr AtomValue AgentKind = atom("agent"); protected: /// Creates a new instance. /// /// \note Protected constructor restricts instantiation for subclasses. MessagingSystem(void) noexcept = default; protected: /// Creates a \c rosa::Agent instance owned by \p this object and returns a /// \c rosa::AgentHandle for it. /// /// \tparam T type of the actual \c rosa::Agent to instantiate /// \tparam Funs types of the functions to instantiate \c rosa::Agent with /// /// \note \c rosa::Agent requires at least one function for its constructor, /// but derived classes may do not need that. That's the reason of allowing /// zero \p Funs for this template function. /// /// \param Name name of the new \c rosa::Unit instance /// \param Fs functions to instantiate \c rosa::Unit with /// /// \return handle for the new \c rosa::Agent instance /// /// \pre Statically, \p T is a subclass of \c rosa::Agent: /// \code /// std::is_base_of::value /// \endcode template AgentHandle createAgent(const std::string &Name, Funs &&... Fs); /// Unregisters and destroys a \c rosa::Agent referred by a /// \c rosa::AgentHandle. /// /// The function uses \c rosa::System::destroyUnit. /// /// \param H refers to the \c rosa::Agent to destroy /// /// \pre The referred \c rosa::Agent is registered. /// /// \post The referred \c rosa::Agent is not registered and also destroyed. void destroyAgent(const AgentHandle &H) noexcept; /// Gives the referenced \c rosa::Agent instance for a \c rosa::AgentHandle. /// /// \note Intended for derived classes to be able to inspect /// \c rosa::AgentHandle instances. /// /// \param H \c rosa::AgentHandle to take the referenced \c rosa::Agent from /// /// \return reference to the \c rosa::Agent instance from \p H static Agent &unwrapAgent(const AgentHandle &H) noexcept { return H.A; } /// Gives the owning \c rosa::MessagingSystem of a \c rosa::Agent instance /// for a \c rosa::AgentHandle. /// /// \note Intended for for derived classes to be able to inspect /// \c rosa::AgentHandle instances. /// /// \param H \c rosa::AgentHandle to take the owning /// \c rosa::MessagingSystem from /// /// \return reference to the \c rosa::MessagingSystem owning the /// \c rosa::Agent instance from \p H static MessagingSystem &unwrapSystem(const AgentHandle &H) noexcept { return H.S; } public: /// Sends a \c rosa::message_t instance to the \c rosa::Agent instance /// referred by a \c rosa::AgentHandle. /// /// \note If the given \c rosa::Message object cannot be handled by the /// referred \c rosa::Agent instance, the \c rosa::Message object is simply /// ignored. /// /// \param H refers to the \c rosa::Agent instance to send to /// \param M message to send /// /// \pre The referred \c rosa::Agent instance is owned by \p this object and /// also registered: \code /// &unwrapSystem(H) == this && isUnitRegistered(unwrapAgent(H)) /// \endcode virtual void send(const AgentHandle &H, message_t &&M) noexcept = 0; /// Sends a message -- created from given constant lvalue references -- /// to the \c rosa::Agent instance referred by a \c rosa::AgentHandle. /// /// \note If the given \c rosa::Message object cannot be handled by the /// referred \c rosa::Agent instance, the \c rosa::Message object is simply /// ignored. /// /// \note The message must consists of at least one value. /// /// \tparam Type type of the first mandatory value /// \tparam Types types of any further values /// /// \param H refers to the \c rosa::Agent instance to send to /// \param T the first value to include in the message /// \param Ts optional further values to include in the message /// /// \pre The referred \c rosa::Agent instance is owned by \p this object and /// also registered: \code /// &unwrapSystem(H) == this && isUnitRegistered(unwrapAgent(H)) /// \endcode template void send(const AgentHandle &H, const Type &T, const Types &... Ts) noexcept; /// Sends a message -- created from given rvalue references -- /// to the \c rosa::Agent instance referred by a \c rosa::AgentHandle. /// /// \note If the given \c rosa::Message object cannot be handled by the /// referred \c rosa::Agent instance, the \c rosa::Message object is simply /// ignored. /// /// \note The message must consists of at least one value. /// /// \tparam Type type of the first mandatory value /// \tparam Types types of any further values /// /// \param H refers to the \c rosa::Agent instance to send to /// \param T the first value to include in the message /// \param Ts optional further values to include in the message /// /// \pre The referred \c rosa::Agent instance is owned by \p this object and /// also registered: \code /// &unwrapSystem(H) == this && isUnitRegistered(unwrapAgent(H)) /// \endcode template void send(const AgentHandle &H, Type &&T, Types &&... Ts) noexcept; }; template AgentHandle MessagingSystem::createAgent(const std::string &Name, Funs &&... Fs) { STATIC_ASSERT((std::is_base_of::value), "not an Agent"); Agent &A = createUnit([&](const id_t Id, MessagingSystem &S) noexcept { return new T(AgentKind, Id, Name, S, std::move(Fs)...); }); return {A}; } template void MessagingSystem::send(const AgentHandle &H, const Type &T, const Types &... Ts) noexcept { send(H, Message::create(T, Ts...)); } template void MessagingSystem::send(const AgentHandle &H, Type &&T, Types &&... Ts) noexcept { send(H, Message::create(std::move(T), std::move(Ts)...)); } } // End namespace rosa #endif // ROSA_CORE_MESSAGINGSYSTEM_HPP diff --git a/include/rosa/core/System.hpp b/include/rosa/core/System.hpp index b9869bc..77ac39b 100644 --- a/include/rosa/core/System.hpp +++ b/include/rosa/core/System.hpp @@ -1,242 +1,248 @@ //===-- rosa/core/System.hpp ------------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/core/System.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Declaration of *System* interface. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_SYSTEM_HPP #define ROSA_CORE_SYSTEM_HPP #include "rosa/config/config.h" #include "rosa/core/forward_declarations.h" #include "rosa/support/debug.hpp" #include "rosa/support/log.h" #include #include #include namespace rosa { /// Base interface for actual agent-systems. /// /// The class provides facilities to keep track of \c rosa::Unit instances owned /// by a \c rosa::System. /// /// \note Any subclass is supposed to provide thread-safe implementation. /// /// \note The class declares only an interface to avoid trouble with multiple /// inheritance in various subclasses as in derived interfaces and derived /// implementations. /// /// \note Actual implementations are supposed to derive from \c rosa::SystemBase /// implenenting a base feature-set. class System { public: /// Signature of creator functions for \c rosa::Unit instances. /// /// \note The function is to be \c noexcept. /// /// \tparam T type derived from \c rosa::Unit /// \tparam S type derived from \c rosa::System template using UnitCreator = std::function; /// Returns an object implementing the \c rosa::System interface. /// /// \param Name name of the new instance /// /// \return \c std::unique_ptr for a new instance of \c rosa::System static std::unique_ptr createSystem(const std::string &Name) noexcept; protected: /// Creates an instance. /// /// \note Protected constructor restricts instantiation for subclasses. System(void) noexcept = default; /// No copying and moving of \c rosa::System. ///@{ System(const System &) = delete; System(System &&) = delete; System &operator=(const System &) = delete; System &operator=(System &&) = delete; ///@} public: /// Destroys \p this object. /// /// \note Any implementation makes sure that a \c rosa::System can be /// destroyed only if it is marked *cleaned* /// \see \c rosa::System::isSystemCleaned virtual ~System(void) = default; /// Tells whether \p this object is the same as \p Other. /// /// \note Whenever checking equality of two objects, use the one with the /// more specialized static type on the left-hand side of the operator. The /// static type of the object on the right-hand side is better to be /// \c rosa::System, ambiguous conversion might happen otherwise. /// /// \param Other another \c rosa::System instance to compare to /// /// \return whether \p this object and \p Other is the same virtual bool operator==(const System &Other) const noexcept = 0; /// Tells whether \p this object is not the same as \p Other. /// /// \note Whenever checking inequality of two objects, use the one with the /// more specialized static type on the left-hand side of the operator. The /// static type of the object on the right-hand side is better to be /// \c rosa::System, ambiguous conversion might happen otherwise. /// /// \param Other another \c rosa::System instance to compare to /// /// \return whether \p this object and \p Other is not the same bool operator!=(const System &Other) const noexcept { return !operator==(Other); } protected: /// Tells the next unique identifier to be used for a newly created /// \c rosa::Unit. /// /// \return \c rosa::id_t which is unique within the context of \p this /// object. /// /// \note Never returs the same value twice. virtual id_t nextId(void) noexcept = 0; /// Tells if \p this object has been marked cleaned and is ready for /// destruction. /// /// \return if \p this object is marked clean. virtual bool isSystemCleaned(void) const noexcept = 0; /// Marks \p this object cleaned. /// /// \note Can be called only once when the System does not have any live /// \c rosa::Unit instances. /// /// \pre \p this object has not yet been marked as cleaned and it has no /// \c rosa::Unit instances registered:\code /// !isSystemCleaned() && empty() /// \endcode /// /// \post \p this object is marked cleaned:\code /// isSystemCleaned() /// \endcode virtual void markCleaned(void) noexcept = 0; /// Registers a \c rosa::Unit instance to \p this object. /// /// \param U \c rosa::Unit to register /// /// \pre \p this object has not yet been marked as cleaned and \p U is not /// registered yet:\code /// !isSystemCleaned() && !isUnitRegistered(U) /// \endcode /// /// \post \p U is registered:\code /// isUnitRegistered(U) /// \endcode virtual void registerUnit(Unit &U) noexcept = 0; /// Unregisters and destroys a registered \c rosa::Unit instance. /// /// \param U \c rosa::Unit to destroy /// /// \pre \p U is registered:\code /// isUnitRegistered(U) /// \endcode /// /// \post \p U is not registered and also destroyed. virtual void destroyUnit(Unit &U) noexcept = 0; /// Tells if a \c rosa::Unit is registered in \p this object. /// /// \param U \c rosa::Unit to check /// /// \return whether \p U is registered in \p this object virtual bool isUnitRegistered(const Unit &U) const noexcept = 0; /// Creates a \c rosa::Unit instance with the given /// \c rosa::System::UnitCreator and registers the new instance. /// /// \tparam T type of the actual \c rosa::Unit to instantiate /// \tparam S type of the actual \c rosa::System instantiating /// /// \param C function creating an instance of type \p T /// /// \note \p S must be the actual subclass that wants to instantiate /// \c rosa::Unit. That cannot be statically enforced, it is the /// reponsibility of the caller to provide the proper \c rosa::System /// subclass. /// /// \pre Statically, \p T is a subclass of \c rosa::Unit and \p S is a /// subclass of \c rosa::System: /// \code /// std::is_base_of::value && std::is_base_of::value /// \endcode /// Dynamically, \p this object has not yet been marked cleaned:\code /// !isSystemCleaned() /// \endcode template T &createUnit(UnitCreator C) noexcept; public: /// Tells the name of \p this object /// /// \note The returned reference remains valid as long as \p this object is /// not destroyed. /// /// \return name of \p this object virtual const std::string &name(void) const noexcept = 0; /// Tells the number of \c rosa::Unit instances constructed in the context of /// \p this object so far, including those being already destroyed. /// /// \return number of \c rosa::Unit instances created so far virtual size_t numberOfConstructedUnits(void) const noexcept = 0; /// Tells the number of live \c rosa::Unit instances in the context \p this /// object, those being constructed and not destroyed yet. /// /// \return number of \c rosa::Unit instances alive virtual size_t numberOfLiveUnits(void) const noexcept = 0; /// Tells if \p this object has no live \c rosa::Unit instances. /// /// \return whether \p this object has any live \c rosa::Unit instances virtual bool empty(void) const noexcept = 0; }; template T &System::createUnit(UnitCreator C) noexcept { STATIC_ASSERT((std::is_base_of::value), "not a Unit"); STATIC_ASSERT((std::is_base_of::value), "not a System"); if (isSystemCleaned()) { ROSA_CRITICAL("Trying to create a Unit in a cleaned System '" + name() + "'"); } const id_t Id = nextId(); T *U = C(Id, static_cast(*this)); registerUnit(*U); LOG_TRACE("Unit created and registered '" + U->FullName + "'"); return *U; } } // End namespace rosa #endif // ROSA_CORE_SYSTEM_HPP diff --git a/include/rosa/core/SystemBase.hpp b/include/rosa/core/SystemBase.hpp index a5ca0d2..68aba64 100644 --- a/include/rosa/core/SystemBase.hpp +++ b/include/rosa/core/SystemBase.hpp @@ -1,138 +1,144 @@ //===-- rosa/core/SystemBase.hpp -----------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/core/SystemBase.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Base implementation of the \c rosa::System interface. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_SYSTEMBASE_HPP #define ROSA_CORE_SYSTEMBASE_HPP #include "rosa/core/System.hpp" #include namespace rosa { /// Base implementation of the \c rosa::System interface. /// /// This implementation provides only equality checking and *name* for /// \c rosa::System, identifiers for \c rosa::Unit instances, and marking the /// \c rosa::System cleaned for destruction. /// /// \note Actual implementations of \c rosa::System and derived interfaces are /// supposed to inherit from this implementation. class SystemBase : public System { protected: /// Creates an instance. /// /// \note Protected constructor restrict instantiation for subclasses. /// /// \param Name name of the new instance SystemBase(const std::string &Name) noexcept; public: /// Destroys \p this object. /// /// \pre \p this object is marked cleaned:\code /// isSystemCleaned() /// \endcode ~SystemBase(void); /// Tells whether \p this object is the same as \p Other. /// /// Two \c rosa::System instances are considered equal if they share a common /// \c rosa::SystemBase::Name member field. That should do among various /// subclasses. /// /// \param Other another \c rosa::System instance to compare to /// /// \return whether \p this object and \p Other is the same bool operator==(const System &Other) const noexcept override; protected: /// The textual name of \p this object implementing \c rosa::System. const std::string Name; private: /// Number of \c rosa::Unit instances constructed by \p this object. /// /// \note Should never be decremented! std::atomic UnitCount; /// Indicates that \p this object has been cleaned and is ready for /// destruction. /// /// The field is initialized as \c false and can be set by /// \c rosa::SystemBase::markCleaned. /// /// \note Subclasses must set the flag upon destructing their instances, which /// indicates to the destructor of the base-class that all the managed /// resources has been properly released. std::atomic SystemIsCleaned; public: /// Tells the name of \p this object /// /// \note The returned reference remains valid as long as \p this object is /// not destroyed. /// /// \return reference to \c rosa::SystemBase::Name const std::string &name(void) const noexcept override; protected: /// Tells the next unique identifier to be used for a newly created /// \c rosa::Unit. /// /// The functions takes the current value of the internal counter /// \c rosa::SystemBase::UnitCount and then increments it. /// /// \note This is the only function modifying /// \c rosa::SystemBase::UnitCount. /// /// \return \c rosa::id_t which is unique within the context of \p this /// object. id_t nextId(void) noexcept override; /// Tells if \p this object has been marked cleaned and is ready for /// destruction. /// /// \return if \p this object is marked clean. bool isSystemCleaned(void) const noexcept override; /// Marks \p this object cleaned by setting /// \c rosa::SystemBase::SystemIsCleaned. /// /// \note Can be called only once when the System does not have any live /// \c rosa::Unit instances. /// /// \pre \p this object has not yet been marked as cleaned and it has no /// \c rosa::Unit instances registered:\code /// !isSystemCleaned() && empty() /// \endcode /// /// \post \p this object is marked cleaned:\code /// isSystemCleaned() /// \endcode void markCleaned(void) noexcept override; /// Tells the number of \c rosa::Unit instances constructed in the context of /// \p this object so far, including those being already destroyed. /// /// \return current value of \c rosa::SystemBase::UnitCount that is the number /// of \c rosa::Unit instances created so far size_t numberOfConstructedUnits(void) const noexcept override; }; } // End namespace rosa -#endif // ROSA_LIB_CORE_SYSTEMBASE_HPP +#endif // ROSA_CORE_SYSTEMBASE_HPP diff --git a/include/rosa/core/Unit.h b/include/rosa/core/Unit.h index 47c42f6..e4ccf46 100644 --- a/include/rosa/core/Unit.h +++ b/include/rosa/core/Unit.h @@ -1,114 +1,120 @@ //===-- rosa/core/Unit.h ----------------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/core/Unit.h /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Declaration of \c rosa::Unit base-class. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_UNIT_H #define ROSA_CORE_UNIT_H #include "rosa/support/atom.hpp" #include "rosa/core/forward_declarations.h" #include #include namespace rosa { /// Base class for every entity in a \c rosa::System that has to be identified /// and traced. /// /// \note Life-cycle of \c rosa::Unit instances is supposed to be managed by the /// \c rosa::System owning the instance, do not create and destroy any /// \c rosa::Unit directly. class Unit { public: /// Identifies the *kind* of \p this object. /// /// \note Kind is dependent on the \c rosa::System owning \p this object. const AtomValue Kind; /// Unique identifier for \p this object. /// /// \note The unique identifier is assigned by the \c rosa::System owning /// \p this object upon creation. const id_t Id; /// Textual identifier of \p this object. /// /// \note Textual identifiers of \c rosa::Unit instances are not necessarily /// unique in their owning \c rosa::System. const std::string Name; protected: /// The \c rosa::System owning \p this object. System &S; public: /// Fully qualified name of \p this object. const std::string FullName; public: /// Creates a new instnace. /// /// \param Kind the kind of the new instance /// \param Id the unique identifier of the new instance /// \param Name the name of the new instance /// \param S \c rosa::System owning the new instance /// /// \pre \p Name is not empty:\code /// !Name.empty() /// \endcode Unit(const AtomValue Kind, const id_t Id, const std::string &Name, System &S) noexcept; /// No copying and moving of \c rosa::Unit instances is possible. ///@{ Unit(const Unit &) = delete; Unit(Unit &&) = delete; Unit &operator=(const Unit &) = delete; Unit &operator=(Unit &&) = delete; ///@} /// Destroys \p this object. virtual ~Unit(void); /// Dumps \p this object into a \c std::string for tracing purposes. /// /// Subclasses are supposed to override this function. /// /// \return \c std::string representing the state of \p this object virtual std::string dump(void) const noexcept; protected: /// Returns a reference to the \c rosa::System owning \p this object. /// /// \note Subclasses may override the function to return a reference of a /// subtype of \c rosa::System. /// /// \return reference of \c rosa::Unit::S virtual System &system() const noexcept; }; /// Dumps a \c rosa::Unit instance to a given \c std::ostream. /// /// \param [in,out] OS output stream to dump to /// \param U \c rosa::Unit to dump /// /// \return \p OS after dumping \p U to it std::ostream &operator<<(std::ostream &OS, const Unit &U); } // End namespace rosa #endif // ROSA_CORE_UNIT_H diff --git a/include/rosa/core/forward_declarations.h b/include/rosa/core/forward_declarations.h index f11f2e0..9a8d40f 100644 --- a/include/rosa/core/forward_declarations.h +++ b/include/rosa/core/forward_declarations.h @@ -1,40 +1,46 @@ //===-- rosa/core/forward_declarations.h ------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/core/forward_declarations.h /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Necessary forward declarations of types in the *Core* library. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_FORWARD_DECLARATIONS_H #define ROSA_CORE_FORWARD_DECLARATIONS_H #include // NOLINT namespace rosa { // Forward declarations of classes. class Agent; class Message; class MessagingSystem; class System; class Unit; /// Type alias used for \c rosa::Unit identifiers. using id_t = uint64_t; /// Type of a \c std::unique_ptr for an immutable *Message*, \c rosa::Message /// instance. using message_t = std::unique_ptr; } // End namespace rosa #endif // ROSA_CORE_FORWARD_DECLARATIONS_H diff --git a/include/rosa/deluxe/DeluxeAgent.hpp b/include/rosa/deluxe/DeluxeAgent.hpp index a75d727..d61a424 100644 --- a/include/rosa/deluxe/DeluxeAgent.hpp +++ b/include/rosa/deluxe/DeluxeAgent.hpp @@ -1,1478 +1,1459 @@ //===-- rosa/deluxe/DeluxeAgent.hpp -----------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \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; - /// Tells at which position in \c rosa::deluxe::DeluxeAgent::InputValues the - /// input from any particular *slave* starts. - /// - /// \note A value in the vector corresponds to the *slave* at the same - /// position and it is the sum of the elements of input values from *slaves* - /// at previous positions. - /// - /// \see \c rosa::deluxe::DeluxeAgent::saveInput - const std::vector InputStorageOffsets; - /// Stores the actual input values. /// /// \note The types of stored values match the corresponding /// \c rosa::TypeNumber values (in \c rosa::Token in order) in \c /// rosa::deluxe::DeluxeAgent::InputTypes. /// - /// \note The position of a value in the \c rosa::AbstractTokenizedStorage - /// indicates which element of the tuple of which argument of \p this object's - /// processing function it is. See also \c - /// rosa::deluxe::DeluxeAgent::DeluxeAgent. - const std::unique_ptr InputValues; + /// \note The position of a \c rosa::AbstractTokenizedStorage in the \c + /// std::vector indicates which argument of \p this object's processing + /// function the tuple is; and the position of the value in the \c + /// rosa::AbstractTokenizedStorage indicates which element of that tuple the + /// value is. See also \c rosa::deluxe::DeluxeAgent::DeluxeAgent. + const std::vector> 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 /// 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 { -/// Calculates storage offsets for values of \p Ts... stored in a \c +/// Creates storages for data of types \p Ts... in a \c std::vector of \c /// rosa::TokenizedStorage. /// /// \note Utilized by \c rosa::deluxe::DeluxeAgnet::DeluxeAgent to initialize \c -/// rosa::deluxe::DeluxeAgent::InputStorageOffsets. +/// rosa::deluxe::DeluxeAgent::InputValues. That is due to not being able to use +/// an initializer list directly; the initializer list always copies but \c +/// std::unique_ptr is not copyable. /// -/// \tparam Ts types whose offsets to calculate -/// \tparam S0 indices for referring to positions in \p Ts... +/// \tparam Ts types to create storages for /// /// \note Instantiation fails if any of the type arguments \p Ts... is not an /// instance of \c rosa::deluxe::DeluxeTuple. /// -/// \note The only argument provides indices statically as template -/// arguments \p S0..., so its actual value is ignored. -/// -/// \return \c std::vector containing the calculated offsets +/// \return \c std::vector with pointers for the created storage objects /// /// \pre Statically, all the type arguments \p Ts... are instances of \c -/// rosa::deluxe::DeluxeTuple and the indices match the types: \code -/// TypeListAllDeluxeTuple>::Value && -/// sizeof...(Ts) == sizeof...(S0) +/// rosa::deluxe::DeluxeTuple: \code +/// TypeListAllDeluxeTuple>::Value /// \endcode -template < - typename... Ts, size_t... S0, - typename = std::enable_if_t>::Value>> -static std::vector storageOffsets(Seq) noexcept { - STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments"); - std::vector Offsets(sizeof...(Ts)); - // Do nothing for no types. - if constexpr (sizeof...(Ts) != 0) { - Offsets[0] = 0; // The offset of the very first value is always `0`. - // Calculate further offsets... - (((S0 != sizeof...(Ts) - 1) && - (Offsets[S0 + 1] = Offsets[S0] + Ts::Length)), - ...); - } - return Offsets; +template +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 { /// 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 { 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]); - const token_size_t StorageOffset = InputStorageOffsets[Pos]; - // The below should hold because of the above, just leave it for sanity check. ASSERT((true && ... && (static_cast(static_cast(S0)) == S0))); + const auto &SlaveInput = InputValues[Pos]; + // Get all elements of the tuple in a fold expression. - return T(*static_cast(InputValues->pointerTo( - static_cast(StorageOffset + S0)))...); + 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 { - ASSERT(sizeof...(As) == sizeof...(S0)); + 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()); } } 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 successfuuly constructed. ASSERT((true && ... && (static_cast(static_cast(S0)) == S0))); 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), - InputStorageOffsets(storageOffsets(seq_t())), - InputValues(new typename TokenizedStorageForTypeList< - typename TypeListUnwrapDeluxeTuple>::Type>:: - Type()), - MasterInputNextPos(0), MasterInputChanged(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 successfuuly constructed. ASSERT((true && ... && (static_cast(static_cast(S0)) == S0))); // Create a static constant array for these indices to be available as lvalue // references when creating messages below. \c S0... when used directly in a // fold expression is a temporary value, which would result in \c // rosa::Message instances being created with rvalue references. Further, all // other values would to copied into a temporary variable for making them /// available as rvalue references (they are constant lvalue references here). static constexpr std::array 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 successfuuly constructed. ASSERT((true && ... && (static_cast(static_cast(S0)) == S0))); // Create a static constant array for these indices to be available as lvalue // references when creating messages below. \c S0... when used directly in a // fold expression is a temporary value, which would result in \c // rosa::Message instances being created with rvalue references. Further, all // other values would to copied into a temporary variable for making them /// available as rvalue references (they are constant lvalue references here). static constexpr std::array 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); - size_t SlavePos = SlaveIds.at(Id); + const size_t SlavePos = SlaveIds.at(Id); LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id << ") saves value from slave at position " << SlavePos << ": (" << static_cast(Pos) << ") " << Value << std::endl; // Save value. - size_t StoragePos = (size_t)InputStorageOffsets[SlavePos] + Pos; - // This assert must hold if \p this object was successfully constructed. - ASSERT(static_cast(static_cast(StoragePos)) == - StoragePos); - *static_cast( - InputValues->pointerTo(static_cast(StoragePos))) = 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/deluxe/DeluxeAtoms.hpp b/include/rosa/deluxe/DeluxeAtoms.hpp index 70963b2..23b8ade 100755 --- a/include/rosa/deluxe/DeluxeAtoms.hpp +++ b/include/rosa/deluxe/DeluxeAtoms.hpp @@ -1,59 +1,65 @@ //===-- rosa/deluxe/DeluxeAtoms.hpp -----------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeAtoms.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Definition of \c rosa::AtomValue values and \c rosa::AtomConstant /// types for the implementation of the *deluxe interface*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXEATOMS_HPP #define ROSA_DELUXE_DELUXEATOMS_HPP #include "rosa/support/atom.hpp" namespace rosa { namespace deluxe { /// Contains some definitions used in the implementation of the *deluxe /// interface* to denote various roles and events /// /// \see \c rosa::deluxe::DeluxeContext /// /// \note Do not apply `using namespace` to this namespace as that may result in /// some identifiers in the original namespace being hidden by those of /// \c rosa::deluxe::atoms. namespace atoms { /// Value to be used as the *kind* of \c rosa::deluxe::DeluxeSensor. /// /// \see \c rosa::Unit::Kind constexpr AtomValue SensorKind = atom("dl_sensor"); /// Value to be used as the *kind* of \c rosa::deluxe::DeluxeAgent. /// /// \see \c rosa::Unit::Kind constexpr AtomValue AgentKind = atom("dl_agent"); /// Type alias denoting system trigger messages. using Trigger = AtomConstant; /// Type alias denoting messages from a slave. using Slave = AtomConstant; /// Type alias denoting messages from a master. using Master = AtomConstant; } // End namespace atoms } // End namespace deluxe } // End namespace rosa #endif // ROSA_DELUXE_DELUXEATOMS_HPP diff --git a/include/rosa/deluxe/DeluxeContext.hpp b/include/rosa/deluxe/DeluxeContext.hpp index a54a2da..f2e8340 100644 --- a/include/rosa/deluxe/DeluxeContext.hpp +++ b/include/rosa/deluxe/DeluxeContext.hpp @@ -1,932 +1,941 @@ //===-- rosa/deluxe/DeluxeContext.hpp ---------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeContext.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Public interface for the *deluxe interface* for working with agent /// systems. /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXECONTEXT_HPP #define ROSA_DELUXE_DELUXECONTEXT_HPP #include "rosa/deluxe/DeluxeSystem.hpp" #include "rosa/support/types.hpp" #include #include #include /// Local helper macro to log and return a /// \c rosa::deluxe::DeluxeContext::ErrorCode value. /// /// Creates a debug message with the stringified value and returns the value. /// /// \param Err \c rosa::deluxe::DeluxeContext::ErrorCode value to log and /// return #define DCRETERROR(Err) \ { \ LOG_DEBUG(#Err); \ return Err; \ } namespace rosa { namespace deluxe { /// Defines the *deluxe interface*. /// /// \todo The classes \c rosa::deluxe::DeluxeSensor and \c /// rosa::deluxe::DeluxeAgent share some common features in relation to their /// *slave* role in the *deluxe interface*. But their definitions are completely /// independent. It could be investigated how to lift their common parts into a /// new *deluxe slave* class, which would serve as base for both, to avoid code /// duplication. class DeluxeContext { /// A system owned by \p this object. /// /// \note The reference is kept in a \c std::shared_ptr because of the member /// function \c rosa::deluxe::DeluxeContext::getSystem. std::shared_ptr System; /// References to all *sensors* and *agents* created by \p this object. std::set DeluxeUnits; public: /// Errors that may be resulted by some of the member functions of the class. enum struct ErrorCode { NoError, TypeMismatch, NotSensor, NotAgent, NotUnit, WrongPosition, AlreadyHasSlave, AlreadyHasMaster, AlreadyHasValueStream, UnsuitableExecutionPolicy }; /// Returns a new instance of \c rosa::deluxe::DeluxeContext. /// /// \param Name name of the underlying \c rosa::DeluxeSystem /// /// \return \c std::unique_ptr for the new instance of /// \c rosa::deluxe::DeluxeContext with a new, empty \c rosa::DeluxeSystem static std::unique_ptr create(const std::string &Name) noexcept; private: /// Creates a new instance. /// /// \note Private constructor restricts instantiation to member functions of /// the class. /// /// \param Name name of the underlying \c rosa::MessagingSystem DeluxeContext(const std::string &Name) noexcept; public: /// Destroys \p this object. ~DeluxeContext(void) noexcept; /// Returns a reference for the underlying \c rosa::MessagingSystem. /// /// \note One cannot do much with a \c rosa::MessagingSystem currently, this /// is for future use. /// /// \return reference for the underlying \c rosa::MessagingSystem. std::weak_ptr getSystem(void) const noexcept; private: /// Creates a new *sensor* in the context of \p this object. /// /// The new *sensor* handles master-input by \p MF. /// /// \tparam MT type of master-input the new *sensor* handles /// \tparam T type of data the new *sensor* operates on /// /// \note Instantiation fails if any of the type arguments \p MT and \p T /// is not an instance of \c rosa::deluxe::DeluxeTuple or \p T is \c /// rosa::deluxe::EmptyDeluxeTuple. /// /// \param Name name of the new *sensor* /// \param MF function for the new *sensors* to process master-input /// values with /// \param F function for the new *sensor* to generate the next value with /// during normal operation /// /// \note \p F is not used during simulation, in which case /// \c rosa::deluxe::DeluxeContext::registerSensorValues is used to /// register an alternative simulation data source with \c /// rosa::deluxe::DeluxeSensor::registerSimulationDataSource. One may /// safely keep relying on the default value of \p F as long as only /// simulation of the system is to be done. /// /// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor. /// /// \return \c rosa::AgentHandle for the new *sensor* template >::Value && !std::is_same::value>> AgentHandle createSensorImpl(const std::string &Name, std::function)> &&MF, std::function &&F) noexcept; public: /// Creates a new *sensor* in the context of \p this object. /// /// The new *sensor* does not receive master-input. /// /// \tparam T type of data the new *sensor* operates on /// /// \note Instantiation fails if type argument \p T is neither a built-in type /// nor an instance of \c rosa::deluxe::DeluxeTuple with at least one element. /// /// \param Name name of the new *sensor* /// \param F function for the new *sensor* to generate the next value with /// during normal operation /// /// \note \p F is not used during simulation, in which case /// \c rosa::deluxe::DeluxeContext::registerSensorValues is used to register /// an alternative simulation data source with /// \c rosa::deluxe::DeluxeSensor::registerSimulationDataSource. One may /// safely keep relying on the default value of \p F as long as only /// simulation of the system is to be done. /// /// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor. /// /// \return \c rosa::AgentHandle for the new *sensor* template ::Value || (IsDeluxeTuple::Value && !std::is_same::value)>> AgentHandle createSensor( const std::string &Name, std::function &&F = [](void) { return T(); }) noexcept; /// Creates a new *sensor* in the context of \p this object. /// /// The new *sensor* handles master-input by \p MF. /// /// \tparam MT type of master-input the new *sensor* handles /// \tparam T type of data the new *sensor* operates on /// /// \note The type arguments \p MT and \p T must be either all built-in types /// or all instances of \c rosa::deluxe::DeluxeTuple. Moreover, \p T cannot be /// \c rosa::deluxe::EmptyDeluxeTuple. Instantiation fails if these conditions /// do not hold. /// /// \param Name name of the new *sensor* /// \param MF function for the new *sensors* to process master-input /// values with \param F function for the new *sensor* to generate /// the next value with during normal operation /// /// \note \p F is not used during simulation, in which case /// \c rosa::deluxe::DeluxeContext::registerSensorValues is used to /// register an alternative simulation data source with \c /// rosa::deluxe::DeluxeSensor::registerSimulationDataSource. One may /// safely keep relying on the default value of \p F as long as only /// simulation of the system is to be done. /// /// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor. /// /// \return \c rosa::AgentHandle for the new *sensor* template , BuiltinTypes>::Value || (TypeListAllDeluxeTuple>::Value && !std::is_same::value)>> AgentHandle createSensor( const std::string &Name, std::function)> &&MF, std::function &&F = [](void) { return T(); }) noexcept; private: /// Creates a new *agent* in the context of \p this object. /// /// The new *agent* receives master-input by \p MF and produces /// master-output. /// /// \tparam MT type of master-input the new *agent* handles /// \tparam T type of data the new *agent* outputs /// \tparam Ts types of master-output the new *agent* produces /// \tparam As types of inputs the new *agent* takes /// /// \note Instantiation fails if any of the type arguments \p MT, \p T, \p /// Ts..., and \p As... is not an instance of \c rosa::deluxe::DeluxeTuple or /// any of \p T and \p As... is \c rosa::deluxe::EmptyDeluxeTuple. /// /// \param Name name of the new *agent* /// \param MF function for the new *agent* to process master-input /// values with \param F function for the new *agent* to process /// input values and generate output with /// /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. /// /// \return \c rosa::AgentHandle for the new *agent* template >::Value && !std::is_same::value && (true && ... && (!std::is_same::value))>> AgentHandle createAgentImpl( const std::string &Name, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept; public: /// Creates a new *agent* in the context of \p this object. /// /// The new *agent* neither receives master-input nor produces /// master-output. /// /// \tparam T type of data the new *agent* outputs /// \tparam As types of inputs the new *agent* takes /// /// \note The type arguments \p T and \p As... must be either all built-in /// types or all instances of \c rosa::deluxe::DeluxeTuple. Moreover, none of /// them can be \c rosa::deluxe::EmptyDeluxeTuple. Instantiation fails if /// these conditions do not hold. /// /// \param Name name of the new *agent* /// \param F function for the new *agent* to process input values and /// generate output with /// /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. /// /// \return \c rosa::AgentHandle for the new *agent* template < typename T, typename... As, typename = std::enable_if_t< TypeListSubsetOf, BuiltinTypes>::Value || (TypeListAllDeluxeTuple>::Value && !std::is_same::value && (true && ... && (!std::is_same::value)))>> AgentHandle createAgent(const std::string &Name, std::function(std::pair...)> &&F) noexcept; /// Creates a new *agent* in the context of \p this object. /// /// The new *agent* receives master-input by \p MF but does not /// produce master-output. /// /// \tparam MT type of master-input the new *agent* handles /// \tparam T type of data the new *agent* outputs /// \tparam As types of inputs the new *agent* takes /// /// \note The type arguments \p MT, \p T, and \p As... must be either all /// built-in types or all instances of \c rosa::deluxe::DeluxeTuple. Moreover, /// none of \p T and \p As... can be \c rosa::deluxe::EmptyDeluxeTuple. /// Instantiation fails if these conditions do not hold. /// /// \param Name name of the new *agent* /// \param MF function for the new *agent* to process master-input /// values with /// \param F function for the new *agent* to process input values and /// generate output with /// /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. /// /// \return \c rosa::AgentHandle for the new *agent* template < typename MT, typename T, typename... As, typename = std::enable_if_t< TypeListSubsetOf, BuiltinTypes>::Value || (TypeListAllDeluxeTuple>::Value && !std::is_same::value && (true && ... && (!std::is_same::value)))>> AgentHandle createAgent(const std::string &Name, std::function)> &&MF, std::function(std::pair...)> &&F) noexcept; /// Creates a new *agent* in the context of \p this object. /// /// The new *agent* does not receive master-input but produces /// master-output. /// /// \tparam T type of data the new *agent* outputs /// \tparam Ts types of master-output the new *agent* produces /// \tparam As types of inputs the new *agent* takes /// /// \note The type arguments \p T, \p Ts, and \p As... must be either all /// built-in types or all instances of \c rosa::deluxe::DeluxeTuple. Moreover, /// none of \p T and \p As... can be \c rosa::deluxe::EmptyDeluxeTuple. /// Instantiation fails if these conditions do not hold. /// /// \param Name name of the new *agent* /// \param F function for the new *agent* to process input values and /// generate output with /// /// \note \p F does not produce master-output for a given position if the /// corresponding type is \c rosa::deluxe::EmptyDeluxeTuple. It is not /// possible to disable master-output at any position by using built-in types. /// /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. /// /// \return \c rosa::AgentHandle for the new *agent* template < typename T, typename... Ts, typename... As, typename = std::enable_if_t< TypeListSubsetOf, BuiltinTypes>::Value || (TypeListAllDeluxeTuple>::Value && !std::is_same::value && (true && ... && (!std::is_same::value)))>> AgentHandle createAgent(const std::string &Name, std::function, Optional...>( std::pair...)> &&F) noexcept; /// Creates a new *agent* in the context of \p this object. /// /// The new *agent* receives master-input by \p MF and produces /// master-output. /// /// \tparam MT type of master-input the new *agent* handles /// \tparam T type of data the new *agent* outputs /// \tparam Ts types of master-output the new *agent* produces /// \tparam As types of inputs the new *agent* takes /// /// \note The type arguments \p MT, \p T, \p Ts, and \p As... must be either /// all built-in types or all instances of \c rosa::deluxe::DeluxeTuple. /// Moreover, none of \p T and \p As... can be \c /// rosa::deluxe::EmptyDeluxeTuple. Instantiation fails if these conditions /// do not hold. /// /// \param Name name of the new *agent* /// \param MF function for the new *agent* to process master-input /// values with /// \param F function for the new *agent* to process input values and /// generate output with /// /// \note \p F does not produce master-output for a given position if the /// corresponding type is \c rosa::deluxe::EmptyDeluxeTuple. It is not /// possible to disable master-output at any position by using built-in types. /// /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. /// /// \return \c rosa::AgentHandle for the new *agent* template < typename MT, typename T, typename... Ts, typename... As, typename = std::enable_if_t< TypeListSubsetOf, BuiltinTypes>::Value || (TypeListAllDeluxeTuple>::Value && !std::is_same::value && (true && ... && (!std::is_same::value)))>> AgentHandle createAgent( const std::string &Name, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept; /// Returns the current execution policy of the referred \p Unit /// /// \see \c rosa::deluxe::DeluxeExecutionPolicy /// /// \note The referred \p Unit is either *sensor* or *agent*. /// /// \note The returned reference is valid only as long as \c /// rosa::deluxe::DeluxeContext::setExecutionPolicy() is not called with the /// *unit* referred by \p Unit and the *unit* is not destroyed. /// /// \param Unit the *unit* whose execution policy is to be obtained /// /// \return the \c rosa::deluxe::DeluxeExecutionPolicy from \p Unit if \p Unit /// is valid Optional getExecutionPolicy(AgentHandle Unit) const noexcept; /// Sets the current execution policy of the referred \p Unit to \p /// ExecutionPolicy. /// /// \see \c rosa::deluxe::DeluxeExecutionPolicy /// /// \note The referred \p Unit is either *sensor* or *agent*. /// /// \param Unit the *unit* whose execution policy is to be set /// \param ExecutionPolicy the new execution policy for \p Unit /// /// \return how successful setting \p ExecutionPolicy for \p Unit was /// /// \note The function may return the following /// \c rosa::deluxe::DeluxeContext::ErrorCode values: /// `ErrorCode` | Comment /// ----------- | ------- /// `NoError` | Success /// `NotUnit` | Referred \p Unit is not valid /// `UnsuitableExecutionPolicy` | \p ExecutionPolicy cannot handle \p Unit ErrorCode setExecutionPolicy( AgentHandle Unit, std::unique_ptr &&ExecutionPolicy) noexcept; /// Connects a *sensor* to an *agent* in the context of \p this object. /// /// \param Agent the *agent* to connect to /// \param Pos the index of slot of \p Agent to connect \p Sensor to /// \param Sensor the *sensor* to connect /// \param Description optional textual description of the connection /// /// \return how successfull connecting \p Sensor to \p Agent at slot /// index \p Pos was /// /// \note The function may return the following /// \c rosa::deluxe::DeluxeContext::ErrorCode values: /// `ErrorCode` | Comment /// ----------- | ------- /// `NoError` | Success /// `NotAgent` | Referred \p Agent is not \c rosa::deluxe::DeluxeAgent /// `NotSensor` | Referred \p Sensor is not \c rosa::deluxe::DeluxeSensor /// `WrongPosition` | \p Pos is not a valid input position of \p Agent /// `TypeMismatch` | Expected input type at position \p Pos of \p Agent is other thanthe output type of \p Sensor or expected master-input of \p Sensor is other than master-output at position \p Pos of \p Agent if any /// `AlreadyHasSlave` | \p Agent at position \p Pos already has a *slave* registered /// `AlreadyHasMaster` | \p Sensor already has a *master* registered ErrorCode connectSensor(AgentHandle Agent, const size_t Pos, AgentHandle Sensor, const std::string &Description = "") noexcept; /// Connectes two *agents* in the context of \p this object. /// /// \param Master the *agent* to connect to /// \param Pos the index of slot of \p Master to connect \p Slave to /// \param Slave the *agent* to connect /// \param Description optional textual description of the connection /// /// \return how succesfull connecting \p Slave to \p Master at slot /// index \p Pos was /// /// \note The function may return the following /// \c rosa::deluxe::DeluxeContext::ErrorCode values: /// `ErrorCode` | Comment /// ----------- | ------- /// `NoError` | Success /// `NotAgent` | Referred \p Master or \p Slave is not \c rosa::deluxe::DeluxeAgent /// `WrongPosition` | \p Pos is not a valid input position of \p Master /// `TypeMismatch` | Expected input type at position \p Pos of \p Master is other than the output type of \p Slave or expected master-input of \p Slave is other than master-output at position \p Pos of \p Master if any /// `AlreadyHasSlave` | \p Master at position \p Pos already has a *slave* registered /// `AlreadyHasMaster` | \p Slave already has a *master* registered ErrorCode connectAgents(AgentHandle Master, const size_t Pos, AgentHandle Slave, const std::string &Description = "") noexcept; /// Initializes \c this object and others managed by \p this object /// for setting up and performing simulation. /// /// \see \c rosa::deluxe::DeluxeContext::registerSensorValues, /// \c rosa::deluxe::DeluxeContext::simulate /// /// Need to clear simulation data sources from all the *sensors*. void initializeSimulation(void) noexcept; public: /// Registers a stream providing values for a *sensor* during /// simulation. /// /// \tparam Iterator type of iterator providing values for \p Sensor - /// \tparam T type of values \p Sensor is operating on, always use - /// default! + /// \tparam T type that can be matched to values \p Sensor is operating on, + /// always use default! /// /// \note Instantiation fails if type argument \p T is neither a built-in type - /// nor an instance of \c rosa::deluxe::DeluxeTuple with at least one element. + /// nor a tuple (i.e., can be converted to \c rosa::deluxe::DeluxeTuple). /// /// \param Sensor the *sensor* to register values for /// \param Start provides values for \p Sensor /// \param End denotes the end of stream of values /// \param Default value to be used when input stream is depleted /// during simulation /// /// \return how successful registering \p Source for \p Sensor /// /// \note The function may return the following /// \c rosa::deluxe::DeluxeContext::ErrorCode values: /// `ErrorCode` | Comment /// ----------- | ------- /// `NoError` | Success - /// `TypeMismatch` | \p Sensor generates values of a type other than - /// \p T `NotSensor` | Referred \p Sensor is not \c - /// rosa::deluxe::DeluxeSensor `AlreadyHasValueStream` | \p Sensor already has - /// simulation data source set - template < - typename Iterator, typename T = typename Iterator::value_type, - typename = std::enable_if_t::Value || - (IsDeluxeTuple::Value && - !std::is_same::value)>> + /// `TypeMismatch` | \p T does not match the type of values + /// generated by \p Sensor + /// `NotSensor` | Referred \p Sensor is not \c + /// rosa::deluxe::DeluxeSensor + /// `AlreadyHasValueStream` | \p Sensor already has simulation data source set + template ::Value || IsTuple::Value>> ErrorCode registerSensorValues(AgentHandle Sensor, Iterator &&Start, const Iterator &End, T Default = {}) noexcept; /// Performs the system contained by \p this object. /// /// The function performs \p NumCycles cycle of simulation. In each /// cycle, all the *agents* and *sensors* registered in \c /// rosa::deluxe::DeluxeContext::DeluxeUnits are trigged for /// execution. /// /// \param NumCycles number of cycles to perform /// /// \pre All the *sensors* in the system contained by \p this object /// generate their output from simulation data sources. void simulate(const size_t NumCycles) const noexcept; }; /// Anonymous namespace with helper features for implementing /// \c rosa::deluxe::DeluxeContext, consider it private. namespace { /// Maps any type \p T to \c rosa::deluxe::EmptyDeluxeTuple. template struct MapToEmptyDeluxeTuple { using Type = EmptyDeluxeTuple; }; /// Convenience template alias for \c MapToEmptyDeluxeTuple. template using empty_deluxe_t = typename MapToEmptyDeluxeTuple::Type; /// Converts a \c std::tuple of \c rosa::Optional built-in types into a /// corresponding \c std::tuple of \c rosa::Optional with each actual value /// wrapped in \c rosa::deluxe::DeluxeTuple. /// /// \tparam Ts types of the values /// \tparam S0 indices for accessing values in \p Values /// /// \param Values the \c std::tuple of \c rosa::Optional with built-in values /// /// \note The second argument provides indices statically as template arguments /// \p S0..., so its actual value is ignored. /// /// \return a \c std::tuple of \c rosa::Optional corresponding to \p Values /// with each actual value wrapped in \c rosa::deluxe::DeluxeTuple /// /// \pre Statically, all type arguments \p Ts... are built-in types and the /// provided indices \p S0... match the length of \p Ts...: \code /// TypeListSubsetOf, BuiltinTypes>::Value && /// sizeof...(Ts) == sizeof...(S0) /// \endcode template std::tuple>...> wrapBuiltinInDeluxeTuple(const std::tuple...> &Values, Seq) noexcept { STATIC_ASSERT((TypeListSubsetOf, BuiltinTypes>::Value), "not built-in types"); STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent type arguments"); return std::make_tuple(std::get(Values) ? Optional>( make_deluxe_tuple(*std::get(Values))) : Optional>()...); } } // End namespace template AgentHandle DeluxeContext::createSensorImpl(const std::string &Name, std::function)> &&MF, std::function &&F) noexcept { AgentHandle H = System->createSensor(Name, std::move(MF), std::move(F)); DeluxeUnits.emplace(H); return H; } template AgentHandle DeluxeContext::createSensor(const std::string &Name, std::function &&F) noexcept { auto EmptyMF = std::function)>( [](std::pair) {}); if constexpr (TypeListContains::Value) { using OutputType = DeluxeTuple; return createSensorImpl( Name, std::move(EmptyMF), std::function( [F{std::move(F)}](void) { return OutputType(F()); })); } else if constexpr (IsDeluxeTuple::Value && !std::is_same::value) { return createSensorImpl(Name, std::move(EmptyMF), std::move(F)); } else { ASSERT(false && "Unexpected type argument"); } } template AgentHandle DeluxeContext::createSensor(const std::string &Name, std::function)> &&MF, std::function &&F) noexcept { if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { using MasterInputType = DeluxeTuple; using OutputType = DeluxeTuple; return createSensorImpl( Name, std::function)>( [MF{std::move(MF)}](std::pair Arg) { MF({std::get<0>(Arg.first), Arg.second}); }), std::function( [F{std::move(F)}](void) { return OutputType(F()); })); } else if constexpr (TypeListAllDeluxeTuple>::Value && !std::is_same::value) { return createSensorImpl(Name, std::move(MF), std::move(F)); } else { ASSERT(false && "Unexpected type arguments"); } } template AgentHandle DeluxeContext::createAgentImpl( const std::string &Name, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept { AgentHandle H = System->createAgent(Name, std::move(MF), std::move(F)); DeluxeUnits.emplace(H); return H; } template AgentHandle DeluxeContext::createAgent( const std::string &Name, std::function(std::pair...)> &&F) noexcept { using NoMasterOutputType = std::tuple>...>; auto EmptyMF = std::function)>( [](std::pair) { return NoMasterOutputType(); }); if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { using OutputType = DeluxeTuple; return createAgentImpl( Name, std::move(EmptyMF), std::function< std::tuple, Optional>...>( std::pair, bool>...)>( [F{std::move(F)}](std::pair, bool>... Args) { const auto Result = F({std::get<0>(Args.first), Args.second}...); return std::tuple_cat( wrapBuiltinInDeluxeTuple(std::tuple(Result), seq_t<1>()), NoMasterOutputType()); })); } else if constexpr (TypeListAllDeluxeTuple>::Value && !std::is_same::value && (true && ... && (!std::is_same::value))) { return createAgentImpl( Name, std::move(EmptyMF), std::function, Optional>...>( std::pair...)>( [F{std::move(F)}](std::pair... Args) { const auto Result = F(Args...); return std::tuple_cat(std::tuple(Result), NoMasterOutputType()); })); } else { ASSERT(false && "Unexpected type arguments"); } } template AgentHandle DeluxeContext::createAgent( const std::string &Name, std::function)> &&MF, std::function(std::pair...)> &&F) noexcept { using NoMasterOutputType = std::tuple>...>; if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { using MasterInputType = DeluxeTuple; using OutputType = DeluxeTuple; return createAgentImpl( Name, std::function)>( [MF{std::move(MF)}](std::pair Arg) { MF({std::get<0>(Arg.first), Arg.second}); return NoMasterOutputType(); }), std::function< std::tuple, Optional>...>( std::pair, bool>...)>( [F{std::move(F)}](std::pair, bool>... Args) { const auto Result = F({std::get<0>(Args.first), Args.second}...); return std::tuple_cat( wrapBuiltinInDeluxeTuple(std::tuple(Result), seq_t<1>()), NoMasterOutputType()); })); } else if constexpr (TypeListAllDeluxeTuple>::Value && !std::is_same::value && (true && ... && (!std::is_same::value))) { return createAgentImpl( Name, std::function)>( [MF{std::move(MF)}](std::pair Arg) { MF(Arg); return NoMasterOutputType(); }), std::function, Optional>...>( std::pair...)>( [F{std::move(F)}](std::pair... Args) { const auto Result = F(Args...); return std::tuple_cat(std::tuple(Result), NoMasterOutputType()); })); } else { ASSERT(false && "Unexpected type arguments"); } } template AgentHandle DeluxeContext::createAgent( const std::string &Name, std::function, Optional...>( std::pair...)> &&F) noexcept { if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { using MasterOutputType = std::tuple>...>; using OutputType = DeluxeTuple; return createAgentImpl( Name, std::function)>( [](std::pair) { return MasterOutputType(); }), std::function< std::tuple, Optional>...>( std::pair, bool>...)>( [F{std::move(F)}](std::pair, bool>... Args) { const auto Result = F({std::get<0>(Args.first), Args.second}...); return wrapBuiltinInDeluxeTuple(Result, seq_t<1 + sizeof...(Ts)>()); })); } else if constexpr (TypeListAllDeluxeTuple< TypeList>::Value && !std::is_same::value && (true && ... && (!std::is_same::value))) { using MasterOutputType = std::tuple...>; return createAgentImpl( Name, std::function)>( [](std::pair) { return MasterOutputType(); }), std::function, Optional...>( std::pair...)>( [F{std::move(F)}](std::pair... Args) { const auto Output = F(Args...); return Output; })); } else { ASSERT(false && "Unexpected type arguments"); } } template AgentHandle DeluxeContext::createAgent( const std::string &Name, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept { if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { using MasterInputType = DeluxeTuple; using MasterOutputType = std::tuple>...>; using OutputType = DeluxeTuple; return createAgentImpl( Name, std::function)>( [MF{std::move(MF)}](std::pair Arg) { const auto Result = MF({std::get<0>(Arg.first), Arg.second}); return wrapBuiltinInDeluxeTuple(Result, seq_t()); }), std::function< std::tuple, Optional>...>( std::pair, bool>...)>( [F{std::move(F)}](std::pair, bool>... Args) { const auto Result = F({std::get<0>(Args.first), Args.second}...); return wrapBuiltinInDeluxeTuple(Result, seq_t<1 + sizeof...(Ts)>()); })); } else if constexpr (TypeListAllDeluxeTuple< TypeList>::Value && !std::is_same::value && (true && ... && (!std::is_same::value))) { using MasterOutputType = std::tuple...>; return createAgentImpl( Name, std::function)>( [MF{std::move(MF)}](std::pair Arg) { const auto Output = MF(Arg); return Output; }), std::function, Optional...>( std::pair...)>( [F{std::move(F)}](std::pair... Args) { const auto Output = F(Args...); return Output; })); } else { ASSERT(false && "Unexpected type arguments"); } } template DeluxeContext::ErrorCode DeluxeContext::registerSensorValues(AgentHandle Sensor, Iterator &&Start, const Iterator &End, T Default) noexcept { // Get the type of values provided by \p Iterator. STATIC_ASSERT((std::is_same::value), "type mismatch"); // Make sure preconditions are met. if (!System->isDeluxeSensor(Sensor)) { DCRETERROR(ErrorCode::NotSensor); } auto S = System->getDeluxeSensor(Sensor); ASSERT(S); // Sanity check. + if (S->simulationDataSourceIsSet()) { DCRETERROR(ErrorCode::AlreadyHasValueStream); } if constexpr (TypeListContains::Value) { if (S->OutputType != TypeToken::Value) { DCRETERROR(ErrorCode::TypeMismatch); } // Register input stream. // \note Need to capture parameters by value so having local copies. S->registerSimulationDataSource(std::function(void)>([= ](void) mutable noexcept->DeluxeTuple { if (Start != End) { LOG_TRACE_STREAM << "Reading next value for sensor '" << S->FullName << "': " << *Start << '\n'; return make_deluxe_tuple(*Start++); } else { LOG_TRACE_STREAM << "Providing default value for sensor '" << S->FullName << "': " << Default << '\n'; return make_deluxe_tuple(Default); } })); - } else if constexpr (IsDeluxeTuple::Value && - !std::is_same::value) { - if (S->OutputType != T::TT) { + } else if constexpr (IsTuple::Value) { + + using TT = matching_deluxe_tuple_t; + if (std::is_same::value || S->OutputType != TT::TT) { DCRETERROR(ErrorCode::TypeMismatch); } // Register input stream. // \note Need to capture parameters by value so having local copies. S->registerSimulationDataSource( - std::function([=](void) mutable noexcept->T { + std::function([=](void) mutable noexcept->TT { if (Start != End) { + const TT DV(*Start++); LOG_TRACE_STREAM << "Reading next value for sensor '" << S->FullName - << "': " << *Start << '\n'; - return *Start++; + << "': " << DV << '\n'; + return DV; } else { + static const TT DD(Default); LOG_TRACE_STREAM << "Providing default value for sensor '" - << S->FullName << "': " << Default << '\n'; - return Default; + << S->FullName << "': " << DD << '\n'; + return DD; } })); } else { ASSERT(false && "Unexpected type argument"); } return ErrorCode::NoError; } } // End namespace deluxe } // End namespace rosa // Undef local macro if not used in the corresponding implementation. #ifndef ROSA_LIB_DELUXE_DELUXECONTEXT_CPP #undef DCRETERROR #endif #endif // ROSA_DELUXE_DELUXECONTEXT_HPP diff --git a/include/rosa/deluxe/DeluxeExecutionPolicy.h b/include/rosa/deluxe/DeluxeExecutionPolicy.h index caaad92..223ca13 100644 --- a/include/rosa/deluxe/DeluxeExecutionPolicy.h +++ b/include/rosa/deluxe/DeluxeExecutionPolicy.h @@ -1,195 +1,201 @@ //===-- rosa/deluxe/DeluxeExecutionPolicy.h ---------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeExecutionPolicy.h /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2019 /// /// \brief Public interface of *execution policies* in the *deluxe interface*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXEEXECUTIONPOLICY_H #define ROSA_DELUXE_DELUXEEXECUTIONPOLICY_H #include "rosa/core/AgentHandle.hpp" #include #include #include #include namespace rosa { namespace deluxe { // Forward declaration of DeluxeSystem. Do not include the corresponding header // in this file because of cyclic dependency. class DeluxeSystem; /// *Execution policy* that controls how *agents* and *sensors* call their /// processing functions. /// /// An *execution policy* can be applied to a deluxe *unit* only if \c /// deluxe::rosa::DeluxeExecutionPolicy::canHandle() allows it. Each deluxe /// *unit* must have a compatible *execution policy* associated to it, and the /// *unit* queries \c rosa::deluxe::DeluxeExecutionPolicy::shouldProcess() on each /// triggering and calls its processing funtion only if it is allowed by the /// *execution policy*. /// /// \see rosa::deluxe::DeluxeExecutionPolicy::decimation() /// \see rosa::deluxe::DeluxeExecutionPolicy::awaitAll() /// \see rosa::deluxe::DeluxeExecutionPolicy::awaitAny() /// /// \todo Extend the interface with query functions about what kind of /// execution policy is behind the interface. This can be done in relation /// to the existing factory functions; for example, if the actual object is /// decimation and with what rate. class DeluxeExecutionPolicy { protected: /// Protected constructor, only implementations can instantiate the class. DeluxeExecutionPolicy(void) noexcept = default; private: /// No instance can be copy-constructed, move-constructed, copied, and moved. /// ///@{ DeluxeExecutionPolicy(const DeluxeExecutionPolicy &) = delete; DeluxeExecutionPolicy(DeluxeExecutionPolicy &&) = delete; DeluxeExecutionPolicy &operator=(const DeluxeExecutionPolicy &) = delete; DeluxeExecutionPolicy &operator=(DeluxeExecutionPolicy &&) = delete; ///@} public: /// Virtual destructor for subclasses. virtual ~DeluxeExecutionPolicy(void) noexcept = default; /// Creates an *execution policy* that allows execution with decimation of /// triggering. /// //// *Decimation* can handle both *agents* and *sensors*. /// Processing functions are executed only on every \p D th /// triggering. In the case of *sensors* in simulation, the simulation data /// source is read on each triggering as it provides values with respect to /// the highest execution frequency, but output is generated by the *sensor* /// only on every \p D th triggering. /// /// \note A rate of \c 0 is allowed as actual argument and is treated as rate /// \c 1 (i.e., execute processing functions on each triggering). /// /// \param D the rate of *decimation* /// /// \return an *execution policy* implementing *decimation* with rate \p D static std::unique_ptr decimation(const size_t D); /// Creates an *execution policy* that allows execution only if all defined /// *slave* positions has new input. /// /// *Await all* can handle only *agents* and only if the particular *agent* /// has at least as many *slave* positions as the largest position defined in /// \p S. Processing functions are executed only if new input has been /// received for all defined *slave* positions. /// /// \param S set of *slave* positions to await input from /// /// \return an *execution policy* implementing *awaiting all* input from set /// \p S static std::unique_ptr awaitAll(const std::set &S); /// Creates an *execution policy* that allows execution if any of the defined /// *slave* positions has new input. /// /// *Await any* can handle only *agents* and only if the particular *agent* /// has at least as many *slave* positions as the largest position defined in /// \p S. Processing functions are executed if new input has been received for /// any of the defined *slave* positions. /// /// \param S set of *slave* positions to await input from /// /// \return an *execution policy* implementing *awaiting any* input from set /// \p S static std::unique_ptr awaitAny(const std::set &S); /// Tells if \p this object can handle the deluxe *unit* referred by \p H. /// /// The *execution policy* implemented by \p this object is applicable to the /// given deluxe *unit* referred by \p H only if the function returns \c true. /// /// \param H reference to the *unit* to check /// \param S the system owning the *unit* referred by \p H /// /// \return if \p this object can handle the *unit* referred by \p H virtual bool canHandle(const AgentHandle H, const DeluxeSystem &S) const noexcept = 0; /// Tells if processing function should be executed on the current triggering. /// /// The function is to be called on each triggering of the deluxe *unit*. /// Decision about execution of processing function is done by \p this object /// according to the implemented *execution policy*. /// /// \param InputChanged flags indicating whether new input has been received /// at *slave* positions /// /// \return if to execute processing function virtual bool shouldProcess(const std::vector &InputChanged) noexcept = 0; /// Dumps \p this object into textual representation. /// /// \return textual representation of \p this object virtual std::string dump(void) const noexcept = 0; protected: /// Tells whether the *unit* referred by \p H is a \c /// rosa::deluxe::DeluxeAgent. /// /// \param H reference to the *unit* to check /// \param S the system owning the *unit* referred by \p H /// /// \return if the *unit* referred by \p H is a \c rosa::deluxe::DeluxeAgent bool isDeluxeAgent(const AgentHandle H, const DeluxeSystem &S) const noexcept; /// Tells the number of inputs handled by the *unit* referred by \p H. /// /// If \p H refers to a \c rosa::deluxe::DeluxeAgent, the function returns the /// number of inputs (i.e., *slave* positions) of the *agent*. Otherwise, the /// function returns \c 0. /// /// \param H reference to the *unit* to check /// \param S the system owning the *unit* referred by \p H /// /// \return the number of inputs handled by the *unit* referred by \p H size_t numberOfDeluxeAgentInputs(const AgentHandle H, const DeluxeSystem &S) const noexcept; }; } // End namespace deluxe } // End namespace rosa namespace std { /// Converts a \c rosa::deluxe::DeluxeExecutionPolicy into \c std::string. /// /// \param EP \c rosa::deluxe::DeluxeExecutionPolicy to convert /// /// \return \c std::string representing \p EP string to_string(const rosa::deluxe::DeluxeExecutionPolicy &EP); /// Dumps a \c rosa::deluxe::DeluxeExecutionPolicy to a given \c std::ostream. /// /// \param [in,out] OS output stream to dump to /// \param EP \c rosa::deluxe::DeluxeExecutionPolicy to dump /// /// \return \p OS after dumping \p EP to it ostream &operator<<(ostream &OS, const rosa::deluxe::DeluxeExecutionPolicy &EP); } // End namespace std #endif // ROSA_DELUXE_DELUXEEXECUTIONPOLICY_H diff --git a/include/rosa/deluxe/DeluxeSensor.hpp b/include/rosa/deluxe/DeluxeSensor.hpp index 95bb94d..afa2693 100644 --- a/include/rosa/deluxe/DeluxeSensor.hpp +++ b/include/rosa/deluxe/DeluxeSensor.hpp @@ -1,668 +1,674 @@ //===-- rosa/deluxe/DeluxeSensor.hpp ----------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeSensor.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Specialization of \c rosa::Agent for *sensor* role of the the *deluxe /// interface*. /// /// \see \c rosa::deluxe::DeluxeContext /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXESENSOR_HPP #define ROSA_DELUXE_DELUXESENSOR_HPP #include "rosa/core/Agent.hpp" #include "rosa/deluxe/DeluxeAtoms.hpp" #include "rosa/deluxe/DeluxeExecutionPolicy.h" #include "rosa/deluxe/DeluxeTuple.hpp" /// Local helper macros to deal with built-in types. /// ///@{ /// Creates function name for member functions in \c rosa::deluxe::DeluxeSensor. /// /// \param N name suffix to use #define DSMASTERHANDLERNAME(N) handleMaster_##N /// Defines member functions for handling messages from *master* in /// \c rosa::deluxe::DeluxeSensor. /// /// \see \c DeluxeSensorMasterInputHandlers /// /// \note No pre- and post-conditions are validated directly by these functions, /// they rather rely on \c rosa::deluxe::DeluxeSensor::saveMasterInput to do /// that. /// /// \param T the type of input to handle /// \param N name suffix for the function identifier #define DSMASTERHANDLERDEFN(T, N) \ void DSMASTERHANDLERNAME(N)(atoms::Master, id_t MasterId, token_size_t Pos, \ T Value) noexcept { \ saveMasterInput(MasterId, Pos, Value); \ } /// Convenience macro for \c DSMASTERHANDLERDEFN with identical arguments. /// /// \see \c DSMASTERHANDLERDEFN /// /// This macro can be used instead of \c DSMASTERHANDLERDEFN if the actual value /// of \p T can be used as a part of a valid identifier. /// /// \param T the type of input to handle #define DSMASTERHANDLERDEF(T) DSMASTERHANDLERDEFN(T, T) /// Results in a \c THISMEMBER reference to a member function defined by /// \c DSMASTERHANDLERDEFN. /// /// Used in the constructor of \c rosa::deluxe::DeluxeSensor to initialize super /// class \c rosa::Agent with member function defined by \c DSMASTERHANDLERDEFN. /// /// \see \c DSMASTERHANDLERDEFN, \c THISMEMBER /// /// \param N name suffix for the function identifier #define DSMASTERHANDLERREF(N) THISMEMBER(DSMASTERHANDLERNAME(N)) ///@} namespace rosa { namespace deluxe { /// Specialization of \c rosa::Agent for *sensor* role of the *deluxe /// interface*. /// /// \see \c rosa::deluxe::DeluxeContext /// /// \invariant There is a compatible *execution policy* set; the actual value in /// \c rosa::deluxe::DeluxeSensor::MasterInputNextPos is valid with respect to /// the corresponding types. /// /// \see Definition of \c rosa::deluxe::DeluxeSensor::inv on the class invariant /// /// \note All member functions validate the class invariant as part of their /// precondition. Moreover, non-const functions validate the invariant before /// return as their postcondition. class DeluxeSensor : public Agent { /// Checks whether \p this object holds the class invariant. /// /// \see Invariant of the class \c rosa::deluxe::DeluxeSensor /// /// \return if \p this object holds the class invariant bool inv(void) const noexcept; /// The \c rosa::deluxe::DeluxeExecutionPolicy that controls the execution of /// \c this object. std::unique_ptr 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::DeluxeSensor::master const Token OutputType; /// 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::DeluxeSensor::master const Token MasterInputType; private: /// Indicates which element of the master-input is expected from the *master*. /// /// The *master* is supposed to send one \c rosa::deluxe::DeluxeTuple value /// element by element in their order of definition. This member field tells /// the element at which position should be received next. /// /// \p this object is supposed to be triggered only when a complete /// master-input has been received, that is the field should hold the value /// `0`. /// /// \see \c rosa::deluxe::DeluxeSensor::handleTrigger /// \c rosa::deluxe::DeluxeSensor::saveMasterInput token_size_t MasterInputNextPos; /// Indicates whether the input value from the *master* has been changed since /// the last trigger received from the system. /// /// The flag is reset to \c false upon handling a trigger and then set to \c /// true by \c rosa::deluxe::DeluxeSensor::saveMasterInput when storig a new /// input value in \c rosa::deluxe::DeluxeSensor::MasterInputValue. bool MasterInputChanged; /// Stores the actual input value from *master*. /// /// \note The type of the stored value matches the types indicated by \c /// rosa::deluxe::DeluxeSensor::MasterInputType. const std::unique_ptr MasterInputValue; /// Alias for function objects used as trigger handler for /// \c rosa::deluxe::DeluxeSensor. /// /// \note The function used for \c H is to be \c noexcept. /// /// \see \c DeluxeSensorTriggerHandlers using H = std::function; /// \defgroup DeluxeSensorTriggerHandlers Trigger handlers of /// rosa::deluxe::DeluxeSensor /// /// \brief Trigger handler functions of \c rosa::deluxe::DeluxeSensor /// /// The actual data source functions and master-input processing function are /// captured in lambda expressions that are in turn wrapped in \c /// std::function objects. The lambda expression calls a processing function, /// either to handle master-input or obtain the next sensory value from data /// source. The next sensory value is sent it to *master* by calling \c /// rosa::deluxe::DeluxeSensor::sendToMaster. Also, the flag \c /// rosa::deluxe::DeluxeSensor::MasterInputChanged is reset when the current /// value is passed to the master-input processing function. The function \c /// rosa::deluxe::DeluxeSensor::handleTrigger needs only to call the proper /// function object. /// Processes master-input. /// /// \ingroup DeluxeSensorTriggerHandlers /// /// The function is called upon the sensor is trigged by the system. const H MFP; /// Produces the next sensory value during normal execution. /// /// \ingroup DeluxeSensorTriggerHandlers /// /// The function is used during normal execution. During simulation, the /// simulation environment sets \c rosa::deluxe::DeluxeSensor::SFP, which is /// used instead of \c rosa::deluxe::DeluxeSensor::FP. const H FP; /// Produces the next sensory value during simulation. /// /// \ingroup DeluxeSensorTriggerHandlers /// /// The function is empty by default. The simulation environment sets it to be /// used during simulation. H SFP; /// The *master* to send values to. /// /// \note *Masters* are set dynamically, hence it is possible that a /// \c rosa::deluxe::DeluxeSensor instance does not have any *master* at a /// given moment. Optional Master; /// Tells the unique identifier of the *master* of \p this object, if any /// registered. /// /// \return the unique identifier of the *master* /// /// \pre A *master* is registered for \p this object: \code /// Master /// \endcode id_t masterId(void) const noexcept; /// Wraps a master-input processing function into a trigger handler. /// /// \see \c rosa::deluxe::DeluxeSensor::MFP and \c DeluxeSensorTriggerHandlers /// /// \tparam Ts types of elements of master-input processed by \p MF /// \tparam S0 indices for accessing master-input values /// /// \param MF function that processes master-input /// /// \note The second argument provides indices statically as template /// arguments \p S0..., so its actual value is ignored. /// /// \note A master-input type of \c rosa::deluxe::EmptyDeluxeTuple indicates /// that \p this object does not receive master-input, \p MF is never called /// if \p Ts is empty. /// /// \return trigger handler function based on \p MF /// /// \pre Statically, the indices match the elements: \code /// sizeof...(Ts) == sizeof...(S0) /// \endcode Dynamically, \p Ts... match \c /// rosa::deluxe::DeluxeSensor::MasterInputType: \code /// MasterInputType == DeluxeTuple::TT /// \endcode template H triggerHandlerFromProcessingFunction( std::function, bool>)> &&MF, Seq) noexcept; /// Wraps a data source function into a trigger handler. /// /// \see \c rosa::deluxe::DeluxeSensor::FP, \c /// rosa::deluxe::DeluxeSensor::SFP, and \c DeluxeSensorTriggerHandlers /// /// \tparam T type of data provided by \p F /// /// \param F function to generate value with /// \param inSimulation if F is a data source for Simulation /// /// \return trigger handler function based on \p F /// /// \pre Statically, the type agument \p T is an instance of \c /// rosa::deluxe::DeluxeTuple: \code /// IsDeluxeTuple::Value /// \endcode Dynamically, \p T matches \c /// rosa::deluxe::DeluxeSensor::OutputType: \code /// OutputType == T::TT /// \endcode template H triggerHandlerFromDataSource(std::function &&F, bool inSimulation) noexcept; public: /// Creates a new instance. /// /// The constructor instantiates the base-class with functions to handle /// messages as defined for the *deluxe interface*. /// /// \todo Enforce \p F and \p MF do not potentially throw exception. /// /// \tparam MT type of master-input handled by \p MF /// \tparam T type of data to operate on /// /// \note Instantiation fails if any of the type arguments \p MT and \p T is /// not an instance of \c rosa::deluxe::DeluxeTuple or \p T is \c /// rosa::deluxe::EmptyDeluxeTuple. /// /// \note If \p MT is \c rosa::deluxe::EmptyDeluxeTuple, the constructed /// object does not receive master-input. /// /// \param Kind kind of the new \c rosa::Unit instance /// \param Id unique identifier of the new \c rosa::Unit instance /// \param Name name of the new \c rosa::Unit instance /// \param S \c rosa::MessagingSystem owning the new instance /// \param MF function to process master-input values with /// \param F function to generate the next value with during normal operation /// /// \pre Statically, \p MT and \p T are instances of \c /// rosa::deluxe::DeluxeTuple and \p T contains at least one element:\code /// TypeListAllDeluxeTuple>::Value && T::Length > 0 /// \endcode /// Dynamically, the instance is created as of kind /// \c rosa::deluxe::atoms::SensorKind: /// \code /// Kind == rosa::deluxe::atoms::SensorKind /// \endcode /// /// \see \c rosa::deluxe::DeluxeTuple template < typename MT, typename T, typename = std::enable_if_t< TypeListAllDeluxeTuple>::Value && (T::Length > 0)>> DeluxeSensor(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, std::function)> &&MF, std::function &&F) noexcept; /// Destroys \p this object. ~DeluxeSensor(void) noexcept; /// Returns the current execution policy of \p this object. /// /// \see \c rosa::deluxe::DeluxeExecutionPolicy /// /// \note The returned reference is valid only as long as \c /// rosa::deluxe::DeluxeSensor::setExecutionPolicy() is not called and \p this /// object is not destroyed. /// /// \return \c rosa::deluxe::DeluxeSensor::ExecutionPolicy const DeluxeExecutionPolicy &executionPolicy(void) const noexcept; /// Sets the current execution policy of \p this object to \p EP. /// /// \see \c rosa::deluxe::DeluxeExecutionPolicy /// /// \note \p EP is set only if it can handle \p this object. /// /// \param EP the new execution policy for \p this object /// /// \return if \p EP was successfully set for \p this object. bool setExecutionPolicy(std::unique_ptr &&EP) noexcept; /// The *master* of \p this object, if any. /// /// \see \c rosa::deluxe::DeluxeSensor::registerMaster /// /// \return the *master* registered for \p this object Optional master(void) const noexcept; /// Registers a *master* for \p this object. /// /// The new *master* is registered by overwriting the reference to any /// already registered *master*. One can clear the registered reference by /// passing an *empty* \c rosa::Optional object as actual argument. /// /// \note The role of the referred *master* is validated by checking its /// *kind*. /// /// \note Any call to \c rosa::deluxe::DeluxeSensor::registerMaster should be /// paired with a corresponding call of \c /// rosa::deluxe::DeluxeAgent::registerSlave, which validates that /// input/output types of master and slave matches. /// /// \param _Master the *master* to register /// /// \pre \p Master is empty or of kind \c rosa::deluxe::atoms::AgentKind: /// \code /// !_Master || unwrapAgent(*_Master).Kind == rosa::deluxe::atoms::AgentKind /// \endcode void registerMaster(const Optional _Master) noexcept; /// Clears the simulation trigger handler of \p this object. /// /// The function assigns \c rosa::deluxe::DeluxeSensor::SFP with \c nullptr. void clearSimulationDataSource(void) noexcept; /// Tells whether a simulation trigger handler is set for \p this object. /// /// The function returns whether \c rosa::deluxe::DeluxeSensor::SFP is not /// \c nullptr. /// /// \return if a simulation trigger handler is set for \p this object. bool simulationDataSourceIsSet(void) const noexcept; /// Registers a simulation data source for \p this object. /// /// A new simulation trigger handler wrapping \p SF is stored in /// \c rosa::deluxe::DeluxeSensor::SFP by overwriting any already registered /// simulation data source. /// /// \todo Enforce SF does not potentially throw exception. /// /// \tparam Ts types of elements of values provided by \p SF /// /// \param SF function to generate value with /// /// \pre \p Ts... match \c rosa::deluxe::DeluxeSensor::OutputType: \code /// OutputType == TypeToken::Value /// \endcode template void registerSimulationDataSource( std::function(void)> &&SF) noexcept; private: /// Sends a value to the *master* of \p this object. /// /// \p Value is getting sent to \c rosa::deluxe::DeluxeSensor::Master if it /// contains a valid handle for a \c rosa::deluxe::DeluxeAgent. The function /// does nothing otherwise. /// /// 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::DeluxeSensor::OutputType: \code /// OutputType == TypeToken::Value /// \endcode template void sendToMaster(const DeluxeTuple &Value, Seq) noexcept; /// Handles master-input and generates the next sensory value upon trigger /// from the system. /// /// Executes \c rosa::deluxe::DeluxeSensor::MFP for processing master-input /// and data generating function \c rosa::deluxe::DeluxeSensor::FP or \c /// rosa::deluxe::DeluxeSensor::SFP if set. /// /// \note The only argument is a \c rosa::AtomConstant, hence its actual /// value is ignored. /// /// \pre Master-input is supposed to be completely received upon triggering: /// \code /// MasterInputNextPos == 0 /// \endcode void handleTrigger(atoms::Trigger) noexcept; /// Stores a new input value from the *master*. /// /// The function stores \p Value at position \p Pos in \c /// rosa::deluxe::DeluxeSensor::MasterInputValue and also sets the /// flag \c rosa::deluxe::DeluxeSensor::MasterInputChanged. The function also /// takes care of checking and updating \c /// rosa::deluxe::DeluxeSensor::MasterInputNextPos: increments its value and /// resets it to `0` when the last element is received. /// /// \note Utilized by member functions of group \c /// DeluxeSensorMasterInputHandlers. /// /// \tparam T type of input to store /// /// \param Id unique identifier of the *master* /// \param Pos position of the value in the \c rosa::deluxe::DeluxeTuple /// \param Value the input value to store /// /// \pre The *master* with \p Id is registered, \p Pos is the expected /// position of master-input, and the input from the *master* at position \p /// Pos is expected to be of type \p T: \code /// Master && masterId() == Id && Pos == MasterInputNextPos && /// typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf::Value /// \endcode template void saveMasterInput(id_t Id, token_size_t Pos, T Value) noexcept; /// \defgroup DeluxeSensorMasterInputHandlers Master-input handlers of /// rosa::deluxe::DeluxeSensor /// /// Definition of member functions handling messages from the *master* with /// different types of input /// /// A *slave* generally needs to be prepared to deal with values of any /// built-in type to handle messages from its *master*. Each type requires a /// separate message handler, which are implemented by these functions. The /// functions instantiate \c rosa::deluxe::DeluxeSensor::saveMasterInput with /// the proper template argument and pass the content of the message on for /// processing. /// /// \note The member functions in this group are defined by \c /// DSMASTERHANDLERDEF. /// /// \note Keep these definitions in sync with \c rosa::BuiltinTypes. /// ///@{ DSMASTERHANDLERDEF(AtomValue) DSMASTERHANDLERDEF(int16_t) DSMASTERHANDLERDEF(int32_t) DSMASTERHANDLERDEF(int64_t) DSMASTERHANDLERDEF(int8_t) DSMASTERHANDLERDEFN(long double, long_double) DSMASTERHANDLERDEFN(std::string, std__string) DSMASTERHANDLERDEF(uint16_t) DSMASTERHANDLERDEF(uint32_t) DSMASTERHANDLERDEF(uint64_t) DSMASTERHANDLERDEF(uint8_t) DSMASTERHANDLERDEF(unit_t) DSMASTERHANDLERDEF(bool) DSMASTERHANDLERDEF(double) DSMASTERHANDLERDEF(float) /// @} }; template DeluxeSensor::H DeluxeSensor::triggerHandlerFromProcessingFunction( std::function, bool>)> &&MF, Seq) noexcept { using MT = DeluxeTuple; STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments"); ASSERT(MasterInputType == MT::TT); // NOTE: Clang 6 warns about unused lambda captures; we suppress that // warning (those variables need to be captured). #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-lambda-capture" #endif // defined __clang__ return [ this, MF ](void) noexcept { // Do not do anything for master-input type \c // rosa::deluxe::EmptyDeluxeTuple. if constexpr (!std::is_same::value) { LOG_TRACE_STREAM << "DeluxeSensor " << FullName << " handles master-input." << std::endl; // The assert must hold if \p this object was successfuuly constructed. ASSERT((true && ... && (static_cast(static_cast(S0)) == S0))); const auto MasterInputArg = std::make_pair( // Get all elements of the tuple in a fold expression. DeluxeTuple(*static_cast( MasterInputValue->pointerTo(static_cast(S0)))...), MasterInputChanged); MasterInputChanged = false; MF(MasterInputArg); } }; #ifdef __clang__ #pragma clang diagnostic pop #endif // defined __clang__ } template DeluxeSensor::H DeluxeSensor::triggerHandlerFromDataSource(std::function &&F, bool inSimulation) noexcept { STATIC_ASSERT(IsDeluxeTuple::Value, "not tuple type argument"); ASSERT(OutputType == T::TT); return [ this, F, inSimulation ](void) noexcept { // Get value and send it to master only if \p ExecutionPolicy allows it. if (ExecutionPolicy->shouldProcess({})) { LOG_TRACE_STREAM << "DeluxeSensor " << Name << " obtains next value." << std::endl; sendToMaster(F(), seq_t()); } else { LOG_TRACE_STREAM << "DeluxeSensor " << Name << " skips next value." << std::endl; if (inSimulation) { // But read input value in Simulation anyway as input values are // provided for the highest execution frequency for simulation - F(); + (void)F(); } } }; } template DeluxeSensor::DeluxeSensor(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, std::function)> &&MF, std::function &&F) noexcept : Agent(Kind, Id, Name, S, THISMEMBER(handleTrigger), DSMASTERHANDLERREF(AtomValue), DSMASTERHANDLERREF(int16_t), DSMASTERHANDLERREF(int32_t), DSMASTERHANDLERREF(int64_t), DSMASTERHANDLERREF(int8_t), DSMASTERHANDLERREF(long_double), DSMASTERHANDLERREF(std__string), DSMASTERHANDLERREF(uint16_t), DSMASTERHANDLERREF(uint32_t), DSMASTERHANDLERREF(uint64_t), DSMASTERHANDLERREF(uint8_t), DSMASTERHANDLERREF(unit_t), DSMASTERHANDLERREF(bool), DSMASTERHANDLERREF(double), DSMASTERHANDLERREF(float)), ExecutionPolicy(DeluxeExecutionPolicy::decimation(1)), OutputType(T::TT), - MasterInputType(MT::TT), MasterInputChanged(false), + MasterInputType(MT::TT), MasterInputNextPos(0), MasterInputChanged(false), MasterInputValue(new typename TokenizedStorageForTypeList< typename UnwrapDeluxeTuple::Type>::Type()), MFP(triggerHandlerFromProcessingFunction(std::move(MF), seq_t())), FP(triggerHandlerFromDataSource(std::move(F), false)), SFP(nullptr) { ASSERT(Kind == atoms::SensorKind); LOG_TRACE_STREAM << "DeluxeSensor " << FullName << " is created." << std::endl; ASSERT(inv()); } template void DeluxeSensor::registerSimulationDataSource( std::function(void)> &&SF) noexcept { ASSERT(OutputType == TypeToken::Value); SFP = triggerHandlerFromDataSource(std::move(SF), true); ASSERT(inv()); } template void DeluxeSensor::sendToMaster(const DeluxeTuple &Value, Seq) noexcept { STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments"); ASSERT(OutputType == TypeToken::Value); // The assert must hold if \p this object was successfuuly constructed. ASSERT((true && ... && (static_cast(static_cast(S0)) == S0))); // Create a static constant array for these indices to be available as lvalue // references when creating messages below. \c S0... when used directly in a // fold expression is a temporary value, which would result in \c // rosa::Message instances being created with rvalue references. Further, all // other values would to copied into a temporary variable for making them /// available as rvalue references (they are constant lvalue references here). static constexpr std::array Indices{{S0...}}; LOG_TRACE_STREAM << "DeluxeSensor " << FullName << "(" << Id << ") sends to master(" << static_cast(Master && *Master) << "): " << Value << 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 DeluxeSensor::saveMasterInput(id_t Id, token_size_t Pos, T Value) noexcept { ASSERT(Master && masterId() == Id && Pos == MasterInputNextPos && typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf::Value); LOG_TRACE_STREAM << "DeluxeSensor " << 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; } } // End namespace deluxe } // End namespace rosa #undef DSMASTERHANDLEREF #undef DSMASTERHANDLEDEF #undef DSMASTERHANDLEDEFN #undef DSMASTERHANDLENAME #endif // ROSA_DELUXE_DELUXESENSOR_HPP diff --git a/include/rosa/deluxe/DeluxeSystem.hpp b/include/rosa/deluxe/DeluxeSystem.hpp old mode 100755 new mode 100644 index f39ce5a..2364d2b --- a/include/rosa/deluxe/DeluxeSystem.hpp +++ b/include/rosa/deluxe/DeluxeSystem.hpp @@ -1,240 +1,246 @@ //===-- rosa/deluxe/DeluxeSystem.hpp ----------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeSystem.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Specialization of \c rosa::MessagingSystem for the *deluxe /// interface*. /// /// \see \c rosa::deluxe::DeluxeContext /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXESYSTEM_HPP #define ROSA_DELUXE_DELUXESYSTEM_HPP #include "rosa/core/MessagingSystem.hpp" #include "rosa/deluxe/DeluxeAgent.hpp" #include "rosa/deluxe/DeluxeSensor.hpp" namespace rosa { namespace deluxe { /// Implements and extends the \c rosa::MessagingSystem interface to be /// used by \c rosa::deluxe::DeluxeContext. /// /// The class is a specialization of \c rosa::MessagingSystem, where objects /// of two specialized subtypes of \c rosa::Agent, \c rosa::deluxe::DeluxeSensor /// and \c rosa::deluxe::DeluxeAgent, constitute a system. The class extends the /// \c rosa::MessagingSystem interface with features required to implement the /// *deluxe interface*. /// /// \see rosa::deluxe::DeluxeContext class DeluxeSystem : public MessagingSystem { friend class DeluxeContext; friend class DeluxeExecutionPolicy; public: /// Returns an object implementing the \c rosa::deluxe::DeluxeSystem /// interface. /// /// \param Name name of the new instance /// /// \return \c std::unique_ptr for the new instance of /// \c rosa::DeluxeSystem static std::unique_ptr createSystem(const std::string &Name) noexcept; protected: /// Creates a new instance. /// /// \note Protected constructor restricts instantiation for subclasses. DeluxeSystem(void) noexcept = default; public: /// Creates a \c rosa::deluxe::DeluxeSensor instance owned by \p this object /// and returns a \p rosa::AgentHandle for it. /// /// \tparam MT type of master-input the new \c rosa::deluxe::DeluxeSensor /// receives /// \tparam T type of data the new \c rosa::deluxe::DeluxeSensor operates on /// /// \note Type arguments \p MT and \p T must be instances of \c /// rosa::deluxe::DeluxeTuple. /// /// \param Name name of the new \c rosa::deluxe::DeluxeSensor /// \param MF function to process master-input values /// \param F function to generate the next value with during normal operation /// /// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor. /// /// \return \c rosa::AgentHandle for new \c rosa::deluxe::DeluxeSensor template AgentHandle createSensor(const std::string &Name, std::function)> &&MF, std::function &&F) noexcept; /// Creates a \c rosa::deluxe::DeluxeAgent instance owned by \p this object /// and returns a \c rosa::AgentHandle for it. /// /// \tparam MT type of master-input the new \c rosa::deluxe::DeluxeAgent /// receives /// \tparam T type of data the new \c rosa::deluxe::DeluxeAgent outputs /// \tparam Ts types of master-output the new \c rosa::deluxe::DeluxeAgent /// produces /// \tparam As types of inputs the new \c rosa::deluxe::DeluxeAgent takes /// /// \note Type arguments \p MT, \p T, \p Ts..., and \p As... must be /// instances of \c rosa::deluxe::DeluxeTuple. /// /// \param Name name of the new \c rosa::deluxe::DeluxeAgent /// \param MF function for the new \c rosa::deluxe::DeluxeAgent to process /// master-input values and generate master-output with /// \param F function for the new \c rosa::deluxe::DeluxeAgent to process /// input values and generate output and master-output with /// /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. /// /// \return \c rosa::AgentHandle for new \c rosa::deluxe::DeluxeAgent template AgentHandle createAgent( const std::string &Name, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept; protected: /// Tells whether a \c rosa::AgentHandle refers to a /// \c rosa::deluxe::DeluxeSensor owned by \p this object. /// /// \param H \c rosa::AgentHandle to check /// /// \return whether \p H refers to a \c rosa::deluxe::DeluxeSensor owned by /// \p this object virtual bool isDeluxeSensor(const AgentHandle &H) const noexcept = 0; /// Extracts a const qualified \c rosa::deluxe::DeluxeSensor reference from a /// const qualified \c rosa::AgentHandle if possible. /// /// The function returns a \c rosa::Optional object containing a const /// qualified reference to a \c rosa::deluxe::DeluxeSensor object extracted /// from a const qualified \c rosa::AgentHandle instance if the referred /// object is of type \c rosa::deluxeDeluxeSensor and owned by \p this object. /// The returned \c rosa::Optional object is empty otherwise. /// /// \see rosa::deluxe::DeluxeSystem::isDeluxeSensor /// /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeSensor /// from /// /// \return const qualified reference to \c rosa::deluxe::DeluxeSensor if /// \p H refers to an object which is of that type and is owned by \p this /// object Optional getDeluxeSensor(const AgentHandle &H) const noexcept; /// Extracts a \c rosa::deluxe::DeluxeSensor reference from a /// \c rosa::AgentHandle if possible. /// /// The function returns a \c rosa::Optional object containing a reference to /// a \c rosa::deluxe::DeluxeSensor object extracted from a /// \c rosa::AgentHandle instance if the referred object is of type /// \c rosa::deluxeDeluxeSensor and owned by \p this object. The returned /// \c rosa::Optional object is empty otherwise. /// /// \see rosa::deluxe::DeluxeSystem::isDeluxeSensor /// /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeSensor /// from /// /// \return reference to \c rosa::deluxe::DeluxeSensor if \p H refers to an /// object which is of that type and is owned by \p this object Optional getDeluxeSensor(AgentHandle &H) const noexcept; /// Tells whether a \c rosa::AgentHandle refers to a /// \c rosa::deluxe::DeluxeAgent owned by \p this object. /// /// \param H \c rosa::AgentHandle to check /// /// \return whether \p H refers to a \c rosa::deluxe::DeluxeAgent owned by /// \p this object virtual bool isDeluxeAgent(const AgentHandle &H) const noexcept = 0; /// Extracts a const qualified \c rosa::deluxe::DeluxeAgent reference from a /// const qualified \c rosa::AgentHandle if possible. /// /// The function returns a \c rosa::Optional object containing a const /// qualified reference to a \c rosa::deluxe::DeluxeAgent object extracted /// from a const qualified \c rosa::AgentHandle instance if the referred /// object is of type \c rosa::deluxeDeluxeAgent and owned by \p this object. /// The returned \c rosa::Optional object is empty otherwise. /// /// \see rosa::deluxe::DeluxeSystem::isDeluxeAgent /// /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeAgent /// from /// /// \return const qualified reference to \c rosa::deluxe::DeluxeAgent if \p H /// refers to an object which is of that type and is owned by \p this object Optional getDeluxeAgent(const AgentHandle &H) const noexcept; /// Extracts a \c rosa::deluxe::DeluxeAgent reference from a /// \c rosa::AgentHandle if possible. /// /// The function returns a \c rosa::Optional object containing a reference to /// a \c rosa::deluxe::DeluxeAgent object extracted from a /// \c rosa::AgentHandle instance if the referred object is of type /// \c rosa::deluxeDeluxeAgent and owned by \p this object. The returned /// \c rosa::Optional object is empty otherwise. /// /// \see rosa::deluxe::DeluxeSystem::isDeluxeAgent /// /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeAgent /// from /// /// \return reference to \c rosa::deluxe::DeluxeAgent if \p H refers to an /// object which is of that type and is owned by \p this object Optional getDeluxeAgent(AgentHandle &H) const noexcept; }; template AgentHandle DeluxeSystem::createSensor(const std::string &Name, std::function)> &&MF, std::function &&F) noexcept { Agent &DS = createUnit( [&](const id_t Id, MessagingSystem &S) { return new DeluxeSensor(atoms::SensorKind, Id, Name, S, std::move(MF), std::move(F)); }); return {DS}; } template AgentHandle DeluxeSystem::createAgent( const std::string &Name, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept { Agent &DA = createUnit( [&](const id_t Id, DeluxeSystem &S) { return new DeluxeAgent(atoms::AgentKind, Id, Name, S, std::move(MF), std::move(F)); }); return {DA}; } } // End namespace deluxe } // End namespace rosa -#endif // ROSA_LIB_DELUXE_DELUXESYSTEM_HPP +#endif // ROSA_DELUXE_DELUXESYSTEM_HPP diff --git a/include/rosa/deluxe/DeluxeTuple.hpp b/include/rosa/deluxe/DeluxeTuple.hpp index e57d29b..5e999e0 100644 --- a/include/rosa/deluxe/DeluxeTuple.hpp +++ b/include/rosa/deluxe/DeluxeTuple.hpp @@ -1,359 +1,425 @@ //===-- rosa/deluxe/DeluxeTuple.hpp -----------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeTuple.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2019 /// /// \brief Facilities for handling multiple input/output values for connections /// in the *deluxe interface*. /// /// \see \c rosa::deluxe::DeluxeContext /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXETUPLE_HPP #define ROSA_DELUXE_DELUXETUPLE_HPP #include "rosa/support/sequence.hpp" #include "rosa/support/type_token.hpp" #include #include namespace rosa { namespace deluxe { /// A tuple to manage multiple input/output values in the *deluxe interface*. /// /// \tparam Ts types of elements of the tuple /// /// \note The template may be instantiated only with built-in types and the /// number of those type may not exceed the capacity of a \c rosa::Token. template struct DeluxeTuple : public std::tuple { // Statically enforce that the class template is instantiated only with // built-in types. STATIC_ASSERT((TypeListSubsetOf, BuiltinTypes>::Value), "not built-in types"); // Statically enforce that the class template is instantiated with not too // many types. // \note Instantiation would fail on \c rosa::deluxe::DeluxeTuple::TT if there - // are too any types; this assertion is for more readable error reporting. + // are too many types; this assertion is for more readable error reporting. STATIC_ASSERT(sizeof...(Ts) <= token::MaxTokenizableListSize, "Too many types"); /// How many elements the instance has. static constexpr token_size_t Length = sizeof...(Ts); /// What types the class contains. /// /// Type information encoded as \c rosa::Token. static constexpr Token TT = TypeToken::Value; /// Default constructor, zero-initializes elements. DeluxeTuple(void) = default; /// Constructor, initializes the underlying \c std::tuple with lvalue /// references. /// /// \param Args value references to the values to store DeluxeTuple(const std::decay_t &... Args) : std::tuple(Args...) {} /// Constructor, initializes the underlying \c std::tuple with rvalue /// references. /// /// \param Args rvalue references to the values to store DeluxeTuple(std::decay_t &&... Args) : std::tuple(std::move(Args)...) {} + /// Contructor, initializes the underlying \c std::tuple from another matching + /// \c std::tuple. + DeluxeTuple(const std::tuple &Args) : std::tuple(Args) {} + /// Default copy-constructor. DeluxeTuple(const DeluxeTuple &) = default; /// Default move-constructor. DeluxeTuple(DeluxeTuple &&) = default; /// Default copy-assignment. DeluxeTuple &operator=(const DeluxeTuple &) = default; /// Default move-assignment. DeluxeTuple &operator=(DeluxeTuple &&) = default; private: /// Dumps \p this object to a given \c std::ostream. /// /// \note Provides implementation for \c rosa::deluxe::DeluxeTuple::dump. /// /// \tparam S0 Indices for accessing elements. /// /// \param [in,out] OS output stream to dump to /// /// \note The second argument provides indices statically as template /// arguments \p S0..., so its actual value is ignored. /// /// \pre Statically, \p S0... matches number of types \p this object was /// created: \code /// sizeof...(S0) == sizeof...(Ts) /// \endcode template void dump(std::ostream &OS, Seq) const noexcept; public: /// Dumps \p this object to a given \c std::ostream. /// /// \param [in,out] OS output stream to dump to void dump(std::ostream &OS) const noexcept; }; template template void DeluxeTuple::dump(std::ostream &OS, Seq) const noexcept { STATIC_ASSERT(sizeof...(S0) == sizeof...(Ts), "inconsistent type arguments"); // Convert value to std::string with std::to_string except for a value of // std::string that does not need conversion. auto dump_to_string = [](const auto &V) { if constexpr (std::is_same, std::string>::value) { return V; } else { return std::to_string(V); } }; OS << "{"; (OS << ... << (" " + dump_to_string(std::get(*this)))); OS << " }"; } template void DeluxeTuple::dump(std::ostream &OS) const noexcept { dump(OS, seq_t()); } /// Type alias for a \c rosa::deluxe::DeluxeTuple that contains no elements. using EmptyDeluxeTuple = DeluxeTuple<>; /// Template specialization for \c rosa::deluxe::EmptyDeluxeTuple. template <> struct DeluxeTuple<> : public std::tuple<> { /// How many elements the instance has. static constexpr token_size_t Length = 0; /// What types the class contains. /// /// Type information encoded as \c rosa::Token. static constexpr Token TT = TypeToken<>::Value; /// Constructor, initializes the underlying \c std::tuple. DeluxeTuple(void) : std::tuple<>() {} /// Default copy-constructor. DeluxeTuple(const DeluxeTuple &) = default; // Default move-constructor. DeluxeTuple(DeluxeTuple &&) = default; /// Default copy-assignment. DeluxeTuple &operator=(const DeluxeTuple &) = default; // Default move-assignment, DeluxeTuple &operator=(DeluxeTuple &&) = default; /// Dumps \p this object to a given \c std::ostream. /// /// \param [in,out] OS output stream to dump to static void dump(std::ostream &OS) noexcept; }; /// Creates a \c rosa::deluxe::DeluxeTuple instance from the given lvalues /// references. /// /// \tparam Ts types of elements of the tuple /// /// \see \c rosa::deluxe::DeluxeTuple /// /// \param Args values to store in the tuple /// /// \return an instance of \c rosa::deluxe::DeluxeTuple with \p Args as /// elements template inline DeluxeTuple make_deluxe_tuple(const Ts &... Args) noexcept { return DeluxeTuple(Args...); } /// Creates a \c rosa::deluxe::DeluxeTuple instance from the given rvalue /// references. /// /// \tparam Ts types of elements of the tuple /// /// \see \c rosa::deluxe::DeluxeTuple /// /// \param Args values to store in the tuple /// /// \return an instance of \c rosa::deluxe::DeluxeTuple with \p Args as /// elements template inline DeluxeTuple make_deluxe_tuple(Ts&&... Args) noexcept { return DeluxeTuple(std::move(Args)...); } +/// Creates a \c rosa::deluxe::DeluxeTuple instance from the given \c std::tuple +/// reference. +/// +/// \tparam Ts types of elements of the tuple +/// +/// \see \c rosa::deluxe::DeluxeTuple +/// +/// \param Args values to store in the tuple +/// +/// \return an instance of \c rosa::deluxe::DeluxeTuple with \p Args as +/// elements +template +inline DeluxeTuple +make_deluxe_tuple(const std::tuple &Args) noexcept { + return DeluxeTuple(Args); +} + /// \defgroup UnwrapDeluxeTuple Implementation of /// rosa::deluxe::UnwrapDeluxeTuple /// /// \brief Unwraps element types from an instance of \c /// rosa::deluxe::DeluxeTuple into a \c rosa::TypeList /// /// Types can be unwrapped from a \c rosa::deluxe::DeluxeTuple instance as \code /// typename UnwrapDeluxeTuple::Type /// \endcode /// /// For example, the following expression evaluates to `true`: \code /// std::is_same>::Type, /// TypeList>::value /// \endcode ///@{ /// Declaration of the template. /// /// \tparam Tuple \c rosa::deluxe::DeluxeTuple to unwrap template struct UnwrapDeluxeTuple; /// Implementation of the template for \c rosa::deluxe::DeluxeTuple instances. template struct UnwrapDeluxeTuple> { using Type = TypeList; }; ///@} -/// \defgroup TypeListUnwrapDeluxeTuple Implementation of -/// \c rosa::deluxe::TypeListUnwrapDeluxeTuple +///@} + +/// \defgroup IsTuple Implementation of \c rosa::deluxe::IsTuple /// -/// \brief Unwraps element types from instances of \c -/// rosa::deluxe::DeluxeTuple in a \c rosa::TypeList. +/// \brief Tells if a type is a tuple as in it can be converted to \c +/// rosa::deluxe::DeluxeTuple. /// -/// Types can be unwrapped from \c rosa::deluxe::DeluxeTuple instances as \code -/// typename TypeListUnwrapDeluxeTuple::Type -/// \endcode +/// \see \c rosa::deluxe::MatchingDeluxeTuple /// -/// For example, the following expression evaluates to `true`: \code -/// std::is_same< -/// typename TypeListUnwrapDeluxeTuple, -/// T3>>::Type, -/// TypeList -/// >::value +/// Whether a type \c T is a tuple can be checked as \code +/// IsTuple::Value /// \endcode ///@{ /// Declaration of the template. /// -/// \tparam List \c rosa::TypeList to check -template struct TypeListUnwrapDeluxeTuple; +/// \tparam T type to check +template struct IsTuple; -/// Specialization for \c rosa::EmptyTypeList. -template <> struct TypeListUnwrapDeluxeTuple { - using Type = EmptyTypeList; +/// Specialization for the case when the type is an instance of \c std::tuple. +template struct IsTuple> { + static constexpr bool Value = true; }; -/// Specialization for the case when the first type in \p List is an instance of -/// \c rosa::deluxe::DeluxeTuple. -template -struct TypeListUnwrapDeluxeTuple, Ts...>> { - using Type = typename TypeListConcat< - typename UnwrapDeluxeTuple>::Type, - typename TypeListUnwrapDeluxeTuple>::Type>::Type; +/// Specialization for the case when the type is an instance of \c std::tuple. +template struct IsTuple> { + static constexpr bool Value = true; }; -/// Implementation for a general first type in \p List. -template -struct TypeListUnwrapDeluxeTuple> { - using Type = typename TypeListPush< - T, typename TypeListUnwrapDeluxeTuple>::Type>::Type; -}; +/// Implementation for a general case of type \p T. +template struct IsTuple { static constexpr bool Value = false; }; ///@} /// \defgroup IsDeluxeTuple Implementation of \c rosa::deluxe::IsDeluxeTuple /// /// \brief Tells if a type is an instance of \c rosa::deluxe::DeluxeTuple. /// /// Whether a type \c T is an instance of \c rosa::deluxe::DeluxeTuple can be /// checked as \code /// IsDeluxeTuple::Value /// \endcode +/// +/// \note `!IsDeluxeTuple::Value || IsTuple::Value` ///@{ /// Declaration of the template. /// /// \tparam T type to check template struct IsDeluxeTuple; /// Specialization for the case when the type is an instance of \c /// rosa::deluxe::DeluxeTuple. template struct IsDeluxeTuple> { static constexpr bool Value = true; }; /// Implementation for a general case of type \p T. template struct IsDeluxeTuple { static constexpr bool Value = false; }; ///@} +/// \defgroup MatchingDeluxeTuple Implementation of \c +/// rosa::deluxe::MatchingDeluxeTuple +/// +/// \brief Gives the \c rosa::deluxe::DeluxeTuple type that matches a given +/// tuple type. +/// +/// The matching \c rosa::deluxe::DeluxeTuple type for a tuple type \p T can be +/// obtained as \code +/// typename MatchingDeluxeTuple::Type +/// \endcode +/// If \p T is \c rosa::deluxe::DeluxeTuple, the matching type is \p T itself. +/// If \p T is \c std::tuple, the matching type if \c rosa::deluxe::DeluxeTuple +/// with the same type parameters as \p T. +/// \c rosa::deluxe::MatchingDeluxeTuple is not defined for other type +/// parameters. +/// +/// \note The template is defined for type \p T only if +/// `rosa::deluxe::IsTuple::Value`. Values of such types can be used to +/// construct a new instance of the class \c rosa::deluxe::DeluxeTuple. +/// +///\see \c rosa::deluxe::IsTuple +///@{ + +/// Declaration of the template. +/// +/// \tparam T type to check +template struct MatchingDeluxeTuple; + +/// Specialization for the case when the type is an instance of \c +/// rosa::deluxe::DeluxeTuple. +template struct MatchingDeluxeTuple> { + using Type = DeluxeTuple; +}; + +/// Specialization for the case when the type is an instance of \c +/// std::tuple. +template struct MatchingDeluxeTuple> { + using Type = DeluxeTuple; +}; + +///@} + +/// Convenience template type alias for easy use of \c +/// rosa::deluxe::MatchingDeluxeTuple. +/// +/// Converts a tuple type to the matching \c rosa::deluxe::DeluxeTuple type. +/// +/// \tparam Tuple type to convert +template +using matching_deluxe_tuple_t = typename MatchingDeluxeTuple::Type; + /// \defgroup TypeListAllDeluxeTuple Implementation of /// \c rosa::deluxe::TypeListAllDeluxeTuple /// /// \brief Tells if all types in a \c rosa::TypeList is an instance of \c /// rosa::deluxe::DeluxeTuple. /// /// Whether a \c rosa::TypeList \c List contains instances of \c /// rosa::deluxe::DeluxeTuple only can be checked as \code /// TypeListAllDeluxeTuple::Value /// \endcode ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to check template struct TypeListAllDeluxeTuple; /// Specialization for \c rosa::EmptyTypeList. template <> struct TypeListAllDeluxeTuple { static constexpr bool Value = true; }; /// Implementation for the general case when there is at leasst one element in /// the list. template struct TypeListAllDeluxeTuple> { static constexpr bool Value = IsDeluxeTuple::Value && TypeListAllDeluxeTuple>::Value; }; ///@} } // End namespace deluxe } // End namespace rosa namespace std { /// Dumps a \c rosa::deluxe::Deluxe instance to a given \c std::ostream. /// /// \param [in,out] OS output stream to dump to /// \param Tuple \c rosa::deluxe::Deluxe to dump /// /// \return \p OS after dumping \p Tuple to it template ostream &operator<<(ostream &OS, const rosa::deluxe::DeluxeTuple &Tuple) { Tuple.dump(OS); return OS; } } // End namespace std #endif // ROSA_DELUXE_DELUXETUPLE_HPP diff --git a/include/rosa/deluxe/namespace.h b/include/rosa/deluxe/namespace.h index 58f8e7b..d28e32c 100755 --- a/include/rosa/deluxe/namespace.h +++ b/include/rosa/deluxe/namespace.h @@ -1,32 +1,38 @@ //===-- rosa/deluxe/namespace.h ---------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/namespace.h /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Documentation for the namespace \c rosa::deluxe. /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_NAMESPACE_H #define ROSA_DELUXE_NAMESPACE_H namespace rosa { /// Contains a simplified, limited in functionality but flexible in modularity, /// interface to define and easily reconfigure agent systems. /// /// **Deluxe Interface** /// /// TODO write this description here! namespace deluxe {} } // End namespace rosa #endif // ROSA_DELUXE_NAMESPACE_H diff --git a/include/rosa/support/atom.hpp b/include/rosa/support/atom.hpp index 1dfe3e0..4746211 100644 --- a/include/rosa/support/atom.hpp +++ b/include/rosa/support/atom.hpp @@ -1,209 +1,214 @@ //===-- rosa/support/atom.hpp -----------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/support/atom.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Facility for *atoms*, short strings statically encoded as integers. /// /// \note This implementation is based on the \c atom implementation of CAF. -/// \todo Check license. /// /// *Atoms* can be used to turn short string literals into statically generated /// types. The literals may consist of at most \c 10 non-special characters, /// legal characters are \c _0-9A-Za-z and the whitespace character. Special /// characters are turned into whitespace, which may result in different string /// literals being encoded into the same integer value, if any of those contain /// at least one special character. /// /// \note The usage of special characters in the string literals used to create /// *atoms* cannot be checked by the compiler. /// /// Example: /// /// \code /// constexpr AtomValue NameValue = atom("name"); /// using NameAtom = AtomConstant; /// /// [](NameAtom){ std::cout << "Argument of type NameAtom"; }(NameAtom::Value) /// \endcode /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_ATOM_HPP #define ROSA_SUPPORT_ATOM_HPP #include "rosa/support/debug.hpp" namespace rosa { /// Maximal length of valid atom strings. constexpr size_t MaxAtomLength = 10; /// Underlying integer type of atom values. using atom_t = uint64_t; /// Turn \c rosa::atom_t into a strongly typed enumeration. /// /// Values of \c rosa::atom_t casted to \c rosa::AtomValue may be used in a /// type-safe way. enum class AtomValue : atom_t {}; /// Anonymous namespace with implementational details, consider it private. namespace { // clang-format off /// Encodes ASCII characters to 6-bit encoding. constexpr unsigned char AtomEncodingTable[] = { /* ..0 ..1 ..2 ..3 ..4 ..5 ..6 ..7 ..8 ..9 ..A ..B ..C ..D ..E ..F */ /* 0.. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1.. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2.. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 3.. */ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0, 0, /* 4.. */ 0, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, /* 5.. */ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 0, 0, 0, 0, 37, /* 6.. */ 0, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, /* 7.. */ 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 0, 0, 0, 0, 0}; // clang-format on /// Decodes 6-bit characters to ASCII constexpr char AtomDecodingTable[] = " 0123456789" "ABCDEFGHIJKLMNOPQRSTUVWXYZ_" "abcdefghijklmnopqrstuvwxyz"; /// Encodes one character and updates the integer representation. /// /// \param Current an encoded value /// \param CharCode a character to add to \p Current /// /// \return \p Current updated with \p CharCode constexpr atom_t nextInterim(atom_t Current, size_t CharCode) { return (Current << 6) | AtomEncodingTable[(CharCode <= 0x7F) ? CharCode : 0]; } /// Encodes a C-string into an integer value to be used as \c rosa::AtomValue. /// /// \param CStr a string to encode /// \param Interim encoded value to add \p CStr to it /// /// \return \p Interim updated with \p CStr constexpr atom_t atomValue(const char *CStr, atom_t Interim = 0xF) { return (*CStr == '\0') ? Interim : atomValue(CStr + 1, nextInterim(Interim, static_cast(*CStr))); } } // End namespace /// Converts a \c std::string into a \c rosa::AtomValue. /// /// \param S \c std::string to convert /// /// \return \c rosa::AtomValue representing \p S AtomValue atom_from_string(const std::string &S); /// Converts a string-literal into a \c rosa::AtomValue. /// /// \tparam Size the length of \p Str /// /// \param Str the string-literal to convert /// /// \return \c rosa::AtomValue representating \p Str /// /// \pre \p Str is not too long:\code /// Size <= MaxAtomLength + 1 /// \endcode template constexpr AtomValue atom(char const (&Str)[Size]) { // Last character is the NULL terminator. STATIC_ASSERT(Size <= MaxAtomLength + 1, "Too many characters in atom definition"); return static_cast(atomValue(Str)); } /// Lifts a \c rosa::AtomValue to a compile-time constant. /// /// \tparam V \c rosa::AtomValue to lift template struct AtomConstant { /// Constructor has to do nothing. constexpr AtomConstant(void) {} /// Returns the wrapped value. /// /// \return \p V constexpr operator AtomValue(void) const { return V; } /// Returns the wrapped value as of type \c rosa::atom_t. /// /// \return \c rosa::atom_t value from \p V static constexpr atom_t value() { return static_cast(V); } /// An instance *of this constant* (*not* a \c rosa::AtomValue). static const AtomConstant Value; }; // Implementation of the static member field \c rosa::AtomConstant::Value. template const AtomConstant AtomConstant::Value = AtomConstant{}; } // End namespace rosa namespace std { /// Converts a \c rosa::AtomValue into \c std::string. /// /// \param What value to convert /// /// \return \c std::string encoded in \p What string to_string(const rosa::AtomValue &What); /// Dumps a \c rosa::AtomValue to a given \c std::ostream. /// /// \param [in,out] OS output stream to dump to /// \param A \c rosa::AtomValue to dump /// /// \return \p OS after dumping \p N to it inline ostream &operator<<(ostream &OS, const rosa::AtomValue &A) { OS << to_string(A); return OS; } /// Converts a \c rosa::AtomConstant into \c std::string. /// /// \tparam V \c rosa::AtomValue to convert /// /// \note The actual argument of type `const rosa::AtomConstant` is ignored /// because the \c rosa::AtomValue to convert is encoded in the type itself. /// /// \return the original string encoded in \p V template string to_string(const rosa::AtomConstant &) { return to_string(V); } /// Dumps a \c rosa::AtomConstant to a given \c std::ostream. /// /// \tparam V the \c rosa::AtomValue to dump /// /// \param [in,out] OS output stream to dump to /// \param A \c rosa::AtomConstant providing \p V /// /// \return \p OS after dumping \p V to it template inline ostream &operator<<(ostream &OS, const rosa::AtomConstant &A) { (void)A; // Shut compiler about unused parameter. OS << to_string(V); return OS; } } // End namespace std #endif // ROSA_SUPPORT_ATOM_HPP diff --git a/include/rosa/support/csv/CSVReader.hpp b/include/rosa/support/csv/CSVReader.hpp index 0491c17..18bd220 100755 --- a/include/rosa/support/csv/CSVReader.hpp +++ b/include/rosa/support/csv/CSVReader.hpp @@ -1,382 +1,494 @@ //===-- rosa/support/csv/CSVReader.hpp --------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/support/csv/CSVReader.hpp /// -/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// \authors David Juhasz (david.juhasz@tuwien.ac.at), Edwin Willegger (edwin.willegger@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Facitilities to read CSV files. /// /// \note The implementation is based on the solution at /// https://stackoverflow.com/a/1120224 /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_CSV_CSVREADER_HPP #define ROSA_SUPPORT_CSV_CSVREADER_HPP #include "rosa/support/debug.hpp" +#include "rosa/support/sequence.hpp" #include #include #include +#include +#include +#include namespace rosa { namespace csv { +/// Indicating it the CSV file contains any header or not +enum class HeaderInformation { + HasHeader, + HasNoHeader +}; + /// Anonymous namespace providing implementation details for /// \c rosa::csv::CSVIterator, consider it private. namespace { -/// Provides facility for parsing values from one row CSV data. +/// Provides facility for parsing one value from a string. /// -/// \tparam T type of values to parse from the line +/// \tparam T type of value to parse /// \tparam IsSignedInt if \p T is a signed integral type, always use default /// \tparam IsUnsignedInt if \p T is an unsigned integral type, always use /// default /// \tparam IsFloat if \p T is a floating-point type, always use default /// \tparam IsString if \p T is \c std::string, always use default /// -/// \note Specializations of this `struct` are provided for arithmentic types +/// \note Specializations of this struct are provided for arithmentic types /// and \c std::string. -template ::value && - std::is_signed::value), +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 CSVRowParser; - -/// Specialization for signed integral types. -/// -/// \tparam T type of values to parse from the line -/// -/// \pre \p T is a signed integral type:\code -/// std::is_integral::value && std::is_signed::value -/// \endcode -template struct CSVRowParser { - STATIC_ASSERT((std::is_integral::value && std::is_signed::value), - "wrong type"); // Sanity check. +struct ValueParser { - /// Parses a given row of CSV data into a given container. /// - /// \p Data is cleared and then filled with values parsed from \p LineStream. - /// Entries in the line are to be separated by commas, the character `,`. A - /// trailing comma results in an empty entry at the end of the line. No empty - /// entry should be present otherwise. /// - /// \note Parsed values are silently converted to type \p T. + /// \param Cell the \c std::string to parse /// - /// \param [in,out] LineStream the line to parse - /// \param [in,out] Data the container to store the parsed values - static void parse(std::stringstream &LineStream, std::vector &Data) { - std::string Cell; - Data.clear(); - while (std::getline(LineStream, Cell, ',')) { - Data.push_back(static_cast(std::stoll(Cell))); - } - // This checks for a trailing comma with no data after it. - if (!LineStream && Cell.empty()) { - // If there was a trailing comma then add an empty element. - Data.push_back(0); - } + /// \return the parsed value + /// + /// \note The function silently fails if cannot parse \p Cell for type \p T. + static T parse(const std::string &Cell) noexcept; +}; + +template +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)); } }; -/// Specialization for unsigned integral types. -/// -/// \tparam T type of values to parse from the line -/// -/// \pre \p T is an unsigned integral type:\code -/// std::is_integral::value && std::is_unsigned::value -/// \endcode -template struct CSVRowParser { +template +struct ValueParser { STATIC_ASSERT((std::is_integral::value && std::is_unsigned::value), "wrong type"); // Sanity check. - - /// Parses a given row of CSV data into a given container. - /// - /// \p Data is cleared and then filled with values parsed from \p LineStream. - /// Entries in the line are to be separated by commas, the character `,`. A - /// trailing comma results in an empty entry at the end of the line. No empty - /// entry should be present otherwise. - /// - /// \note Parsed values are silently converted to type \p T. - /// - /// \param [in,out] LineStream the line to parse - /// \param [in,out] Data the container to store the parsed values - static void parse(std::stringstream &LineStream, std::vector &Data) { - std::string Cell; - Data.clear(); - while (std::getline(LineStream, Cell, ',')) { - Data.push_back(static_cast(std::stoull(Cell))); - } - // This checks for a trailing comma with no data after it. - if (!LineStream && Cell.empty()) { - // If there was a trailing comma then add an empty element. - Data.push_back(0); - } + static T parse(const std::string &Cell) noexcept { + return static_cast(std::stoull(Cell)); } }; -/// Specialization for floating-point types. -/// -/// \tparam T type of values to parse from the line -/// -/// \pre \p T is a floating-point type:\code -/// std::is_floating_point::value -/// \endcode -template struct CSVRowParser { +template +struct ValueParser { STATIC_ASSERT((std::is_floating_point::value), "wrong type"); // Sanity check. - - /// Parses a given row of CSV data into a given container. - /// - /// \p Data is cleared and then filled with values parsed from \p LineStream. - /// Entries in the line are to be separated by commas, the character `,`. A - /// trailing comma results in an empty entry at the end of the line. No empty - /// entry should be present otherwise. - /// - /// \note Parsed values are silently converted to type \p T. - /// - /// \param [in,out] LineStream the line to parse - /// \param [in,out] Data the container to store the parsed values - static void parse(std::stringstream &LineStream, std::vector &Data) { - std::string Cell; - Data.clear(); - while (std::getline(LineStream, Cell, ',')) { - Data.push_back(static_cast(std::stold(Cell))); - } - // This checks for a trailing comma with no data after it. - if (!LineStream && Cell.empty()) { - // If there was a trailing comma then add an empty element. - Data.push_back(0); - } + static T parse(const std::string &Cell) noexcept { + return static_cast(std::stold(Cell)); } }; -/// Specialization for \c std::string. -/// -/// \tparam T type of values to parse from the line -/// -/// \pre \p T is \c std::string:\code -/// std::is_same::value -/// \endcode -template struct CSVRowParser { +template +struct ValueParser { STATIC_ASSERT((std::is_same::value), "wrong type"); // Sanity check. - - /// Parses a given row of CSV data into a given container. - /// - /// \p Data is cleared and then filled with values parsed from \p LineStream. - /// Entries in the line are to be separated by commas, the character `,`. A - /// trailing comma results in an empty entry at the end of the line. No empty - /// entry should be present otherwise. - /// - /// \param [in,out] LineStream the line to parse - /// \param [in,out] Data the container to store the parsed values - static void parse(std::stringstream &LineStream, std::vector &Data) { - std::string Cell; - Data.clear(); - while (std::getline(LineStream, Cell, ',')) { - Data.push_back(Cell); - } - // This checks for a trailing comma with no data after it. - if (!LineStream && Cell.empty()) { - // If there was a trailing comma then add an empty element. - Data.push_back(""); - } - } + static T parse(const std::string &Cell) noexcept { return Cell; } }; /// Parses and stores entries from a row of CSV data. /// -/// \tparam T type of values to parse and store, i.e. entries in the row +/// \tparam Ts types of values to parse and store, i.e. entries in the row /// /// \note The implementation relies on \c rosa::csv::CSVRowParser, which is -/// implemented only for `arithmetic` types -- signed and unsigned integral and -/// floating-point types -- and for \c std::string. Those are the valid values -/// for \p T. -template -class CSVRow { -public: - /// Gives a constant reference for an entry at a given position of the row. +/// implemented only for `arithmetic` types -- signed and unsigned integral +/// and floating-point types -- and for \c std::string. Those are the valid +/// values for \p Ts. +template 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. /// - /// \note No bounds checking is performed. + /// \tparam S0 indices to access tuple elements. /// - /// \param Index the position of the entry + /// \param [in,out] LineStream the line to parse /// - /// \return constant reference for the stored entry at position \p Index - const T &operator[](const size_t Index) const noexcept { return Data[Index]; } + /// \note The last argument is used only to get \p S0, the actual value of + /// the parameter is ignored. + template + 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)), + ...); + + } - /// Tells the number of entries stored in the row. +public: + + /// Constructor with all possible parameters /// - /// \return number of stored entries. - size_t size(void) const noexcept { return Data.size(); } + /// 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 HeaderInfo is the first line of the file a header row or not. + /// \param Delimeter to seperate between the data entries within one row. + CSVRow(const size_t SkipRows = 0, + const HeaderInformation HeaderInfo = HeaderInformation::HasHeader, + const char Delimeter = ',') : + SkipRows(SkipRows), HeaderInfo(HeaderInfo), Delimeter(Delimeter), + RowNumber(0), IsHeaderRead(false) { + } + + /// Parses and stores one row of CSV data. /// /// The function reads one line from \p Str and parses it into /// \c rosa::csv::CSVRow::Data using \c rosa::csv::CSVRowParser. /// /// \param [in,out] Str input stream of a CSV file - void readNextRow(std::istream &Str) { + void readNextRow(std::istream &Str) noexcept { std::string Line; + std::getline(Str, Line); - std::stringstream LineStream(Line); - CSVRowParser::parse(LineStream, Data); + + if(Line.size() > 0){ + std::stringstream LineStream(Line); + parseRow(LineStream, Delimeter, seq_t()); + + RowNumber = RowNumber + 1; + } + } + /// Read header row and stores it as \p std::string. + /// + /// The function reads the first line of the csv file and stores the entries + /// in a vector. + /// + /// \param [in,out] Str input stream of a CSV file + void readHeader(std::istream &Str) noexcept { + std::string Line; + std::getline(Str, Line); + std::stringstream LineStream(Line); + std::string Value; + + while( getline(LineStream, Value, Delimeter) ){ + Header.push_back(Value); + } + + IsHeaderRead = true; + } + + /// The number of rows to skip once. + /// + /// This function returns the number of data rows to skip + /// at the beginning of the file. + /// + /// \return The number of rows to skip at the beginning of a csv file. + inline size_t SkipNumRows() const noexcept { + return this->SkipRows; + } + + /// The current row number within the csv file. + /// + /// This function returns the current row number. The header + /// row is not counted as a row. + /// + /// \returns the current row number within the csv file. + inline size_t CurRow() const noexcept { + return this->RowNumber; + } + + /// Indiciates if the header was already read. + /// + /// This function returns true, if the header of a csv file which contains + /// a header file is already read. + /// The user has to pass in the attribute HeaderInfo the information if the + /// file has in the first row the header row or not. + /// + /// \return if the header of a file is already read. + inline bool IsHeaderReadDone() const noexcept{ + return this->IsHeaderRead; + } + + + /// Indicates if the file contains a header row in the first row. + /// + /// This function returns if the file contains a header row. + /// The information if the file contains a header row or not, has to be passed by the user. + /// The standard value is HeaderInformation::HasHeader + /// + /// \return if the csv file contains a header row in the first line of the file. + inline HeaderInformation HasFileHeader() const noexcept { + return this->HeaderInfo; + } + + /// Set the number of rows to skip. + /// + /// This function sets the number of rows to skip at the beginning of + /// the reading of the file. + /// + /// \param SkipRowsBeginning the number of rows you want to skip at the beginning of the file. + inline void SetSkipRows(const size_t SkipRowsBeginning) noexcept { + this->SkipRows = SkipRowsBeginning; + } + + /// 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 HeaderInf if the first row is a header row or not. + inline void SetHeaderInfo(const HeaderInformation HeaderInf) noexcept { + this->HeaderInfo = HeaderInf; + } + + /// Set the seperator between data entries. + /// + /// This funcction sets the separator between the data entries of the csv file. + /// + /// \param separator the character that separates the data values. + inline void SetDelimeter(char separator) { + this->Delimeter = separator; + } + + + + + /// 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::vector Data; ///< Stores parsed entries + 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. +/// \c rosa::csv::CSVRow::readNextRow on \p Data until all lines are +/// skipped. /// -/// \note A CSV file should contain no empty lines. +/// If the function is called for the first time and the file contains +/// a header than is the header and the first data row read in after the +/// number of rows that the user wants to skip. +/// +/// \tparam Ts type of values to read from the row +/// +/// \note The CSV file should contain a line with fields matching \p Ts... /// /// \param [in,out] Str input stream of a CSV file /// \param [in,out] Data object to read the next line into /// /// \return \p Str after reading one line from it -template -std::istream &operator>>(std::istream &Str, CSVRow &Data) { +template +std::istream &operator>>(std::istream &Str, CSVRow &Data) { + + if( Data.HasFileHeader() == HeaderInformation::HasHeader && !Data.IsHeaderReadDone() ) { + Data.readHeader(Str); + } + + while(Data.CurRow() < (Data.SkipNumRows())){ + Data.readNextRow(Str); + } + + //read the lines after you skipped the number of rows you want to skip Data.readNextRow(Str); + return Str; } } // End namespace -/// Provides `InputIterator` features for iterating over a CSV file in a -/// flat way. +/// Provides `InputIterator` features for iterating over a CSV file. /// -/// The iterator hides rows of the CSV file, and iterates over the entries -/// row-by-row. +/// The iterator parses rows into `std::tuple` values and iterates over the +/// file row by row. /// -/// \note A CSV file should contain no empty lines. +/// \tparam Ts types of values stored in one row of the CSV file /// -/// \tparam T type of values to iterate over, i.e. entries in the CSV file. +/// \note The iterator expects each row to consists of fields matching \p Ts. /// /// \note The implementation relies on \c rosa::csv::CSVRow, which in turn /// relies on \c rosa::csv::CSVRowParser, which is implemented only for /// `arithmetic` types -- signed and unsigned integral types and floating-point -/// types -- and for \c std::string. Those are the valid values for \p T. -template -class CSVFlatIterator { +/// types -- and for \c std::string. Those are the valid values for \p Ts +template class CSVIterator { public: - /// \defgroup CSVFlatIteratorTypedefs Typedefs of rosa::csv::CSVFlatIterator + /// \defgroup CSVIteratorTypedefs Typedefs of rosa::csv::CSVIterator /// /// Standard `typedef`s for iterators. /// ///@{ typedef std::input_iterator_tag - iterator_category; ///< Category of the iterator. - typedef T value_type; ///< Type of values iterated over. - typedef std::size_t difference_type; ///< Type to identify distance. - typedef T *pointer; ///< Pointer to the type iterated over. - typedef T &reference; ///< Reference to the type iterated over. + iterator_category; ///< Category of the iterator. + typedef std::tuple 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 - CSVFlatIterator(std::istream &S) - : Str(S.good() ? &S : nullptr), Pos((size_t)(-1)) { - // \c rosa::csv::CSVFlatIterator::Pos is initialized to `-1` so the first - // incrementation here will set it properly. + /// \param SkipRows the number of rows you want to skip only once at the beginning of the file. + /// If you have an header in the file, it is supposed to be the first row, and it will be always read out. + /// But after this header the next number of Rows will be skipped. + /// \param HeaderInfo is used to know wheter the file contains an header row or not. + /// The header has to be in the first row. + /// \param Delimeter is the separator between the differnt values of the csv file. + CSVIterator(std::istream &S, const size_t SkipRows = 0, + const HeaderInformation HeaderInfo = HeaderInformation::HasHeader, + const char Delimeter = ',') : Str(S.good() ? &S : nullptr), + SkipRows(SkipRows), HeaderInfo(HeaderInfo), Delimeter(Delimeter), Row(){ + + Row.SetSkipRows(SkipRows); + Row.SetHeaderInfo(HeaderInfo); + Row.SetDelimeter(Delimeter); + + // \c rosa::csv::CSVIterator::Row is initialized empty so the first + // incrementation here will read the first row. ++(*this); } /// Creates an empty new instance. - CSVFlatIterator(void) noexcept : Str(nullptr) {} + CSVIterator(void) noexcept + : Str(nullptr), SkipRows(0), HeaderInfo(HeaderInformation::HasHeader), + Delimeter(','), Row() {} /// Pre-increment operator. /// - /// The implementation moves over the entries in the current row and advances - /// to the next row when the end of the current row is reached. If the end of - /// the input stream is reached, the operator becomes empty and has no - /// further effect. + /// The implementation reads the next row. If the end of the input stream is + /// reached, the operator becomes empty and has no further effect. /// /// \return \p this object after incrementing it. - CSVFlatIterator &operator++() { + CSVIterator &operator++() { if (Str) { - ++Pos; - if (Pos == Row.size()) { - if (!((*Str) >> Row)) { - Str = nullptr; - --Pos; // Stay on the last entry forever. - } else { - Pos = 0; - } + if (!((*Str) >> Row)) { + Str = nullptr; } } return *this; } /// Post-increment operator. /// /// The implementation uses the pre-increment operator and returns a copy of /// the original state of \p this object. /// /// \return \p this object before incrementing it. - CSVFlatIterator operator++(int) { - CSVFlatIterator Tmp(*this); + CSVIterator operator++(int) { + CSVIterator Tmp(*this); ++(*this); return Tmp; } /// Returns a constant reference to the current entry. /// /// \note Should not dereference the iterator when it is empty. /// /// \return constant reference to the current entry. - const T &operator*(void)const noexcept { return Row[Pos]; } + const std::tuple &operator*(void)const noexcept { return Row.tuple(); } /// Returns a constant pointer to the current entry. /// /// \note Should not dereference the iterator when it is empty. /// /// \return constant pointer to the current entry. - const T *operator->(void)const noexcept { return &Row[Pos]; } + const std::tuple *operator->(void)const noexcept { + return &Row.tuple(); + } /// Tells if \p this object is equal to another one. /// /// Two \c rosa::csv::CSVReader instances are equal if and only if they are /// the same or both are empty. /// /// \param RHS other object to compare to /// /// \return whether \p this object is equal with \p RHS - bool operator==(const CSVFlatIterator &RHS) const noexcept { + bool operator==(const CSVIterator &RHS) const noexcept { return ((this == &RHS) || ((this->Str == nullptr) && (RHS.Str == nullptr))); } /// Tells if \p this object is not equal to another one. /// /// \see rosa::csv::CSVReader::operator== /// /// \param RHS other object to compare to /// /// \return whether \p this object is not equal with \p RHS. - bool operator!=(const CSVFlatIterator &RHS) const noexcept { + bool operator!=(const CSVIterator &RHS) const noexcept { return !((*this) == RHS); } + /// Set the delimeter used in the csv file. + /// \param Separator the character which separates the values in the csv file. + inline void setDelimeter(char Separator) noexcept { + this->Delimeter = Separator; + } + + /// get the delimeter currently set to separate the values in the csv file. + /// \return the current character, which is used to separte teh values in the csv file. + inline char getDelimeter() const noexcept { + return this->Delimeter; + } + private: - std::istream *Str; ///< Input stream of a CSV file to iterate over. - CSVRow Row; ///< Content of the current row iterating over. - size_t Pos; ///< Current position within the current row. + std::istream *Str; ///< Input stream of a CSV file to iterate over. + size_t SkipRows; ///< Number of Rows to skip only once at the beginning of the file. + HeaderInformation HeaderInfo; ///< does the csv file contain a header or not, if this information is + ///< not given correclty, the reading of the header would result in + ///< in an error. + char Delimeter; ///< Delimeter between the entries in the csv file. + CSVRow Row; ///< Content of the current row + }; } // End namespace csv } // End namespace rosa #endif // ROSA_SUPPORT_CSV_CSVREADER_HPP diff --git a/include/rosa/support/csv/CSVWriter.hpp b/include/rosa/support/csv/CSVWriter.hpp index b67de6a..9dd6b9f 100755 --- a/include/rosa/support/csv/CSVWriter.hpp +++ b/include/rosa/support/csv/CSVWriter.hpp @@ -1,99 +1,227 @@ //===-- rosa/support/csv/CSVWriter.hpp --------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/support/csv/CSVWriter.hpp /// -/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// \authors David Juhasz (david.juhasz@tuwien.ac.at) +/// Edwin Willegger (edwin.willegger@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \brief Facitilities to write CSV files. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_CSV_CSVWRITER_HPP #define ROSA_SUPPORT_CSV_CSVWRITER_HPP +#include #include +#include +#include +#include + +#include "rosa/support/log.h" + namespace rosa { namespace csv { /// Provides facilities to write values into a CSV file. /// /// The writer emits a comma, the character `,`, between each written values. /// The resulted stream is a flat CSV file as it consists of onlyone row, no new /// line is emitted. /// /// \tparam T type of values to write template class CSVWriter { public: /// Creates a new instance. /// /// \param [in,out] S output stream to write to /// /// \note The writer operates on non-binary outputs as long as \p S is in /// good state. CSVWriter(std::ostream &S) : Str(S.good() && !(S.flags() & std::ios::binary) ? &S : nullptr), IsFirst(true) {} /// Tells if the last operation was successful. /// /// \return if the last operation was successful bool good(void) const noexcept { return Str != nullptr; } /// Writes an entry to the output stream. /// /// The implementation does anything only if the last operation was /// successful. If so, \p V is written to \c rosa::csv::CSVWriter::Str. /// The emitted value is preceded with a comma if the actual call is not the /// first one for \p this object. Success of the operation is checked at the /// end. /// /// \param V value to write void write(const T &V) { if (Str) { if (!IsFirst) { *Str << ','; } else { IsFirst = false; } *Str << V; if (!Str->good()) { Str = nullptr; } } } private: std::ostream *Str; ///< Output stream to write to. bool IsFirst; ///< Denotes if the next write would be the first one. }; +/// Writes a tuple of values into a CSV file +/// +/// \tparam Ts types of values to write +template class CSVTupleWriter { + +public: + +// typedef value_type ; ///< Type of values written. + typedef std::tuple value_type; + + /// Creates a new instance. + /// + /// \param [in,out] S output stream to write to + /// + /// \note The writer operates on non-binary outputs as long as \p S is in + /// good state. + CSVTupleWriter(std::ostream &S) + : Str(S.good() && !(S.flags() & std::ios::binary) ? &S : nullptr), + IsHeaderWritten(false), IsDataWritten(false) {} + + /// Tells if the last operation was successful. + /// + /// \return if the last operation was successful + bool good(void) const noexcept { + return Str != nullptr; + } + + + /// Write the values of a tuple to a CSV file with \c rosa::csv::CSVTupleWriter. + /// + /// \see rosa::csv::CSVTupleWriter + /// + /// + /// \param [in,out] values tuple, which values are written in a recusive fashion into a stream. + template + void write(const std::tuple &values) { + constexpr size_t size = sizeof...(Ts); + + LOG_TRACE_STREAM << "Writing tuple values into file \n"; + LOG_TRACE_STREAM << " Tuple has " << std::to_string(size) << " elements. \n"; + + LOG_TRACE_STREAM << " Value is " << std::get(values); + + if(Str){ + /// Write the current element of the tuple into the stream and add a separtor after it, + /// and call the function for the next element in the tuple. + if constexpr(i+1 != sizeof...(Ts)){ + *Str << std::get(values) << ", "; + write(values); + /// If the last element is written into the stream than begin a new line. + }else if constexpr(i + 1 == sizeof...(Ts)){ + *Str << std::get(values) << '\n'; + /// every time the last data value of a line is written, the flag indicates that data was already written into the file. + IsDataWritten = true; + } + } + } + + /// Write the header values to a CSV file with \c rosa::csv::CSVTupleWriter. + /// + /// \note The function has no effect if anything has already been written + /// to the output stream either by \c + /// rosa::csv::CSVTupleWriter::writeHeader() or \c + /// rosa::csv::CSVTupleWriter::write(). + /// + /// \see rosa::csv::CSVTupleWriter + /// + /// \param header the content of the header line. + void writeHeader(const std::array &header){ + size_t index = 0; + /// write into the stream only, if it is not a nullptr, and if no data and no header was already written into it. + if(Str && IsDataWritten == false && IsHeaderWritten == false){ + index = 0; + for (auto i = header.begin(); i != header.end(); ++i){ + index = index + 1; + /// write into the stream every entry with a delimeter, in this case ", " until + /// the last entry + if(index != header.size()){ + *Str << *i << ", "; + /// write the last entry into the stream, without any delimeter + }else { + *Str << *i; + } + } + /// finish the header line and start a new line. + *Str << '\n'; + /// now it is not possible to write additional header lines. + IsHeaderWritten = true; + } + } + +private: + std::ostream *Str; ///< Output stream to write to. + bool IsHeaderWritten; ///< If an header line was already written into the stream. If set than no additional header could be written. + bool IsDataWritten; ///< If one line of data has already been written into the stream, than no headerline could be added. +}; + +/// Writes all values of a tuple to a CSV file with \c rosa::csv::CSVTupleWriter. +/// +/// \see rosa::csv::CSVTupleWriter +/// +/// \tparam Ts types of values to write +/// +/// \param [in,out] W object to write with +/// \param V values to write +/// +/// \return \p W after writing \p V with it +template +CSVTupleWriter &operator<<(CSVTupleWriter &W, const std::tuple &V) +{ + W.write(V); + return W; +} + /// Writes a value to a CSV file with \c rosa::csv::CSVWriter. /// /// \see rosa::csv::CSVWriter /// /// \tparam T type of value to write /// /// \param [in,out] W object to write with /// \param V value to write /// /// \return \p W after writing \p V with it template CSVWriter &operator<<(CSVWriter &W, const T& V) { W.write(V); return W; } } // End namespace csv } // End namespace rosa #endif // ROSA_SUPPORT_CSV_CSVWRITER_HPP diff --git a/include/rosa/support/csv/namespace.h b/include/rosa/support/csv/namespace.h index 85ad49e..b077a57 100755 --- a/include/rosa/support/csv/namespace.h +++ b/include/rosa/support/csv/namespace.h @@ -1,25 +1,31 @@ //===-- rosa/support/csv/namespace.h ----------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/support/csv/namespace.h /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Documentation for the namespace \c rosa::csv. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_CSV_NAMESPACE_H #define ROSA_SUPPORT_CSV_NAMESPACE_H namespace rosa { /// Provides facilities to work with CSV files. namespace csv {} } // End namespace rosa #endif // ROSA_SUPPORT_CSV_NAMESPACE_H diff --git a/include/rosa/support/debug.hpp b/include/rosa/support/debug.hpp index ca3b966..c8230a9 100644 --- a/include/rosa/support/debug.hpp +++ b/include/rosa/support/debug.hpp @@ -1,127 +1,133 @@ //===-- rosa/support/debug.hpp ----------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/support/debug.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \brief Facility for debugging /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_DEBUG_HPP #define ROSA_SUPPORT_DEBUG_HPP #include "rosa/config/config.h" #include "rosa/support/terminal_colors.h" #include "rosa/support/type_helper.hpp" #include #include namespace rosa { /// Returns an output stream to use for debugging. std::ostream &dbgs(void); /// Prints a \c std::array to a \c std::ostream. /// /// \tparam T type of values stored in \p A /// \tparam Size number of elements in \p A /// /// \param [in,out] OS \c std::ostream to print to /// \param A \c std::array to print to \p OS /// /// \return \p OS after printing \p A template std::ostream &operator<<(std::ostream &OS, const std::array &A) { OS << '['; for (unsigned I = 0; I < Size; ++I) { if (I) { OS << ','; } OS << PRINTABLE(A[I]); } OS << ']'; return OS; } } // End namespace rosa /// \def ASSERT(stmt) /// \brief Enforces an assertion. /// /// \note Takes effect only when \c ROSA_ENABLE_ASSERTIONS is defined. /// /// Checks if \p stmt evaluates to true. If not, prints an error message about /// violating the assertion and aborts execution. /// /// \param stmt statement to evaluate, needs to be convertable to \c bool #ifndef ROSA_ENABLE_ASSERTIONS -#define ASSERT(stmt) ROSA_IGNORE_UNUSED(stmt) +#define ASSERT(stmt) ROSA_IGNORE_UNUSED_EXPR(stmt) #elif defined(ROSA_WINDOWS) #define ASSERT(stmt) \ if (static_cast(stmt) == false) { \ rosa::dbgs() << rosa::terminal::Color::Default << __FILENAME__ << ":" \ << __LINE__ << ": requirement failed: '" << #stmt << "'" \ << std::endl; \ ::abort(); \ } \ ROSA_VOID_STMT #else // defined(ROSA_LINUX) #include #define ASSERT(stmt) \ if (static_cast(stmt) == false) { \ rosa::dbgs() << rosa::terminal::Color::Default << __FILENAME__ << ":" \ << __LINE__ << ": requirement failed: '" << #stmt << "'" \ << std::endl; \ void *array[20]; \ auto bt_size = ::backtrace(array, 20); \ ::backtrace_symbols_fd(array, bt_size, 2); \ ::abort(); \ } \ ROSA_VOID_STMT #endif // defined(ROSA_ENABLE_ASSERTIONS) /// \def DEBUG(X) /// \brief Executes the given piece of code only if \c NDEBUG is not defined. /// /// \param X the code to execute /// \def DEBUGVAR(V) /// \brief Dumps the given variable to the default debug output. /// /// \param V the variable to dump /// /// \sa \c rosa::dbgs() #ifndef NDEBUG #define DEBUG(X) \ do { \ X; \ } while (false) #define DEBUGVAR(V) \ do { \ rosa::dbgs() << rosa::terminal::Color::Default << #V << " (" \ << __FILENAME__ << ":" << __LINE__ << "): " << (V) \ << std::endl; \ } while (false) #else // defined(NDEBUG) -#define DEBUG(X) ROSA_IGNORE_UNUSED(X) -#define DEBUGVAR(X) ROSA_IGNORE_UNUSED(X) +#define DEBUG(X) ROSA_IGNORE_UNUSED_STMT(X) +#define DEBUGVAR(X) ROSA_IGNORE_UNUSED_EXPR(X) #endif // defined(NDEBUG) /// Enforces static assertion. /// /// \param COND the condition to evaluate, must be statically evaluable /// \param DIAG error message if \p COND does not evaluate to \c true #define STATIC_ASSERT(COND, DIAG) static_assert((COND), DIAG) #endif // ROSA_SUPPORT_DEBUG_HPP diff --git a/include/rosa/support/iterator/namespace.h b/include/rosa/support/iterator/namespace.h new file mode 100644 index 0000000..027978d --- /dev/null +++ b/include/rosa/support/iterator/namespace.h @@ -0,0 +1,25 @@ +//===-- rosa/support/iterator/namespace.h -----------------------*- C++ -*-===/ +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===/ +/// +/// \file rosa/support/iterator/namespace.h +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Documentation for the namespace \c rosa::iterator. +/// +//===----------------------------------------------------------------------===/ + +#ifndef ROSA_SUPPORT_ITERATOR_NAMESPACE_H +#define ROSA_SUPPORT_ITERATOR_NAMESPACE_H + +namespace rosa { +/// Provides facilities to work with iterators. +namespace iterator {} +} // End namespace rosa + +#endif // ROSA_SUPPORT_ITERATOR_NAMESPACE_H diff --git a/include/rosa/support/iterator/split_tuple_iterator.hpp b/include/rosa/support/iterator/split_tuple_iterator.hpp new file mode 100644 index 0000000..f5af821 --- /dev/null +++ b/include/rosa/support/iterator/split_tuple_iterator.hpp @@ -0,0 +1,469 @@ +//===-- rosa/support/iterator/split_tuple_iterator.hpp ----------*- C++ -*-===/ +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===/ +/// +/// \file rosa/support/iterator/split_tuple_iterator.hpp +/// +/// \authors David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Facilities to split iterators of \c std::tuple into iterators of +/// their elements. +/// +//===----------------------------------------------------------------------===/ + +#ifndef ROSA_SUPPORT_ITERATOR_SPLIT_TUPLE_ITERATOR_HPP +#define ROSA_SUPPORT_ITERATOR_SPLIT_TUPLE_ITERATOR_HPP + +#include "rosa/support/debug.hpp" +#include "rosa/support/sequence.hpp" + +#include +#include +#include +#include + +namespace rosa { +namespace iterator { + +/// Anonymous namespace providing implementation for splitting tuple iterators; +/// consider it private. +namespace { + +/// Iterator of values provided by a buffer based on an index +/// +/// \tparam TB buffer providing values to iterate +/// \tparam I index of the values to iterator from \p TB +/// +/// \note \p TB is expected to implemnet an interface matching that of \c +/// TupleIteratorBuffer. +template +class SplittedElementIterator { +public: + /// Type alias for the values the iterator iterates. + using T = typename TB::template element_type; + + /// \defgroup SplittedElementIteratorTypedefs Typedefs of + /// \c SplittedElementIterator + /// + /// Standard `typedef`s for iterators. + /// + ///@{ + typedef std::input_iterator_tag + iterator_category; ///< Category of the iterator. + typedef T value_type; ///< Type of values iterated over. + typedef std::size_t difference_type; ///< Type to identify distance. + typedef T *pointer; ///< Pointer to the type iterated over. + typedef T &reference; ///< Reference to the type iterated ver. + ///@} + + /// Creates a new instance. + /// + /// \param Buffer buffer providing values for \p this object + /// + /// \note \p Buffer is captured as a \c std::shared_ptr because it is shared + /// among iterators for its element positions. + SplittedElementIterator(std::shared_ptr Buffer) + : Buffer(Buffer), Value() { + // \c Value is initialized empty so the first incrementation here will read + // the first value. + ++(*this); + } + + /// Creates an empty new instance. + SplittedElementIterator(void) noexcept : Buffer(), Value() {} + + /// Pre-increment operator. + /// + /// The implementation reads the next value from \c Buffer. If no more values + /// can be provided by \c Buffer, \p this object becomes empty and the + /// operator has no further effect. + /// + /// \return \p this object after incrementing it. + SplittedElementIterator &operator++() { + if (Buffer->template hasMore()) { + Value = Buffer->template next(); + } else { + // Reached end of range, remove reference of \c Buffer. + Buffer.reset(); + } + return *this; + } + + /// Post-increment operator. + /// + /// The implementation uses the pre-increment operator and returns a copy of + /// the original state of \p this object. + /// + /// \return \p this object before incrementing it. + SplittedElementIterator operator++(int) { + SplittedElementIterator Tmp(*this); + ++(*this); + return Tmp; + } + + /// Returns a constant reference to the current value. + /// + /// \note Should not dereference the iterator when it is empty. + /// + /// \return constant reference to the current value. + const T &operator*(void)const noexcept { return Value; } + + /// Returns a constant pointer to the current value. + /// + /// \note Should not dereference the iterator when it is empty. + /// + /// \return constant pointer to the current value. + const T *operator->(void)const noexcept { return &Value; } + + /// Tells if \p this object is equal to another one. + /// + /// Two \c SplittedElementIterator instances are equal if and only if they are + /// the same or both are empty. + /// + /// \param RHS other object to compare to + /// + /// \return whether \p this object is equal with \p RHS + bool operator==(const SplittedElementIterator &RHS) const noexcept { + return ((this == &RHS) || (!Buffer && !RHS.Buffer)); + } + + /// Tells if \p this object is not equal to another one. + /// + /// \see SplittedElementIterator::operator== + /// + /// \param RHS other object to compare to + /// + /// \return whether \p this object is not equal with \p RHS. + bool operator!=(const SplittedElementIterator &RHS) const noexcept { + return !((*this) == RHS); + } + +private: + std::shared_ptr Buffer; ///< Buffer providing values for \p this object + T Value; ///< The current value of the iterator +}; + +///\defgroup TupleBufferContainer Type converter turning a \c std::tuple of +///types into a \c std::tuple of container of those types. +/// +/// The new type is used for buffering elements of tuples separately. +/// +///@{ + +/// Template declaration. +/// +/// \tparam T type to convert +/// +/// \note The template is defined only when \p T is a \c std::tuple. +/// +/// Usage for a type \c Tuple as:\code +/// typename TupleBufferContainer::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 iterator of \c std::tuple. +/// +/// The class can be instantiated with a range of iterator of \c std::tuple and +/// provides an interface for accessing elements of the iterated tuples one by +/// one. +/// +/// \note The class is utilized by \c SplittedElementIterator. +/// +/// \tparam TupleIterator the iterator type that provides values of \c +/// std::tuple +/// +/// \todo Consider thread safety of the class. +template +class TupleIteratorBuffer { +public: + /// Type alias for the value type of \p TupleIterator. + /// \note The type is expected to be \c std::tuple. + using iterator_value_type = typename TupleIterator::value_type; + + /// The number of elements of \c iterator_value_type. + static constexpr size_t iterator_value_size = + std::tuple_size_v; + + /// Template type alias to get element types of \c iterator_value_type by + /// index. + /// \tparam I the index of the element + template + using element_type = + typename std::tuple_element::type; + + /// Type alias for index sequence for accessing elements of \c + /// iterator_value_type. + using element_idx_seq_t = seq_t; + + /// Creates a new instance. + /// + /// \param Begin the beginning of the iterator range to buffer + /// \param End the end of the iterator range to buffer + TupleIteratorBuffer(TupleIterator &&Begin, const TupleIterator &End) noexcept + : Iterator(Begin), End(End), Buffer() {} + + /// Tells whether there is any more value in the handled range for index \p I. + /// + /// \tparam I the index of the element to check + /// + /// \return whether there is more value for index \p I + template bool hasMore(void) const noexcept { + const auto &ElementBuffer = std::get(Buffer); + // There is more value if some has already been buffered or the end of the + // handled range is not reached yet. + return !ElementBuffer.empty() || Iterator != End; + } + + /// Gives the next value from the handled range for index \p I. + /// + /// \note The function should not be called if there is no more value for + /// index \p I (i.e., \c hasMore() returns \c false). If it is called in + /// that situation, it returns the default value of \c element_type. + /// + /// \tparam I the index of the element to fetch next value for + /// + /// \return the next value for index \p I + template element_type next(void) noexcept { + auto &ElementBuffer = std::get(Buffer); + if (ElementBuffer.empty()) { + // This position needs a new value from Iterator if there is more. + if (Iterator != End) { + // Push next value for all elements. + push(*Iterator++, element_idx_seq_t()); + } else { + // No more values; called when hasMore() is false. + // Return default value. + return element_type(); + } + } + // Buffer is not empty here, return the next value. + ASSERT(!ElementBuffer.empty() && "Unexpected condition"); + // Get the next value and also pop it from the buffer. + const element_type Elem = ElementBuffer.front(); + ElementBuffer.pop(); + return Elem; + } + +private: + /// Buffers next tuple value elementwise into element containers. + /// + /// \tparam S0 indices for accessing elements of \c std::tuple + /// + /// \param Tuple the values to buffer + /// + /// \note The second parameter provides indices as template parameter \p S0 + /// and its actual value is ignored. + template + void push(const iterator_value_type &Tuple, Seq) noexcept { + (std::get(Buffer).push(std::get(Tuple)), ...); + } + + TupleIterator Iterator; ///< Current input iterator + const TupleIterator End; ///< End of iterator range to handle + tuple_buffer_container_t + Buffer; ///< Container for elementwise buffering +}; + +/// Template type alias for iterator of an element based on buffered iterator of +/// \c std::tuple. +/// +/// The alias utilizes \c SplittedElementIterator for iterating over the values +/// provided by \p TB. +/// +/// \tparam TB buffer providing values to iterate +/// \tparam I index of the values to iterator from \p TB +template +using element_iterator_t = SplittedElementIterator; + +/// Template type alias for iterator-range of an element based on buffered +/// iterator of \c std::tuple. +/// +/// \tparam TB buffer providing values to iterate +/// \tparam I index of the values to iterator from \p TB +template +using element_iterator_range_t = + std::pair, element_iterator_t>; + +///\defgroup ElementIteratorRanges Type converter turning a buffer of \c +/// std::tuple into a \c std::tuple of corresponding \c +/// element_iterator_range_t. +/// +///@{ + +/// Template declaration. +/// +/// \tparam TB buffer providing values +/// \tparam S type providing indices for accessing elements of \c std::tuple +/// +/// \note \p TB is expected to implement an interface matching that of \c +/// TupleIteratorBuffer. +/// +/// \note The template is defined only when \p S is a \c rosa::Seq. +/// +/// Usage for a proper buffer type \c TB:\code +/// typename ElementIteratorRanges::Type +/// \endcode +template struct ElementIteratorRanges; + +/// Template definition. +template +struct ElementIteratorRanges> { + /// The converted type. + using Type = std::tuple...>; +}; + +///@} + +/// Convenience template type alias for easy use of \c ElementIteratorRanges. +/// +/// Converts a buffer of \c std::tuple into a \c std::tuple of corresponding \c +/// element_iterator_range_t. +/// +/// \tparam TB buffer providing values +/// +/// \note \p TB is expected to implement an interface matching that of \c +/// TupleIteratorBuffer. +template +using element_iterator_ranges_t = + typename ElementIteratorRanges::Type; + +/// Template type alias for turning an iterator of \c std::tuple into +/// corresponding \c element_iterator_ranges_t. +/// +/// The alias utilizes \c TupleIteratorBuffer for buffering the values provided +/// by \p TupleIterator. The elementwise iterators are \c +/// SplittedElementIterator. +/// +/// \tparam TupleIterator iterator of \c std::tuple +template +using splitted_tuple_iterator_ranges_t = + element_iterator_ranges_t>; + +/// Creates elementwise iterator ranges for an iterator range of \c std::tuple. +/// +/// \note Implementation for \c rosa::iterator::splitTupleIterator. +/// +/// \tparam TupleIterator iterator of \c std::tuple +/// \tparam S0 indices for accessing elements of \c std::tuple +/// +/// \param Begin the beginning of the iterator range to handle +/// \param End the end of the iterator range to handle +/// +/// \note The last parameter provides indices as template parameter \p S0 and +/// its actual value is ignored. +/// +/// \return \c std::tuple of elementwise iterator ranges corresponding to \p +/// Begin and \p End +template +splitted_tuple_iterator_ranges_t +splitTupleIteratorImpl(TupleIterator &&Begin, const TupleIterator &End, + Seq) noexcept { + using TB = TupleIteratorBuffer; + // Create a buffer in shared pointer. The buffer will be destructed once + // all corresponding element iterators (created below) have reached the end of + // the handled range or have been destructed. + auto Buffer = std::make_shared(std::move(Begin), End); + return {std::make_pair(element_iterator_t(Buffer), + element_iterator_t())...}; +} + +} // End namespace + +/// Gives the beginning of a range created by \c splitTupleIterator(). +/// +/// \see rosa::iterator::splitTupleIterator() +/// +/// \note The signature of the template function uses implementation details. +/// +/// \tparam TB buffer providing values to iterate +/// \tparam I index of the values to iterator from \p TB +/// +/// \param Range range to get the beginning of +/// +/// \return beginning of \p Range +template +element_iterator_t &begin(element_iterator_range_t &Range) { + return Range.first; +} + +/// Gives the end of a range created by \c splitTupleIterator(). +/// +/// \see rosa::iterator::splitTupleIterator() +/// +/// \note The signature of the template function uses implementation details. +/// +/// \tparam TB buffer providing values to iterate +/// \tparam I index of the values to iterator from \p TB +/// +/// \param Range range to get the end of +/// +/// \return end of \p Range +template +element_iterator_t &end(element_iterator_range_t &Range) { + return Range.second; +} + +/// Creates elementwise iterator ranges for an iterator range of \c std::tuple. +/// +/// \note The implementation utilizes \c splitTupleIteratorImpl. +/// +/// Obtain element iterator ranges for an iterator range \c Begin and \c End of +/// iterator type \c TupleIterator of \c std::tuple as \code +/// auto [T1Range, T2Range, T3Range] = splitTupleIterator(std::move(Begin), End); +/// auto [T1Begin, T1End] = T1Range; +/// auto [T2Begin, T2End] = T2Range; +/// auto [T3Begin, T3End] = T3Range; +/// \endcode +/// One range can also be dissected by dedicated functions like \code +/// auto T1Begin = begin(T1Range); +/// auto T1End = end(T1Range); +/// \endcode +/// +/// The function returns a tuple of ranges (i.e., \c std::pair of iterators), +/// which can be assigned to variables using structured binding. The ranges can +/// be dissected into begin and end iterators by separate structured bindings. +/// +/// The function moves its first argument (i.e., \p Begin cannot be used +/// directly after splitting it). If the first argument is a variable, it needs +/// to be moved explicitly by \c std::move() as in the example. +/// +/// \tparam TupleIterator iterator of \c std::tuple +/// +/// \param Begin the beginning of the iterator range to handle +/// \param End the end of the iterator range to handle +/// +/// \return \c std::tuple of elementwise iterator ranges corresponding to \p +/// Begin and \p End +template +splitted_tuple_iterator_ranges_t +splitTupleIterator(TupleIterator &&Begin, const TupleIterator &End) noexcept { + return splitTupleIteratorImpl( + std::move(Begin), End, + seq_t>()); +} + +} // End namespace iterator +} // End namespace rosa + +#endif // ROSA_SUPPORT_ITERATOR_SPLIT_TUPLE_ITERATOR_HPP diff --git a/include/rosa/support/log.h b/include/rosa/support/log.h index 9573bfb..c5b60ce 100644 --- a/include/rosa/support/log.h +++ b/include/rosa/support/log.h @@ -1,259 +1,265 @@ //===-- rosa/support/log.h --------------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/support/log.h /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \brief Facility for logging. /// /// \note One call for the various logging macros is supposed to be used /// for registering one log entry. That goes natural with the non-stream /// implementations, which accept one string as argument. A more flexible way /// for printing log entries, for example for colorizing text, is to use macros /// providing a log stream. It is important to note, however, that the stream /// obtained from one macro evaluation fits for printing one log entry, without /// nested/overlapping log entry emissions and the entry being closed with a /// newline. Should this simple recommendation not being followed, the result /// becomes hard to read due to missing line breaks and overlapping entries. /// /// \todo Thread-safety is another issue, which need to be addressed for proper /// logging. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_LOG_H #define ROSA_SUPPORT_LOG_H #include "rosa/config/config.h" #include "rosa/support/terminal_colors.h" #include #include /* **************************************************************************** * Log Levels * * ****************************************************************************/ namespace rosa { /// Type-safe definition of log levels, use this in code. /// \note Keep values in sync with the corresponding preprocessor definitions. enum class LogLevel { Error, ///< Log errors only Warning, ///< Like \c rosa::LogLevel::Error and also log warnings Info, ///< Like \c rosa::LogLevel::Warning and also log general infos Debug, ///< Like \c rosa::LogLevel::Info and also log debug infos Trace, ///< Like \c rosa::LogLevel::Debug and also log trace infos NumLogLevels ///< Number of log levels }; /// Converts a \c rosa::LogLevel to its textual representation. /// /// \param logLevel \c rosa::LogLevel to convert /// /// \return \c std::string representing \p logLevel /// /// \pre `logLevel` is valid:\code /// logLevel != LogLevel::NumLogLevels /// \endcode std::string logLevelToString(const LogLevel logLevel); /// Prints colorized tag for the given \c rosa::LogLevel. /// /// \param [in,out] OS \c std::ostream to print to /// \param logLevel \c rosa::LogLevel to print tag for /// /// \return \p OS after printing a tag for \p logLevel /// /// \pre \p logLevel is valid:\code /// logLevel != LogLevel::NumLogLevels /// \endcode std::ostream &operator<<(std::ostream &OS, const LogLevel logLevel); } // End namespace rosa /// \name Valid log level constants /// \note Only for preprocessor definitions in this file. /// \note Keep the defintions in sync with the values of \c rosa::LogLevel. ///@{ #define ROSA_LOG_LEVEL_ERROR \ 0 ///< Value corresponding to \c rosa::LogLevel::Error #define ROSA_LOG_LEVEL_WARNING \ 1 ///< Value corresponding to \c rosa::LogLevel::Warning #define ROSA_LOG_LEVEL_INFO \ 2 ///< Value corresponding to \c rosa::LogLevel::Info #define ROSA_LOG_LEVEL_DEBUG \ 3 ///< Value corresponding to \c rosa::LogLevel::Debug #define ROSA_LOG_LEVEL_TRACE \ 4 ///< Value corresponding to \c rosa::LogLevel::Trace ///@} /* **************************************************************************** * Logger Implementation * * ****************************************************************************/ /// Stream to print logs to /// /// \todo Make it configurable, e.g. printing into a file. #define ROSA_LOG_OSTREAM std::clog /// Prints a log message to \c ROSA_LOG_OSTREAM. /// /// \param level \c rosa::LogLevel of the log entry /// \param output message to print #define ROSA_LOG_IMPL(level, output) \ do { \ ROSA_LOG_OSTREAM << (level) << " " << __func__ << "@" << __FILENAME__ \ << ":" << __LINE__ << ": " << (output) << '\n'; \ } while (false) /// Returns a stream to print a log message to. /// /// \param level \c rosa::LogLevel of the log entry that is about to be printed #define ROSA_LOG_STREAM_IMPL(level) \ ([](void) -> std::ostream & { \ return ROSA_LOG_OSTREAM << (level) << " " << __func__ << "@" \ << __FILENAME__ << ":" << __LINE__ << ": "; \ }()) namespace rosa { /// Dummy \c std::ostream printing to nowhere. extern std::ostream LogSink; } // End namespace rosa /// An output stream ignoring all its input. #define ROSA_LOG_STREAM_IGNORE rosa::LogSink /* **************************************************************************** * Logging Interface * * ****************************************************************************/ /// \name Logging interface /// /// Preprocesser macros for convenience. ///@{ /// \def LOG_ERROR_STREAM /// \brief Returns a stream to print a log entry of level /// \c rosa::LogLevel::Error. /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Error. /// \def LOG_ERROR(output) /// \brief Prints a log entry of level \c rosa::LogLevel::Error. /// \param output the message to print /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Error. /// \def LOG_WARNING_STREAM /// \brief Returns a stream to print a log entry of level /// \c rosa::LogLevel::Warning. /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Warning. /// \def LOG_WARNING(output) /// \brief Prints a log entry of level \c rosa::LogLevel::Warning. /// \param output the message to print /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Warning. /// \def LOG_INFO_STREAM /// \brief Returns a stream to print a log entry of level /// \c rosa::LogLevel::Info. /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Info. /// \def LOG_INFO(output) /// \brief Prints a log entry of level \c rosa::LogLevel::Info. /// \param output the message to print /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Info. /// \def LOG_DEBUG_STREAM /// \brief Returns a stream to print a log entry of level /// \c rosa::LogLevel::Debug. /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Debug. /// \def LOG_DEBUG(output) /// \brief Prints a log entry of level \c rosa::LogLevel::Debug. /// \param output the message to print /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Debug. /// \def LOG_TRACE_STREAM /// \brief Returns a stream to print a log entry of level /// \c rosa::LogLevel::Trace. /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Trace. /// \def LOG_TRACE(output) /// \brief Prints a log entry of level \c rosa::LogLevel::Trace. /// \param output the message to print /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Trace. ///@} // Define logging macros if logging is enabled. #ifdef ROSA_LOG_LEVEL #define LOG_ERROR_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Error) #define LOG_ERROR(output) ROSA_LOG_IMPL(rosa::LogLevel::Error, output) #if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_WARNING #define LOG_WARNING_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Warning) #define LOG_WARNING(output) ROSA_LOG_IMPL(rosa::LogLevel::Warning, output) #endif #if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_INFO #define LOG_INFO_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Info) #define LOG_INFO(output) ROSA_LOG_IMPL(rosa::LogLevel::Info, output) #endif #if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_DEBUG #define LOG_DEBUG_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Debug) #define LOG_DEBUG(output) ROSA_LOG_IMPL(rosa::LogLevel::Debug, output) #endif #if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_TRACE #define LOG_TRACE_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Trace) #define LOG_TRACE(output) ROSA_LOG_IMPL(rosa::LogLevel::Trace, output) #endif #endif // defined ROSA_LOG_LEVEL // Define all disabled logging features as void. #ifndef LOG_ERROR #define LOG_ERROR_STREAM ROSA_LOG_STREAM_IGNORE -#define LOG_ERROR(output) ROSA_IGNORE_UNUSED(output) +#define LOG_ERROR(output) ROSA_IGNORE_UNUSED_EXPR(output) #endif #ifndef LOG_WARNING #define LOG_WARNING_STREAM ROSA_LOG_STREAM_IGNORE -#define LOG_WARNING(output) ROSA_IGNORE_UNUSED(output) +#define LOG_WARNING(output) ROSA_IGNORE_UNUSED_EXPR(output) #endif #ifndef LOG_INFO #define LOG_INFO_STREAM ROSA_LOG_STREAM_IGNORE -#define LOG_INFO(output) ROSA_IGNORE_UNUSED(output) +#define LOG_INFO(output) ROSA_IGNORE_UNUSED_EXPR(output) #endif #ifndef LOG_DEBUG #define LOG_DEBUG_STREAM ROSA_LOG_STREAM_IGNORE -#define LOG_DEBUG(output) ROSA_IGNORE_UNUSED(output) +#define LOG_DEBUG(output) ROSA_IGNORE_UNUSED_EXPR(output) #endif #ifndef LOG_TRACE #define LOG_TRACE_STREAM ROSA_LOG_STREAM_IGNORE -#define LOG_TRACE(output) ROSA_IGNORE_UNUSED(output) +#define LOG_TRACE(output) ROSA_IGNORE_UNUSED_EXPR(output) #endif #endif // ROSA_SUPPORT_LOG_H diff --git a/include/rosa/support/math.hpp b/include/rosa/support/math.hpp index f88a13d..0ca3262 100644 --- a/include/rosa/support/math.hpp +++ b/include/rosa/support/math.hpp @@ -1,57 +1,63 @@ //===-- rosa/support/math.hpp -----------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/support/math.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Math helpers. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_MATH_HPP #define ROSA_SUPPORT_MATH_HPP #include #include #include #include namespace rosa { /// Computes log base 2 of a number. /// /// \param N the number to compute log base 2 for /// /// \return log base 2 of \p N constexpr size_t log2(const size_t N) { return ((N < 2) ? 1 : 1 + log2(N / 2)); } /// Tells the next representable floating point value. /// /// \tparam T type to operate on /// /// \note The second type argument enforces \p T being a floating point type, /// always use the default value! /// /// \param V value to which find the next representable one /// /// \return the next representable value of type \p T after value \p V /// /// \pre Type \p T must be a floating point type, which is enforced by /// `std::enable_if` in the second type argument. template ::value>> T nextRepresentableFloatingPoint(const T V) { return std::nextafter(V, std::numeric_limits::infinity()); } } // End namespace rosa #endif // ROSA_SUPPORT_MATH_HPP diff --git a/include/rosa/support/sequence.hpp b/include/rosa/support/sequence.hpp index 62b8408..a2ba12a 100755 --- a/include/rosa/support/sequence.hpp +++ b/include/rosa/support/sequence.hpp @@ -1,57 +1,62 @@ //===-- rosa/support/sequence.hpp -------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/support/sequence.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Template facilities to statically generate a sequence of numbers. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_SEQUENCE_HPP #define ROSA_SUPPORT_SEQUENCE_HPP #include namespace rosa { /// \defgroup Seq Implementation of rosa::Seq /// /// Facility to statically generate sequences of numbers. /// ///@{ /// Template with an empty struct to store a sequence of numbers in compile time /// as template arguments. /// /// Generate a sequence of numbers from `0` up to (including) `(N - 1)` like /// \code /// typename GenSeq::Type /// \endcode template struct Seq {}; /// Sequence generator, the general case when counting down by extending the /// sequence. template struct GenSeq : GenSeq {}; /// Sequence generator, the terminal case when storing the generated sequence /// into \c Seq. template struct GenSeq<0, S...> { using Type = Seq; }; ///@} /// Convenience template alias for using \c rosa::GenSeq to obtain an instance -/// of \c rosa::Seq. -/// -/// \see \c rosa::Seq and \c rosa::GenSeq -template using seq_t = typename GenSeq::Type; - + /// of \c rosa::Seq. + /// + /// \see \c rosa::Seq and \c rosa::GenSeq + template using seq_t = typename GenSeq::Type; } // End namespace rosa #endif // ROSA_SUPPORT_SEQUENCE_HPP diff --git a/include/rosa/support/squashed_int.hpp b/include/rosa/support/squashed_int.hpp index 2d69d4b..3e1c031 100644 --- a/include/rosa/support/squashed_int.hpp +++ b/include/rosa/support/squashed_int.hpp @@ -1,140 +1,145 @@ //===-- rosa/support/squashed_int.hpp ---------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/support/squashed_int.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Facilities for squashing integer types into standard equivalents. /// /// \note This implementation is partially based on the \c squashed_int /// implementation of CAF. -/// \todo Check license. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_SQUASHED_INT_HPP #define ROSA_SUPPORT_SQUASHED_INT_HPP #include "rosa/support/type_list.hpp" #include "rosa/support/type_pair.hpp" namespace rosa { /// Compile-time list of integer types. /// /// \note This list is used to select a proper type as \c rosa::type_nr_t, /// always make sure that \c rosa::type_nr_t remains correct whenever changing /// the list. using IntegerTypesBySize = TypeList< // bytes none_t, // 0 TypePair, // 1 TypePair, // 2 none_t, // 3 TypePair, // 4 none_t, // 5 none_t, // 6 none_t, // 7 TypePair // 8 >; /// Squashes integral types (except \c bool) into \c [u]int_[8|16|32|64]_t /// equivalents. /// /// The squashed type for a type \c T can be obtained as \code /// typename SquashedInt::Type /// \endcode /// /// \tparam T the integral type to squash /// /// \pre \p T is an integral type:\code /// std::is_integral::value /// \endcode template struct SquashedInt { STATIC_ASSERT((std::is_integral::value && !std::is_same::value), "squashing a non-integral type or bool"); using TPair = typename TypeListAt::Type; using Type = typename std::conditional::value, typename TPair::First, typename TPair::Second>::type; }; /// Convenience alias for obtaining a squashed integer type. template using squashed_int_t = typename SquashedInt::Type; /// \defgroup SquashedType Implementation for squashing types /// /// \brief Squashes a type. /// /// The squashed type for a type \c T can be obtained as \code /// typename SquashedType::Type /// \endcode /// The resulting type is squashed with \c rosa::SquashedInt if \c T is /// integral but not \c bool, and remains \p T otherwise. ///@{ /// Definition for the general case, when squashing a non-integral type. /// /// \tparam T the type to squash /// \tparam IsIntegral Always use the default value! template ::value> struct SquashedType { using Type = T; }; /// Specialization for the case when squashing an integral type. /// /// \tparam T the type to squash template struct SquashedType { using Type = squashed_int_t; }; /// Specialization for the type \c bool. /// /// \note The type \c bool is an integral type and would be squashed by the /// general case to \c uint8_t without this specialization. template <> struct SquashedType { using Type = bool; }; ///@} /// Convenience alias for obtaining a squashed type. template using squashed_t = typename SquashedType::Type; /// \defgroup SquashedTypeList Implementation for squashing lists of types /// /// \brief Squashes a \c rosa::TypeList elementwise. /// /// Replaces all types in a \c rosa::TypeList with their corresponding squashed /// types by using \c rosa::SquashedType. The squashed \c rosa::TypeList /// corresponding to \c List can be obtained as \code /// typename SquashedTypeList::Type /// \endcode ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to squash template struct SquashedTypeList; // Specialization for \c rosa::EmptyTypeList. template <> struct SquashedTypeList { using Type = EmptyTypeList; }; /// Specialization for non-empty \c rosa::TypeList. template struct SquashedTypeList> { using Type = typename TypeListPush< squashed_t, typename SquashedTypeList>::Type>::Type; }; ///@} } // End namespace rosa #endif // ROSA_SUPPORT_SQUASHED_INT_HPP diff --git a/include/rosa/support/terminal_colors.h b/include/rosa/support/terminal_colors.h index 5f21ab8..2c8ddbe 100644 --- a/include/rosa/support/terminal_colors.h +++ b/include/rosa/support/terminal_colors.h @@ -1,70 +1,76 @@ //===-- rosa/support/terminal_colors.h --------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/support/terminal_colors.h /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Facility for printing colorized text to terminals supporting it. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_TERMINAL_COLORS_H #define ROSA_SUPPORT_TERMINAL_COLORS_H #include namespace rosa { /// Encloses entities related to terminal I/O. namespace terminal { /// Text colors for colorizable terminals. enum class Color { Default, Black, Red, Green, Yellow, Blue, Magenta, Cyan, Lightgrey, Darkgrey, Lightred, Lightgreen, Lightyellow, Lightblue, LightMagenta, Lightcyan, White, NumColors ///< Number of \c rosa::terminal::Color values }; /// Handles \c rosa::terminal::Color values sent to output streams. /// /// The operator sends terminal commands through \p os to the /// associated terminal to change text color to \p color. /// /// \note If \p os is not a terminal output, the terminal commands simply appear /// as text in the stream. /// /// \param [in,out] os \c std::ostream to apply \p color to /// \param color \c rosa::terminal::Color to apply for \p os /// /// \return \p os after applying \p color to it /// /// \pre \p color is valid:\code /// color != Color::NumColors /// \endcode std::ostream &operator<<(std::ostream &os, const Color color); } // End namespace terminal } // End namespace rosa #endif // ROSA_SUPPORT_TERMINAL_COLORS_H diff --git a/include/rosa/support/tokenized_storages.hpp b/include/rosa/support/tokenized_storages.hpp index dca7f4e..3353284 100755 --- a/include/rosa/support/tokenized_storages.hpp +++ b/include/rosa/support/tokenized_storages.hpp @@ -1,620 +1,626 @@ //===-- rosa/support/tokenized_storages.hpp ---------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/support/tokenized_storages.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Definition of storage helper template for storing values in a /// type-safe way based on type tokens. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_TOKENIZED_STORAGES_HPP #define ROSA_SUPPORT_TOKENIZED_STORAGES_HPP #include "rosa/support/type_token.hpp" #include #include namespace rosa { /// Defines a simple interface for storing and accessing values of different /// types. /// /// While the interface provides features to access values and know their /// types, it is the users responsibility to use particular values according to /// their actual types. No facilities for type-safe access of values is /// provided by the class. /// /// \see \c rosa::TokenizedStorage for a type-safe specialization of the /// interface. class AbstractTokenizedStorage { protected: /// Protected constructor restricts instantiation for derived classes. AbstractTokenizedStorage(void) noexcept = default; public: /// No copying and moving of \c rosa::AbstractTokenizedStorage instances. ///@{ AbstractTokenizedStorage(const AbstractTokenizedStorage&) = delete; AbstractTokenizedStorage &operator=(const AbstractTokenizedStorage&) = delete; AbstractTokenizedStorage(AbstractTokenizedStorage&& Other) = delete; AbstractTokenizedStorage &operator=(AbstractTokenizedStorage&&) = delete; ///@} /// Destroys \p this object. virtual ~AbstractTokenizedStorage(void) noexcept = default; /// Tells how many values are stored in \p this object. /// /// \return number of values stored in \p this object virtual size_t size(void) const noexcept = 0; /// Tells the type of the value stored at a position. /// /// \param Pos the index of the value whose type is to returned /// /// \return \c rosa::TypeNumber for the value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code /// Pos < size() /// \endcode virtual TypeNumber typeAt(const token_size_t Pos) const noexcept = 0; /// Provides an untyped pointer for the value stored at a position. /// /// \param Pos the index of the value to return an untyped pointer for /// /// \return untyped pointer for the value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code /// Pos < size() /// \endcode virtual void *pointerTo(const token_size_t Pos) noexcept = 0; /// Provides a constant untyped pointer for the value stored at a position. /// /// \param Pos the index of the value to return an untyped pointer for /// /// \return constant untyped pointer for the value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code /// Pos < Offsets.size() /// \endcode virtual const void *pointerTo(const token_size_t Pos) const noexcept = 0; }; /// Template class storing values and providing dynamic type-safe access to /// them in a lightweight way based on type tokens. /// /// \see rosa/support/type_token.hpp /// /// \tparam Types types whose values are to be stored template class TokenizedStorage; /// \defgroup TokenizedStorageForTypeList Implementation of /// rosa::TokenizedStorageForTypeList /// /// \brief Transforms a \c rosa::TypeList instance to the corresponding /// \c rosa::TokenizedStorage instance. /// /// A \c rosa::TypeList \c List instance can be turned into a corresponding \c /// rosa::TokenizedStorage instance as \code /// typename TokenizedStorageForTypeList::Type /// \endcode /// /// For example, the following expression evaluates to `true`: \code /// std::is_same>::Type, /// TokenizedStorage>::value /// \endcode ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to transform template struct TokenizedStorageForTypeList; /// Implementation of the template for \c rosa::TypeList instances. template struct TokenizedStorageForTypeList> { using Type = TokenizedStorage; }; ///@} /// Nested namespace with implementation for \c rosa::TokenizedStorage, consider /// it private. namespace { /// Initializes a pre-allocated memory area with values from constant lvalue /// references. /// /// \tparam Types types whose values are to be stored /// /// \param Arena pre-allocated memory area to store values to /// \param Ts the values to store in \p Arena /// /// \note \p Arena needs to be a valid pointer to a memory area big enough for /// values of \p Types. template inline void createArenaElements(void *const Arena, const Types &... Ts) noexcept; /// \defgroup createLvalueArenaElement Implementation of creating lvalue arena elements /// /// Stores values from constant lvalue references into a pre-allocated memory /// area. /// /// \note To be used by the implementation of \c createArenaElements. /// /// \todo Document these functions. ///@{ /// \note This terminal case is used for both constant lvalue references and /// value references. template inline void createArenaElement(void *const, const std::vector &Offsets) { ASSERT(Pos == Offsets.size()); } template inline void createArenaElement(void *const Arena, const std::vector &Offsets, const Type &T, const Types &... Ts) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); new (static_cast(static_cast(static_cast(Arena) + Offsets[Pos]))) Type(T); createArenaElement(Arena, Offsets, Ts...); } template inline void createArenaElement(void *const Arena, const std::vector &Offsets, const AtomConstant &, const Types &... Ts) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); *static_cast( static_cast(static_cast(Arena) + Offsets[Pos])) = V; createArenaElement(Arena, Offsets, Ts...); } ///@} /// Implementation of the template. /// /// \tparam Types types of values to store /// /// \param Arena pre-allocated memory area to store values to /// \param Ts values to store in \p Arena /// /// \pre \p Arena is not \p nullptr. template inline void createArenaElements(void *const Arena, const Types &... Ts) noexcept { ASSERT(Arena != nullptr); createArenaElement<0>(Arena, TokenizedStorage::Offsets, Ts...); } /// Initializes a pre-allocated memory area with values from rvalue references. /// /// \tparam Types types whose values are to be stored /// /// \param Arena pre-allocated memory area to store values to /// \param Ts the values to store in \p Arena /// /// \note \p Arena needs to be a valid pointer to a memory area big enough for /// values of \p Types. template inline void createArenaElements(void *const Arena, Types &&... Ts) noexcept; /// \defgroup createRvalueArenaElement Implementation of creating rvalue arena elements /// /// Stores values from rvalue references into a pre-allocated memory area. /// /// \note To be used by the implementation of \c createArenaElements. /// /// \todo Document these functions. ///@{ template inline void createArenaElement(void *const Arena, const std::vector &Offsets, Type &&T, Types &&... Ts) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); new (static_cast(static_cast( static_cast(Arena) + Offsets[Pos]))) Type(std::move(T)); createArenaElement(Arena, Offsets, std::move(Ts)...); } template inline void createArenaElement(void *const Arena, const std::vector &Offsets, AtomConstant &&, Types &&... Ts) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); *static_cast( static_cast(static_cast(Arena) + Offsets[Pos])) = V; createArenaElement(Arena, Offsets, std::move(Ts)...); } ///@} /// Implementation of the template. /// /// \tparam Types types of values to store /// /// \param Arena pre-allocated memory area to store values to /// \param Ts values to store in \p Arena /// /// \pre \p Arena is not \c nullptr. template inline void createArenaElements(void *const Arena, Types &&... Ts) noexcept { ASSERT(Arena != nullptr); createArenaElement<0>(Arena, TokenizedStorage::Offsets, std::move(Ts)...); } /// Destroys values allocated by \c createArenaElements. /// /// \tparam Types types whose values are stored in \p Arena /// /// \param Arena the memory area to destroy values from /// /// \note \p Arena needs to be a valid pointer to a memory area where values of /// \p Types are stored. template inline void destroyArenaElements(void *const Arena) noexcept; /// \defgroup destroyArenaElement Implementation of destroying arena elements /// /// Destroys values from a memory area. /// /// \note To be used by the implementation of \c destroyArenaElements. /// /// \todo Document these functions. ///@{ template inline void destroyArenaElement(void *const, const std::vector &Offsets) noexcept { ASSERT(Pos == Offsets.size()); } template inline void destroyArenaElement(void *const Arena, const std::vector &Offsets) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); static_cast( static_cast(static_cast(Arena) + Offsets[Pos])) ->~Type(); destroyArenaElement(Arena, Offsets); } ///@} /// Implementation of the template. /// /// \tparam Types types of values to destroy /// /// \param Arena the memory area to destroy values from /// /// \pre \p Arena is not \c nullptr. template inline void destroyArenaElements(void *const Arena) noexcept { ASSERT(Arena != nullptr); destroyArenaElement<0, Types...>(Arena, TokenizedStorage::Offsets); } } // End namespace /// Implementation of the template \c rosa::TokenizedStorage as a /// specialization of \c rosa::AbstractTokenizedStorage. /// /// The class provides facilities for storing values and providing type-safe /// access to them. /// /// \tparam Types types of values to store template class TokenizedStorage : public AbstractTokenizedStorage { public: /// \c rosa::Token for the stored values. static constexpr Token ST = TypeToken...>::Value; /// Byte offsets to access stored values in \c rosa::TokenizedStorage::Arena. static const std::vector Offsets; private: /// A BLOB storing all the values one after the other. void *const Arena; /// Generates byte offsets for accessing values stored in /// \c rosa::TokenizedStorage::Arena. /// /// \return \c std::vector containing byte offsets for accessing values stored /// in \c rosa::TokenizedStorage::Arena static std::vector offsets(void) noexcept { Token T = ST; // Need a mutable copy. const token_size_t N = lengthOfToken(T); // Number of types encoded in \c T. std::vector O(N); // Allocate vector of proper size. // Do nothing for 0 elements. if (N > 0) { token_size_t I = 0; // Start indexing from position \c 0. O[0] = 0; // First offset is always \c 0. while (I < N - 1) { ASSERT(I + 1 < O.size() && lengthOfToken(T) == N - I); // Calculate next offset based on the previous one. // \note The offset of the last value is stored at `O[N - 1]`, which is // set when `I == N - 2`. Hence the limit of the loop. O[I + 1] = O[I] + sizeOfHeadOfToken(T); dropHeadOfToken(T), ++I; } ASSERT(I + 1 == O.size() && lengthOfToken(T) == 1); } return O; } public: /// Creates an instance with default values. /// /// \note This constructor requires that all actual template arguments \p /// Types... are default constructible. TokenizedStorage(void) noexcept : Arena(::operator new(sizeOfValuesOfToken(ST))) { ASSERT(Arena != nullptr); // Sanity check. createArenaElements(Arena, Types()...); } /// Creates an instance from constant lvalue references. /// /// \param Ts values to store TokenizedStorage(const std::decay_t &... Ts) noexcept : Arena(::operator new(sizeOfValuesOfToken(ST))) { ASSERT(Arena != nullptr); // Sanity check. createArenaElements(Arena, Ts...); } /// Creates an instance from rvalue references. /// /// \param Ts values to store TokenizedStorage(std::decay_t &&... Ts) noexcept : Arena(::operator new(sizeOfValuesOfToken(ST))) { ASSERT(Arena != nullptr); // Sanity check. createArenaElements(Arena, std::move(Ts)...); } /// No copying and moving of \c rosa::TokenizedStorage instances. /// /// \note This restriction may be relaxed as moving should be easy to /// implement, only requires the possiblity to validate Arena pointer. ///@{ TokenizedStorage(const TokenizedStorage&) = delete; TokenizedStorage &operator=(const TokenizedStorage&) = delete; TokenizedStorage(TokenizedStorage&& Other) = delete; TokenizedStorage &operator=(TokenizedStorage&&) = delete; ///@} // Destroys \p this object. ~TokenizedStorage(void) { destroyArenaElements...>(Arena); ::operator delete(Arena); } /// Tells how many values are stored in \p this object. /// /// \return number of values stored in \p this object size_t size(void) const noexcept override { return Offsets.size(); } /// Tells the type of the value stored at a position. /// /// \param Pos the index of the value whose type is to returned /// /// \return \c rosa::TypeNumber for the value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code /// Pos < size() /// \endcode TypeNumber typeAt(const token_size_t Pos) const noexcept override { ASSERT(Pos < size()); Token TT = ST; dropNOfToken(TT, Pos); return headOfToken(TT); } /// Provides an untyped pointer for the value stored at a position. /// /// \param Pos the index of the value to return an untyped pointer for /// /// \return untyped pointer for the value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code /// Pos < size() /// \endcode void *pointerTo(const token_size_t Pos) noexcept override { ASSERT(Pos < size()); return static_cast(Arena) + Offsets[Pos]; } /// Provides a constant untyped pointer for the value stored at a position. /// /// \param Pos the index of the value to return an untyped pointer for /// /// \return constant untyped pointer for the value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code /// Pos < Offsets.size() /// \endcode const void *pointerTo(const token_size_t Pos) const noexcept override { ASSERT(Pos < size()); return static_cast(Arena) + Offsets[Pos]; } /// Tells if the value stored at a given index is of a given type. /// /// \note Any \c rosa::AtomConstant is encoded in \c rosa::Token as /// the \c rosa::AtomValue wrapped into it. /// /// \tparam T type to match against /// /// \param Pos index the type of the value at is to be matched against \p Type /// /// \return if the value at index \p Pos of type \p T /// /// \pre \p Pos is a valid index:\code /// Pos < Offsets.size() /// \endcode template bool isTypeAt(const size_t Pos) const noexcept { ASSERT(Pos < size()); Token TT = ST; dropNOfToken(TT, Pos); return isHeadOfTokenTheSameType(TT); } /// Gives a reference of a value of a given type stored at a given index. /// /// \note The constant variant of the function relies on this implementation, /// the function may not modify \p this object! /// /// \tparam T type to give a reference of /// /// \param Pos index to set the reference for /// /// \return reference of \p T for the value stored at index \p Pos /// /// \pre \p Pos is a valid index and the value at index \p Pos is of type /// \p T: /// \code /// Pos < Size && isTypeAt(Pos) /// \endcode template T &valueAt(const token_size_t Pos) noexcept { ASSERT(Pos < size() && isTypeAt(Pos)); return *static_cast(pointerTo(Pos)); } /// Gives a constant reference of a value of a given type stored at a given /// index. /// /// \tparam T type to give a reference of /// /// \param Pos index to set the reference for /// /// \return constant reference of \p T for the value stored at index \p Pos /// /// \pre \p Pos is a valid index and the value at index \p Pos is of type /// \p T: /// \code /// Pos < Size && isTypeAt(Pos) /// \endcode template const T &valueAt(const token_size_t Pos) const noexcept { // \note Just use the non-const implementation as that does not modify // \p this object. return const_cast(this)->valueAt(Pos); } }; // Implementation of the static member field \c rosa::TokenizedStorage::Offsets. template const std::vector TokenizedStorage::Offsets = TokenizedStorage::offsets(); /// Specialization of the template \c rosa::TokenizedStorage for storing /// nothing. /// /// \note The specialization implements the interface defined by \c /// rosa::AbstractTokenizedStorage but most of the functions cannot be called /// because nothing is stored in instances of the class. template <> class TokenizedStorage<> : public AbstractTokenizedStorage { public: /// \c rosa::Token for the stored values. static constexpr Token ST = TypeToken<>::Value; /// Byte offsets to access stored values in \c rosa::TokenizedStorage::Arena. static const std::vector Offsets; /// Creates an instance. TokenizedStorage(void) noexcept {} /// No copying and moving of \c rosa::TokenizedStorage instances. /// /// \note This restriction may be relaxed as moving should be easy to /// implement, only requires the possiblity to validate Arena pointer. ///@{ TokenizedStorage(const TokenizedStorage &) = delete; TokenizedStorage &operator=(const TokenizedStorage &) = delete; TokenizedStorage(TokenizedStorage &&Other) = delete; TokenizedStorage &operator=(TokenizedStorage &&) = delete; ///@} // Destroys \p this object. ~TokenizedStorage(void) {} /// Tells how many values are stored in \p this object. /// /// \return `0` size_t size(void) const noexcept override { return 0; } /// Tells the type of the value stored at a position. /// /// \pre Do not call. TypeNumber typeAt(const token_size_t) const noexcept override { ASSERT(false); return TypeNumber(0); } /// Provides an untyped pointer for the value stored at a position. /// /// \pre Do not call. void *pointerTo(const token_size_t) noexcept override { ASSERT(false); return nullptr; } /// Provides a constant untyped pointer for the value stored at a position. /// /// \pre Do not call. const void *pointerTo(const token_size_t) const noexcept override { ASSERT(false); return nullptr; } /// Tells if the value stored at a given index is of a given type. /// /// \pre Do not call. template bool isTypeAt(const size_t) const noexcept { ASSERT(false); return false; } /// Gives a reference of a value of a given type stored at a given index. /// /// \tparam T type to give a reference of /// \pre Do not call. template T &valueAt(const token_size_t) noexcept { ASSERT(false); return *static_cast(nullptr); } /// Gives a constant reference of a value of a given type stored at a given /// index. /// /// \tparam T type to give a reference of /// /// \pre Do not call. template const T &valueAt(const token_size_t) const noexcept { // \note Just use the non-const implementation as that does not modify // \p this object. return *static_cast(nullptr); } }; } // End namespace rosa #endif // ROSA_SUPPORT_TOKENIZED_STORAGES_HPP diff --git a/include/rosa/support/type_helper.hpp b/include/rosa/support/type_helper.hpp index 5e683d0..82021ee 100644 --- a/include/rosa/support/type_helper.hpp +++ b/include/rosa/support/type_helper.hpp @@ -1,137 +1,143 @@ //===-- rosa/support/type_helper.hpp ----------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/support/type_helper.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Helper facilities for type-related stuff. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_TYPE_HELPER_HPP #define ROSA_SUPPORT_TYPE_HELPER_HPP #include #include namespace rosa { /* ************************************************************************** * * Printable * * ************************************************************************** */ /// \defgroup PrintableType Implementation of rosa::PrintableType /// /// A value of type \c [u]int8_t is treated as a character when being put to an /// output stream, which can result in invisible characters being printed. To /// avoid that, such a value needs to be casted to a wider type. It can be done /// by using the following template to find a target type to cast our value to. /// The template also turns enumerations into their underlying types. Moreover, /// any reference type is turned into the referred type and any non-const type /// is turned into their const-qualified type. /// /// \note It is important to remove references before checking /// const-qualification because constant references are not const-qualified /// types. /// /// The corresponding printable type for a type \c T can be obtained as \code /// typename PrintableType::Type /// \endcode ///@{ /// Definition for the general case /// /// \tparam T type to cast /// \tparam IsReference always use the default value! /// \tparam IsConst always use the default value! /// \tparam IsEnum always use the default value! template ::value, bool IsConst = std::is_const::value, bool IsEnum = std::is_enum::value> struct PrintableType { using Type = T; }; /// Specialization for reference types. template struct PrintableType { using Type = typename PrintableType::type>::Type; }; /// Specialization for non-reference, non-const types. template struct PrintableType { using Type = typename PrintableType::Type; }; /// Specialization for non-reference, const, enum types. template struct PrintableType { using Type = typename PrintableType::type>::Type; }; /// Specialization for \c const uint8_t. template <> struct PrintableType { using Type = const unsigned int; }; /// Specialization for \c const int8_t. template <> struct PrintableType { using Type = const int; }; ///@} /// Convenience template alias for using \c rosa::PrintableType. template using printable_t = typename PrintableType::Type; /// Casts values to their corresponding printable types. /// /// \param V value to cast #define PRINTABLE(V) static_cast>(V) /* ************************************************************************** * * Unsigned * * ************************************************************************** */ /// \defgroup Unsigned Implemtation of rosa::Unsigned /// /// \brief Converts integral types to their corresponding unsigned type. /// /// Provides the unsigned integer type corresponding to \c T` if \c T is an /// integral (except \c bool) or enumeration type. Keeps \c T otherwise. /// /// The corresponding unsigned type for a type \c T can be obtained as \code /// typename Unsigned::Type /// \endcode ///@{ /// Definition for the general case when converting a non-integral type. /// /// \tparam T type to convert /// \tparam IsIntegral always use the default value! template ::value> struct Unsigned { using Type = T; }; /// Specialization for the case when converting an integral type. template struct Unsigned { using Type = typename std::make_unsigned::type; }; ///@} /// Convenience template alias for using \c rosa::Unsigned. template using unsigned_t = typename Unsigned::Type; } // End namespace rosa #endif // ROSA_SUPPORT_TYPE_HELPER_HPP diff --git a/include/rosa/support/type_list.hpp b/include/rosa/support/type_list.hpp index b3e8107..ad06837 100644 --- a/include/rosa/support/type_list.hpp +++ b/include/rosa/support/type_list.hpp @@ -1,468 +1,473 @@ //===-- rosa/support/type_list.hpp ------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/support/type_list.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Facilities for types representing lists of types. /// /// \note This implementation is partially based on the \c type_list /// implementation of CAF. -/// \todo Check license. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_TYPE_LIST_HPP #define ROSA_SUPPORT_TYPE_LIST_HPP #include "rosa/support/debug.hpp" #include "rosa/support/types.hpp" #include namespace rosa { /// A list of types. /// /// \tparam Ts types to make a list of template struct TypeList { /// Constructor, needs to do nothing. constexpr TypeList(void) {} }; /// The empty \c rosa::Typelist. using EmptyTypeList = TypeList<>; /// \defgroup TypeListAtImpl Implementation of rosa::TypeListAt /// /// \brief Gets the type at index \p Pos from a list of types. /// /// \note Only to be used by the implementation of \c rosa::TypeListAt. ///@{ /// Declaration of the template. /// /// \tparam Pos index to take the element from /// \tparam Ts types template struct TypeListAtImpl; /// Definition for the general case when \p Pos is not \c 0 and there is type in /// the list. template struct TypeListAtImpl { using Type = typename TypeListAtImpl::Type; }; /// Specialization for the case when \p Pos is \c 0. template struct TypeListAtImpl<0, T, Ts...> { using Type = T; }; /// Specialization for the case when there is no more type. /// /// In this case, the found type is \c rosa::none_t. template struct TypeListAtImpl { using Type = none_t; }; ///@} /// \defgroup TypeListAt Definition of rosa::TypeListAt /// /// \brief Gets the element at index \p Pos of \p List. /// /// /// The type at index \c Pos in a \c rosa::TypeList \c List can be obtained as /// \code /// typename TypeListAt::Type /// \endcode /// /// \note The resulting type is \c rosa::none_t if \code /// TypeListSize::Value < Pos /// \endcode ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to take an element from /// \tparam Pos index to take the element from template struct TypeListAt; /// Implementation using \c rosa::TypeListAtImpl. template struct TypeListAt, Pos> { using Type = typename TypeListAtImpl::Type; }; ///@} /// \defgroup TypeListIndexOfImpl Implementation of rosa::TypeListIndexOf /// /// \brief Tells the index of the first occurence of a type in a list of types. /// /// \note Only to be used by the implementation of \c rosa::TypeListIndexOf. ///@{ /// Declaration of the template. /// /// \tparam Pos the number types already being checked from the beginning of the /// list /// \tparam X type to search for /// \tparam Ts remaining list of types template struct TypeListIndexOfImpl; /// Specialization for the case when the list is over. /// /// In this case, the found index is \c -1. template struct TypeListIndexOfImpl { static constexpr int Value = -1; }; /// Specialization for the case when the first type in the remaining list /// is a match. template struct TypeListIndexOfImpl { static constexpr int Value = Pos; }; /// Implementation for the general case when need to continue looking. template struct TypeListIndexOfImpl { static constexpr int Value = TypeListIndexOfImpl::Value; }; ///@} /// \defgroup TypeListIndexOf Definition of rosa::TypeListIndexOf /// /// \brief Tells the index of the first occurence of type in a /// \c rosa::TypeList. /// /// The index of the first occurence of type \c T in \c rosa::TypeList \c List /// can be obtained as \code /// TypeListIndexOf::Value /// \endcode /// /// \note The resulting index is \c -1 if \c T is not present in \c List. ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to search in /// \tparam T type to search for template struct TypeListIndexOf; /// Implementation of the template using \c rosa::TypeListIndexOfImpl. template struct TypeListIndexOf, T> { static constexpr int Value = TypeListIndexOfImpl<0, T, Ts...>::Value; }; ///@} /// \defgroup TypeListHead Implementation of rosa::TypeListHead /// /// \brief Gets the first element of a \c rosa::TypeList. /// /// The first element of a \c rosa::TypeList \c List can be obtained as \code /// typename TypeListHead::Type /// \endcode /// /// \note The resulting type is \c rosa::none_t if \c List is /// \c rosa::EmptyTypeList. ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to get the first element of template struct TypeListHead; /// Specialization for \c rosa::EmptyTypeList. /// /// In this case, the found type is \c rosa::none_t. template <> struct TypeListHead { using Type = none_t; }; /// Implementation for a non-empty \c rosa::TypeList. template struct TypeListHead> { using Type = T; }; ///@} /// \defgroup TypeListTail Implementation of rosa::TypeListTail /// /// \brief Gets the tail of a \c rosa::TypeList. /// /// The tail of a \c rosa::TypeList \c List, that is \c List except for its /// first element, can be obtained as \code /// typename TypeListTail::Type /// \endcode /// /// \note If \c List is \c rosa::EmptyTypeList, then the resulting type is also /// \c rosa::EmptyTypeList. ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to take the tail of template struct TypeListTail; /// Specialization for \c rosa::EmptyTypeList. /// /// In this case, the resulting type is \c rosa::EmptyTypeList. template <> struct TypeListTail { using Type = EmptyTypeList; }; /// Implementation for a non-empty \c rosa::TypeList. template struct TypeListTail> { using Type = TypeList; }; ///@} /// \defgroup TypeListPush Implementation of rosa::TypeListPush /// /// \brief Extends a \c rosa::TypeList with a type. /// /// Whether the new type is pushed in the front or in the back of the /// \c rosa::TypeList depends on the order of template arguments, as shown in /// the following example: \code /// using List = TypeList /// typename TypeListPush::Type; // TypeList /// typename TypeListPush::Type; // TypeList /// \endcode ///@{ /// Declaration of the template. /// /// \tparam P a type if \p Q is a \c rosa::TypeList, a \c rosa::TypeList /// otherwise /// \tparam Q a type if \p P is a \c rosa::TypeList, a \c rosa::TypeList /// otherwise template struct TypeListPush; /// Implementation for the case when pushing at the back of the /// \c rosa::TypeList. template struct TypeListPush, T> { using Type = TypeList; }; /// Implementation for the case when pushing to the front of the /// \c rosa::TypeList. template struct TypeListPush> { using Type = TypeList; }; ///@} /// \defgroup TypeListDrop Implementation of rosa::TypeListDrop /// /// \brief Drops some elements from the beginning of a \c rosa::TypeList. /// /// The first \c N types of a \c rosa::TypeList \c List can be dropped as \code /// typename TypeListDrop::Type /// \endcode ///@{ /// Declaration of the template. /// /// \tparam N number of types to drop /// \tparam List \c rosa::TypeList to drop the first \p N element of template struct TypeListDrop; /// Specialization for \c rosa::EmptyTypeList. template struct TypeListDrop { using Type = EmptyTypeList; }; /// Implementation for a non-empty \c rosa::TypeList. template struct TypeListDrop> { using Type = typename std::conditional< N == 0, TypeList, typename TypeListDrop>::Type>::type; }; ///@} /// \defgroup TypeListConcat Implementation of rosa::TypeListConcat /// /// \brief Concatenates two \c rosa::TypeList instances. /// /// Two instances of \c rosa::TypeList \c List1 and \c List2 can be /// concatenated as \code /// typename TypeListConcat::Type /// \endcode ///@{ /// Declaration of the template /// /// \tparam List1 the first instance of \c rosa::TypeList /// \tparam List2 the second instance of \c rosa::TypeList template struct TypeListConcat; /// Implementation of the template for \c rosa::TypeList instances. template struct TypeListConcat, TypeList> { using Type = TypeList; }; ///@} /// \defgroup TypeListSize Implementation of rosa::TypeListSize /// /// \brief Tells the number of types stored in a \c rosa::TypeList. /// /// The size of a \c rosa::TypeList \c List can be obtained as \code /// TypeListSize::Value /// \endcode ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to get the size of template struct TypeListSize; /// Implementation of the template. template struct TypeListSize> { static constexpr size_t Value = sizeof...(Ts); }; template constexpr size_t TypeListSize>::Value; ///@} /// Tests whether a \c rosa::TypeList is empty. /// /// \tparam List \c rosa::TypeList to check template struct TypeListEmpty { /// Denotes whether \p List is an empty \c rosa::TypeList or not. static constexpr bool Value = std::is_same::value; }; /// \defgroup TypeListContains Implementation of rosa::TypeListContains /// /// \brief Tells if a \c rosa::TypeList contains a given type. /// /// Whether a \c rosa::TypeList \c List contains the type \c T can be checked as /// \code /// TypeListContains::Value /// \endcode ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to search in /// \tparam T type to search for template struct TypeListContains; /// Implementation of the template. template struct TypeListContains, T> { static constexpr bool Value = std::conditional, T>::Value == -1, std::false_type, std::true_type>::type::value; }; ///@} /// \defgroup TypeListSubsetOf Implementation of rosa::TypeListSubsetOf /// /// \brief Tells if a \c rosa::TypeList is a subset of another one. /// /// Whether a \c rosa::TypeList \c ListA is a subset of another /// \c rosa::TypeList \c ListB can be checked as \code /// TypeListSubsetOf::Value /// \endcode ///@{ /// Declaration of the template. /// /// \tparam ListA \c rosa::TypeList to check if is a subset of \p ListB /// \tparam ListB \c rosa::TypeList to check if is a superset of \p ListA /// \tparam Fwd always use the default value! template struct TypeListSubsetOf; /// Specialization for the case when all the elements of the original \p ListA /// was found in \p ListB. template struct TypeListSubsetOf { static constexpr bool Value = true; }; /// Specializaton for the case when an element of the original \p ListA cannot /// be found in \p ListB. template struct TypeListSubsetOf { static constexpr bool Value = false; }; /// Definition for the general case. template struct TypeListSubsetOf, List> : TypeListSubsetOf, List, TypeListContains::Value> {}; ///@} /// \defgroup TypeListFindImpl Implementation of rosa::TypeListFind /// /// \brief Finds the first type in a list of types that satisfies a predicate. /// /// \note Only to be used by the implementation of \c rosa::TypeListFind. ///@{ /// Declaration of the template. /// /// \tparam Pred the predicate to check types against /// \tparam Ts list of types to check template