Page MenuHomePhorge

No OneTemporary

Size
700 KB
Referenced Files
None
Subscribers
None
This file is larger than 256 KB, so syntax highlighting was skipped.
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 <fstream>
using namespace rosa;
using namespace rosa::agent;
using namespace rosa::deluxe;
using namespace rosa::terminal;
const std::string AppName = "SA-EWS1";
/// Paths for the CSV files for simulation.
///
///@{
const std::string HRCSVPath = "HR.csv";
const std::string BRCSVPath = "BR.csv";
const std::string SpO2CSVPath = "SpO2.csv";
const std::string BPSysCSVPath = "BPSys.csv";
const std::string BodyTempCSVPath = "BodyTemp.csv";
const std::string ScoreCSVPath = "Score.csv";
///@}
/// How many cycles of simulation to perform.
const size_t NumberOfSimulationCycles = 16;
/// Warning levels for abstraction.
enum WarningScore { No = 0, Low = 1, High = 2, Emergency = 3 };
/// Helper function creating a deluxe agent for pre-processing sensory values.
///
/// Received values are first validated for confidence. Values which the
/// validator does not mark confident are ignored. Confident values are
/// abstracted into a \c WarningScore value, which is the result of the
/// processing function.
///
/// \note The result, \c WarningScore, is returned as \c uint32_t because
/// enumeration types are not integrated into built-in types. Hence, a master
/// to these agents receives its input as \c uint32_t values, and may cast them
/// to \c WarningScore explicitly.
///
/// \tparam T type of values to receive from the sensor
///
/// \param C the deluxe context to create the agent in
/// \param Name name of the new agent
/// \param CC confidence validator to use
/// \param A abstraction to use
///
/// \return handle for the new agent
template <typename T>
AgentHandle createLowLevelAgent(std::unique_ptr<DeluxeContext> &C,
const std::string &Name,
const Confidence<T> &CC,
const Abstraction<T, WarningScore> &A) {
using handler = std::function<Optional<uint32_t>(std::pair<T, bool>)>;
using result = Optional<uint32_t>;
return C->createAgent(
Name, handler([&, Name](std::pair<T, bool> I) -> result {
LOG_INFO_STREAM << "\n******\n"
<< Name << " " << (I.second ? "<New>" : "<Old>")
<< " value: " << I.first << "\n******\n";
return (I.second && CC(I.first)) ? result(A(I.first)) : result();
}));
}
int main(void) {
LOG_INFO_STREAM
<< '\n'
<< library_string() << " -- " << Color::Red << AppName << "app"
<< Color::Default << '\n'
<< Color::Yellow
<< "CSV files are read from and written to the current working directory."
<< Color::Default << '\n';
std::unique_ptr<DeluxeContext> C = DeluxeContext::create(AppName);
//
// Create deluxe sensors.
//
LOG_INFO("Creating sensors.");
// All sensors are created without defining a normal generator function, but
// with the default value of the second argument. That, however, requires the
// data type to be explicitly defined. This is good for simulation only.
AgentHandle HRSensor = C->createSensor<int32_t>("HR Sensor");
AgentHandle BRSensor = C->createSensor<int32_t>("BR Sensor");
AgentHandle SpO2Sensor = C->createSensor<int32_t>("SpO2 Sensor");
AgentHandle BPSysSensor = C->createSensor<int32_t>("BPSys Sensor");
AgentHandle BodyTempSensor = C->createSensor<float>("BodyTemp Sensor");
//
// Create functionalities.
//
LOG_INFO("Creating Functionalities for Agents.");
//
// Define confidence validators.
//
// Lower bounds are inclusive and upper bounds are exclusive.
Confidence<int32_t> HRConfidence(0, 501);
Confidence<int32_t> BRConfidence(0, 301);
Confidence<int32_t> SpO2Confidence(0, 101);
Confidence<int32_t> BPSysConfidence(0,501);
Confidence<float> BodyTempConfidence(-60,
nextRepresentableFloatingPoint(50.0f));
//
// Define abstractions.
//
RangeAbstraction<int32_t, WarningScore> HRAbstraction(
{{{0, 40}, Emergency},
{{40, 51}, High},
{{51, 60}, Low},
{{60, 100}, No},
{{100, 110}, Low},
{{110, 129}, High},
{{129, 200}, Emergency}},
Emergency);
RangeAbstraction<int32_t, WarningScore> BRAbstraction({{{0, 9}, High},
{{9, 14}, No},
{{14, 20}, Low},
{{20, 29}, High},
{{29, 50}, Emergency}},
Emergency);
RangeAbstraction<int32_t, WarningScore> SpO2Abstraction({{{1, 85}, Emergency},
{{85, 90}, High},
{{90, 95}, Low},
{{95, 100}, No}},
Emergency);
RangeAbstraction<int32_t, WarningScore> BPSysAbstraction(
{{{0, 70}, Emergency},
{{70, 81}, High},
{{81, 101}, Low},
{{101, 149}, No},
{{149, 169}, Low},
{{169, 179}, High},
{{179, 200}, Emergency}},
Emergency);
RangeAbstraction<float, WarningScore> BodyTempAbstraction(
{{{0.f, 28.f}, Emergency},
{{28.f, 32.f}, High},
{{32.f, 35.f}, Low},
{{35.f, 38.f}, No},
{{38.f, 39.5f}, High},
{{39.5f, 100.f}, Emergency}},
Emergency);
//
// Create low-level deluxe agents with \c createLowLevelAgent.
//
LOG_INFO("Creating low-level agents.");
AgentHandle HRAgent =
createLowLevelAgent(C, "HR Agent", HRConfidence, HRAbstraction);
AgentHandle BRAgent =
createLowLevelAgent(C, "BR Agent", BRConfidence, BRAbstraction);
AgentHandle SpO2Agent =
createLowLevelAgent(C, "SpO2 Agent", SpO2Confidence, SpO2Abstraction);
AgentHandle BPSysAgent =
createLowLevelAgent(C, "BPSys Agent", BPSysConfidence, BPSysAbstraction);
AgentHandle BodyTempAgent = createLowLevelAgent(
C, "BodyTemp Agent", BodyTempConfidence, BodyTempAbstraction);
//
// Connect sensors to low-level agents.
//
LOG_INFO("Connect sensors to their corresponding low-level agents.");
C->connectSensor(HRAgent, 0, HRSensor, "HR Sensor Channel");
C->connectSensor(BRAgent, 0, BRSensor, "BR Sensor Channel");
C->connectSensor(SpO2Agent, 0, SpO2Sensor, "SpO2 Sensor Channel");
C->connectSensor(BPSysAgent, 0, BPSysSensor, "BPSys Sensor Channel");
C->connectSensor(BodyTempAgent, 0, BodyTempSensor, "BodyTemp Sensor Channel");
//
// Create a high-level deluxe agent.
//
LOG_INFO("Create high-level agent.");
// The new agent logs its input values and results in the the sum of them.
AgentHandle BodyAgent = C->createAgent(
"Body Agent",
std::function<Optional<uint32_t>(
std::pair<uint32_t, bool>, std::pair<uint32_t, bool>,
std::pair<uint32_t, bool>, std::pair<uint32_t, bool>,
std::pair<uint32_t, bool>)>(
[](std::pair<uint32_t, bool> HR, std::pair<uint32_t, bool> BR,
std::pair<uint32_t, bool> SpO2, std::pair<uint32_t, bool> BPSys,
std::pair<uint32_t, bool> BodyTemp) -> Optional<uint32_t> {
LOG_INFO_STREAM << "\n*******\nBody Agent trigged with values:\n"
<< (HR.second ? "<New>" : "<Old>")
<< " HR warning score: " << HR.first << "\n"
<< (BR.second ? "<New>" : "<Old>")
<< " BR warning score: " << BR.first << "\n"
<< (SpO2.second ? "<New>" : "<Old>")
<< " SpO2 warning score: " << SpO2.first << "\n"
<< (BPSys.second ? "<New>" : "<Old>")
<< " BPSys warning score: " << BPSys.first << "\n"
<< (BodyTemp.second ? "<New>" : "<Old>")
<< " BodyTemp warning score: " << BodyTemp.first
<< "\n******\n";
return {HR.first + BR.first + SpO2.first + BPSys.first +
BodyTemp.first};
}));
//
// Connect low-level agents to the high-level agent.
//
LOG_INFO("Connect low-level agents to the high-level agent.");
C->connectAgents(BodyAgent, 0, HRAgent, "HR Agent Channel");
C->connectAgents(BodyAgent, 1, BRAgent, "BR Agent Channel");
C->connectAgents(BodyAgent, 2, SpO2Agent, "SpO2 Agent Channel");
C->connectAgents(BodyAgent, 3, BPSysAgent, "BPSys Agent Channel");
C->connectAgents(BodyAgent, 4, BodyTempAgent, "BodyTemp Agent Channel");
//
// For simulation output, create a logger agent writing the output of the
// high-level agent into a CSV file.
//
LOG_INFO("Create a logger agent.");
// Create CSV writer.
std::ofstream ScoreCSV(ScoreCSVPath);
csv::CSVWriter<uint32_t> ScoreWriter(ScoreCSV);
// The agent writes each new input value into a CSV file and produces nothing.
AgentHandle LoggerAgent = C->createAgent(
"Logger Agent",
std::function<Optional<unit_t>(std::pair<uint32_t, bool>)>(
[&ScoreWriter](std::pair<uint32_t, bool> Score) -> Optional<unit_t> {
if (Score.second) {
// The state of \p ScoreWriter is not checked, expecting good.
ScoreWriter << Score.first;
}
return {};
}));
//
// Connect the high-level agent to the logger agent.
//
LOG_INFO("Connect the high-level agent to the logger agent.");
C->connectAgents(LoggerAgent, 0, BodyAgent, "Body Agent Channel");
//
// Do simulation.
//
LOG_INFO("Setting up and performing simulation.");
//
// Initialize deluxe context for simulation.
//
C->initializeSimulation();
//
// Open CSV files and register them for their corresponding sensors.
//
// Type aliases for iterators.
- using CSVInt = csv::CSVFlatIterator<int32_t>;
- using CSVFloat = csv::CSVFlatIterator<float>;
+ using CSVInt = csv::CSVIterator<int32_t>;
+ using CSVFloat = csv::CSVIterator<float>;
std::ifstream HRCSV(HRCSVPath);
C->registerSensorValues(HRSensor, CSVInt(HRCSV), CSVInt());
std::ifstream BRCSV(BRCSVPath);
C->registerSensorValues(BRSensor, CSVInt(BRCSV), CSVInt());
std::ifstream SpO2CSV(SpO2CSVPath);
C->registerSensorValues(SpO2Sensor, CSVInt(SpO2CSV), CSVInt());
std::ifstream BPSysCSV(BPSysCSVPath);
C->registerSensorValues(BPSysSensor, CSVInt(BPSysCSV), CSVInt());
std::ifstream BodyTempCSV(BodyTempCSVPath);
C->registerSensorValues(BodyTempSensor, CSVFloat(BodyTempCSV), CSVFloat());
//
// Simulate.
//
C->simulate(NumberOfSimulationCycles);
return 0;
}
diff --git a/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 <https://cmake.org/cmake/help/latest/module/CPack.html>`_.
* 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 <build_deps>` 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 <nortti-noexceptions>`, 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 <http://www.boost.org/LICENSE_1_0.txt>`_:
+
+.. 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 <cstdint>
+#include <cstddef>
+#include <iostream>
+#include <istream>
+#include <sstream>
+#include <fstream>
+#include <ostream>
+#include <string>
+#include <vector>
+#include <typeinfo>
+#include <map>
+#include <algorithm>
+#include <tuple>
+#include <type_traits>
+
+//includes for an complete example to read and write
+//with sensors and agents.
+#include "rosa/deluxe/DeluxeContext.hpp"
+
+#include "rosa/config/version.h"
+
+//includes to test the basic functionality
+//to read and write tuples.
+#include "rosa/support/csv/CSVReader.hpp"
+#include "rosa/support/csv/CSVWriter.hpp"
+#include "rosa/support/iterator/split_tuple_iterator.hpp"
+#include "rosa/support/writer/split_tuple_writer.hpp"
+
+/// the name of the example
+const std::string ExampleName = "csvfiles";
+
+/// How many cycles of simulation to perform.
+const size_t NumberOfSimulationCycles = 10;
+
+/// Paths for the CSV files for simulation.
+/// input csv files
+const std::string csvPath = "../examples/CSVFiles/";
+const std::string csvFileWithHeader = csvPath + "HR-New.csv";
+const std::string csvFileNoHeader = csvPath + "HR.csv";
+const std::string csvFileHeaderSemi = csvPath + "HR-New-Semicolon.csv";
+/// output csv files
+const std::string csvFileWriteHea = csvPath + "csvwriter_noheader.csv";
+const std::string csvFileWriteNoHeaSplit = csvPath + "csvSplitwriter_noheader.csv";
+
+using namespace rosa;
+
+///
+/// This function tests the basic CSVIterator capablities, and shows you
+/// how you could work with this class.
+///
+void testtupleCSVReader(void){
+
+ //different streams to get the csv data out of the files
+ //file contains header and valid data entries, delimter = ','
+ std::ifstream file_header_data(csvFileWithHeader);
+ //file contains header and valid data entries, delimter = ','
+ std::ifstream file_header_data_2(csvFileWithHeader);
+ //file contains header and valid data entries, delimter = ','
+ std::ifstream file_header_data_3(csvFileWithHeader);
+ //file contains header and valid data entries, delimter = ','
+ std::ifstream file_header_data_4(csvFileWithHeader);
+ //file contains header and valid data entries, delimter = ','
+ std::ifstream file_header_data_5(csvFileWithHeader);
+ //file contains header and valid data entries, delimter = ','
+ std::ifstream file_header_data_6(csvFileWithHeader);
+ //file contains no header an valid data entries, delimter = ','
+ std::ifstream file2(csvFileNoHeader);
+ //file contains header and valid data entries, delimter = ';'
+ std::ifstream file3(csvFileHeaderSemi);
+
+ csv::CSVIterator<int, std::string, std::string, int, int> it(file_header_data);
+
+ it.setDelimeter(',');
+
+ (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<int, std::string, std::string, int, int> value = *it;
+
+ //
+ // Show the value of one iterator
+ //
+ LOG_INFO( "Values are: ");
+ LOG_INFO(std::get<0>(value) );
+ LOG_INFO(std::get<1>(value) );
+
+
+ //--------------------------------------------------------------------
+ //testing differnet parameters to the constructor
+
+ //uncomment to see that it is not possible to iterate over an vector in the tuple.
+ //rosa::csv::CSVIterator<double, std::vector<int>> it2(file, 1);
+
+ //try to skip a valid number of lines after the header
+ csv::CSVIterator<double, float, int, int, float> it2_0(file_header_data_2, 1);
+ //try to skip a valid number of lines after the header, but you assume that the file has no header
+ //uncomment this line to crash the programm
+ //csv::CSVIterator<double, float, int, int, float> it2_1(file_header_data_3, 0, csv::HeaderInformation::HasNoHeader);
+
+ //try to skip a valid number of lines after the header, but you assume that the file has no header
+ //uncomment this line to crash the program
+ //csv::CSVIterator<double, float, int, int, float> it2_2(file_header_data_4, 1, csv::HeaderInformation::HasNoHeader);
+
+ //try to skip a valid number of lines of a file without header
+ csv::CSVIterator<double, float, int, int, float> it2_3(file2, 1, csv::HeaderInformation::HasNoHeader);
+
+ //try to skip a valid number of lines after the header, but with different delimeter
+ csv::CSVIterator<double, float, int, int, float> it2_4(file3, 2, csv::HeaderInformation::HasHeader, ';');
+
+ // if you skip more lines than valid, you generate an infinte loop
+ //csv::CSVIterator<double, float, int, int, float> it3(file_header_data_5, 500);
+
+ //if you don't need data from all columns just select the number of columns you
+ //need. You get the data back from the first column (index 0) to the fourth column
+ //all values from the fifth column are ignored.
+ csv::CSVIterator<double, float, int, float> it4(file_header_data_6);
+}
+
+///
+/// This function tests the basic CSVTupleWriter capablities, and shows you
+/// how you could work with this class.
+///
+void testtupleCSVWriter(void){
+ //
+ // Create output writer with an file
+ //
+ std::ofstream file_header_out(csvFileWriteHea);
+ csv::CSVTupleWriter<int, float, std::string> wri(file_header_out);
+
+ //
+ // Create test tuples
+ //
+ std::tuple<int, float, std::string> values(5, 8.3f, "hallo");
+ std::tuple<int, float, std::string> values2(3, 8.3f, "end");
+
+ //
+ // Create test header lines for the test tuples
+ //
+ std::array<std::string, 3> header{
+ {"zero column", "first column", "second column"}};
+
+ std::array<std::string, 4> headerWrong{
+ {"zero column", "first column", "second column", "third column"}};
+
+ std::array<std::string, 2> headerWrongShort{
+ {"zero column", "first column"}};
+
+ //if you uncomment this line than it would be possible for you to write the header into the stream
+ //in the next line.
+ //wri.write(values);
+ wri.writeHeader(header);
+ wri.write(values);
+ wri.write(values);
+ // it is not possible to write an additional header into the stream.
+ wri.writeHeader(header);
+ wri.write(values);
+ wri << values;
+ wri << values2;
+
+ //uncomment this line to see, that you can't write a header with the too many elements.
+ //wri.writeHeader(headerWrong);
+ //uncomment this line to see, that you can't write a header with the too few elements.
+ //wri.writeHeader(headerWrongShort);
+
+}
+
+///
+/// This function tests the basic splitTupleIterator capablities, and shows you
+/// how you could work with this class, this class is used if you want to split
+/// a CSVIterator in separate parts.
+///
+void testsplitTupleIterator(void)
+{
+ //
+ // Create deluxe context
+ //
+ std::unique_ptr<rosa::deluxe::DeluxeContext> C =
+ deluxe::DeluxeContext::create(ExampleName);
+
+ //
+ // Create deluxe sensors.
+ //
+ LOG_INFO("Creating sensors.");
+
+
+ // All sensors are created without defining a normal generator function, but
+ // with the default value of the second argument. That, however, requires the
+ // data type to be explicitly defined. This is good for simulation only.
+ // Three different sensors were created, this is just a random number taken.
+ AgentHandle Elem0Sensor = C->createSensor<int>("Element1 Sensor");
+ AgentHandle Elem1Sensor = C->createSensor<float>("Element2 Sensor");
+ AgentHandle Elem2Sensor = C->createSensor<std::string>("Element3 Sensor");
+
+
+ //
+ // Initialize deluxe context for simulation.
+ //
+ C->initializeSimulation();
+
+ // Type aliases for iterators
+ using Iterator = rosa::csv::CSVIterator<int, float, std::string>;
+ using IteratorValue = std::tuple<int, float, std::string>;
+
+ static_assert (std::is_same<typename Iterator::value_type, IteratorValue>::value, "Iterator must provide tuples" );
+
+ //
+ // Open CSV file and register the columns to the corresponding sensors.
+ //
+ std::ifstream TestCSV(csvFileWithHeader);
+
+ //
+ // Test data looks like:
+ // Element1, Element2, Element3, Element4, Element5 -- is the header line
+ // 3, 5, 8, 9.5, 17 -- first line of values
+ // 100, -8, 30, 18.8, 29 -- other line of values were also in the file
+ // 5, 20, -100, -200.1, -30 -- if you have less number of values than simulation rounds all values
+ // -- beyond your last value will be zero.
+
+ //get element iterator ranges
+ auto [Elem0Range, Elem1Range, Elem2Range] = iterator::splitTupleIterator(Iterator(TestCSV), Iterator());
+
+ //dissect a range into begin and end iterators by structred bindings
+ auto[Elem0Begin, Elem0End] = Elem0Range;
+
+ //deissect a range with functions
+ auto Elem1Begin = iterator::begin(Elem1Range);
+ auto Elem1End = iterator::end(Elem1Range);
+
+ C->registerSensorValues(Elem0Sensor, std::move(Elem0Begin), Elem0End);
+ C->registerSensorValues(Elem1Sensor, std::move(Elem1Begin), Elem1End);
+ C->registerSensorValues(Elem2Sensor, std::move(iterator::begin(Elem2Range)),
+ iterator::end(Elem2Range));
+
+ //
+ // Simulate.
+ //
+ C->simulate(NumberOfSimulationCycles);
+
+}
+
+
+///
+/// This function tests the basic splitTupleWriter capablities, and shows you
+/// how you could work with this class, this class is used if you want to split
+/// a CSVWriter in separate parts.
+///
+void testsplitTupleWriter(void){
+ //
+ // Create output writer with an file
+ //
+ std::ofstream file_header_out(csvFileWriteNoHeaSplit);
+ csv::CSVTupleWriter<int, float, std::string> wri(file_header_out);
+
+ // if you omit, the type definition in the template, than auto generated types were used,
+ // and they may not fit to the used CSVTupleWriter.
+ wri << std::make_tuple<int, float, std::string>(1000, 50.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 <cmath>
#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 <vector>
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 <typename T, typename... Funs>
static AgentHandle createMyAgent(MessagingSystem *S, const std::string &Name,
Funs &&... Fs) {
return ((SystemTester *)S)->createAgent<T>(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<atom("tick")>;
private:
enum class Categories { Bad, Normal, Good };
static const std::map<Categories, const char *> CategoryNames;
- History<uint8_t, 10, HistoryPolicy::FIFO> H;
+ StaticLengthHistory<uint8_t, 10, HistoryPolicy::FIFO> H;
Confidence<uint8_t> C;
RangeAbstraction<uint8_t, Categories> A;
PartialFunction<int, int> L;
RangeConfidence<float, Categories, float> RCL;
RangeConfidence<float, Categories, float> 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<Categories, float> res_lin = RCL(H.entry());
- for (auto i : res_lin){
- LOG_INFO_STREAM << " " << CategoryNames.at(i.first)
- << " " << i.second << "," ;
+ std::map<Categories, float> 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<Categories, float> res_sine = RCS(H.entry());
- for (auto i : res_sine){
- LOG_INFO_STREAM << " " << CategoryNames.at(i.first)
- << " " << i.second << "," ;
+ std::map<Categories, float> 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<LinearFunction<int, int>>(0, 1)},
{{2, 4}, std::make_shared<LinearFunction<int, int>>(2, 0)},
{{4, 6}, std::make_shared<LinearFunction<int, int>>(6, -1)}},
0),
RCL({
{Categories::Bad, PartialFunction<float, float>({
{{0.f, 3.f}, std::make_shared<LinearFunction<float, float>>
(0.f, 1.f/3)},
{{3.f, 6.f}, std::make_shared<LinearFunction<float, float>>
(1.f, 0.f)},
{{6.f, 9.f}, std::make_shared<LinearFunction<float, float>>
(3.f, -1.f/3)},
},0)},
{Categories::Normal, PartialFunction<float, float>({
{{6.f, 9.f}, std::make_shared<LinearFunction<float, float>>
(-2.f, 1.f/3)},
{{9.f, 12.f}, std::make_shared<LinearFunction<float, float>>
(1.f, 0.f)},
{{12.f, 15.f}, std::make_shared<LinearFunction<float, float>>
(5.f, -1.f/3)},
},0)},
{Categories::Good, PartialFunction<float, float>({
{{12.f, 15.f}, std::make_shared<LinearFunction<float, float>>
(-4.f, 1.f/3)},
{{15.f, 18.f}, std::make_shared<LinearFunction<float, float>>
(1.f, 0.f)},
{{18.f, 21.f}, std::make_shared<LinearFunction<float, float>>
(7.f, -1.f/3)},
},0)}
}),
RCS({
{Categories::Bad, PartialFunction<float, float>({
{{0.f, 3.f}, std::make_shared<SineFunction<float, float>>
(Pi/3, 0.5f, -Pi/2, 0.5f)},
{{3.f, 6.f}, std::make_shared<LinearFunction<float, float>>(1.f, 0.f)},
{{6.f, 9.f}, std::make_shared<SineFunction<float, float>>
(Pi/3, 0.5f, -Pi/2 + 3, 0.5f)},
},0)},
{Categories::Normal, PartialFunction<float, float>({
{{6.f, 9.f}, std::make_shared<SineFunction<float, float>>
(Pi/3, 0.5f, -Pi/2, 0.5f)},
{{9.f, 12.f}, std::make_shared<LinearFunction<float, float>>(1.f, 0.f)},
{{12.f, 15.f}, std::make_shared<SineFunction<float, float>>
(Pi/3, 0.5f, -Pi/2 + 3, 0.5f)},
},0)},
{Categories::Good, PartialFunction<float, float>({
{{12.f, 15.f}, std::make_shared<SineFunction<float, float>>
(Pi/3, 0.5f, -Pi/2, 0.5f)},
{{15.f, 18.f}, std::make_shared<LinearFunction<float, float>>(1.f, 0.f)},
{{18.f, 21.f}, std::make_shared<SineFunction<float, float>>
(Pi/3, 0.5f, -Pi/2 + 3, 0.5f)},
},0)}
}, true){}
};
const std::map<MyAgent::Categories, const char *> 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<MessagingSystem> S = MessagingSystem::createSystem("Sys");
MessagingSystem *SP = S.get();
AgentHandle A = SystemTester::createMyAgent<MyAgent>(SP, "MyAgent");
- std::vector<uint8_t> Vs{0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 13,
- 15, 14, 15, 16, 19, 20, 21};
+ std::vector<uint8_t> 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, uint8_t>(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<Unit, System>([&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<System> 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 <algorithm>
#include <cmath>
#include <vector>
using namespace rosa;
using namespace rosa::deluxe;
using namespace rosa::terminal;
/// How many cycles of simulation to perform.
const size_t NumberOfSimulationCycles = 16;
int main(void) {
LOG_INFO_STREAM << '\n'
<< library_string() << " -- " << Color::Red
<< "deluxe-interface example" << Color::Default << '\n';
std::unique_ptr<DeluxeContext> C = DeluxeContext::create("Deluxe");
//
// Create deluxe sensors.
//
LOG_INFO("Creating sensors.");
// All sensors are created without defining a normal generator function, but
// with the default value of the last argument. That, however, requires the
// data type to be explicitly defined. This is good for simulation only.
// The first and second sensors do not receive master-input.
AgentHandle BoolSensor = C->createSensor<bool>("BoolSensor");
AgentHandle IntSensor = C->createSensor<int32_t>("IntSensor");
// This sensor receives master-input and dumps it to \c LOG_INFO_STREAM.
const std::string FloatSensorName = "FloatSensor";
AgentHandle FloatSensor = C->createSensor<uint32_t, float>(
FloatSensorName, [&FloatSensorName](std::pair<uint32_t, bool> I) {
LOG_INFO_STREAM << "\n******\n"
<< FloatSensorName
<< " master-input " << (I.second ? "<New>" : "<Old>")
<< " value: " << I.first << "\n******\n";
});
// This sensor do not receive master-input but produces tuples.
using TupleType = DeluxeTuple<float, float>;
AgentHandle TupleSensor = C->createSensor<TupleType>("TupleSensor");
//
// Check and set execution policy for sensors.
//
LOG_INFO("Execution policies for sensors.");
LOG_INFO(std::to_string(*C->getExecutionPolicy(IntSensor)));
C->setExecutionPolicy(IntSensor, DeluxeExecutionPolicy::decimation(2));
C->setExecutionPolicy(FloatSensor, DeluxeExecutionPolicy::decimation(2));
LOG_INFO(std::to_string(*C->getExecutionPolicy(IntSensor)));
//
// Create low-level deluxe agents with \c createLowLevelAgent.
//
LOG_INFO("Creating low-level agents.");
// All agents below dump their received values to \c LOG_INFO_STREAM on each
// triggering.
// This agent does not receive master-input and does not produce
// master-output. It results in the value it received.
const std::string BoolAgentName = "BoolAgent";
using BoolResult = Optional<bool>;
using BoolHandler = std::function<BoolResult(std::pair<bool, bool>)>;
AgentHandle BoolAgent = C->createAgent(
BoolAgentName,
BoolHandler([&BoolAgentName](std::pair<bool, bool> I) -> BoolResult {
LOG_INFO_STREAM << "\n******\n"
<< BoolAgentName << " "
<< (I.second ? "<New>" : "<Old>")
<< " value: " << I.first << "\n******\n";
return {I.first};
}));
// This agent receives master-input but does not produce master-output. The
// agent maintains a state in \c IntAgentOffset. The master-input handler
// updates \c IntAgentOffset according to each received (new) value from its
// master. The slave-input handler results in the sum of the received value
// and the actual value of \c IntAgentOffset.
const std::string IntAgentName = "IntAgent";
using IntMasterHandler = std::function<void(std::pair<uint32_t, bool>)>;
using IntResult = Optional<int32_t>;
using IntHandler = std::function<IntResult(std::pair<int32_t, bool>)>;
uint32_t IntAgentOffset = 0;
AgentHandle IntAgent = C->createAgent(
IntAgentName,
// Master-input handler.
IntMasterHandler([&IntAgentName,
&IntAgentOffset](std::pair<uint32_t, bool> I) {
LOG_INFO_STREAM << "\n******\n"
<< IntAgentName
<< " master-input " << (I.second ? "<New>" : "<Old>")
<< " value: " << I.first << "\n******\n";
if (I.second) {
IntAgentOffset = I.first;
}
}),
// Slave-input handler.
IntHandler([&IntAgentName,
&IntAgentOffset](std::pair<int32_t, bool> I) -> IntResult {
LOG_INFO_STREAM << "\n******\n"
<< IntAgentName << " " << (I.second ? "<New>" : "<Old>")
<< " value: " << I.first << "\n******\n";
return {I.first + IntAgentOffset};
}));
// This agent receives master-input and produces master-output. The
// master-input handler propagaates each received (new) value to its slave as
// master-output. The slave-input handler results in the value it received and
// produces no actual master-output.
const std::string FloatAgentName = "FloatAgent";
using FloatMasterResult = std::tuple<Optional<uint32_t>>;
using FloatMasterHandler =
std::function<FloatMasterResult(std::pair<uint32_t, bool>)>;
using FloatResult = std::tuple<Optional<float>, Optional<uint32_t>>;
using FloatHandler = std::function<FloatResult(std::pair<float, bool>)>;
AgentHandle FloatAgent = C->createAgent(
FloatAgentName,
// Master-input handler.
FloatMasterHandler([&FloatAgentName](
std::pair<uint32_t, bool> I) -> FloatMasterResult {
LOG_INFO_STREAM << "\n******\n"
<< FloatAgentName
<< " master-input " << (I.second ? "<New>" : "<Old>")
<< " value: " << I.first << "\n******\n";
const auto Output =
I.second ? Optional<uint32_t>(I.first) : Optional<uint32_t>();
return {Output};
}),
// Slave-input handler.
FloatHandler([&FloatAgentName](std::pair<float, bool> I) -> FloatResult {
LOG_INFO_STREAM << "\n******\n"
<< FloatAgentName << " "
<< (I.second ? "<New>" : "<Old>")
<< " value: " << I.first << "\n******\n";
return {{I.first}, {}};
}));
// This agent does not receive master-input and does not produce
// master-output. It results in the sum of the values it receives in a tuple.
const std::string TupleAgentName = "TupleAgent";
using TupleSumResult = Optional<DeluxeTuple<double>>;
using TupleHandler =
std::function<TupleSumResult(std::pair<TupleType, bool>)>;
AgentHandle TupleAgent = C->createAgent(
TupleAgentName,
TupleHandler(
[&TupleAgentName](std::pair<TupleType, bool> I) -> TupleSumResult {
LOG_INFO_STREAM << "\n******\n"
<< TupleAgentName << " "
<< (I.second ? "<New>" : "<Old>")
<< " value: " << I.first << "\n******\n";
return {std::get<0>(I.first) + std::get<1>(I.first)};
}));
//
// Set execution policies for low-level agents.
//
LOG_INFO("Setting Execution policies for low-level agents.");
C->setExecutionPolicy(IntAgent, DeluxeExecutionPolicy::awaitAll({0}));
C->setExecutionPolicy(FloatAgent, DeluxeExecutionPolicy::awaitAll({0}));
//
// Connect sensors to low-level agents.
//
LOG_INFO("Connect sensors to their corresponding low-level agents.");
C->connectSensor(BoolAgent, 0, BoolSensor, "Bool Sensor Channel");
C->connectSensor(IntAgent, 0, IntSensor, "Int Sensor Channel");
C->connectSensor(FloatAgent, 0, FloatSensor, "Float Sensor Channel");
C->connectSensor(TupleAgent, 0, TupleSensor, "Tuple Sensor Channel");
//
// Create a high-level deluxe agent.
//
LOG_INFO("Create high-level agent.");
using SingleDoubleOutputType = Optional<DeluxeTuple<double>>;
using SingleUInt32OutputType = Optional<DeluxeTuple<uint32_t>>;
using NoOutputType = Optional<EmptyDeluxeTuple>;
// This agent does not receive master-input but produces master-output for its
// slaves at positions `1` and `2` but not for that at position `0`. The agent
// maintains a state in \c SumAgentState. The handler increments \c
// SumAgentState upon each received (new) `true` value from its slave at
// position `0`. Whenever \c SumAgentState has been updated, it is sent to the
// slaves at positions `1` and `2`. The handler results in the sum of the
// values received from slaves at positions `1`, `2`, and `3`.
using SumResult =
std::tuple<SingleDoubleOutputType, NoOutputType, SingleUInt32OutputType,
SingleUInt32OutputType, NoOutputType>;
using SumHandler = std::function<SumResult(
std::pair<DeluxeTuple<bool>, bool>, std::pair<DeluxeTuple<int32_t>, bool>,
std::pair<DeluxeTuple<float>, bool>,
std::pair<DeluxeTuple<double>, bool>)>;
uint32_t SumAgentState = 0;
AgentHandle SumAgent = C->createAgent(
"Sum Agent",
SumHandler([&SumAgentState](
std::pair<DeluxeTuple<bool>, bool> I0,
std::pair<DeluxeTuple<int32_t>, bool> I1,
std::pair<DeluxeTuple<float>, bool> I2,
std::pair<DeluxeTuple<double>, bool> I3) -> SumResult {
const auto V0 = std::get<0>(I0.first);
const auto V1 = std::get<0>(I1.first);
const auto V2 = std::get<0>(I2.first);
const auto V3 = std::get<0>(I3.first);
LOG_INFO_STREAM << "\n*******\nSum Agent triggered with values:\n"
<< (I0.second ? "<New>" : "<Old>")
<< " bool value: " << V0 << "\n"
<< (I1.second ? "<New>" : "<Old>")
<< " int value: " << V1 << "\n"
<< (I2.second ? "<New>" : "<Old>")
<< " float value: " << V2 << "\n"
<< (I3.second ? "<New>" : "<Old>")
<< " double value: " << V3 << "\n******\n";
if (I0.second && V0) {
++SumAgentState;
}
const SingleUInt32OutputType MasterOutput =
I0.second && V0
? SingleUInt32OutputType(DeluxeTuple<uint32_t>(SumAgentState))
: SingleUInt32OutputType();
const DeluxeTuple<double> Output = {V1 + V2 + V3};
return {{Output}, {}, {MasterOutput}, {MasterOutput}, {}};
}));
//
// Connect low-level agents to the high-level agent.
//
LOG_INFO("Connect low-level agents to the high-level agent.");
C->connectAgents(SumAgent, 0, 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<Optional<unit_t>(std::pair<double, bool>)>(
[](std::pair<double, bool> Sum) -> Optional<unit_t> {
if (Sum.second) {
LOG_INFO_STREAM << "Result: " << Sum.first << "\n";
}
return {};
}));
//
// Connect the high-level agent to the logger agent.
//
LOG_INFO("Connect the high-level agent to the logger agent.");
C->connectAgents(LoggerAgent, 0, SumAgent, "Sum Agent Channel");
//
// Do simulation.
//
LOG_INFO("Setting up and performing simulation.");
//
// Initialize deluxe context for simulation.
//
C->initializeSimulation();
//
// Create some vectors and register them for their corresponding sensors.
//
std::vector<bool> BoolValues(NumberOfSimulationCycles);
std::generate(BoolValues.begin(), BoolValues.end(),
[i = 0](void) mutable -> bool { return (++i % 4) == 0; });
C->registerSensorValues(BoolSensor, BoolValues.begin(), BoolValues.end());
std::vector<int32_t> IntValues(NumberOfSimulationCycles);
std::generate(IntValues.begin(), IntValues.end(),
[i = 0](void) mutable { return ++i; });
C->registerSensorValues(IntSensor, IntValues.begin(), IntValues.end());
std::vector<float> FloatValues(NumberOfSimulationCycles);
std::generate(FloatValues.begin(), FloatValues.end(),
[f = 0.5f](void) mutable {
f += 0.3f;
return std::floor(f) + 0.5f;
});
C->registerSensorValues(FloatSensor, FloatValues.begin(), FloatValues.end());
std::vector<TupleType> TupleValues(NumberOfSimulationCycles);
std::generate(TupleValues.begin(), TupleValues.end(),
[f1 = 0.f, f2 = 3.14f](void) mutable -> TupleType {
f1 += f2;
f2 -= f1;
return {f1, f2};
});
C->registerSensorValues(TupleSensor, TupleValues.begin(), TupleValues.end());
//
// Simulate.
//
C->simulate(NumberOfSimulationCycles);
return 0;
}
diff --git a/examples/messaging-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 <typename T, typename... Funs>
static AgentHandle createMyAgent(MessagingSystem *S, const std::string &Name,
Funs &&... Fs) {
return ((SystemTester *)S)->createAgent<T>(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<atom("tick")>;
using Report = AtomConstant<atom("report")>;
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<Report>([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<MessagingSystem> S = MessagingSystem::createSystem("Sys");
MessagingSystem *SP = S.get();
LOG_INFO("\n\n** Stateless Agents\n");
AgentHandle Agent1 = SystemTester::createMyAgent<Agent>(
SP, "Agent1", Invoker::F<std::string>([](const std::string &M) noexcept {
LOG_INFO("Agent1: " + M);
}));
using Print = AtomConstant<atom("print")>;
using Forward = AtomConstant<atom("forward")>;
AgentHandle Agent2 = SystemTester::createMyAgent<Agent>(
SP, "Agent2", Invoker::F<Print, uint8_t>([](Print, uint8_t N) noexcept {
LOG_INFO("Agent2: " + std::to_string(N));
}),
Invoker::F<Forward, uint8_t>([&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<Print, uint8_t>(Agent2, Print::Value, 42);
LOG_INFO("Sending a forward-message to Agent2...");
SP->send<Forward, uint8_t>(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<Forward, uint8_t>(Agent2, Forward::Value, 42);
SystemTester::destroyMyAgent(SP, Agent2);
LOG_INFO("\n\n** Stateful Agents\n");
AgentHandle Agent3 = SystemTester::createMyAgent<MyAgent>(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<uint8_t, uint16_t>(1, 2);
auto &Msg = *PMsg;
Log << "Checking on a 'Message with TypeList<<uint8_t, uint16_t>>':"
<< "\n Size: " << Msg.Size
<< "\n Pos 0 is uint8_t: " << Msg.isTypeAt<uint8_t>(0)
<< "\n Pos 1 is uint16_t: " << Msg.isTypeAt<uint16_t>(1)
<< "\n Pos 2 is uint32_t: " << Msg.isTypeAt<uint32_t>(1)
<< "\n Value at pos 0: " << PRINTABLE(Msg.valueAt<uint8_t>(0))
<< "\n Value at pos 1: " << Msg.valueAt<uint16_t>(1) << "\n\n";
// MessageMatcher.
using MyMatcher = MsgMatcher<uint8_t, uint16_t>;
Log << "Matching against 'TypeList<uint8_t, uint16_t>':"
<< "\n matching: " << MyMatcher::doesStronglyMatch(Msg);
auto Vs = MyMatcher::extractedValues(Msg);
Log << "\n value: '(" << PRINTABLE(std::get<0>(Vs)) << ", "
<< std::get<1>(Vs) << ")'\n\n";
using MyWrongMatcher = MsgMatcher<uint8_t, uint8_t>;
Log << "Matching against 'TypeList<uint8_t, uint8_t>':"
<< "\n matching: " << MyWrongMatcher::doesStronglyMatch(Msg) << "\n\n";
using MyAtom = AtomConstant<atom("atom")>;
const MyAtom &A = MyAtom::Value;
using MyNAtom = AtomConstant<atom("noatom")>;
auto PAMsg = Message::create(A);
auto &AMsg = *PAMsg;
Log << "Checking on a 'Message with TypeList<AtomConstant<atom(\"atom\")>>':"
<< "\n Size: " << AMsg.Size
<< "\n Pos 0 is 'AtomValue': " << AMsg.isTypeAt<AtomValue>(0)
<< "\n Pos 0 is 'AtomConstant<atom(\"noatom\")>': "
<< AMsg.isTypeAt<MyNAtom>(0) << "\n\n";
using MyAtomMatcher = MsgMatcher<MyAtom>;
Log << "Matching against 'TypeList<AtomConstant<atom(\"atom\")>>':"
<< "\n matching: " << MyAtomMatcher::doesStronglyMatch(AMsg)
<< "\n value: '("
<< std::to_string(std::get<0>(MyAtomMatcher::extractedValues(AMsg)))
<< ")'"
<< "\n\n";
using MyWrongAtomMatcher = MsgMatcher<MyNAtom>;
Log << "Matching against 'TypeList<AtomConstant<atom(\"noatom\")>>':"
<< "\n matching: " << MyWrongAtomMatcher::doesStronglyMatch(AMsg)
<< "\n\n";
// Invoker.
auto IP = Invoker::wrap(Invoker::F<MyAtom>([&Log](MyAtom) noexcept->void {
Log << "** Handling 'Message with "
"TypeList<AtomConstant<atom(\"atom\")>>'.\n";
}));
auto &I = *IP; // Get a reference from the pointer.
Log << "Invoking a function of signature 'void(AtomConstant<atom(\"atom\")>) "
"noexcept':"
<< "\n with 'Message with TypeList<uint8_t, uint16_t>'"
<< "\n does Message match Invoker: " << I.match(Msg)
<< "\n invoking...";
I(Msg);
Log << "\n with 'Message with TypeList<AtomConstant<atom(\"atom\")>>'..."
<< "\n does Message match Invoker: " << I.match(AMsg)
<< "\n invoking...";
I(AMsg);
Log << "\n\n";
// MessageHandler.
MessageHandler Handler{
Invoker::F<uint8_t, uint16_t>([&Log](uint8_t, uint16_t) {
Log << "** Handling 'Message with TypeList<uint8_t, uint16_t>'\n";
}),
Invoker::F<MyAtom>([&Log](MyAtom) {
Log << "** Handling 'Message with "
"TypeList<AtomConstant<atom(\"atom\")>>'\n";
})};
auto PANMsg = Message::create(MyNAtom::Value);
auto &ANMsg = *PANMsg;
Log << "Handling Messages with 'MessageHandler "
"{ Invoker::F<uint8_t, uint16_t>, "
"Invoker::F<AtomConstant<atom(\"atom\")>> }':"
<< "\n 'Message with TypeList<uint8_t, uint16_t>'"
<< "\n can handle: " << Handler.canHandle(Msg) << "\n handling...";
Handler(Msg);
Log << "\n 'Message with TypeList<AtomConstant<atom(\"atom\")>>'"
<< "\n can handle: " << Handler.canHandle(AMsg) << "\n handling...";
Handler(AMsg);
Log << "\n 'Message with TypeList<AtomConstant<atom(\"noatom\")>>'"
<< "\n can handle: " << Handler.canHandle(ANMsg)
<< "\n handling...";
Handler(ANMsg);
Log << "\n\n";
Log << "Terminating, destroying automatic variables.\n";
return 0;
}
diff --git a/examples/type-facilities/type-facilities.cpp b/examples/type-facilities/type-facilities.cpp
index 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<uint8_t>::Value;
Log << "\n type number: " << PRINTABLE_TN(TN)
<< "\n size: " << TypeForNumber<TN>::Size
<< "\n name: " << TypeForNumber<TN>::Name << "\n\n";
Log << "Type number information on 'std::string':";
constexpr TypeNumber TNS = TypeNumberOf<std::string>::Value;
Log << "\n type number: " << PRINTABLE_TN(TNS)
<< "\n size: " << TypeForNumber<TNS>::Size
<< "\n name: " << TypeForNumber<TNS>::Name << "\n\n";
Log << "Type number information of AtomConstants:";
using Atom1 = AtomConstant<atom("atom1")>;
using Atom2 = AtomConstant<atom("atom2")>;
Log << "\n std::is_same<Atom1, Atom2>::value: "
<< std::is_same<Atom1, Atom2>::value << "\n TypeNumberOf<Atom1>::Value: "
<< PRINTABLE_TN(TypeNumberOf<Atom1>::Value)
<< "\n TypeNumberOf<Atom2>::Value: "
<< PRINTABLE_TN(TypeNumberOf<Atom2>::Value)
<< "\n name: " << TypeForNumber<TypeNumberOf<Atom1>::Value>::Name
<< "\n\n";
Log << "Type token information on 'TypeList<uint8_t, uint16_t, "
"std::string>':";
// \c rosa::Token is generated statically.
constexpr Token T = TypeToken<uint8_t, uint16_t, std::string>::Value;
STATIC_ASSERT(
(T == TypeListToken<TypeList<uint8_t, uint16_t, std::string>>::Value),
"alias template definition is wrong");
Token T_ = T; // We need a non-const value for dropping head later.
// Iterate over encoded entries in \c T_.
while (!emptyToken(T_)) {
Log << "\n token: " << PRINTABLE_TOKEN(T_)
<< "\n valid: " << validToken(T_) << "\n empty: " << emptyToken(T_)
<< "\n length: " << static_cast<size_t>(lengthOfToken(T_))
<< "\n full size: " << sizeOfValuesOfToken(T_)
<< "\n head type number: " << PRINTABLE_TN(headOfToken(T_))
<< "\n size of head: " << sizeOfHeadOfToken(T_)
<< "\n name of head: " << nameOfHeadOfToken(T_)
<< "\n is head uint8_t: " << isHeadOfTokenTheSameType<uint8_t>(T_)
<< "\n is head uint16_t: " << isHeadOfTokenTheSameType<uint16_t>(T_)
<< "\n is head std::string: "
<< isHeadOfTokenTheSameType<std::string>(T_) << "\nDropping head...";
dropHeadOfToken(T_);
}
// Here when Token became empty.
Log << "\n token: " << PRINTABLE_TOKEN(T_) << "\n empty: " << emptyToken(T_)
<< '\n';
return 0;
}
diff --git a/include/rosa/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 <algorithm>
#include <map>
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 <typename T, typename A> 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 <typename T, typename A>
class MapAbstraction : public Abstraction<T, A>, private std::map<T, A> {
// Make sure the actual type arguments are enumerations.
STATIC_ASSERT((std::is_enum<T>::value && std::is_enum<A>::value),
"mapping not enumerations");
// Bringing into scope inherited members.
using Abstraction<T, A>::Default;
using std::map<T, A>::end;
using std::map<T, A>::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<T, A> &Map, const A Default) noexcept
: Abstraction<T, A>(Default),
std::map<T, A>(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 <typename T, typename A>
class RangeAbstraction : public Abstraction<T, A>,
private std::map<std::pair<T, T>, A> {
// Make sure the actual type arguments are matching our expectations.
STATIC_ASSERT((std::is_arithmetic<T>::value), "abstracting not arithmetic");
/// \todo check if this compiles with the definition of abstractions as
/// self-aware properties
//STATIC_ASSERT((std::is_enum<A>::value), "abstracting not to enumeration");
// Bringing into scope inherited members.
using Abstraction<T, A>::Default;
using std::map<std::pair<T, T>, A>::begin;
using std::map<std::pair<T, T>, A>::end;
using std::map<std::pair<T, T>, 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<std::pair<T, T>, A> &Map, const A &Default)
: Abstraction<T, A>(Default), std::map<std::pair<T, T>, A>(Map) {
// Sanity check.
ASSERT(std::all_of(
begin(), end(), [this](const std::pair<std::pair<T, T>, A> &P) {
return P.first.first <= P.first.second &&
std::all_of(++find(P.first), end(),
[&P](const std::pair<std::pair<T, T>, 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 <limits>
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 <typename T> class Confidence : public Functionality {
// Make sure the actual type argument is an arithmetic type.
STATIC_ASSERT(std::is_arithmetic<T>::value, "not arithmetic Confidence");
public:
/// Unsigned type corresponding to \p T.
using UT = unsigned_t<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<T>::value
? std::numeric_limits<T>::min()
: std::numeric_limits<T>::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<T>::value || !std::numeric_limits<T>::has_infinity)
? std::numeric_limits<T>::max()
: std::numeric_limits<T>::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<UT>::value || !std::numeric_limits<UT>::has_infinity)
? std::numeric_limits<UT>::max()
: std::numeric_limits<UT>::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 <size_t N, HistoryPolicy P>
- bool operator()(const History<T, N, P> &H) const noexcept {
+ bool operator()(const StaticLengthHistory<T, N, P> &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 <algorithm>
-#include <vector>
#include <cmath>
#include <memory>
+#include <vector>
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 <typename D, typename R> class LinearFunction :
- public Abstraction<D, R>{
+template <typename D, typename R>
+class LinearFunction : public Abstraction<D, R> {
// Make sure the actual type arguments are matching our expectations.
STATIC_ASSERT((std::is_arithmetic<D>::value),
- "LinearFunction not arithmetic T");
+ "LinearFunction not arithmetic T");
STATIC_ASSERT((std::is_arithmetic<R>::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<D, R>(Intercept),
- Intercept(Intercept),
+ : Abstraction<D, R>(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<D, R>(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<D, R> p1, std::pair<D, R> p2) noexcept
+ : LinearFunction<D, R>(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<D, R> p1, std::pair<D, R> 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 <typename D, typename R> class SineFunction :
- public Abstraction<D, R>{
+template <typename D, typename R>
+class SineFunction : public Abstraction<D, R> {
// Make sure the actual type arguments are matching our expectations.
STATIC_ASSERT((std::is_arithmetic<D>::value),
- "SineFunction not arithmetic T");
+ "SineFunction not arithmetic T");
STATIC_ASSERT((std::is_arithmetic<R>::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<D, R>(Average),
- Frequency(Frequency),
- Amplitude(Amplitude),
- Phase(Phase),
- Average(Average) {}
+ : Abstraction<D, R>(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 <typename D, typename R>
+class StepFunction : public Abstraction<D, R> {
+ // Make sure the actual type arguments are matching our expectations.
+ STATIC_ASSERT((std::is_arithmetic<D>::value), "abstracting not arithmetic");
+ STATIC_ASSERT((std::is_arithmetic<R>::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<D, R>(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 <typename D, typename R>
class PartialFunction : public Abstraction<D, R> {
// Make sure the actual type arguments are matching our expectations.
STATIC_ASSERT((std::is_arithmetic<D>::value), "abstracting not arithmetic");
STATIC_ASSERT((std::is_arithmetic<R>::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<D, std::shared_ptr<Abstraction<D, R>>> 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::pair<D, D>,
- std::shared_ptr<Abstraction<D, R>>> &Map,
- const R Default)
+ PartialFunction(
+ const std::map<std::pair<D, D>, std::shared_ptr<Abstraction<D, R>>> &Map,
+ const R Default)
: Abstraction<D, R>(Default),
- RA(Map, std::shared_ptr<Abstraction<D, R>>
- (new Abstraction<D, R>(Default))) {
+ RA(Map,
+ std::shared_ptr<Abstraction<D, R>>(new Abstraction<D, R>(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 <array>
+#include <vector>
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 <typename T, size_t N, HistoryPolicy P>
-class History : public Functionality, private std::array<T, N + 1> {
-
- // Bring into scope inherited functions that are used.
- using std::array<T, N + 1>::max_size;
- using std::array<T, N + 1>::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 <typename T, HistoryPolicy P> 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<const size_t>::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<T>::value && std::is_signed<T>::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 X = T>
typename std::enable_if<
std::is_arithmetic<X>::value && std::is_signed<X>::value, X>::type
- trend(const size_t D = N - 1) const noexcept {
+ trend(const size_t D) const noexcept {
STATIC_ASSERT((std::is_same<X, T>::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<T>::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 X = T>
typename std::enable_if<std::is_arithmetic<X>::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<X, T>::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 <typename R> 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 <typename T, size_t N, HistoryPolicy P>
+class StaticLengthHistory : public History<T, P>, private std::array<T, N + 1> {
+
+ // Bring into scope inherited functions that are used.
+ using std::array<T, N + 1>::max_size;
+ using std::array<T, N + 1>::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<T, P>::policy;
+ using History<T, P>::empty;
+ using History<T, P>::full;
+ using History<T, P>::addEntry;
+ using History<T, P>::trend;
+ using History<T, P>::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<const size_t>::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 <typename T, size_t N, HistoryPolicy P>
-History<T, N, P> &operator<<(History<T, N, P> &H, const T &V) noexcept {
+StaticLengthHistory<T, N, P> &operator<<(StaticLengthHistory<T, N, P> &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 <typename T, HistoryPolicy P>
+class DynamicLengthHistory : public History<T, P>, private std::vector<T> {
+
+ // Bring into scope inherited functions that are used.
+ using std::vector<T>::erase;
+ using std::vector<T>::begin;
+ using std::vector<T>::end;
+ using std::vector<T>::size;
+ using std::vector<T>::max_size;
+ using std::vector<T>::resize;
+ using std::vector<T>::push_back;
+ using std::vector<T>::pop_back;
+ using std::vector<T>::operator[];
+
+ /// The current length of the DynamicLengthHistory.
+ size_t Length;
+
+public:
+ using History<T, P>::policy;
+ using History<T, P>::empty;
+ using History<T, P>::full;
+ using History<T, P>::addEntry;
+ using History<T, P>::trend;
+ using History<T, P>::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 <typename T, HistoryPolicy P>
+DynamicLengthHistory<T, P> &operator<<(DynamicLengthHistory<T, P> &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 <algorithm>
-#include <vector>
#include <cmath>
#include <memory>
+#include <vector>
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 <typename D, typename I, typename R>
class RangeConfidence : protected Abstraction<D, std::map<I, R>>,
- private std::map<I, PartialFunction<D, R>>{
+ private std::map<I, PartialFunction<D, R>> {
// Make sure the actual type arguments are matching our expectations.
STATIC_ASSERT((std::is_arithmetic<D>::value), "abstracting not arithmetic");
STATIC_ASSERT((std::is_arithmetic<R>::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<I, PartialFunction<D, R>> &Abstractions,
- bool IgnoreDefaults = false)
- : Abstraction<D, std::map<I, R>>({}),
- std::map<I, PartialFunction<D, R>>(Abstractions),
- IgnoreDefaults(IgnoreDefaults){
- }
+ bool IgnoreDefaults = false)
+ : Abstraction<D, std::map<I, R>>({}), std::map<I, PartialFunction<D, R>>(
+ 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<I, PartialFunction<D, R>>)*this)){
- if(!p.second.isDefaultAt(V))
- return false;
- }
+ for (auto const &P : ((std::map<I, PartialFunction<D, R>>)*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<I, R> operator()(const D &V) const noexcept override {
- std::map<I, R> ret;
- for (auto const& p : ((std::map<I, PartialFunction<D, R>>)*this)){
- if(!IgnoreDefaults || !p.second.isDefaultAt(V))
- ret.insert(std::pair<I, R>(p.first, p.second(V)));
+ std::map<I, R> Ret;
+ for (auto const &P : ((std::map<I, PartialFunction<D, R>>)*this)) {
+ if (!IgnoreDefaults || !P.second.isDefaultAt(V))
+ Ret.insert(std::pair<I, R>(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 <iostream>
// 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<void>(0)
-/// Ignors anything.
+/// Ignors an expression.
+///
+/// \param x expression
+#define ROSA_IGNORE_UNUSED_EXPR(x) static_cast<void>(x)
+
+/// Ignors a statement (not an expression).
///
-/// \param x anything
-#define ROSA_IGNORE_UNUSED(x) static_cast<void>(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 <cstdlib>
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 <string> // 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 <memory>
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<Ref>.
///
/// \note It can be statically checked if \p Ref is derived from
/// \c rosa::AbstractAgent<Ref>, but the static assertion cannot be defined
/// directly in the class body. That is because a class \c C derived from
/// \c rosa::AbstractAgent<C> is not complete when the static assertion in the
/// definition of \c rosa::AbstractAgent<C> would be evaluated. Thus, the static
/// assertion is placed in the constructor of \c rosa::AbstractAgent.
template <typename Ref> class AbstractAgent {
protected:
/// Creates a new instance of \c rosa::AbstractAgent<Ref>.
///
/// \note The constructor is protected, thus restricting class instantiation
/// for derived classes only.
///
/// \pre \p Ref is derived from \c rosa::AbstractAgent<Ref>:\code
/// std::is_base_of<AbstractAgent<Ref>, 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 <typename Type, typename... Types>
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 <typename Type, typename... Types>
void send(Type &&T, Types &&... Ts) noexcept;
};
template <typename Ref> AbstractAgent<Ref>::AbstractAgent(void) noexcept {
STATIC_ASSERT((std::is_base_of<AbstractAgent<Ref>, Ref>::value),
"not derived Agent"); // Sanity check.
}
template <typename Ref>
template <typename Type, typename... Types>
void AbstractAgent<Ref>::send(const Type &T, const Types &... Ts) noexcept {
sendMessage(Message::create<Type, Types...>(T, Ts...));
}
template <typename Ref>
template <typename Type, typename... Types>
void AbstractAgent<Ref>::send(Type &&T, Types &&... Ts) noexcept {
sendMessage(Message::create<Type, Types...>(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<AgentHandle> {
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 <typename Fun, typename... Funs>
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 <typename Fun, typename... Funs>
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<AgentHandle> {
/// \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<AgentHandle> {
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 <typename V> void cr(V &&X) {
ASSERT(!Valid);
Valid = true;
new (&Value) AgentHandle(std::forward<V>(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 <functional>
#include <memory>
namespace rosa {
/// Wraps a function and provides a simple interface to invoke the stored
/// function by passing actual arguments as a \c rosa::Message object.
///
/// \note A \c rosa::Invoker instance is supposed to be owned by a
/// \c rosa::MessageHandler instance, and not being used directly from user
/// code.
class Invoker {
protected:
/// Creates an instance.
///
/// \note Protected constructor restricts instantiation to derived classes.
Invoker(void) noexcept;
public:
/// Destroys \p this object.
virtual ~Invoker(void);
/// Possible results of an invocation.
enum class Result {
NoMatch, ///< The wrapped function could not be invoked
Invoked ///< The wrapped function has been invoked
};
/// Type alias for a smart-pointer for \c rosa::Invoker.
using invoker_t = std::unique_ptr<const Invoker>;
/// Type alias for \c rosa::Invoker::Result.
using result_t = Result;
/// Tells if a \c rosa::Message object can be used to invoke the function
/// wrapped in \p this object.
///
/// \param Msg \c rosa::Message to check
///
/// \return whether \p Msg can be used to invoke the wrapped function
virtual bool match(const Message &Msg) const noexcept = 0;
/// Tries to invoke the wrapped function with a \c rosa::Message object.
///
/// The wrapped function is invoked if the actual \c rosa::Message object can
/// be used to invoke it.
///
/// \param Msg \c rosa::Message to try to invoke the wrapped function with
///
/// \return whether the wrapped function could be invoked with \p Msg
virtual result_t operator()(const Message &Msg) const noexcept = 0;
/// Instantiates an implementation of \c rosa::Invoker with the given
/// function.
///
/// \note As there is no empty \c rosa::Message, no \c rosa::Invoker wraps a
/// function without any argument.
///
/// \todo Enforce F does not potentially throw exception.
///
/// \tparam T type of the first mandatory argument
/// \tparam Ts types of any further arguments
///
/// \param F function to wrap
///
/// \return new \c rosa::Invoker::invoker_t object created from the given
/// function
template <typename T, typename... Ts>
static invoker_t wrap(std::function<void(T, Ts...)> &&F) noexcept;
/// Convenience template alias for casting callable stuff to function objects
/// for wrapping.
///
/// \tparam Ts types of arguments
///
/// \todo Should make it possible to avoid using an explicit conversion for
/// the arguments of wrap.
template <typename... Ts> using F = std::function<void(Ts...)>;
/// Convenience template for preparing non-static member functions into
/// function objects for wrapping.
///
/// \tparam C type whose non-static member the function is
/// \tparam Ts types of arguments
///
/// \see \c THISMEMBER
template <typename C, typename... Ts>
static inline F<Ts...> M(C *O, void (C::*Fun)(Ts...) noexcept) noexcept;
};
/// Convenience preprocessor macro for the typical use of \c rosa::Invoker::M.
/// It can be used inside a class to turn a non-static member function into a
/// function object capturing this pointer, so using the actual object when
/// handling a \c rosa::Message.
///
/// \param FUN the non-static member function to wrap
///
/// \note Inside the class \c MyClass, use\code
/// THISMEMBER(fun)
/// \endcode instead of\code
/// Invoker::M(this, &MyClass::fun)
/// \endcode
#define THISMEMBER(FUN) \
Invoker::M(this, &std::decay<decltype(*this)>::type::FUN)
/// Nested namespace with implementation of \c rosa::Invoker and helper
/// templates, consider it private.
namespace {
/// \defgroup InvokerImpl Implementation for rosa::Invoker
///
/// Implements the \c rosa::Invoker interface for functions with different
/// signatures.
///
///@{
/// Declaration of \c rosa::InvokerImpl implementing \c rosa::Invoker.
///
/// \tparam Fun function to wrap
template <typename Fun> class InvokerImpl;
/// Implementation of \c rosa::InvokerImpl for \c std::function.
///
/// \tparam T type of the first mandatory argument
/// \tparam Ts types of further arguments
///
/// \note As there is no empty \c rosa::Message, no \c rosa::Invoker wraps a
/// function without any argument, i.e., no
/// \c std::function<void(void)>.
template <typename T, typename... Ts>
class InvokerImpl<std::function<void(T, Ts...)>> final
: public Invoker {
/// Type alias for the stored function.
using function_t = std::function<void(T, Ts...)>;
/// Type alias for correctly typed argument-tuples as obtained from
/// \c rosa::Message.
using args_t = std::tuple<const T &, const Ts &...>;
/// Alias for \c rosa::MessageMatcher for the arguments of the stored
/// function.
using Matcher = MsgMatcher<T, Ts...>;
/// The wrapped function.
const function_t F;
/// Invokes \c InvokerImpl::F by unpacking arguments from a \c std::tuple with
/// the help of the actual template arguments.
///
/// \tparam S sequence of numbers indexing \c std::tuple for arguments
///
/// \param Args arguments to invoke \c InvokerImpl::F with
///
/// \pre the length of \p S and size of \p Args are matching:\code
/// sizeof...(S) == std::tuple_size<args_t>::value
/// \endcode
template <size_t... S>
inline void invokeFunction(Seq<S...>, const args_t &Args) const noexcept;
public:
/// Creates an instance.
///
/// \param F function to wrap
///
/// \pre \p F is valid:\code
/// bool(F)
/// \endcode
InvokerImpl(function_t &&F) noexcept : F(F) {
ASSERT(bool(F)); // Sanity check.
}
/// Destroys \p this object.
~InvokerImpl(void) = default;
/// Tells if a \c rosa::Message object can be used to invoke the function
/// wrapped in \p this object.
///
/// \param Msg \c rosa::Message to check
///
/// \return whether \p Msg can be used to invoke the wrapped function
bool match(const Message &Msg) const noexcept override {
return Matcher::doesStronglyMatch(Msg);
- };
+ }
/// Tries to invoke the wrapped function with a \c rosa::Message object.
///
/// The wrapped function is invoked if the actual \c rosa::Message object can
/// be used to invoke it.
///
/// \param Msg \c rosa::Message to try to invoke the wrapped function with
///
/// \return whether the wrapped function could be invoked with \p Msg
result_t operator()(const Message &Msg) const noexcept override {
if (match(Msg)) {
LOG_TRACE("Invoking with matching arguments");
invokeFunction(seq_t<sizeof...(Ts) + 1>(), Matcher::extractedValues(Msg));
return result_t::Invoked;
} else {
LOG_TRACE("Tried to invoke with non-matching arguments");
return result_t::NoMatch;
}
}
};
template <typename T, typename... Ts>
template <size_t... S>
void InvokerImpl<std::function<void(T, Ts...)>>::invokeFunction(
Seq<S...>, const args_t &Args) const noexcept {
- ASSERT(sizeof...(S) == std::tuple_size<args_t>::value); // Sanity check.
+ STATIC_ASSERT(sizeof...(S) == std::tuple_size<args_t>::value,
+ "wrong number of type parameters");
F(std::get<S>(Args)...);
}
///@}
} // End namespace
template <typename T, typename... Ts>
Invoker::invoker_t
Invoker::wrap(std::function<void(T, Ts...)> &&F) noexcept {
return std::unique_ptr<Invoker>(
new InvokerImpl<std::function<void(T, Ts...)>>(std::move(F)));
}
template <typename C, typename... Ts>
Invoker::F<Ts...> Invoker::M(C *O, void (C::*Fun)(Ts...) noexcept) noexcept {
return [ O, Fun ](Ts... Vs) noexcept->void { (O->*Fun)(Vs...); };
}
} // End namespace rosa
#endif // ROSA_CORE_INVOKER_HPP
diff --git a/include/rosa/core/Message.hpp b/include/rosa/core/Message.hpp
index 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 <typename Type, typename... Types>
Message(const Type &, const Types &...) noexcept;
/// No copying and moving of \c rosa::Message instances.
///@{
Message(const Message &) = delete;
Message(Message &&) = delete;
Message &operator=(const Message &) = delete;
Message &operator=(Message &&) = delete;
///@}
public:
/// Creates a \c rosa::message_t object from constant lvalue references.
///
/// \tparam Type type of the mandatory first argument
/// \tparam Types types of any further arguments
///
/// \param T the first value to include in the \c rosa::Message
/// \param Ts optional further values to include in the \c rosa::Message
///
/// \return new \c rosa::message_t object created from the given arguments
template <typename Type, typename... Types>
static message_t create(const Type &T, const Types &... Ts) noexcept;
/// Creates a \c rosa::message_t object from rvalue references.
///
/// \tparam Type type of the mandatory first argument
/// \tparam Types types of any further arguments
///
/// \param T the first value to include in the \c rosa::Message
/// \param Ts optional further values to include in the \c rosa::Message
///
/// \return new \c rosa::message_t object created from the given arguments
template <typename Type, typename... Types>
static message_t create(Type &&T, Types &&... Ts) noexcept;
/// Represents the types of the values stored in \p this object.
///
/// A valid, non-empty \c rosa::Token representing the types of the values
/// stored in \p this object.
const Token T;
/// The number of values stored in \p this object.
///
/// That is the number of types encoded in \c rosa::Message::T.
const token_size_t Size;
/// Destroys \p this object.
virtual ~Message(void);
/// Tells if the value stored at a given index is of a given type.
///
/// \note Any \c rosa::AtomConstant is encoded in \c rosa::Token as
/// the \c rosa::AtomValue wrapped into it.
///
/// \tparam Type type to match against
///
/// \param Pos index the type of the value at is to be matched against \p Type
///
/// \return if the value at index \p Pos of type \p Type
///
/// \pre \p Pos is a valid index:\code
/// Pos < Size
/// \endcode
template <typename Type> bool isTypeAt(const token_size_t Pos) const noexcept;
/// Gives a constant reference of a value of a given type stored at a given
/// index.
///
/// \tparam Type type to give a reference of
///
/// \param Pos index to set the reference for
///
/// \return constant reference of \p Type for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index and the value at index \p Pos is of type
/// \p Type:
/// \code
/// Pos < Size && isTypeAt<Type>(Pos)
/// \endcode
template <typename Type>
const Type &valueAt(const 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 <typename... Types> class LocalMessage;
/// Implementation of the template \c rosa::LocalMessage providing facilities
/// for storing values as a \c rosa::Message object.
///
/// \tparam Type type of the first mandatory value of the \c rosa::Message
/// \tparam Types of any further values
template <typename Type, typename... Types>
class LocalMessage<Type, Types...> final
: public Message,
private TokenizedStorage<Type, Types...> {
public:
/// Creates an instance from constant lvalue references.
///
/// \param T the mandatory first value to store in the \c rosa::Message object
/// \param Ts optional further values to store in the \c rosa::Message object
LocalMessage(const Type &T, const Types &... Ts) noexcept
: Message(T, Ts...),
TokenizedStorage<Type, Types...>(T, Ts...) {
ASSERT(this->T == this->ST && Size == this->size()); // Sanity check.
}
/// Creates an instance from rvalue references.
///
/// \param T the mandatory first value to store in the \c rosa::Message object
/// \param Ts optional further values to store in the \c rosa::Message object
LocalMessage(Type &&T, Types &&... Ts) noexcept
: Message(T, Ts...),
TokenizedStorage<Type, Types...>(std::move(T), std::move(Ts)...) {
ASSERT(this->T == this->ST && Size == this->size()); // Sanity check.
}
/// Provides an untyped pointer for the constant value stored at a position.
///
/// \param Pos the index of the value to return an untyped pointer for
///
/// \return untyped pointer for the constant value stored at index \p Pos
///
/// \pre \p Pos is a valid index:\code
/// Pos < Size
/// \endcode
const void *pointerTo(const token_size_t Pos) const noexcept override {
ASSERT(Pos < Size);
return TokenizedStorage<Type, Types...>::pointerTo(Pos);
}
/// Aborts the program!
///
/// Since \c rosa::Message instances are supposed to be immutable, the
/// non-const inherited function is overridden so that it aborts execution.
void *pointerTo(const token_size_t) noexcept override {
ROSA_CRITICAL("Unallowed operation of rosa::LocalMessage");
}
};
} // End namespace
template <typename Type, typename... Types>
Message::Message(const Type &, const Types &...) noexcept
: T(TypeToken<typename std::decay<Type>::type,
typename std::decay<Types>::type...>::Value),
Size(lengthOfToken(T)) {
ASSERT(validToken(T) &&
lengthOfToken(T) == (1 + sizeof...(Types))); // Sanity check.
LOG_TRACE("Creating Message with Token(" + std::to_string(T) + ")");
}
/// \note The implementation instantiates a private local template class
/// \c LocalMessage.
template <typename Type, typename... Types>
message_t Message::create(const Type &T, const Types &... Ts) noexcept {
return message_t(new LocalMessage<Type, Types...>(T, Ts...));
}
/// \note The implementation instantiates a private local template class
/// \c LocalMessage.
template <typename Type, typename... Types>
message_t Message::create(Type &&T, Types &&... Ts) noexcept {
return message_t(
new LocalMessage<Type, Types...>(std::move(T), std::move(Ts)...));
}
template <typename Type>
bool Message::isTypeAt(const token_size_t Pos) const noexcept {
ASSERT(Pos < Size);
Token TT = T;
dropNOfToken(TT, Pos);
return isHeadOfTokenTheSameType<Type>(TT);
}
template <typename Type>
const Type &Message::valueAt(const token_size_t Pos) const noexcept {
ASSERT(Pos < Size && isTypeAt<Type>(Pos));
return *static_cast<const Type *>(pointerTo(Pos));
}
} // End namespace rosa
#endif // ROSA_CORE_MESSAGE_HPP
diff --git a/include/rosa/core/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 <vector>
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>([](uint8_t) { /* ... */ }),
/// rosa::Invoker::F<uint8_t>([](uint8_t) { /* Never invoked */ })
/// };
/// \endcode
/// Applying a \c rosa::Message with \c rosa::TypeList<uint8_t> 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<invoker_t>;
/// 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 <typename Fun, typename... Funs>
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 <typename Fun, typename... Funs>
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 <typename Fun, typename... Funs>
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 <typename Fun, typename... Funs>
MessageHandler::MessageHandler(Fun &&F, Funs &&... Fs) noexcept
: Invokers(createInvokers(std::move(F), std::move(Fs)...)) {
LOG_TRACE("MessageHandler is created");
}
template <typename Fun, typename... Funs>
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 <typename Fun, typename... Funs>
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 <tuple>
namespace rosa {
/// Provides features to type-check a \c rosa::Message instance and extract
/// stored values from it into an \c std::tuple instance with matching type
/// arguments.
///
/// \tparam List \c rosa::TypeList to check the stored values against
template <typename List> struct MessageMatcher;
/// Definition of \c rosa::MessageMatcher for non-empty lists of types, like
/// \c rosa::Message itself.
///
/// \tparam Type first mandatory type
/// \tparam Types any further types
template <typename Type, typename... Types>
struct MessageMatcher<TypeList<Type, Types...>> {
/// \c rosa::Token associated to the given \c rosa::TypeList.
static constexpr Token T = TypeToken<Type, Types...>::Value;
/// Tells if the values stored in a \c rosa::Message instance are matching
/// types given as \c rosa::TypeList<T, Types...>, considering
/// \c rosa::AtomConstant instead of \c rosa::AtomValue.
///
/// \param Msg \c rosa::Message to match
///
/// \return whether the types of values stored in \p Msg matches
/// \c rosa::TypeList<Type, Types...>
static inline bool doesStronglyMatch(const Message &Msg) noexcept;
/// Gives a \c std::tuple with references to the values stored in a
/// type-matching instance of \c rosa::Message.
///
/// \param Msg \c rosa::Message to extract values from
///
/// \return \c std::tuple with references to the values stored in \p Msg
///
/// \pre Types of the values stored in \p Msg matches
/// \c rosa::TypeList<Type, Types...>:\code
/// doesStronglyMatch(Msg)
/// \endcode
static inline std::tuple<const Type &, const Types &...>
extractedValues(const Message &Msg) noexcept;
};
/// Turns a list of types into a \c rosa::TypeList for \c rosa::MessageMatcher.
template <typename Type, typename... Types>
using MsgMatcher = MessageMatcher<TypeList<Type, Types...>>;
/// Nested namespace with implementation for features of
/// \c rosa::MessageMatcher, consider it private.
namespace {
/// \defgroup MessageMatcherImpl Implementation for rosa::MessageMatcher
///
/// An implementation of type-checking and value extraction for
/// \c rosa::MessageMatcher.
///
///@{
/// Template declaration of \c MessageMatcherImpl.
///
/// \tparam List \c rosa::TypeList to match against
template <typename List> struct MessageMatcherImpl;
/// Specialization for \c rosa::EmptyTypeList.
template <> struct MessageMatcherImpl<EmptyTypeList> {
static bool doesStronglyMatchFrom(const Message &Msg,
const 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 <AtomValue V, typename... Ts>
struct MessageMatcherImpl<TypeList<AtomConstant<V>, 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<AtomValue>(Pos) &&
Msg.valueAt<AtomValue>(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<TypeList<Ts...>>::doesStronglyMatchFrom(Msg,
Pos + 1);
}
static std::tuple<const AtomConstant<V> &, 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<V>::Value),
MessageMatcherImpl<TypeList<Ts...>>::extractedValuesFrom(Msg, Pos + 1));
}
};
/// Definition for the general case when a regular built-in type (not a
/// \c rosa::AtomConstant) is in the head.
template <typename T, typename... Ts>
struct MessageMatcherImpl<TypeList<T, Ts...>> {
static bool doesHeadStronglyMatchAt(const Message &Msg,
const token_size_t Pos) noexcept {
// Matching the head if there is a type stored at \p Pos, and the stored
// type is \p T.
return Pos < Msg.Size && Msg.isTypeAt<T>(Pos);
}
static bool doesStronglyMatchFrom(const Message &Msg,
const token_size_t Pos) noexcept {
// Matching a non-empty list if the head is matching and the rest of the
// list is matching.
return doesHeadStronglyMatchAt(Msg, Pos) &&
MessageMatcherImpl<TypeList<Ts...>>::doesStronglyMatchFrom(Msg,
Pos + 1);
}
static std::tuple<const T &, const Ts &...>
extractedValuesFrom(const Message &Msg, const token_size_t Pos) noexcept {
// Extracting for a non-empty list with a matching head by getting the
// value for the head and concatenating it with values extracted for the
// rest of the list.
ASSERT(doesHeadStronglyMatchAt(Msg, Pos));
return std::tuple_cat(
std::tie(Msg.valueAt<T>(Pos)),
MessageMatcherImpl<TypeList<Ts...>>::extractedValuesFrom(Msg, Pos + 1));
}
};
///@}
} // End namespace
template <typename Type, typename... Types>
bool MessageMatcher<TypeList<Type, Types...>>::doesStronglyMatch(
const Message &Msg) noexcept {
// \note Fail quick on \c rosa::MessageMatcher::T, then match against list
// with squashed integers the way \c rosa::Token is generated.
return T == Msg.T &&
MessageMatcherImpl<typename SquashedTypeList<
TypeList<Type, Types...>>::Type>::doesStronglyMatchFrom(Msg, 0);
}
template <typename Type, typename... Types>
std::tuple<const Type &, const Types &...>
MessageMatcher<TypeList<Type, Types...>>::extractedValues(
const Message &Msg) noexcept {
ASSERT(doesStronglyMatch(Msg));
// \note Match against a list with squashed integers as \c rosa::Token is
// generated.
return MessageMatcherImpl<typename SquashedTypeList<
TypeList<Type, Types...>>::Type>::extractedValuesFrom(Msg, 0);
}
} // End namespace rosa
#endif // ROSA_CORE_MESSAGEMATCHER_HPP
diff --git a/include/rosa/core/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<MessagingSystem>
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<Agent, T>::value
/// \endcode
template <typename T, typename... Funs>
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 <typename Type, typename... Types>
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 <typename Type, typename... Types>
void send(const AgentHandle &H, Type &&T, Types &&... Ts) noexcept;
};
template <typename T, typename... Funs>
AgentHandle MessagingSystem::createAgent(const std::string &Name,
Funs &&... Fs) {
STATIC_ASSERT((std::is_base_of<Agent, T>::value), "not an Agent");
Agent &A = createUnit<T, MessagingSystem>([&](const id_t Id,
MessagingSystem &S) noexcept {
return new T(AgentKind, Id, Name, S, std::move(Fs)...);
});
return {A};
}
template <typename Type, typename... Types>
void MessagingSystem::send(const AgentHandle &H, const Type &T,
const Types &... Ts) noexcept {
send(H, Message::create<Type, Types...>(T, Ts...));
}
template <typename Type, typename... Types>
void MessagingSystem::send(const AgentHandle &H, Type &&T,
Types &&... Ts) noexcept {
send(H, Message::create<Type, Types...>(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 <functional>
#include <memory>
#include <string>
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 <typename T, typename S>
using UnitCreator = std::function<T *(const id_t, S &)>;
/// 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<System> 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<Unit, T>::value && std::is_base_of<System, S>::value
/// \endcode
/// Dynamically, \p this object has not yet been marked cleaned:\code
/// !isSystemCleaned()
/// \endcode
template <typename T, typename S> T &createUnit(UnitCreator<T, S> 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 <typename T, typename S>
T &System::createUnit(UnitCreator<T, S> C) noexcept {
STATIC_ASSERT((std::is_base_of<Unit, T>::value), "not a Unit");
STATIC_ASSERT((std::is_base_of<System, S>::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<S &>(*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 <atomic>
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<size_t> 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<bool> 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 <ostream>
#include <string>
namespace rosa {
/// Base class for every entity in a \c rosa::System that has to be identified
/// and traced.
///
/// \note Life-cycle of \c rosa::Unit instances is supposed to be managed by the
/// \c rosa::System owning the instance, do not create and destroy any
/// \c rosa::Unit directly.
class Unit {
public:
/// Identifies the *kind* of \p this object.
///
/// \note Kind is dependent on the \c rosa::System owning \p this object.
const AtomValue Kind;
/// Unique identifier for \p this object.
///
/// \note The unique identifier is assigned by the \c rosa::System owning
/// \p this object upon creation.
const id_t Id;
/// Textual identifier of \p this object.
///
/// \note Textual identifiers of \c rosa::Unit instances are not necessarily
/// unique in their owning \c rosa::System.
const std::string Name;
protected:
/// The \c rosa::System owning \p this object.
System &S;
public:
/// 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 <memory> // 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<const Message>;
} // 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 <map>
/// Local helper macros to deal with built-in types.
///
///@{
/// Creates function name for member functions in \c rosa::deluxe::DeluxeAgent.
///
/// \param N name suffix to use
#define DASLAVEHANDLERNAME(N) handleSlave_##N
/// Creates function name for member functions in \c rosa::deluxe::DeluxeAgent.
///
/// \param N name suffix to use
#define DAMASTERHANDLERNAME(N) handleMaster_##N
/// Defines member functions for handling messages from *slaves* in
/// \c rosa::deluxe::DeluxeAgent.
///
/// \see \c DeluxeAgentInputHandlers
///
/// \note No pre- and post-conditions are validated directly by these functions,
/// they rather rely on \c rosa::deluxe::DeluxeAgent::saveInput to do that.
///
/// \param T the type of input to handle
/// \param N name suffix for the function identifier
#define DASLAVEHANDLERDEFN(T, N) \
void DASLAVEHANDLERNAME(N)(atoms::Slave, id_t SlaveId, token_size_t Pos, \
T Value) noexcept { \
saveInput(SlaveId, Pos, Value); \
}
/// Defines member functions for handling messages from *master* in
/// \c rosa::deluxe::DeluxeAgent.
///
/// \see \c DeluxeAgentMasterInputHandlers
///
/// \note No pre- and post-conditions are validated directly by these functions,
/// they rather rely on \c rosa::deluxe::DeluxeAgent::saveMasterInput to do
/// that.
///
/// \param T the type of input to handle
/// \param N name suffix for the function identifier
#define DAMASTERHANDLERDEFN(T, N) \
void DAMASTERHANDLERNAME(N)(atoms::Master, id_t MasterId, token_size_t Pos, \
T Value) noexcept { \
saveMasterInput(MasterId, Pos, Value); \
}
/// Convenience macro for \c DASLAVEHANDLERDEFN with identical arguments.
///
/// \see \c DASLAVEHANDLERDEFN
///
/// This macro can be used instead of \c DASLAVEHANDLERDEFN if the actual value
/// of \p T can be used as a part of a valid identifier.
///
/// \param T the type of input to handle
#define DASLAVEHANDLERDEF(T) DASLAVEHANDLERDEFN(T, T)
/// Convenience macro for \c DAMASTERHANDLERDEFN with identical arguments.
///
/// \see \c DAMASTERHANDLERDEFN
///
/// This macro can be used instead of \c DAMASTERHANDLERDEFN if the actual value
/// of \p T can be used as a part of a valid identifier.
///
/// \param T the type of input to handle
#define DAMASTERHANDLERDEF(T) DAMASTERHANDLERDEFN(T, T)
/// Results in a \c THISMEMBER reference to a member function defined by
/// \c DASLAVEHANDLERDEFN.
///
/// Used in the constructor of \c rosa::deluxe::DeluxeAgent to initialize super
/// class \c rosa::Agent with member function defined by \c DASLAVEHANDLERDEFN.
///
/// \see \c DASLAVEHANDLERDEFN, \c THISMEMBER
///
/// \param N name suffix for the function identifier
#define DASLAVEHANDLERREF(N) THISMEMBER(DASLAVEHANDLERNAME(N))
/// Results in a \c THISMEMBER reference to a member function defined by
/// \c DAMASTERHANDLERDEFN.
///
/// Used in the constructor of \c rosa::deluxe::DeluxeAgent to initialize super
/// class \c rosa::Agent with member function defined by \c DAMASTERHANDLERDEFN.
///
/// \see \c DAMASTERHANDLERDEFN, \c THISMEMBER
///
/// \param N name suffix for the function identifier
#define DAMASTERHANDLERREF(N) THISMEMBER(DAMASTERHANDLERNAME(N))
///@}
namespace rosa {
namespace deluxe {
/// Specialization of \c rosa::Agent for *agent* role of the *deluxe interface*.
///
/// \see \c rosa::deluxe::DeluxeContext
///
/// \invariant There is a compatible *execution policy* set, all input-related
/// container objects have a size matching \c
/// rosa::deluxe::DeluxeAgent::NumberOfInputs, thus having a corresponding entry
/// for each input. \c rosa::deluxe::DeluxeAgent::NumberOfMasterOutputs matches
/// \c rosa::deluxe::DeluxeAgent::NumberOfInputs. All master-output-related
/// container objects have a size matching \c
/// rosa::deluxe::DeluxeAgent::NumberOfMasterOutputs. Types and type-related
/// information of input and master-output values are consistent throughout all
/// the input-related and master-output-related containers, respectively. The
/// actual values in \c rosa::deluxe::DeluxeAgent::InputNextPos and \c
/// rosa::deluxe::DeluxeAgent::MasterInputNextPos are valid with respect to the
/// corresponding types. No *slave* is registered at more than one input
/// position. *Slave* registrations and corresponding reverse lookup
/// information are consistent.
///
/// \see Definition of \c rosa::deluxe::DeluxeAgent::inv on the class invariant
///
/// \note All member functions validate the class invariant as part of their
/// precondition. Moreover, non-const functions validate the invariant before
/// return as their postcondition.
class DeluxeAgent : public Agent {
/// Checks whether \p this object holds the class invariant.
///
/// \see Invariant of the class \c rosa::deluxe::DeluxeAgent
///
/// \return if \p this object holds the class invariant
bool inv(void) const noexcept;
/// The \c rosa::deluxe::DeluxeExecutionPolicy that controls the execution of
/// \c this object.
std::unique_ptr<DeluxeExecutionPolicy> ExecutionPolicy;
public:
/// The type of values produced by \p this object.
///
/// That is the types of values \p this object sends to its *master* in a \c
/// rosa::deluxe::DeluxeTUple.
///
/// \see \c rosa::deluxe::DeluxeAgent::master
const Token OutputType;
/// Number of inputs processed by \p this object.
const size_t NumberOfInputs;
/// The type of values \p this object processes from its *master*.
///
/// That is the types of values \p this object receives from its *master* in a
/// \c rosa::deluxe::DeluxeTuple.
///
/// \see \c rosa::deluxe::DeluxeAgent::master
const Token MasterInputType;
/// Number of outputs produces by \p this object for its *slaves*.
///
/// \note This values is equal to \c
/// rosa::deluxe::DeluxeAgent::NumberOfInputs.
///
/// \see \c rosa::deluxe::DeluxeAgent::slave.
const size_t NumberOfMasterOutputs;
private:
/// Types of input values produced by *slaves* of \p this object.
///
/// \note The \c rosa::Token values stored correspond to \c
/// rosa::deluxe::DeluxeTuple instances at each argument position. The \c
/// rosa::TypeNumber values from the stored \c rosa::Token values match the
/// corresponding values in \c rosa::deluxe::DeluxeAgent::InputValues in
/// order.
///
/// \note The position of a \c rosa::Token in the \c std::vector indicates
/// which argument of \p this object's processing function it belongs to. See
/// also \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
const std::vector<Token> InputTypes;
/// Indicates which element of an input is expected from any particular
/// *slave*.
///
/// The *slave* is supposed to send one \c rosa::deluxe::DeluxeTuple value
/// element by element in their order of definition. This member field tells
/// the element at which position in the tuple should be received next from
/// the *slave* at a given position.
///
/// \p this object is supposed to be triggered only when input values has been
/// received completely, that is all values in the field should hold the value
/// `0`.
///
/// \see \c rosa::deluxe::DeluxeAgent::handleTrigger
/// \c rosa::deluxe::DeluxeAgent::saveInput
std::vector<token_size_t> InputNextPos;
/// Indicates whether any particular input value has been changed since the
/// last trigger received from the system.
///
/// All the flags are reset to \c false upon handling a trigger and then set
/// to \c true by \c rosa::deluxe::DeluxeAgent::saveInput when storing a new
/// input value in \c rosa::deluxe::DeluxeAgent::InputValues.
///
/// \note The position of a flag in the \c std::vector indicates which
/// argument of \p this object's processing function it belongs to. See also
/// \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
std::vector<bool> InputChanged;
- /// Tells at which position in \c rosa::deluxe::DeluxeAgent::InputValues the
- /// input from any particular *slave* starts.
- ///
- /// \note A value in the vector corresponds to the *slave* at the same
- /// position and it is the sum of the elements of input values from *slaves*
- /// at previous positions.
- ///
- /// \see \c rosa::deluxe::DeluxeAgent::saveInput
- const std::vector<token_size_t> InputStorageOffsets;
-
/// Stores the actual input values.
///
/// \note The types of stored values match the corresponding
/// \c rosa::TypeNumber values (in \c rosa::Token in order) in \c
/// rosa::deluxe::DeluxeAgent::InputTypes.
///
- /// \note The position of a value in the \c rosa::AbstractTokenizedStorage
- /// indicates which element of the tuple of which argument of \p this object's
- /// processing function it is. See also \c
- /// rosa::deluxe::DeluxeAgent::DeluxeAgent.
- const std::unique_ptr<AbstractTokenizedStorage> InputValues;
+ /// \note The position of a \c rosa::AbstractTokenizedStorage in the \c
+ /// std::vector indicates which argument of \p this object's processing
+ /// function the tuple is; and the position of the value in the \c
+ /// rosa::AbstractTokenizedStorage indicates which element of that tuple the
+ /// value is. See also \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
+ const std::vector<std::unique_ptr<AbstractTokenizedStorage>> InputValues;
/// Indicates which element of the master-input is expected from the *master*.
///
/// The *master* is supposed to send one \c rosa::deluxe::DeluxeTuple value
/// element by element in their order of definition. This member field tells
/// the element at which position should be received next.
///
/// \p this object is supposed to be triggered only when a complete
/// master-input has been received, that is the field should hold the value
/// `0`.
///
/// \see \c rosa::deluxe::DeluxeAgent::handleTrigger
/// \c rosa::deluxe::DeluxeAgent::saveMasterInput
token_size_t MasterInputNextPos;
/// Indicates whether the input value from the *master* has been changed since
/// the last trigger received from the system.
///
/// The flag is reset to \c false upon handling a trigger and then set to \c
/// true by \c rosa::deluxe::DeluxeAgent::saveMasterInput when storig a new
/// input value in \c rosa::deluxe::DeluxeAgent::MasterInputValue.
bool MasterInputChanged;
/// Stores the actual input value from *master*.
///
/// \note The type of the stored value matches the types indicated by \c
/// rosa::deluxe::DeluxeAgent::MasterInputType.
const std::unique_ptr<AbstractTokenizedStorage> MasterInputValue;
/// Types of output values produced by \p this object for its *slaves*.
///
/// That is the types of values \p this object sends to its *slaves* in a \c
/// rosa::deluxe::DeluxeTuple.
///
/// \note The position of a type in the \c std::vector indicates which
/// *slave* of \p this object the type belongs to. See also
/// \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
const std::vector<Token> MasterOutputTypes;
/// Alias for function objects used as trigger handler for
/// \c rosa::deluxe::DeluxeAgent.
///
/// \note The function used for \c H is to be \c noexcept.
///
/// \see \c rosa::deluxe::DeluxeAgent::FP
using H = std::function<void(void)>;
/// Handles trigger from the system.
///
/// The actual functions processing *slave* and *master* inputs and generating
/// optional output to *master* and *slaves* are captured in a lambda
/// expression that is in turn wrapped in a \c std::function object. The
/// lambda expression calls the master-input processing function with the
/// actual master-input data and sends its result -- if any -- to *slaves* by
/// calling \c rosa::deluxe::DeluxeAgent::handleMasterOutputs; then calls the
/// input processing function with the actual input data and sends its result
/// -- if any -- to *master* by calling \c
/// rosa::deluxe::DeluxeAgent::sendToMaster and *slaves* by calling \c
/// rosa::deluxe::DeluxeAgent::handleMasterOutputs. Also, all the flags stored
/// in \c rosa::deluxe::DeluxeAgent::InputChanged and \c
/// rosa::deluxe::DeluxeAgent::MasterInputChanged are reset when the current
/// values are processed. The function \c
/// rosa::deluxe::DeluxeAgent::handleTrigger needs only to call the
/// function object.
///
/// \see \c
/// rosa::deluxe::DeluxeAgent::triggerHandlerFromProcessingFunctions
const H FP;
/// The *master* to send values to.
///
/// \note *Masters* are set dynamically, hence it is possible that a
/// \c rosa::deluxe::DeluxeAgent instance does not have any *master* at a
/// given moment.
Optional<AgentHandle> Master;
/// The *slaves* sending input to \p this object.
///
/// \note The position of a *slave* in the \c std::vector indicates which
/// argument of \p this object's processing function it belongs to. See also
/// \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
///
/// \note *Slaves* are set dynamically, hence it is possible that a
/// \c rosa::deluxe::DeluxeAgent instance does have input positions without
/// any *slave* associated to them.
///
/// \note Reverse lookup information is maintained in
/// \c rosa::deluxe::DeluxeAgent::SlaveIds, which is to be kept in sync with
/// the *slaves* stored here.
std::vector<Optional<AgentHandle>> Slaves;
/// Associates \c rosa::id_t values to corresponding indices of registered
/// *slaves*.
///
/// \see \c rosa::deluxe::DeluxeAgent::Slaves
std::map<id_t, size_t> SlaveIds;
/// Tells the unique identifier of the *master* of \p this object, if any
/// registered.
///
/// \return the unique identifier of the *master*
///
/// \pre A *master* is registered for \p this object: \code
/// Master
/// \endcode
id_t masterId(void) const noexcept;
/// Tells whether types stored in \c rosa::TypeList \p As match the input
/// types of \p this object.
///
/// \tparam As \c rosa::TypeList containing types to match against values in
/// \c rosa::deluxe::DeluxeAgent::InputTypes
///
/// \note Instatiation of the template fails if \p As is not \c
/// rosa::TypeList.
///
/// \return if types in \p As are instances of \c rosa::deluxe::DeluxeTuple
/// and their types match \c rosa::Token values stored in \c
/// rosa::deluxe::DeluxeAgent::InputTypes
template <typename As> bool inputTypesMatch(void) const noexcept;
/// Tells whether types stored in \c rosa::TypeList \p Ts match the
/// master-output types of \p this object.
///
/// \tparam Ts \c rosa::TypeList containing types to match against values in
/// \c rosa::deluxe::DeluxeAgent::MasterOutputTypes
///
/// \note Instatiation of the template fails if \p As is not \c
/// rosa::TypeList.
///
/// \return if types in \p Ts match \c rosa::Token and in turn \c
/// rosa::TypeNumber values stored in \c
/// rosa::deluxe::DeluxeAgent::MasterOutputTypes
template <typename Ts> bool masterOutputTypesMatch(void) const noexcept;
/// Gives the current input value for slave position \p Pos.
///
/// \tparam Pos slave position to get input value for
/// \tparam Ts types of elements of the input value
/// \tparam S0 indices for accessing elements of the input value
///
/// \note The arguments provide types and indices statically as template
/// arguments \p Ts... \p S0..., respectively, so their actual values are
/// ignored.
///
/// \return current input value for slave position \p Pos
///
/// \pre Statically, the provided indices \p S0... match the length of \p
/// Ts...: \code
/// sizeof...(Ts) == sizeof...(S0)
/// \endcode Dynamically, \p Pos is a valid slave position and type arguments
/// \p Ts... match the corresponding input value: \code
/// Pos < NumberOfInputs && DeluxeTuple<Ts...>::TT == InputTypes[Pos]
/// \endcode
template <size_t Pos, typename... Ts, size_t... S0>
DeluxeTuple<Ts...> prepareInputValueAtPos(TypeList<Ts...>, Seq<S0...>) const
noexcept;
/// Gives an \c std::tuple containing the current input values and their
/// change flags so that they can be used for the processing function.
///
/// \tparam As types of the input values
/// \tparam S0 indices for accessing input values and their change flags
///
/// \note The only argument provides indices statically as template arguments
/// \p S0..., so its actual value is ignored.
///
/// \return current input values and their change flags prepared for invoking
/// the processing function with them
///
/// \pre Statically, all type arguments \p As... are instances of \c
/// rosa::deluxe::DeluxeTuple and the provided indices \p S0... match the
/// length of \p As...: \code
/// TypeListAllDeluxeTuple<TypeList<As...>>::Value &&
/// sizeof...(As) == sizeof...(S0)
/// \endcode Dynamically, type arguments \p As... match the input types of \p
/// this object: \code
/// inputTypesMatch<TypeList<As...>>()
/// \endcode
template <typename... As, size_t... S0>
std::tuple<std::pair<As, bool>...> prepareCurrentInputs(Seq<S0...>) const
noexcept;
/// Invokes a processing function matching the input, output, and
/// master-output types of \p this object with actual arguments provided in a
/// \c std::tuple.
///
/// \note \p Args providing the actual arguments for \p F is to be created by
/// \c rosa::deluxe::DeluxeAgent::prepareCurrentInputs.
///
/// \tparam T output type of the processing function
/// \tparam Ts types of master-output values of the processing function
/// \tparam As types of inputs for the processing function
/// \tparam S0 indices starting with `0` for extracting actual arguments from
/// \p Args
///
/// \param F the processing function to invoke
/// \param Args the actual arguments to invoke \p F with
///
/// \note The last argument provides indices statically as template arguments
/// \p S0..., so its actual value is ignored.
///
/// \return the result of \p F for actual arguments \p Args
///
/// \pre The provided sequence of indices \p S0... constitutes a proper
/// sequence for extracting all actual arguments for
/// \p F from \p Args: \code
/// sizeof...(As) == sizeof...(S0)
/// \endcode
template <typename T, typename... Ts, typename... As, size_t... S0>
static std::tuple<Optional<T>, Optional<Ts>...>
invokeWithTuple(std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)>
F,
const std::tuple<std::pair<As, bool>...> Args,
Seq<S0...>) noexcept;
/// Handles a master-output value for a particular *slave* position.
///
/// \p Value is a \c rosa::Optional resulted by a processing function and
/// contains a master-output value for the *slave* at position \p Pos. The
/// function takes the master-output value and sends its actual value, if any,
/// to the corresponding *slave*.
///
/// \note A master-output of type \c rosa::deluxe::EmptyDeluxeTuple indicates
/// no actual output and hence no message is generated for a position whose
/// corresponding master-output type is \c rosa::deluxe::EmptyDeluxeTuple.
///
/// \note The function provides position-based implementation for \c
/// rosa::deluxe::DeluxeAgent::handleMasterOutputs.
///
/// \tparam Pos the position of the master-output to send \p Value for
/// \tparam Ts types of elements in \p Value
///
/// \param Value \c rosa::deluxe::DeluxeTuple resulted by the processing
/// function for *slave* position \p Pos
///
/// \pre \p Pos is a valid master-output position and \p Value matches the
/// master-output type of \p this object at position \p Pos: \code
/// Pos < NumberOfMasterOutputs &&
/// DeluxeTuple<Ts...>::TT == MasterOutputTypes[Pos]
/// \endcode
template <size_t Pos, typename... Ts>
void
handleMasterOutputAtPos(const Optional<DeluxeTuple<Ts...>> &Value) noexcept;
/// Handles master-output values from \p Output.
///
/// \p Output is a \c std::tuple resulted by a processing function and
/// contains master-output values starting at position \p Offset. The function
/// takes master-output values and sends each actual value to the
/// corresponding *slave*.
///
/// \tparam Offset index of the first master-output value in \p Output
/// \tparam Ts output types stored in \p Output
/// \tparam S0 indices starting with `0` for extracting master-output values
/// from \p Output
///
/// \note Instantiation fails if any of the type arguments \p Ts... starting
/// at position \p Offset is not an instance of \c rosa::deluxe::DeluxeTuple
/// or the number of types \p Ts... is not consistent with the other template
/// arguments.
///
/// \param Output \c std::tuple resulted by a processing function
///
/// \pre Statically, type arguments \p Ts... starting at position \p Offset
/// are instances of \c rosa::deluxe::DeluxeTuple and the number of types \p
/// Ts... is consistent with the other template arguments: \code
/// TypeListAllDeluxeTuple<
/// typename TypeListDrop<Offset, TypeList<Ts...>>::Type>::Value &&
/// sizeof...(Ts) == Offset + sizeof...(S0)
/// \endcode Dynamically, \p Output matches the master-output types \p this
/// object was created with and the provided sequence of indices \p S0...
/// constitues a proper sequence for extracting all master-output values from
/// \p Output: \code
/// masterOutputTypesMatch<typename TypeListDrop<Offset,
/// TypeList<Ts...>>::Type>() &&
/// sizeof...(S0) == NumberOfMasterOutputs
/// \endcode
template <size_t Offset, typename... Ts, size_t... S0>
void handleMasterOutputs(const std::tuple<Optional<Ts>...> &Output,
Seq<S0...>) noexcept;
/// Wraps processing functions into a trigger handler.
///
/// \see \c rosa::deluxe::DeluxeAgent::FP
///
/// \note The function cannot be const qualified because the lambda
/// expression defined in it needs to capture \p this object by a non-const
/// reference
///
/// \tparam MTs types of elements of master-input processed by \p MF
/// \tparam T type of output
/// \tparam Ts types of master-output values
/// \tparam As types of input values
/// \tparam S0 indices for accessing master-input values
///
/// \note Instantiation fails if any of the type arguments \p T, \p Ts...,
/// and \p As... is not an instance of \c rosa::deluxe::DeluxeTuple.
///
/// \param MF function processing master-input and generating output
/// \param F function processing inputs and generating output
///
/// \note The last argument provides indices statically as template
/// arguments \p S0..., so its actual value is ignored.
///
/// \note A master-input type of \c rosa::deluxe::EmptyDeluxeTuple indicates
/// that \p this object does not receive master-input, \p MF is never called
/// if \p MTs is empty.
///
/// \return trigger handler function based on \p F and \p MF
///
/// \pre Statically, type arguments \p T, \p Ts..., and \p As... are
/// instances of \c rosa::deluxe::DeluxeTuple and the indices match
/// master-input elements: \code
/// TypeListAllDeluxeTuple<TypeList<T, Ts..., As...>>::Value &&
/// sizeof...(MTs) == sizeof...(S0)
/// \endcode Dynamically, template arguments \p MTs..., \p T, \p Ts..., and
/// \p As... match the corresponding types \p this object was created with:
/// \code
/// MasterInputType == DeluxeTuple<MTs...>::TT && OutputType == T::TT &&
/// inputTypesMatch<TypeList<As...>>() &&
/// masterOutputTypesMatch<TypeList<Ts...>>()
/// \endcode
template <typename... MTs, typename T, typename... Ts, typename... As,
size_t... S0>
H triggerHandlerFromProcessingFunctions(
std::function<std::tuple<Optional<Ts>...>(
std::pair<DeluxeTuple<MTs...>, bool>)> &&MF,
std::function<
std::tuple<Optional<T>, Optional<Ts>...>(std::pair<As, bool>...)> &&F,
Seq<S0...>) noexcept;
public:
/// Creates a new instance.
///
/// The constructor instantiates the base-class with functions to handle
/// messages as defined for the *deluxe interface*.
///
/// The function \p F generates a \c std::tuple of values: the first value is
/// the output for the *master* and the rest is for the *slaves*. All output
/// generated by the function is optional as an agent may decide not to output
/// anything at some situation.
///
/// \todo Enforce \p F and \p MF do not potentially throw exception.
///
/// \tparam MT type of master-input handled by \p MF
/// \tparam T type of output of \p F
/// \tparam Ts type of master-output values of \p F and \p MF
/// \tparam As types of input values of \p F
///
/// \note Instantiation fails if any of the type arguments \p MT, \p T, \p
/// Ts..., and \p As... is not an instance of \c rosa::deluxe::DeluxeTuple or
/// any of \p T and \p As... is \c rosa::deluxe::EmptyDeluxeTuple or the
/// number of inputs and master-outputs are not equal.
///
/// \note If \p MT is \c rosa::deluxe::EmptyDeluxeTuple, the constructed
/// object does not receive master-input. Similarly, if any of \p Ts... is \c
/// rosa::deluxe::EmptyDeluxeTuple, the constructed object does not generated
/// master-output for the corresponding *slave* position.
///
/// \param Kind kind of the new \c rosa::Unit instance
/// \param Id unique identifier of the new \c rosa::Unit instance
/// \param Name name of the new \c rosa::Unit instance
/// \param S \c rosa::MessagingSystem owning the new instance
/// \param MF function to process master-input values and generate
/// master-output with
/// \param F function to process input values and generate output and
/// master-output with
///
/// \pre Statically, all the type arguments \p MT, \p T, \p Ts..., and \p
/// As... are instances of \c rosa::deluxe::DeluxeTuple, with \p T and \p
/// As... containing at least one element, and the number of input and
/// master-output types are equal: \code
/// TypeListAllDeluxeTuple<TypeList<MT, T, Ts..., As...>::Value &&
/// T::Length > 0 && (true && ... && As::Length > 0) &&
/// sizeof...(Ts) == sizeof...(As)
///\endcode
/// Dynamically, the instance is created as of kind \c
/// rosa::deluxe::atoms::AgentKind: \code
/// Kind == rosa::deluxe::atoms::AgentKind
/// \endcode
///
/// \see \c rosa::deluxe::DeluxeTuple
template <typename MT, typename T, typename... Ts, typename... As,
typename = std::enable_if_t<
TypeListAllDeluxeTuple<TypeList<MT, T, Ts..., As...>>::Value &&
(T::Length > 0) && (true && ... && (As::Length > 0)) &&
sizeof...(Ts) == sizeof...(As)>>
DeluxeAgent(
const AtomValue Kind, const id_t Id, const std::string &Name,
MessagingSystem &S,
std::function<std::tuple<Optional<Ts>...>(std::pair<MT, bool>)> &&MF,
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)> &&F) noexcept;
/// Destroys \p this object.
~DeluxeAgent(void) noexcept;
/// Returns the current execution policy of \p this object.
///
/// \see \c rosa::deluxe::DeluxeExecutionPolicy
///
/// \note The returned reference is valid only as long as \c
/// rosa::deluxe::DeluxeAgent::setExecutionPolicy() is not called and \p this
/// object is not destroyed.
///
/// \return \c rosa::deluxe::DeluxeAgent::ExecutionPolicy
const DeluxeExecutionPolicy &executionPolicy(void) const noexcept;
/// Sets the current execution policy of \p this object to \p EP.
///
/// \see \c rosa::deluxe::DeluxeExecutionPolicy
///
/// \note \p EP is set only if it can handle \p this object.
///
/// \param EP the new execution policy for \p this object
///
/// \return if \p EP was successfully set for \p this object.
bool setExecutionPolicy(std::unique_ptr<DeluxeExecutionPolicy> &&EP) noexcept;
/// The *master* of \p this object, if any is registered.
///
/// \see \c rosa::deluxe::DeluxeAgent::registerMaster
///
/// \return the *master* registered for \p this object
Optional<AgentHandle> master(void) const noexcept;
/// Registers a *master* for \p this object.
///
/// The new *master* is registered by overwriting the reference to any
/// already registered *master*. One can clear the registered reference by
/// passing an *empty* \c rosa::Optional object as actual argument.
///
/// \note The role of the referred *master* is validated by checking its
/// *kind*.
///
/// \note Any call to \c rosa::deluxe::DeluxeAgent::registerMaster should be
/// paired with a corresponding call of \c
/// rosa::deluxe::DeluxeAgent::registerSlave, which validates that
/// input/output types of master and slave matches.
///
/// \param _Master the *master* to register
///
/// \pre \p _Master is empty or of kind \c rosa::deluxe::atoms::AgentKind:
/// \code
/// !_Master || unwrapAgent(*_Master).Kind == rosa::deluxe::atoms::AgentKind
/// \endcode
void registerMaster(const Optional<AgentHandle> _Master) noexcept;
/// Tells the types of values consumed from the *slave* at a position.
///
/// That is the type of values \p this object expect to be sent to it in a \c
/// rosa::deluxe::DeluxeTuple by its *slave* registered at position \p Pos.
///
/// \see \c rosa::deluxe::DeluxeAgent::slave
///
/// \param Pos position of *slave*
///
/// \return \c rosa::Token representing the types of values consumed from
/// the *slave* at position \p Pos
///
/// \pre \p Pos is a valid index of input: \code
/// Pos < NumberOfInputs
/// \endcode
Token inputType(const size_t Pos) const noexcept;
/// Tells the types of values produced for the *slave* at a position.
///
/// That is the types of values \p this object potentially sends in a \c
/// rosa::deluxe::DeluxeTuple to its *slave* registered at position \p Pos.
///
/// \see \c rosa::deluxe::DeluxeAgent::slave
///
/// \param Pos position of *slave*
///
/// \return \c rosa::Token representing the types of values produced for
/// the *slave* at position \p Pos
///
/// \pre \p Pos is a valid index of input: \code
/// Pos < NumberOfMasterOutputs
/// \endcode
Token masterOutputType(const size_t Pos) const noexcept;
/// The *slave* of \p this object registered at a position, if any.
///
/// \see \c rosa::deluxe::DeluxeAgent::registerSlave
///
/// \param Pos position of *slave*
///
/// \return the *slave* registered for \p this object at position \p Pos
///
/// \pre \p Pos is a valid index of input: \code
/// Pos < NumberOfInputs
/// \endcode
Optional<AgentHandle> slave(const size_t Pos) const noexcept;
/// Registers a *slave* for \p this object at a position.
///
/// The new *slave* is registered by overwriting the reference to any already
/// registered *slave* at position \p Pos. One can clear the registered
/// reference by passing an *empty* \c rosa::Optional object as actual
/// argument. If \p Slave is already registered for another position, the
/// other position gets cleared.
///
/// \note The role of the referred *slave* is validated by checking its
/// *kind*.
///
/// \note The type of values produced by the referred *slave* is validated by
/// matching its `OutputType` against the corresponding value in
/// \c rosa::deluxe::DeluxeAgent::InputTypes.
///
/// \note The type of master-input values processed by the referred *slave* is
/// validated by matching its `MasterInputType` against the corresponding
/// value in \c rosa::deluxe::DeluxeAgent::MasterOutputTypes.
///
/// \param Pos position to register \p Slave at
/// \param Slave the *slave* to register
///
/// \pre \p Pos is a valid index of input, \p Slave is empty or of kind
/// \c rosa::deluxe::atoms::AgentKind or \c rosa::deluxe::atoms::SensorKind,
/// and \p Slave -- if not empty -- produces values of types matching the
/// expected input type at position \p Pos and processes values of types
/// matching the produced master-output type at position \p Pos:
/// \code
/// Pos < NumberOfInputs &&
/// (!Slave ||
/// (unwrapAgent(*Slave.)Kind == rosa::deluxe::atoms::SensorKind &&
/// static_cast<const DeluxeSensor &>(unwrapAgent(*Slave)).OutputType ==
/// InputTypes[Pos] &&
/// (emptyToken(MasterOutputTypes[Pos]) ||
/// static_cast<const DeluxeSensor &>(unwrapAgent(*Slave)).MasterInputType
/// == MasterOutputTypes[Pos])) ||
/// (unwrapAgent(*Slave).Kind == rosa::deluxe::atoms::AgentKind &&
/// static_cast<const DeluxeAgent &>(unwrapAgent(*Slave)).OutputType ==
/// InputTypes[Pos] &&
/// (emptyToken(MasterOutputTypes[Pos]) ||
/// static_cast<const DeluxeAgent &>(unwrapAgent(*Slave)).MasterInputType ==
/// MasterOutputTypes[Pos])))
/// \endcode
void registerSlave(const size_t Pos,
const Optional<AgentHandle> Slave) noexcept;
/// Tells the position of a registered *slave*.
///
/// \param Slave \c rosa::AgentHandle for the *slave* to check
///
/// \return position of \p Slave if it is registered and found,
/// \c rosa::deluxe::DeluxeAgent::NumberOfInputs otherwise.
size_t positionOfSlave(AgentHandle Slave) const noexcept;
private:
/// Sends a value to the *master* of \p this object.
///
/// \p Value is getting sent to \c rosa::deluxe::DeluxeAgent::Master if it
/// contains a valid handle for a \c rosa::deluxe::DeluxeAgent. The function
/// does nothing otherwise.
///
/// The elements from \p Value are sent one by one in separate messages to the
/// *master*.
///
/// \tparam Ts types of the elements in \p Value
/// \tparam S0 indices for accessing elements of \p Value
///
/// \param Value value to send
///
/// \note The second argument provides indices statically as template
/// arguments \p S0..., so its actual value is ignored.
///
/// \pre Statically, the indices match the elements: \code
/// sizeof...(Ts) == sizeof...(S0)
/// \endcode Dynamically, \p Ts match \c
/// rosa::deluxe::DeluxeiAgent::OutputType: \code
/// OutputType == TypeToken<Ts...>::Value
/// \endcode
template <typename... Ts, size_t... S0>
void sendToMaster(const DeluxeTuple<Ts...> &Value, Seq<S0...>) noexcept;
/// Sends a value to a *slave* of \p this object at position \p Pos.
///
/// \p Value is getting sent to \c rosa::deluxe::DeluxeAgent::Slaves[Pos] if
/// it contains a valid handle. The function does nothing otherwise.
///
/// The elements from \p Value are sent one by one in separate messages to the
/// *slave*.
///
/// \tparam Ts types of the elements in \p Value
/// \tparam S0 indices for accessing elements of \p Value
///
/// \param Pos the position of the *slave* to send \p Value to
/// \param Value value to send
///
/// \pre Statically, the indices match the elements: \code
/// sizeof...(Ts) == sizeof...(S0)
/// \endcode Dynamically, \p Pos is a valid *slave* position and \p Ts match
/// \c rosa::deluxe::DeluxeiAgent::MasterOutputTypes[Pos]: \code
/// Pos < NumberOfMasterOutputs &&
/// MasterOutputTypes[Pos] == TypeToken<Ts...>::Value
/// \endcode
template <typename... Ts, size_t... S0>
void sendToSlave(const size_t Pos, const DeluxeTuple<Ts...> &Value,
Seq<S0...>) noexcept;
/// Generates the next output by processing current input values upon trigger
/// from the system.
///
/// Executes \c rosa::deluxe::DeluxeAgent::FP.
///
/// \note The only argument is a \c rosa::AtomConstant, hence its actual
/// value is ignored.
///
/// \pre Master-input and all input from *slaves* are supposed to be
/// completely received upon triggering: \code
/// MasterInputNextPos == 0 &&
/// std::all_of(InputNextPos.begin(), InputNextPos.end(),
/// [](const token_size_t &I){return I == 0;})
/// \endcode
void handleTrigger(atoms::Trigger) noexcept;
/// Stores a new input value from a *slave*.
///
/// The function stores \p Value at position \p Pos in \c
/// rosa::deluxe::DeluxeAgent::InputValues at the position associated to \p Id
/// in \c rosa::deluxe::DeluxeAgent::SlaveIds and also sets the corresponding
/// flag in \c rosa::deluxe::DeluxeAgent::InputChanged. The function also
/// takes care of checking and updating \c
/// rosa::deluxe::DeluxeSensor::MasterInputNextPos at the corresponding
/// position: increments the value and resets it to `0` when the last element
/// is received.
///
/// \note Utilized by member functions of group \c DeluxeAgentInputHandlers.
///
/// \tparam T type of input to store
///
/// \param Id unique identifier of *slave*
/// \param Pos position of the value in the \c rosa::deluxe::DeluxeTuple
/// \param Value the input value to store
///
/// \pre The *slave* with \p Id is registered, \p Pos is the expected
/// position of input from the *slave*, and the input from it is expected to
/// be of type \p T: \code
/// SlaveIds.find(Id) != SlaveIds.end() &&
/// Pos == InputNextPos[SlaveIds.find(Id)->second] &&
/// typeAtPositionOfToken(InputTypes[SlaveIds.find(Id)->second], Pos) ==
/// TypeNumberOf<T>::Value
/// \endcode
template <typename T>
void saveInput(id_t Id, token_size_t Pos, T Value) noexcept;
/// Stores a new input value from the *master*.
///
/// The function stores \p Value at position \p Pos in \c
/// rosa::deluxe::DeluxeAgent::MasterInputValue and also sets the
/// flag \c rosa::deluxe::DeluxeAgent::MasterInputChanged. The function also
/// takes care of checking and updating \c
/// rosa::deluxe::DeluxeAgent::MasterInputNextPos: increments its value and
/// reset to `0` when the last element is received.
///
/// \note Utilized by member functions of group \c
/// DeluxeAgentMasterInputHandlers.
///
/// \tparam T type of input to store
///
/// \param Id unique identifier of the *master*
/// \param Pos position of the value in the \c rosa::deluxe::DeluxeTuple
/// \param Value the input value to store
///
/// \pre The *master* with \p Id is registered, \p Pos is the expected
/// position of master-input, and the input from the *master* at position \p
/// Pos is expected to be of type \p T: \code
/// Master && masterId() == Id && Pos == MasterInputNextPos &&
/// typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf<T>::Value
/// \endcode
template <typename T>
void saveMasterInput(id_t Id, token_size_t Pos, T Value) noexcept;
/// \defgroup DeluxeAgentInputHandlers Input handlers of
/// rosa::deluxe::DeluxeAgent
///
/// Definition of member functions handling messages from *slaves* with
/// different types of input
///
/// A *master* generally needs to be prepared to deal with values of any
/// built-in type to handle messages from its *slaves*. Each type requires a
/// separate message handler, which are implemented by these functions. The
/// functions instantiate \c rosa::deluxe::DeluxeAgent::saveInput with the
/// proper template argument and pass the content of the message on for
/// processing.
///
/// \note The member functions in this group are defined by \c
/// DASLAVEHANDLERDEF.
///
/// \note Keep these definitions in sync with \c rosa::BuiltinTypes.
///
///@{
DASLAVEHANDLERDEF(AtomValue)
DASLAVEHANDLERDEF(int16_t)
DASLAVEHANDLERDEF(int32_t)
DASLAVEHANDLERDEF(int64_t)
DASLAVEHANDLERDEF(int8_t)
DASLAVEHANDLERDEFN(long double, long_double)
DASLAVEHANDLERDEFN(std::string, std__string)
DASLAVEHANDLERDEF(uint16_t)
DASLAVEHANDLERDEF(uint32_t)
DASLAVEHANDLERDEF(uint64_t)
DASLAVEHANDLERDEF(uint8_t)
DASLAVEHANDLERDEF(unit_t)
DASLAVEHANDLERDEF(bool)
DASLAVEHANDLERDEF(double)
DASLAVEHANDLERDEF(float)
/// @}
/// \defgroup DeluxeAgentMasterInputHandlers Master-input handlers of
/// rosa::deluxe::DeluxeAgent
///
/// Definition of member functions handling messages from the *master* with
/// different types of input
///
/// A *slave* generally needs to be prepared to deal with values of any
/// built-in type to handle messages from its *master*. Each type requires a
/// separate message handler, which are implemented by these functions. The
/// functions instantiate \c rosa::deluxe::DeluxeAgent::saveMasterInput with
/// the proper template argument and pass the content of the message on for
/// processing.
///
/// \note The member functions in this group are defined by \c
/// DAMASTERHANDLERDEF.
///
/// \note Keep these definitions in sync with \c rosa::BuiltinTypes.
///
///@{
DAMASTERHANDLERDEF(AtomValue)
DAMASTERHANDLERDEF(int16_t)
DAMASTERHANDLERDEF(int32_t)
DAMASTERHANDLERDEF(int64_t)
DAMASTERHANDLERDEF(int8_t)
DAMASTERHANDLERDEFN(long double, long_double)
DAMASTERHANDLERDEFN(std::string, std__string)
DAMASTERHANDLERDEF(uint16_t)
DAMASTERHANDLERDEF(uint32_t)
DAMASTERHANDLERDEF(uint64_t)
DAMASTERHANDLERDEF(uint8_t)
DAMASTERHANDLERDEF(unit_t)
DAMASTERHANDLERDEF(bool)
DAMASTERHANDLERDEF(double)
DAMASTERHANDLERDEF(float)
/// @}
};
/// Anonymous namespace with implementation for \c
/// rosa::deluxe::DeluxeAgent::DeluxeAgent, \c
/// rosa::deluxe::DeluxeAgent::inputTypesMatch, and \c
/// rosa::deluxe::DeluxeAgent::masterOutputTypesMatch, consider it private.
namespace {
-/// Calculates storage offsets for values of \p Ts... stored in a \c
+/// Creates storages for data of types \p Ts... in a \c std::vector of \c
/// rosa::TokenizedStorage.
///
/// \note Utilized by \c rosa::deluxe::DeluxeAgnet::DeluxeAgent to initialize \c
-/// rosa::deluxe::DeluxeAgent::InputStorageOffsets.
+/// rosa::deluxe::DeluxeAgent::InputValues. That is due to not being able to use
+/// an initializer list directly; the initializer list always copies but \c
+/// std::unique_ptr is not copyable.
///
-/// \tparam Ts types whose offsets to calculate
-/// \tparam S0 indices for referring to positions in \p Ts...
+/// \tparam Ts types to create storages for
///
/// \note Instantiation fails if any of the type arguments \p Ts... is not an
/// instance of \c rosa::deluxe::DeluxeTuple.
///
-/// \note The only argument provides indices statically as template
-/// arguments \p S0..., so its actual value is ignored.
-///
-/// \return \c std::vector containing the calculated offsets
+/// \return \c std::vector with pointers for the created storage objects
///
/// \pre Statically, all the type arguments \p Ts... are instances of \c
-/// rosa::deluxe::DeluxeTuple and the indices match the types: \code
-/// TypeListAllDeluxeTuple<TypeList<Ts...>>::Value &&
-/// sizeof...(Ts) == sizeof...(S0)
+/// rosa::deluxe::DeluxeTuple: \code
+/// TypeListAllDeluxeTuple<TypeList<Ts...>>::Value
/// \endcode
-template <
- typename... Ts, size_t... S0,
- typename = std::enable_if_t<TypeListAllDeluxeTuple<TypeList<Ts...>>::Value>>
-static std::vector<token_size_t> storageOffsets(Seq<S0...>) noexcept {
- STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments");
- std::vector<token_size_t> Offsets(sizeof...(Ts));
- // Do nothing for no types.
- if constexpr (sizeof...(Ts) != 0) {
- Offsets[0] = 0; // The offset of the very first value is always `0`.
- // Calculate further offsets...
- (((S0 != sizeof...(Ts) - 1) &&
- (Offsets[S0 + 1] = Offsets[S0] + Ts::Length)),
- ...);
- }
- return Offsets;
+template <typename... Ts>
+std::vector<std::unique_ptr<AbstractTokenizedStorage>>
+makeInputStorages(void) noexcept {
+ std::vector<std::unique_ptr<AbstractTokenizedStorage>> InputStorages;
+ (InputStorages.push_back(
+ std::make_unique<typename TokenizedStorageForTypeList<
+ typename UnwrapDeluxeTuple<Ts>::Type>::Type>()),
+ ...);
+ return InputStorages;
}
/// Template \c struct whose specializations provide a recursive implementation
/// for \c TypesMatchList.
///
/// \tparam As types to match
template <typename... As> struct TypesMatchImpl;
/// Template specialization for the case, when at least one type is to
/// be matched and that is an instance of \c rosa::deluxe::DeluxeTuple.
///
/// \tparam Ts types of elements in the \c rosa::deluxe::DeluxeTuple to match
/// \tparam As further types to match
template <typename... Ts, typename... As>
struct TypesMatchImpl<DeluxeTuple<Ts...>, As...> {
/// Tells whether types \c rosa::deluxe::DeluxeTuple<Ts...> and \p As... match
/// \c rosa::Token values stored in \p Tokens starting at position \p Pos.
///
/// The function has got a recursive implementation: it matches the first
/// type \c rosa::deluxe::DeluxeTuple<Ts...> against \c rosa::Token at
/// position \p Pos of \p Tokens, then further types \p As... are matched
/// recursively starting at position \c (Pos + 1).
///
/// \param Tokens container of \c rosa::Token values to match types against
/// \param Pos position in \p Tokens to start matching at
///
/// \return if types \c rosa::deluxe::DeluxeTuple<Ts...> and \p As... match \c
/// rosa::Token values stored in \p Tokens starting at position \p Pos
static bool f(const std::vector<Token> &Tokens, size_t Pos) noexcept {
return Pos < Tokens.size() && TypeToken<Ts...>::Value == Tokens[Pos] &&
TypesMatchImpl<As...>::f(Tokens, Pos + 1);
}
};
/// Template specialization for the case, when at least one type is to
/// be matched and that is *not* an instance of \c rosa::deluxe::DeluxeTuple.
///
/// \tparam T first type to match
/// \tparam As further types to match
template <typename T, typename... As>
struct TypesMatchImpl<T, As...> {
/// Tells whether types \p T and \p As... match \c rosa::Token values stored
/// in \p Tokens starting at position \p Pos.
///
/// This specialization is used only when \p T is not an instance of \c
/// rosa::deluxe::DeluxeTuple, in which case the match is not successful.
///
/// \note The function takes two parameters to match the general signature but
/// the actual values are ignored.
///
/// \return `false`
static bool f(const std::vector<Token> &, size_t) noexcept { return false; }
};
/// Template specialization for the terminal case, when no type remains to
/// check.
template <> struct TypesMatchImpl<> {
/// Tells whether \p Pos is the number of values stored in \p Tokens.
///
/// In this terminal case, there is no more types to match because all the
/// types are supposed to be already matched successfully. The whole list of
/// types already matched is a complete match if it covers all values in
/// \p Tokens. That is true if \p Pos points exactly to the end of \p Tokens.
///
/// \param Tokens container of \c rosa::Token values to match types against
/// \param Pos position in \p Tokens to start matching at
///
/// \return if \p Pos is the number of values stored in \p Tokens
static bool f(const std::vector<Token> &Tokens, size_t Pos) noexcept {
return Pos == Tokens.size();
}
};
/// Template \c struct that provides an implementation for \c
/// rosa::deluxe::DeluxeAgent::inputTypesMatch and \c
/// rosa::deluxe::DeluxeAgent::masterOutputTypesMatch.
///
/// \note Match a list of types \p List against a \c std::vector of
/// \c rosa::Token values, \c Tokens, like \code
/// bool match = TypesMatchList<List>::f(Tokens);
/// \endcode
/// If any type in \c rosa::TypeList \p Listis not an instance of \c
/// rosa::deluxe::DeluxeTuple, the match gives a negative result.
///
/// \tparam List \c rosa::TypeList that contains types to match
template <typename List> struct TypesMatchList;
/// Template specialization implementing the feature.
///
/// \tparam As types to match
template <typename... As> struct TypesMatchList<TypeList<As...>> {
/// Tells whether types \p As... match \c rosa::Token values stored in \p
/// Tokens.
///
/// The function unwraps the types from \c rosa::TypeList and utilizes \c
/// TypesMatchImpl to do the check.
///
/// \param Tokens container of \c rosa::Token values to match types against
///
/// \return if types \p As... match \c rosa::Token values stored in \p Tokens
static bool f(const std::vector<Token> &Tokens) noexcept {
return TypesMatchImpl<As...>::f(Tokens, 0);
}
};
} // End namespace
template <typename As>
bool DeluxeAgent::inputTypesMatch(void) const noexcept {
return TypesMatchList<As>::f(InputTypes);
}
template <typename Ts>
bool DeluxeAgent::masterOutputTypesMatch(void) const noexcept {
return TypesMatchList<Ts>::f(MasterOutputTypes);
}
template <size_t Pos, typename... Ts, size_t... S0>
DeluxeTuple<Ts...> DeluxeAgent::prepareInputValueAtPos(TypeList<Ts...>,
Seq<S0...>) const
noexcept {
using T = DeluxeTuple<Ts...>;
STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent type arguments");
ASSERT(inv() && Pos < NumberOfInputs && T::TT == InputTypes[Pos]);
- const token_size_t StorageOffset = InputStorageOffsets[Pos];
-
// The below should hold because of the above, just leave it for sanity check.
ASSERT((true && ... &&
(static_cast<size_t>(static_cast<token_size_t>(S0)) == S0)));
+ const auto &SlaveInput = InputValues[Pos];
+
// Get all elements of the tuple in a fold expression.
- return T(*static_cast<const Ts *>(InputValues->pointerTo(
- static_cast<token_size_t>(StorageOffset + S0)))...);
+ return T(*static_cast<const Ts *>(
+ SlaveInput->pointerTo(static_cast<token_size_t>(S0)))...);
}
template <typename... As, size_t... S0>
std::tuple<std::pair<As, bool>...>
DeluxeAgent::prepareCurrentInputs(Seq<S0...>) const noexcept {
STATIC_ASSERT(TypeListAllDeluxeTuple<TypeList<As...>>::Value,
"not tuple types");
STATIC_ASSERT(sizeof...(As) == sizeof...(S0), "inconsistent type arguments");
ASSERT(inv() && inputTypesMatch<TypeList<As...>>());
return std::make_tuple(std::make_pair(
prepareInputValueAtPos<S0>(typename UnwrapDeluxeTuple<As>::Type(),
seq_t<As::Length>()),
InputChanged[S0])...);
}
template <typename T, typename... Ts, typename... As, size_t... S0>
std::tuple<Optional<T>, Optional<Ts>...> DeluxeAgent::invokeWithTuple(
std::function<
std::tuple<Optional<T>, Optional<Ts>...>(std::pair<As, bool>...)>
F,
const std::tuple<std::pair<As, bool>...> Args, Seq<S0...>) noexcept {
- ASSERT(sizeof...(As) == sizeof...(S0));
+ STATIC_ASSERT(sizeof...(As) == sizeof...(S0),
+ "wrong number of type parameters");
return F(std::get<S0>(Args)...);
}
template <size_t Pos, typename... Ts>
void DeluxeAgent::handleMasterOutputAtPos(
const Optional<DeluxeTuple<Ts...>> &Value) noexcept {
using MOT = DeluxeTuple<Ts...>;
ASSERT(inv() && Pos < NumberOfMasterOutputs &&
MOT::TT == MasterOutputTypes[Pos]);
// Do not do anything for master-output of type \c
// rosa::deluxe::EmptyDeluxeTuple and when \p Value is empty.
if constexpr (!std::is_same<MOT, EmptyDeluxeTuple>::value) {
if (Value) {
sendToSlave(Pos, *Value, seq_t<MOT::Length>());
}
} else {
(void)Value;
}
ASSERT(inv());
}
template <size_t Offset, typename... Ts, size_t... S0>
void DeluxeAgent::handleMasterOutputs(const std::tuple<Optional<Ts>...> &Output,
Seq<S0...>) noexcept {
using MOTs = typename TypeListDrop<Offset, TypeList<Ts...>>::Type;
STATIC_ASSERT(TypeListAllDeluxeTuple<MOTs>::Value,
"not tuple type arguments");
STATIC_ASSERT(sizeof...(Ts) == Offset + sizeof...(S0),
"inconsistent arguments");
ASSERT(inv() && masterOutputTypesMatch<MOTs>() &&
sizeof...(S0) == NumberOfMasterOutputs);
// Handle each master-output position in a fold expression.
(handleMasterOutputAtPos<S0>(std::get<Offset + S0>(Output)), ...);
ASSERT(inv());
}
template <typename... MTs, typename T, typename... Ts, typename... As,
size_t... S0>
DeluxeAgent::H DeluxeAgent::triggerHandlerFromProcessingFunctions(
std::function<
std::tuple<Optional<Ts>...>(std::pair<DeluxeTuple<MTs...>, bool>)> &&MF,
std::function<
std::tuple<Optional<T>, Optional<Ts>...>(std::pair<As, bool>...)> &&F,
Seq<S0...>) noexcept {
using MT = DeluxeTuple<MTs...>;
STATIC_ASSERT((TypeListAllDeluxeTuple<TypeList<T, Ts..., As...>>::Value),
"not tuple type arguments");
STATIC_ASSERT(sizeof...(MTs) == sizeof...(S0), "inconsistent arguments");
ASSERT(MasterInputType == MT::TT && OutputType == T::TT &&
inputTypesMatch<TypeList<As...>>() &&
masterOutputTypesMatch<TypeList<Ts...>>());
return [ this, MF, F ]() noexcept {
// \note These indices work for both inputs and master-outputs.
using SlaveIndices = seq_t<sizeof...(As)>;
// Handle master-input.
// Do not do anything for master-input type \c
// rosa::deluxe::EmptyDeluxeTuple.
if (!std::is_same<MT, EmptyDeluxeTuple>::value) {
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " handles master-input."
<< std::endl;
// The assert must hold if \p this object was successfuuly constructed.
ASSERT((true && ... &&
(static_cast<size_t>(static_cast<token_size_t>(S0)) == S0)));
const auto MasterInputArg = std::make_pair(
// Get all elements of the tuple in a fold expression.
MT(*static_cast<const MTs *>(
MasterInputValue->pointerTo(static_cast<token_size_t>(S0)))...),
MasterInputChanged);
MasterInputChanged = false;
const std::tuple<Optional<Ts>...> MasterOutput = MF(MasterInputArg);
handleMasterOutputs<0>(MasterOutput, SlaveIndices());
}
// Handle inputs.
// Call the processing function only if \p ExecutionPolicy allows.
if (ExecutionPolicy->shouldProcess(InputChanged)) {
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " handles input."
<< std::endl;
const auto InputArgs = prepareCurrentInputs<As...>(SlaveIndices());
std::fill(InputChanged.begin(), InputChanged.end(), false);
const std::tuple<Optional<T>, Optional<Ts>...> Output =
invokeWithTuple(F, InputArgs, SlaveIndices());
const auto OutputToMaster = std::get<0>(Output);
if (OutputToMaster) {
sendToMaster(*OutputToMaster, seq_t<T::Length>());
}
handleMasterOutputs<1>(Output, SlaveIndices());
} else {
LOG_TRACE_STREAM << "DeluxeAgent " << Name << " skips input."
<< std::endl;
}
};
}
template <typename MT, typename T, typename... Ts, typename... As, typename>
DeluxeAgent::DeluxeAgent(
const AtomValue Kind, const id_t Id, const std::string &Name,
MessagingSystem &S,
std::function<std::tuple<Optional<Ts>...>(std::pair<MT, bool>)> &&MF,
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)> &&F) noexcept
: Agent(Kind, Id, Name, S, THISMEMBER(handleTrigger),
DASLAVEHANDLERREF(AtomValue), DASLAVEHANDLERREF(int16_t),
DASLAVEHANDLERREF(int32_t), DASLAVEHANDLERREF(int64_t),
DASLAVEHANDLERREF(int8_t), DASLAVEHANDLERREF(long_double),
DASLAVEHANDLERREF(std__string), DASLAVEHANDLERREF(uint16_t),
DASLAVEHANDLERREF(uint32_t), DASLAVEHANDLERREF(uint64_t),
DASLAVEHANDLERREF(uint8_t), DASLAVEHANDLERREF(unit_t),
DASLAVEHANDLERREF(bool), DASLAVEHANDLERREF(double),
DASLAVEHANDLERREF(float), DAMASTERHANDLERREF(AtomValue),
DAMASTERHANDLERREF(int16_t), DAMASTERHANDLERREF(int32_t),
DAMASTERHANDLERREF(int64_t), DAMASTERHANDLERREF(int8_t),
DAMASTERHANDLERREF(long_double), DAMASTERHANDLERREF(std__string),
DAMASTERHANDLERREF(uint16_t), DAMASTERHANDLERREF(uint32_t),
DAMASTERHANDLERREF(uint64_t), DAMASTERHANDLERREF(uint8_t),
DAMASTERHANDLERREF(unit_t), DAMASTERHANDLERREF(bool),
DAMASTERHANDLERREF(double), DAMASTERHANDLERREF(float)),
ExecutionPolicy(DeluxeExecutionPolicy::decimation(1)), OutputType(T::TT),
NumberOfInputs(sizeof...(As)), MasterInputType(MT::TT),
NumberOfMasterOutputs(NumberOfInputs), InputTypes({As::TT...}),
InputNextPos(NumberOfInputs, 0), InputChanged(NumberOfInputs, false),
- InputStorageOffsets(storageOffsets<As...>(seq_t<sizeof...(As)>())),
- InputValues(new typename TokenizedStorageForTypeList<
- typename TypeListUnwrapDeluxeTuple<TypeList<As...>>::Type>::
- Type()),
- MasterInputNextPos(0), MasterInputChanged(false),
+ InputValues(makeInputStorages<As...>()), MasterInputNextPos(0),
+ MasterInputChanged(false),
MasterInputValue(new typename TokenizedStorageForTypeList<
typename UnwrapDeluxeTuple<MT>::Type>::Type()),
MasterOutputTypes({Ts::TT...}),
FP(triggerHandlerFromProcessingFunctions(std::move(MF), std::move(F),
seq_t<MT::Length>())),
Slaves(NumberOfInputs) {
ASSERT(Kind == atoms::AgentKind);
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " is created." << std::endl;
ASSERT(inv());
}
template <typename... Ts, size_t... S0>
void DeluxeAgent::sendToMaster(const DeluxeTuple<Ts...> &Value,
Seq<S0...>) noexcept {
STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments");
ASSERT(inv() && OutputType == TypeToken<Ts...>::Value);
// The assert must hold if \p this object was successfuuly constructed.
ASSERT((true && ... &&
(static_cast<size_t>(static_cast<token_size_t>(S0)) == S0)));
// Create a static constant array for these indices to be available as lvalue
// references when creating messages below. \c S0... when used directly in a
// fold expression is a temporary value, which would result in \c
// rosa::Message instances being created with rvalue references. Further, all
// other values would to copied into a temporary variable for making them
/// available as rvalue references (they are constant lvalue references here).
static constexpr std::array<token_size_t, sizeof...(S0)> Indices{{S0...}};
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id
<< ") sends to master ("
<< static_cast<bool>(Master && *Master) << "): " << Value
<< " (" << sizeof...(S0) << ")" << std::endl;
// There is a handle and the referred *master* is in a valid state.
if (Master && *Master) {
// Handle each element of the tuple in a fold expression.
(Master->sendMessage(Message::create(atoms::Slave::Value, Id, Indices[S0],
std::get<S0>(Value))),
...);
}
ASSERT(inv());
}
template <typename... Ts, size_t... S0>
void DeluxeAgent::sendToSlave(const size_t Pos, const DeluxeTuple<Ts...> &Value,
Seq<S0...>) noexcept {
STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments");
ASSERT(inv() && Pos < NumberOfMasterOutputs &&
MasterOutputTypes[Pos] == TypeToken<Ts...>::Value);
// The assert must hold if \p this object was successfuuly constructed.
ASSERT((true && ... &&
(static_cast<size_t>(static_cast<token_size_t>(S0)) == S0)));
// Create a static constant array for these indices to be available as lvalue
// references when creating messages below. \c S0... when used directly in a
// fold expression is a temporary value, which would result in \c
// rosa::Message instances being created with rvalue references. Further, all
// other values would to copied into a temporary variable for making them
/// available as rvalue references (they are constant lvalue references here).
static constexpr std::array<token_size_t, sizeof...(S0)> Indices{{S0...}};
// There is a handle and the referred *slave* is in a valid state.
auto Slave = Slaves[Pos];
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id
<< ") sends to slave (" << static_cast<bool>(Slave && *Slave)
<< ") at position " << Pos << ": " << Value << " ("
<< sizeof...(S0) << ")" << std::endl;
if (Slave && *Slave) {
// Handle each element of the tuple in a fold expression.
(Slave->sendMessage(Message::create(atoms::Master::Value, Id, Indices[S0],
std::get<S0>(Value))),
...);
}
}
template <typename T>
void DeluxeAgent::saveInput(id_t Id, token_size_t Pos, T Value) noexcept {
ASSERT(inv() && SlaveIds.find(Id) != SlaveIds.end() &&
Pos == InputNextPos[SlaveIds.find(Id)->second] &&
typeAtPositionOfToken(InputTypes[SlaveIds.find(Id)->second], Pos) ==
TypeNumberOf<T>::Value);
- size_t SlavePos = SlaveIds.at(Id);
+ const size_t SlavePos = SlaveIds.at(Id);
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id
<< ") saves value from slave at position " << SlavePos
<< ": (" << static_cast<size_t>(Pos) << ") " << Value
<< std::endl;
// Save value.
- size_t StoragePos = (size_t)InputStorageOffsets[SlavePos] + Pos;
- // This assert must hold if \p this object was successfully constructed.
- ASSERT(static_cast<size_t>(static_cast<token_size_t>(StoragePos)) ==
- StoragePos);
- *static_cast<T *>(
- InputValues->pointerTo(static_cast<token_size_t>(StoragePos))) = Value;
+ *static_cast<T *>(InputValues[SlavePos]->pointerTo(Pos)) = Value;
// Update position of next value.
if (++InputNextPos[SlavePos] == lengthOfToken(InputTypes[SlavePos])) {
InputNextPos[SlavePos] = 0;
}
// Set flag.
InputChanged[SlavePos] = true;
ASSERT(inv());
}
template <typename T>
void DeluxeAgent::saveMasterInput(id_t Id, token_size_t Pos, T Value) noexcept {
ASSERT(inv() && Master && masterId() == Id && Pos == MasterInputNextPos &&
typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf<T>::Value);
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id
<< ") saves value from master: (" << static_cast<size_t>(Pos)
<< ") " << Value << std::endl;
// Save value.
*static_cast<T *>(MasterInputValue->pointerTo(Pos)) = Value;
// Update position of next value.
if (++MasterInputNextPos == lengthOfToken(MasterInputType)) {
MasterInputNextPos = 0;
}
// Set flag.
MasterInputChanged = true;
ASSERT(inv());
}
} // End namespace deluxe
} // End namespace rosa
#undef DASLAVEHANDLEREF
#undef DAMASTERHANDLEREF
#undef DASLAVEHANDLEDEF
#undef DAMASTERHANDLEDEF
#undef DASLAVEHANDLEDEFN
#undef DAMASTERHANDLEDEFN
#undef DASLAVEHANDLENAME
#undef DAMASTERHANDLENAME
#endif // ROSA_DELUXE_DELUXEAGENT_HPP
diff --git a/include/rosa/deluxe/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<atom("dl_trigger")>;
/// Type alias denoting messages from a slave.
using Slave = AtomConstant<atom("dl_slave")>;
/// Type alias denoting messages from a master.
using Master = AtomConstant<atom("dl_master")>;
} // End namespace atoms
} // End namespace deluxe
} // End namespace rosa
#endif // ROSA_DELUXE_DELUXEATOMS_HPP
diff --git a/include/rosa/deluxe/DeluxeContext.hpp b/include/rosa/deluxe/DeluxeContext.hpp
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 <iterator>
#include <memory>
#include <set>
/// Local helper macro to log and return a
/// \c rosa::deluxe::DeluxeContext::ErrorCode value.
///
/// Creates a debug message with the stringified value and returns the value.
///
/// \param Err \c rosa::deluxe::DeluxeContext::ErrorCode value to log and
/// return
#define DCRETERROR(Err) \
{ \
LOG_DEBUG(#Err); \
return Err; \
}
namespace rosa {
namespace deluxe {
/// Defines the *deluxe interface*.
///
/// \todo The classes \c rosa::deluxe::DeluxeSensor and \c
/// rosa::deluxe::DeluxeAgent share some common features in relation to their
/// *slave* role in the *deluxe interface*. But their definitions are completely
/// independent. It could be investigated how to lift their common parts into a
/// new *deluxe slave* class, which would serve as base for both, to avoid code
/// duplication.
class DeluxeContext {
/// A system owned by \p this object.
///
/// \note The reference is kept in a \c std::shared_ptr because of the member
/// function \c rosa::deluxe::DeluxeContext::getSystem.
std::shared_ptr<DeluxeSystem> System;
/// References to all *sensors* and *agents* created by \p this object.
std::set<AgentHandle> DeluxeUnits;
public:
/// Errors that may be resulted by some of the member functions of the class.
enum struct ErrorCode {
NoError,
TypeMismatch,
NotSensor,
NotAgent,
NotUnit,
WrongPosition,
AlreadyHasSlave,
AlreadyHasMaster,
AlreadyHasValueStream,
UnsuitableExecutionPolicy
};
/// Returns a new instance of \c rosa::deluxe::DeluxeContext.
///
/// \param Name name of the underlying \c rosa::DeluxeSystem
///
/// \return \c std::unique_ptr for the new instance of
/// \c rosa::deluxe::DeluxeContext with a new, empty \c rosa::DeluxeSystem
static std::unique_ptr<DeluxeContext>
create(const std::string &Name) noexcept;
private:
/// Creates a new instance.
///
/// \note Private constructor restricts instantiation to member functions of
/// the class.
///
/// \param Name name of the underlying \c rosa::MessagingSystem
DeluxeContext(const std::string &Name) noexcept;
public:
/// Destroys \p this object.
~DeluxeContext(void) noexcept;
/// Returns a reference for the underlying \c rosa::MessagingSystem.
///
/// \note One cannot do much with a \c rosa::MessagingSystem currently, this
/// is for future use.
///
/// \return reference for the underlying \c rosa::MessagingSystem.
std::weak_ptr<MessagingSystem> getSystem(void) const noexcept;
private:
/// Creates a new *sensor* in the context of \p this object.
///
/// The new *sensor* handles master-input by \p MF.
///
/// \tparam MT type of master-input the new *sensor* handles
/// \tparam T type of data the new *sensor* operates on
///
/// \note Instantiation fails if any of the type arguments \p MT and \p T
/// is not an instance of \c rosa::deluxe::DeluxeTuple or \p T is \c
/// rosa::deluxe::EmptyDeluxeTuple.
///
/// \param Name name of the new *sensor*
/// \param MF function for the new *sensors* to process master-input
/// values with
/// \param F function for the new *sensor* to generate the next value with
/// during normal operation
///
/// \note \p F is not used during simulation, in which case
/// \c rosa::deluxe::DeluxeContext::registerSensorValues is used to
/// register an alternative simulation data source with \c
/// rosa::deluxe::DeluxeSensor::registerSimulationDataSource. One may
/// safely keep relying on the default value of \p F as long as only
/// simulation of the system is to be done.
///
/// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor.
///
/// \return \c rosa::AgentHandle for the new *sensor*
template <typename MT, typename T,
typename =
std::enable_if<TypeListAllDeluxeTuple<TypeList<MT, T>>::Value &&
!std::is_same<T, EmptyDeluxeTuple>::value>>
AgentHandle createSensorImpl(const std::string &Name,
std::function<void(std::pair<MT, bool>)> &&MF,
std::function<T(void)> &&F) noexcept;
public:
/// Creates a new *sensor* in the context of \p this object.
///
/// The new *sensor* does not receive master-input.
///
/// \tparam T type of data the new *sensor* operates on
///
/// \note Instantiation fails if type argument \p T is neither a built-in type
/// nor an instance of \c rosa::deluxe::DeluxeTuple with at least one element.
///
/// \param Name name of the new *sensor*
/// \param F function for the new *sensor* to generate the next value with
/// during normal operation
///
/// \note \p F is not used during simulation, in which case
/// \c rosa::deluxe::DeluxeContext::registerSensorValues is used to register
/// an alternative simulation data source with
/// \c rosa::deluxe::DeluxeSensor::registerSimulationDataSource. One may
/// safely keep relying on the default value of \p F as long as only
/// simulation of the system is to be done.
///
/// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor.
///
/// \return \c rosa::AgentHandle for the new *sensor*
template <typename T, typename = std::enable_if_t<
TypeListContains<BuiltinTypes, T>::Value ||
(IsDeluxeTuple<T>::Value &&
!std::is_same<T, EmptyDeluxeTuple>::value)>>
AgentHandle createSensor(
const std::string &Name,
std::function<T(void)> &&F = [](void) { return T(); }) noexcept;
/// Creates a new *sensor* in the context of \p this object.
///
/// The new *sensor* handles master-input by \p MF.
///
/// \tparam MT type of master-input the new *sensor* handles
/// \tparam T type of data the new *sensor* operates on
///
/// \note The type arguments \p MT and \p T must be either all built-in types
/// or all instances of \c rosa::deluxe::DeluxeTuple. Moreover, \p T cannot be
/// \c rosa::deluxe::EmptyDeluxeTuple. Instantiation fails if these conditions
/// do not hold.
///
/// \param Name name of the new *sensor*
/// \param MF function for the new *sensors* to process master-input
/// values with \param F function for the new *sensor* to generate
/// the next value with during normal operation
///
/// \note \p F is not used during simulation, in which case
/// \c rosa::deluxe::DeluxeContext::registerSensorValues is used to
/// register an alternative simulation data source with \c
/// rosa::deluxe::DeluxeSensor::registerSimulationDataSource. One may
/// safely keep relying on the default value of \p F as long as only
/// simulation of the system is to be done.
///
/// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor.
///
/// \return \c rosa::AgentHandle for the new *sensor*
template <typename MT, typename T,
typename = std::enable_if<
TypeListSubsetOf<TypeList<MT, T>, BuiltinTypes>::Value ||
(TypeListAllDeluxeTuple<TypeList<MT, T>>::Value &&
!std::is_same<T, EmptyDeluxeTuple>::value)>>
AgentHandle createSensor(
const std::string &Name, std::function<void(std::pair<MT, bool>)> &&MF,
std::function<T(void)> &&F = [](void) { return T(); }) noexcept;
private:
/// Creates a new *agent* in the context of \p this object.
///
/// The new *agent* receives master-input by \p MF and produces
/// master-output.
///
/// \tparam MT type of master-input the new *agent* handles
/// \tparam T type of data the new *agent* outputs
/// \tparam Ts types of master-output the new *agent* produces
/// \tparam As types of inputs the new *agent* takes
///
/// \note Instantiation fails if any of the type arguments \p MT, \p T, \p
/// Ts..., and \p As... is not an instance of \c rosa::deluxe::DeluxeTuple or
/// any of \p T and \p As... is \c rosa::deluxe::EmptyDeluxeTuple.
///
/// \param Name name of the new *agent*
/// \param MF function for the new *agent* to process master-input
/// values with \param F function for the new *agent* to process
/// input values and generate output with
///
/// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
///
/// \return \c rosa::AgentHandle for the new *agent*
template <typename MT, typename T, typename... Ts, typename... As,
typename = std::enable_if_t<
TypeListAllDeluxeTuple<TypeList<MT, T, Ts..., As...>>::Value &&
!std::is_same<T, EmptyDeluxeTuple>::value &&
(true && ... && (!std::is_same<As, EmptyDeluxeTuple>::value))>>
AgentHandle createAgentImpl(
const std::string &Name,
std::function<std::tuple<Optional<Ts>...>(std::pair<MT, bool>)> &&MF,
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)> &&F) noexcept;
public:
/// Creates a new *agent* in the context of \p this object.
///
/// The new *agent* neither receives master-input nor produces
/// master-output.
///
/// \tparam T type of data the new *agent* outputs
/// \tparam As types of inputs the new *agent* takes
///
/// \note The type arguments \p T and \p As... must be either all built-in
/// types or all instances of \c rosa::deluxe::DeluxeTuple. Moreover, none of
/// them can be \c rosa::deluxe::EmptyDeluxeTuple. Instantiation fails if
/// these conditions do not hold.
///
/// \param Name name of the new *agent*
/// \param F function for the new *agent* to process input values and
/// generate output with
///
/// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
///
/// \return \c rosa::AgentHandle for the new *agent*
template <
typename T, typename... As,
typename = std::enable_if_t<
TypeListSubsetOf<TypeList<T, As...>, BuiltinTypes>::Value ||
(TypeListAllDeluxeTuple<TypeList<T, As...>>::Value &&
!std::is_same<T, EmptyDeluxeTuple>::value &&
(true && ... && (!std::is_same<As, EmptyDeluxeTuple>::value)))>>
AgentHandle
createAgent(const std::string &Name,
std::function<Optional<T>(std::pair<As, bool>...)> &&F) noexcept;
/// Creates a new *agent* in the context of \p this object.
///
/// The new *agent* receives master-input by \p MF but does not
/// produce master-output.
///
/// \tparam MT type of master-input the new *agent* handles
/// \tparam T type of data the new *agent* outputs
/// \tparam As types of inputs the new *agent* takes
///
/// \note The type arguments \p MT, \p T, and \p As... must be either all
/// built-in types or all instances of \c rosa::deluxe::DeluxeTuple. Moreover,
/// none of \p T and \p As... can be \c rosa::deluxe::EmptyDeluxeTuple.
/// Instantiation fails if these conditions do not hold.
///
/// \param Name name of the new *agent*
/// \param MF function for the new *agent* to process master-input
/// values with
/// \param F function for the new *agent* to process input values and
/// generate output with
///
/// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
///
/// \return \c rosa::AgentHandle for the new *agent*
template <
typename MT, typename T, typename... As,
typename = std::enable_if_t<
TypeListSubsetOf<TypeList<MT, T, As...>, BuiltinTypes>::Value ||
(TypeListAllDeluxeTuple<TypeList<MT, T, As...>>::Value &&
!std::is_same<T, EmptyDeluxeTuple>::value &&
(true && ... && (!std::is_same<As, EmptyDeluxeTuple>::value)))>>
AgentHandle
createAgent(const std::string &Name,
std::function<void(std::pair<MT, bool>)> &&MF,
std::function<Optional<T>(std::pair<As, bool>...)> &&F) noexcept;
/// Creates a new *agent* in the context of \p this object.
///
/// The new *agent* does not receive master-input but produces
/// master-output.
///
/// \tparam T type of data the new *agent* outputs
/// \tparam Ts types of master-output the new *agent* produces
/// \tparam As types of inputs the new *agent* takes
///
/// \note The type arguments \p T, \p Ts, and \p As... must be either all
/// built-in types or all instances of \c rosa::deluxe::DeluxeTuple. Moreover,
/// none of \p T and \p As... can be \c rosa::deluxe::EmptyDeluxeTuple.
/// Instantiation fails if these conditions do not hold.
///
/// \param Name name of the new *agent*
/// \param F function for the new *agent* to process input values and
/// generate output with
///
/// \note \p F does not produce master-output for a given position if the
/// corresponding type is \c rosa::deluxe::EmptyDeluxeTuple. It is not
/// possible to disable master-output at any position by using built-in types.
///
/// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
///
/// \return \c rosa::AgentHandle for the new *agent*
template <
typename T, typename... Ts, typename... As,
typename = std::enable_if_t<
TypeListSubsetOf<TypeList<T, Ts..., As...>, BuiltinTypes>::Value ||
(TypeListAllDeluxeTuple<TypeList<T, Ts..., As...>>::Value &&
!std::is_same<T, EmptyDeluxeTuple>::value &&
(true && ... && (!std::is_same<As, EmptyDeluxeTuple>::value)))>>
AgentHandle
createAgent(const std::string &Name,
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)> &&F) noexcept;
/// Creates a new *agent* in the context of \p this object.
///
/// The new *agent* receives master-input by \p MF and produces
/// master-output.
///
/// \tparam MT type of master-input the new *agent* handles
/// \tparam T type of data the new *agent* outputs
/// \tparam Ts types of master-output the new *agent* produces
/// \tparam As types of inputs the new *agent* takes
///
/// \note The type arguments \p MT, \p T, \p Ts, and \p As... must be either
/// all built-in types or all instances of \c rosa::deluxe::DeluxeTuple.
/// Moreover, none of \p T and \p As... can be \c
/// rosa::deluxe::EmptyDeluxeTuple. Instantiation fails if these conditions
/// do not hold.
///
/// \param Name name of the new *agent*
/// \param MF function for the new *agent* to process master-input
/// values with
/// \param F function for the new *agent* to process input values and
/// generate output with
///
/// \note \p F does not produce master-output for a given position if the
/// corresponding type is \c rosa::deluxe::EmptyDeluxeTuple. It is not
/// possible to disable master-output at any position by using built-in types.
///
/// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
///
/// \return \c rosa::AgentHandle for the new *agent*
template <
typename MT, typename T, typename... Ts, typename... As,
typename = std::enable_if_t<
TypeListSubsetOf<TypeList<MT, T, Ts..., As...>,
BuiltinTypes>::Value ||
(TypeListAllDeluxeTuple<TypeList<MT, T, Ts..., As...>>::Value &&
!std::is_same<T, EmptyDeluxeTuple>::value &&
(true && ... && (!std::is_same<As, EmptyDeluxeTuple>::value)))>>
AgentHandle createAgent(
const std::string &Name,
std::function<std::tuple<Optional<Ts>...>(std::pair<MT, bool>)> &&MF,
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)> &&F) noexcept;
/// Returns the current execution policy of the referred \p Unit
///
/// \see \c rosa::deluxe::DeluxeExecutionPolicy
///
/// \note The referred \p Unit is either *sensor* or *agent*.
///
/// \note The returned reference is valid only as long as \c
/// rosa::deluxe::DeluxeContext::setExecutionPolicy() is not called with the
/// *unit* referred by \p Unit and the *unit* is not destroyed.
///
/// \param Unit the *unit* whose execution policy is to be obtained
///
/// \return the \c rosa::deluxe::DeluxeExecutionPolicy from \p Unit if \p Unit
/// is valid
Optional<const DeluxeExecutionPolicy &>
getExecutionPolicy(AgentHandle Unit) const noexcept;
/// Sets the current execution policy of the referred \p Unit to \p
/// ExecutionPolicy.
///
/// \see \c rosa::deluxe::DeluxeExecutionPolicy
///
/// \note The referred \p Unit is either *sensor* or *agent*.
///
/// \param Unit the *unit* whose execution policy is to be set
/// \param ExecutionPolicy the new execution policy for \p Unit
///
/// \return how successful setting \p ExecutionPolicy for \p Unit was
///
/// \note The function may return the following
/// \c rosa::deluxe::DeluxeContext::ErrorCode values:
/// `ErrorCode` | Comment
/// ----------- | -------
/// `NoError` | Success
/// `NotUnit` | Referred \p Unit is not valid
/// `UnsuitableExecutionPolicy` | \p ExecutionPolicy cannot handle \p Unit
ErrorCode setExecutionPolicy(
AgentHandle Unit,
std::unique_ptr<DeluxeExecutionPolicy> &&ExecutionPolicy) noexcept;
/// Connects a *sensor* to an *agent* in the context of \p this object.
///
/// \param Agent the *agent* to connect to
/// \param Pos the index of slot of \p Agent to connect \p Sensor to
/// \param Sensor the *sensor* to connect
/// \param Description optional textual description of the connection
///
/// \return how successfull connecting \p Sensor to \p Agent at slot
/// index \p Pos was
///
/// \note The function may return the following
/// \c rosa::deluxe::DeluxeContext::ErrorCode values:
/// `ErrorCode` | Comment
/// ----------- | -------
/// `NoError` | Success
/// `NotAgent` | Referred \p Agent is not \c rosa::deluxe::DeluxeAgent
/// `NotSensor` | Referred \p Sensor is not \c rosa::deluxe::DeluxeSensor
/// `WrongPosition` | \p Pos is not a valid input position of \p Agent
/// `TypeMismatch` | Expected input type at position \p Pos of \p Agent is other thanthe output type of \p Sensor or expected master-input of \p Sensor is other than master-output at position \p Pos of \p Agent if any
/// `AlreadyHasSlave` | \p Agent at position \p Pos already has a *slave* registered
/// `AlreadyHasMaster` | \p Sensor already has a *master* registered
ErrorCode connectSensor(AgentHandle Agent, const size_t Pos,
AgentHandle Sensor,
const std::string &Description = "") noexcept;
/// Connectes two *agents* in the context of \p this object.
///
/// \param Master the *agent* to connect to
/// \param Pos the index of slot of \p Master to connect \p Slave to
/// \param Slave the *agent* to connect
/// \param Description optional textual description of the connection
///
/// \return how succesfull connecting \p Slave to \p Master at slot
/// index \p Pos was
///
/// \note The function may return the following
/// \c rosa::deluxe::DeluxeContext::ErrorCode values:
/// `ErrorCode` | Comment
/// ----------- | -------
/// `NoError` | Success
/// `NotAgent` | Referred \p Master or \p Slave is not \c rosa::deluxe::DeluxeAgent
/// `WrongPosition` | \p Pos is not a valid input position of \p Master
/// `TypeMismatch` | Expected input type at position \p Pos of \p Master is other than the output type of \p Slave or expected master-input of \p Slave is other than master-output at position \p Pos of \p Master if any
/// `AlreadyHasSlave` | \p Master at position \p Pos already has a *slave* registered
/// `AlreadyHasMaster` | \p Slave already has a *master* registered
ErrorCode connectAgents(AgentHandle Master, const size_t Pos,
AgentHandle Slave,
const std::string &Description = "") noexcept;
/// Initializes \c this object and others managed by \p this object
/// for setting up and performing simulation.
///
/// \see \c rosa::deluxe::DeluxeContext::registerSensorValues,
/// \c rosa::deluxe::DeluxeContext::simulate
///
/// Need to clear simulation data sources from all the *sensors*.
void initializeSimulation(void) noexcept;
public:
/// Registers a stream providing values for a *sensor* during
/// simulation.
///
/// \tparam Iterator type of iterator providing values for \p Sensor
- /// \tparam T type of values \p Sensor is operating on, always use
- /// default!
+ /// \tparam T type that can be matched to values \p Sensor is operating on,
+ /// always use default!
///
/// \note Instantiation fails if type argument \p T is neither a built-in type
- /// nor an instance of \c rosa::deluxe::DeluxeTuple with at least one element.
+ /// nor a tuple (i.e., can be converted to \c rosa::deluxe::DeluxeTuple).
///
/// \param Sensor the *sensor* to register values for
/// \param Start provides values for \p Sensor
/// \param End denotes the end of stream of values
/// \param Default value to be used when input stream is depleted
/// during simulation
///
/// \return how successful registering \p Source for \p Sensor
///
/// \note The function may return the following
/// \c rosa::deluxe::DeluxeContext::ErrorCode values:
/// `ErrorCode` | Comment
/// ----------- | -------
/// `NoError` | Success
- /// `TypeMismatch` | \p Sensor generates values of a type other than
- /// \p T `NotSensor` | Referred \p Sensor is not \c
- /// rosa::deluxe::DeluxeSensor `AlreadyHasValueStream` | \p Sensor already has
- /// simulation data source set
- template <
- typename Iterator, typename T = typename Iterator::value_type,
- typename = std::enable_if_t<TypeListContains<BuiltinTypes, T>::Value ||
- (IsDeluxeTuple<T>::Value &&
- !std::is_same<T, EmptyDeluxeTuple>::value)>>
+ /// `TypeMismatch` | \p T does not match the type of values
+ /// generated by \p Sensor
+ /// `NotSensor` | Referred \p Sensor is not \c
+ /// rosa::deluxe::DeluxeSensor
+ /// `AlreadyHasValueStream` | \p Sensor already has simulation data source set
+ template <typename Iterator, typename T = typename Iterator::value_type,
+ typename = std::enable_if_t<
+ TypeListContains<BuiltinTypes, T>::Value || IsTuple<T>::Value>>
ErrorCode registerSensorValues(AgentHandle Sensor, Iterator &&Start,
const Iterator &End, T Default = {}) noexcept;
/// Performs the system contained by \p this object.
///
/// The function performs \p NumCycles cycle of simulation. In each
/// cycle, all the *agents* and *sensors* registered in \c
/// rosa::deluxe::DeluxeContext::DeluxeUnits are trigged for
/// execution.
///
/// \param NumCycles number of cycles to perform
///
/// \pre All the *sensors* in the system contained by \p this object
/// generate their output from simulation data sources.
void simulate(const size_t NumCycles) const noexcept;
};
/// Anonymous namespace with helper features for implementing
/// \c rosa::deluxe::DeluxeContext, consider it private.
namespace {
/// Maps any type \p T to \c rosa::deluxe::EmptyDeluxeTuple.
template <typename T> struct MapToEmptyDeluxeTuple {
using Type = EmptyDeluxeTuple;
};
/// Convenience template alias for \c MapToEmptyDeluxeTuple.
template <typename T>
using empty_deluxe_t = typename MapToEmptyDeluxeTuple<T>::Type;
/// Converts a \c std::tuple of \c rosa::Optional built-in types into a
/// corresponding \c std::tuple of \c rosa::Optional with each actual value
/// wrapped in \c rosa::deluxe::DeluxeTuple.
///
/// \tparam Ts types of the values
/// \tparam S0 indices for accessing values in \p Values
///
/// \param Values the \c std::tuple of \c rosa::Optional with built-in values
///
/// \note The second argument provides indices statically as template arguments
/// \p S0..., so its actual value is ignored.
///
/// \return a \c std::tuple of \c rosa::Optional corresponding to \p Values
/// with each actual value wrapped in \c rosa::deluxe::DeluxeTuple
///
/// \pre Statically, all type arguments \p Ts... are built-in types and the
/// provided indices \p S0... match the length of \p Ts...: \code
/// TypeListSubsetOf<TypeList<Ts...>, BuiltinTypes>::Value &&
/// sizeof...(Ts) == sizeof...(S0)
/// \endcode
template <typename... Ts, size_t... S0>
std::tuple<Optional<DeluxeTuple<Ts>>...>
wrapBuiltinInDeluxeTuple(const std::tuple<Optional<Ts>...> &Values,
Seq<S0...>) noexcept {
STATIC_ASSERT((TypeListSubsetOf<TypeList<Ts...>, BuiltinTypes>::Value),
"not built-in types");
STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent type arguments");
return std::make_tuple(std::get<S0>(Values)
? Optional<DeluxeTuple<Ts>>(
make_deluxe_tuple<Ts>(*std::get<S0>(Values)))
: Optional<DeluxeTuple<Ts>>()...);
}
} // End namespace
template <typename MT, typename T, typename>
AgentHandle
DeluxeContext::createSensorImpl(const std::string &Name,
std::function<void(std::pair<MT, bool>)> &&MF,
std::function<T(void)> &&F) noexcept {
AgentHandle H = System->createSensor(Name, std::move(MF), std::move(F));
DeluxeUnits.emplace(H);
return H;
}
template <typename T, typename>
AgentHandle DeluxeContext::createSensor(const std::string &Name,
std::function<T(void)> &&F) noexcept {
auto EmptyMF = std::function<void(std::pair<EmptyDeluxeTuple, bool>)>(
[](std::pair<EmptyDeluxeTuple, bool>) {});
if constexpr (TypeListContains<BuiltinTypes, T>::Value) {
using OutputType = DeluxeTuple<T>;
return createSensorImpl(
Name, std::move(EmptyMF),
std::function<OutputType(void)>(
[F{std::move(F)}](void) { return OutputType(F()); }));
} else if constexpr (IsDeluxeTuple<T>::Value &&
!std::is_same<T, EmptyDeluxeTuple>::value) {
return createSensorImpl(Name, std::move(EmptyMF), std::move(F));
} else {
ASSERT(false && "Unexpected type argument");
}
}
template <typename MT, typename T, typename>
AgentHandle
DeluxeContext::createSensor(const std::string &Name,
std::function<void(std::pair<MT, bool>)> &&MF,
std::function<T(void)> &&F) noexcept {
if constexpr (TypeListSubsetOf<TypeList<MT, T>, BuiltinTypes>::Value) {
using MasterInputType = DeluxeTuple<MT>;
using OutputType = DeluxeTuple<T>;
return createSensorImpl(
Name,
std::function<void(std::pair<MasterInputType, bool>)>(
[MF{std::move(MF)}](std::pair<MasterInputType, bool> Arg) {
MF({std::get<0>(Arg.first), Arg.second});
}),
std::function<OutputType(void)>(
[F{std::move(F)}](void) { return OutputType(F()); }));
} else if constexpr (TypeListAllDeluxeTuple<TypeList<MT, T>>::Value &&
!std::is_same<T, EmptyDeluxeTuple>::value) {
return createSensorImpl(Name, std::move(MF), std::move(F));
} else {
ASSERT(false && "Unexpected type arguments");
}
}
template <typename MT, typename T, typename... Ts, typename... As, typename>
AgentHandle DeluxeContext::createAgentImpl(
const std::string &Name,
std::function<std::tuple<Optional<Ts>...>(std::pair<MT, bool>)> &&MF,
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)> &&F) noexcept {
AgentHandle H = System->createAgent(Name, std::move(MF), std::move(F));
DeluxeUnits.emplace(H);
return H;
}
template <typename T, typename... As, typename>
AgentHandle DeluxeContext::createAgent(
const std::string &Name,
std::function<Optional<T>(std::pair<As, bool>...)> &&F) noexcept {
using NoMasterOutputType = std::tuple<Optional<empty_deluxe_t<As>>...>;
auto EmptyMF =
std::function<NoMasterOutputType(std::pair<EmptyDeluxeTuple, bool>)>(
[](std::pair<EmptyDeluxeTuple, bool>) {
return NoMasterOutputType();
});
if constexpr (TypeListSubsetOf<TypeList<T, As...>, BuiltinTypes>::Value) {
using OutputType = DeluxeTuple<T>;
return createAgentImpl(
Name, std::move(EmptyMF),
std::function<
std::tuple<Optional<OutputType>, Optional<empty_deluxe_t<As>>...>(
std::pair<DeluxeTuple<As>, bool>...)>(
[F{std::move(F)}](std::pair<DeluxeTuple<As>, bool>... Args) {
const auto Result = F({std::get<0>(Args.first), Args.second}...);
return std::tuple_cat(
wrapBuiltinInDeluxeTuple(std::tuple(Result), seq_t<1>()),
NoMasterOutputType());
}));
} else if constexpr (TypeListAllDeluxeTuple<TypeList<T, As...>>::Value &&
!std::is_same<T, EmptyDeluxeTuple>::value &&
(true && ... &&
(!std::is_same<As, EmptyDeluxeTuple>::value))) {
return createAgentImpl(
Name, std::move(EmptyMF),
std::function<std::tuple<Optional<T>, Optional<empty_deluxe_t<As>>...>(
std::pair<As, bool>...)>(
[F{std::move(F)}](std::pair<As, bool>... Args) {
const auto Result = F(Args...);
return std::tuple_cat(std::tuple(Result), NoMasterOutputType());
}));
} else {
ASSERT(false && "Unexpected type arguments");
}
}
template <typename MT, typename T, typename... As, typename>
AgentHandle DeluxeContext::createAgent(
const std::string &Name, std::function<void(std::pair<MT, bool>)> &&MF,
std::function<Optional<T>(std::pair<As, bool>...)> &&F) noexcept {
using NoMasterOutputType = std::tuple<Optional<empty_deluxe_t<As>>...>;
if constexpr (TypeListSubsetOf<TypeList<MT, T, As...>, BuiltinTypes>::Value) {
using MasterInputType = DeluxeTuple<MT>;
using OutputType = DeluxeTuple<T>;
return createAgentImpl(
Name,
std::function<NoMasterOutputType(std::pair<MasterInputType, bool>)>(
[MF{std::move(MF)}](std::pair<MasterInputType, bool> Arg) {
MF({std::get<0>(Arg.first), Arg.second});
return NoMasterOutputType();
}),
std::function<
std::tuple<Optional<OutputType>, Optional<empty_deluxe_t<As>>...>(
std::pair<DeluxeTuple<As>, bool>...)>(
[F{std::move(F)}](std::pair<DeluxeTuple<As>, bool>... Args) {
const auto Result = F({std::get<0>(Args.first), Args.second}...);
return std::tuple_cat(
wrapBuiltinInDeluxeTuple(std::tuple(Result), seq_t<1>()),
NoMasterOutputType());
}));
} else if constexpr (TypeListAllDeluxeTuple<TypeList<MT, T, As...>>::Value &&
!std::is_same<T, EmptyDeluxeTuple>::value &&
(true && ... &&
(!std::is_same<As, EmptyDeluxeTuple>::value))) {
return createAgentImpl(
Name,
std::function<NoMasterOutputType(std::pair<MT, bool>)>(
[MF{std::move(MF)}](std::pair<MT, bool> Arg) {
MF(Arg);
return NoMasterOutputType();
}),
std::function<std::tuple<Optional<T>, Optional<empty_deluxe_t<As>>...>(
std::pair<As, bool>...)>(
[F{std::move(F)}](std::pair<As, bool>... Args) {
const auto Result = F(Args...);
return std::tuple_cat(std::tuple(Result), NoMasterOutputType());
}));
} else {
ASSERT(false && "Unexpected type arguments");
}
}
template <typename T, typename... Ts, typename... As, typename>
AgentHandle DeluxeContext::createAgent(
const std::string &Name,
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)> &&F) noexcept {
if constexpr (TypeListSubsetOf<TypeList<T, Ts..., As...>,
BuiltinTypes>::Value) {
using MasterOutputType = std::tuple<Optional<DeluxeTuple<Ts>>...>;
using OutputType = DeluxeTuple<T>;
return createAgentImpl(
Name,
std::function<MasterOutputType(std::pair<EmptyDeluxeTuple, bool>)>(
[](std::pair<EmptyDeluxeTuple, bool>) {
return MasterOutputType();
}),
std::function<
std::tuple<Optional<OutputType>, Optional<DeluxeTuple<Ts>>...>(
std::pair<DeluxeTuple<As>, bool>...)>(
[F{std::move(F)}](std::pair<DeluxeTuple<As>, bool>... Args) {
const auto Result = F({std::get<0>(Args.first), Args.second}...);
return wrapBuiltinInDeluxeTuple(Result,
seq_t<1 + sizeof...(Ts)>());
}));
} else if constexpr (TypeListAllDeluxeTuple<
TypeList<T, Ts..., As...>>::Value &&
!std::is_same<T, EmptyDeluxeTuple>::value &&
(true && ... &&
(!std::is_same<As, EmptyDeluxeTuple>::value))) {
using MasterOutputType = std::tuple<Optional<Ts>...>;
return createAgentImpl(
Name,
std::function<MasterOutputType(std::pair<EmptyDeluxeTuple, bool>)>(
[](std::pair<EmptyDeluxeTuple, bool>) {
return MasterOutputType();
}),
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)>(
[F{std::move(F)}](std::pair<As, bool>... Args) {
const auto Output = F(Args...);
return Output;
}));
} else {
ASSERT(false && "Unexpected type arguments");
}
}
template <typename MT, typename T, typename... Ts, typename... As, typename>
AgentHandle DeluxeContext::createAgent(
const std::string &Name,
std::function<std::tuple<Optional<Ts>...>(std::pair<MT, bool>)> &&MF,
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)> &&F) noexcept {
if constexpr (TypeListSubsetOf<TypeList<MT, T, Ts..., As...>,
BuiltinTypes>::Value) {
using MasterInputType = DeluxeTuple<MT>;
using MasterOutputType = std::tuple<Optional<DeluxeTuple<Ts>>...>;
using OutputType = DeluxeTuple<T>;
return createAgentImpl(
Name,
std::function<MasterOutputType(std::pair<MasterInputType, bool>)>(
[MF{std::move(MF)}](std::pair<MasterInputType, bool> Arg) {
const auto Result = MF({std::get<0>(Arg.first), Arg.second});
return wrapBuiltinInDeluxeTuple(Result, seq_t<sizeof...(Ts)>());
}),
std::function<
std::tuple<Optional<OutputType>, Optional<DeluxeTuple<Ts>>...>(
std::pair<DeluxeTuple<As>, bool>...)>(
[F{std::move(F)}](std::pair<DeluxeTuple<As>, bool>... Args) {
const auto Result = F({std::get<0>(Args.first), Args.second}...);
return wrapBuiltinInDeluxeTuple(Result,
seq_t<1 + sizeof...(Ts)>());
}));
} else if constexpr (TypeListAllDeluxeTuple<
TypeList<MT, T, Ts..., As...>>::Value &&
!std::is_same<T, EmptyDeluxeTuple>::value &&
(true && ... &&
(!std::is_same<As, EmptyDeluxeTuple>::value))) {
using MasterOutputType = std::tuple<Optional<Ts>...>;
return createAgentImpl(
Name,
std::function<MasterOutputType(std::pair<MT, bool>)>(
[MF{std::move(MF)}](std::pair<MT, bool> Arg) {
const auto Output = MF(Arg);
return Output;
}),
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)>(
[F{std::move(F)}](std::pair<As, bool>... Args) {
const auto Output = F(Args...);
return Output;
}));
} else {
ASSERT(false && "Unexpected type arguments");
}
}
template <typename Iterator, typename T, typename>
DeluxeContext::ErrorCode
DeluxeContext::registerSensorValues(AgentHandle Sensor, Iterator &&Start,
const Iterator &End, T Default) noexcept {
// Get the type of values provided by \p Iterator.
STATIC_ASSERT((std::is_same<T, typename Iterator::value_type>::value),
"type mismatch");
// Make sure preconditions are met.
if (!System->isDeluxeSensor(Sensor)) {
DCRETERROR(ErrorCode::NotSensor);
}
auto S = System->getDeluxeSensor(Sensor);
ASSERT(S); // Sanity check.
+
if (S->simulationDataSourceIsSet()) {
DCRETERROR(ErrorCode::AlreadyHasValueStream);
}
if constexpr (TypeListContains<BuiltinTypes, T>::Value) {
if (S->OutputType != TypeToken<T>::Value) {
DCRETERROR(ErrorCode::TypeMismatch);
}
// Register input stream.
// \note Need to capture parameters by value so having local copies.
S->registerSimulationDataSource(std::function<DeluxeTuple<T>(void)>([=
](void) mutable noexcept->DeluxeTuple<T> {
if (Start != End) {
LOG_TRACE_STREAM << "Reading next value for sensor '" << S->FullName
<< "': " << *Start << '\n';
return make_deluxe_tuple<T>(*Start++);
} else {
LOG_TRACE_STREAM << "Providing default value for sensor '"
<< S->FullName << "': " << Default << '\n';
return make_deluxe_tuple<T>(Default);
}
}));
- } else if constexpr (IsDeluxeTuple<T>::Value &&
- !std::is_same<T, EmptyDeluxeTuple>::value) {
- if (S->OutputType != T::TT) {
+ } else if constexpr (IsTuple<T>::Value) {
+
+ using TT = matching_deluxe_tuple_t<T>;
+ if (std::is_same<T, EmptyDeluxeTuple>::value || S->OutputType != TT::TT) {
DCRETERROR(ErrorCode::TypeMismatch);
}
// Register input stream.
// \note Need to capture parameters by value so having local copies.
S->registerSimulationDataSource(
- std::function<T(void)>([=](void) mutable noexcept->T {
+ std::function<TT(void)>([=](void) mutable noexcept->TT {
if (Start != End) {
+ const TT DV(*Start++);
LOG_TRACE_STREAM << "Reading next value for sensor '" << S->FullName
- << "': " << *Start << '\n';
- return *Start++;
+ << "': " << DV << '\n';
+ return DV;
} else {
+ static const TT DD(Default);
LOG_TRACE_STREAM << "Providing default value for sensor '"
- << S->FullName << "': " << Default << '\n';
- return Default;
+ << S->FullName << "': " << DD << '\n';
+ return DD;
}
}));
} else {
ASSERT(false && "Unexpected type argument");
}
return ErrorCode::NoError;
}
} // End namespace deluxe
} // End namespace rosa
// Undef local macro if not used in the corresponding implementation.
#ifndef ROSA_LIB_DELUXE_DELUXECONTEXT_CPP
#undef DCRETERROR
#endif
#endif // ROSA_DELUXE_DELUXECONTEXT_HPP
diff --git a/include/rosa/deluxe/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 <memory>
#include <ostream>
#include <set>
#include <vector>
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 <sup>th</sup>
/// 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 <sup>th</sup> 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<DeluxeExecutionPolicy> 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<DeluxeExecutionPolicy>
awaitAll(const std::set<size_t> &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<DeluxeExecutionPolicy>
awaitAny(const std::set<size_t> &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<bool> &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<DeluxeExecutionPolicy> ExecutionPolicy;
public:
/// The type of values produced by \p this object.
///
/// That is the types of values \p this object sends to its *master* in a
/// \c rosa::deluxe::DeluxeTuple.
///
/// \see \c rosa::deluxe::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<AbstractTokenizedStorage> MasterInputValue;
/// Alias for function objects used as trigger handler for
/// \c rosa::deluxe::DeluxeSensor.
///
/// \note The function used for \c H is to be \c noexcept.
///
/// \see \c DeluxeSensorTriggerHandlers
using H = std::function<void(void)>;
/// \defgroup DeluxeSensorTriggerHandlers Trigger handlers of
/// rosa::deluxe::DeluxeSensor
///
/// \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<AgentHandle> Master;
/// Tells the unique identifier of the *master* of \p this object, if any
/// registered.
///
/// \return the unique identifier of the *master*
///
/// \pre A *master* is registered for \p this object: \code
/// Master
/// \endcode
id_t masterId(void) const noexcept;
/// Wraps a master-input processing function into a trigger handler.
///
/// \see \c rosa::deluxe::DeluxeSensor::MFP and \c DeluxeSensorTriggerHandlers
///
/// \tparam Ts types of elements of master-input processed by \p MF
/// \tparam S0 indices for accessing master-input values
///
/// \param MF function that processes master-input
///
/// \note The second argument provides indices statically as template
/// arguments \p S0..., so its actual value is ignored.
///
/// \note A master-input type of \c rosa::deluxe::EmptyDeluxeTuple indicates
/// that \p this object does not receive master-input, \p MF is never called
/// if \p Ts is empty.
///
/// \return trigger handler function based on \p MF
///
/// \pre Statically, the indices match the elements: \code
/// sizeof...(Ts) == sizeof...(S0)
/// \endcode Dynamically, \p Ts... match \c
/// rosa::deluxe::DeluxeSensor::MasterInputType: \code
/// MasterInputType == DeluxeTuple<Ts...>::TT
/// \endcode
template <typename... Ts, size_t... S0>
H triggerHandlerFromProcessingFunction(
std::function<void(std::pair<DeluxeTuple<Ts...>, bool>)> &&MF,
Seq<S0...>) noexcept;
/// Wraps a data source function into a trigger handler.
///
/// \see \c 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<T>::Value
/// \endcode Dynamically, \p T matches \c
/// rosa::deluxe::DeluxeSensor::OutputType: \code
/// OutputType == T::TT
/// \endcode
template <typename T>
H triggerHandlerFromDataSource(std::function<T(void)> &&F,
bool inSimulation) noexcept;
public:
/// Creates a new instance.
///
/// The constructor instantiates the base-class with functions to handle
/// messages as defined for the *deluxe interface*.
///
/// \todo Enforce \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<TypeList<MT, T>>::Value && T::Length > 0
/// \endcode
/// Dynamically, the instance is created as of kind
/// \c rosa::deluxe::atoms::SensorKind:
/// \code
/// Kind == rosa::deluxe::atoms::SensorKind
/// \endcode
///
/// \see \c rosa::deluxe::DeluxeTuple
template <
typename MT, typename T,
typename = std::enable_if_t<
TypeListAllDeluxeTuple<TypeList<MT, T>>::Value && (T::Length > 0)>>
DeluxeSensor(const AtomValue Kind, const id_t Id, const std::string &Name,
MessagingSystem &S,
std::function<void(std::pair<MT, bool>)> &&MF,
std::function<T(void)> &&F) noexcept;
/// Destroys \p this object.
~DeluxeSensor(void) noexcept;
/// Returns the current execution policy of \p this object.
///
/// \see \c rosa::deluxe::DeluxeExecutionPolicy
///
/// \note The returned reference is valid only as long as \c
/// rosa::deluxe::DeluxeSensor::setExecutionPolicy() is not called and \p this
/// object is not destroyed.
///
/// \return \c rosa::deluxe::DeluxeSensor::ExecutionPolicy
const DeluxeExecutionPolicy &executionPolicy(void) const noexcept;
/// Sets the current execution policy of \p this object to \p EP.
///
/// \see \c rosa::deluxe::DeluxeExecutionPolicy
///
/// \note \p EP is set only if it can handle \p this object.
///
/// \param EP the new execution policy for \p this object
///
/// \return if \p EP was successfully set for \p this object.
bool setExecutionPolicy(std::unique_ptr<DeluxeExecutionPolicy> &&EP) noexcept;
/// The *master* of \p this object, if any.
///
/// \see \c rosa::deluxe::DeluxeSensor::registerMaster
///
/// \return the *master* registered for \p this object
Optional<AgentHandle> master(void) const noexcept;
/// Registers a *master* for \p this object.
///
/// The new *master* is registered by overwriting the reference to any
/// already registered *master*. One can clear the registered reference by
/// passing an *empty* \c rosa::Optional object as actual argument.
///
/// \note The role of the referred *master* is validated by checking its
/// *kind*.
///
/// \note Any call to \c rosa::deluxe::DeluxeSensor::registerMaster should be
/// paired with a corresponding call of \c
/// rosa::deluxe::DeluxeAgent::registerSlave, which validates that
/// input/output types of master and slave matches.
///
/// \param _Master the *master* to register
///
/// \pre \p Master is empty or of kind \c rosa::deluxe::atoms::AgentKind:
/// \code
/// !_Master || unwrapAgent(*_Master).Kind == rosa::deluxe::atoms::AgentKind
/// \endcode
void registerMaster(const Optional<AgentHandle> _Master) noexcept;
/// Clears the simulation trigger handler of \p this object.
///
/// The function assigns \c rosa::deluxe::DeluxeSensor::SFP with \c nullptr.
void clearSimulationDataSource(void) noexcept;
/// Tells whether a simulation trigger handler is set for \p this object.
///
/// The function returns whether \c rosa::deluxe::DeluxeSensor::SFP is not
/// \c nullptr.
///
/// \return if a simulation trigger handler is set for \p this object.
bool simulationDataSourceIsSet(void) const noexcept;
/// Registers a simulation data source for \p this object.
///
/// A new simulation trigger handler wrapping \p SF is stored in
/// \c rosa::deluxe::DeluxeSensor::SFP by overwriting any already registered
/// simulation data source.
///
/// \todo Enforce SF does not potentially throw exception.
///
/// \tparam 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<Ts...>::Value
/// \endcode
template <typename... Ts>
void registerSimulationDataSource(
std::function<DeluxeTuple<Ts...>(void)> &&SF) noexcept;
private:
/// Sends a value to the *master* of \p this object.
///
/// \p Value is getting sent to \c rosa::deluxe::DeluxeSensor::Master if it
/// contains a valid handle for a \c rosa::deluxe::DeluxeAgent. The function
/// does nothing otherwise.
///
/// 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<Ts...>::Value
/// \endcode
template <typename... Ts, size_t... S0>
void sendToMaster(const DeluxeTuple<Ts...> &Value, Seq<S0...>) 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<T>::Value
/// \endcode
template <typename T>
void saveMasterInput(id_t Id, token_size_t Pos, T Value) noexcept;
/// \defgroup DeluxeSensorMasterInputHandlers Master-input handlers of
/// rosa::deluxe::DeluxeSensor
///
/// Definition of member functions handling messages from the *master* with
/// different types of input
///
/// A *slave* generally needs to be prepared to deal with values of any
/// built-in type to handle messages from its *master*. Each type requires a
/// separate message handler, which are implemented by these functions. The
/// functions instantiate \c rosa::deluxe::DeluxeSensor::saveMasterInput with
/// the proper template argument and pass the content of the message on for
/// processing.
///
/// \note The member functions in this group are defined by \c
/// DSMASTERHANDLERDEF.
///
/// \note Keep these definitions in sync with \c rosa::BuiltinTypes.
///
///@{
DSMASTERHANDLERDEF(AtomValue)
DSMASTERHANDLERDEF(int16_t)
DSMASTERHANDLERDEF(int32_t)
DSMASTERHANDLERDEF(int64_t)
DSMASTERHANDLERDEF(int8_t)
DSMASTERHANDLERDEFN(long double, long_double)
DSMASTERHANDLERDEFN(std::string, std__string)
DSMASTERHANDLERDEF(uint16_t)
DSMASTERHANDLERDEF(uint32_t)
DSMASTERHANDLERDEF(uint64_t)
DSMASTERHANDLERDEF(uint8_t)
DSMASTERHANDLERDEF(unit_t)
DSMASTERHANDLERDEF(bool)
DSMASTERHANDLERDEF(double)
DSMASTERHANDLERDEF(float)
/// @}
};
template <typename... Ts, size_t... S0>
DeluxeSensor::H DeluxeSensor::triggerHandlerFromProcessingFunction(
std::function<void(std::pair<DeluxeTuple<Ts...>, bool>)> &&MF,
Seq<S0...>) noexcept {
using MT = DeluxeTuple<Ts...>;
STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments");
ASSERT(MasterInputType == MT::TT);
// NOTE: Clang 6 warns about unused lambda captures; we suppress that
// warning (those variables need to be captured).
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-lambda-capture"
#endif // defined __clang__
return [ this, MF ](void) noexcept {
// Do not do anything for master-input type \c
// rosa::deluxe::EmptyDeluxeTuple.
if constexpr (!std::is_same<MT, EmptyDeluxeTuple>::value) {
LOG_TRACE_STREAM << "DeluxeSensor " << FullName
<< " handles master-input." << std::endl;
// The assert must hold if \p this object was successfuuly constructed.
ASSERT((true && ... &&
(static_cast<size_t>(static_cast<token_size_t>(S0)) == S0)));
const auto MasterInputArg = std::make_pair(
// Get all elements of the tuple in a fold expression.
DeluxeTuple<Ts...>(*static_cast<const Ts *>(
MasterInputValue->pointerTo(static_cast<token_size_t>(S0)))...),
MasterInputChanged);
MasterInputChanged = false;
MF(MasterInputArg);
}
};
#ifdef __clang__
#pragma clang diagnostic pop
#endif // defined __clang__
}
template <typename T>
DeluxeSensor::H
DeluxeSensor::triggerHandlerFromDataSource(std::function<T(void)> &&F,
bool inSimulation) noexcept {
STATIC_ASSERT(IsDeluxeTuple<T>::Value, "not tuple type argument");
ASSERT(OutputType == T::TT);
return [ this, F, inSimulation ](void) noexcept {
// Get value and send it to master only if \p ExecutionPolicy allows it.
if (ExecutionPolicy->shouldProcess({})) {
LOG_TRACE_STREAM << "DeluxeSensor " << Name << " obtains next value."
<< std::endl;
sendToMaster(F(), seq_t<T::Length>());
} else {
LOG_TRACE_STREAM << "DeluxeSensor " << Name << " skips next value."
<< std::endl;
if (inSimulation) {
// But read input value in Simulation anyway as input values are
// provided for the highest execution frequency for simulation
- F();
+ (void)F();
}
}
};
}
template <typename MT, typename T, typename>
DeluxeSensor::DeluxeSensor(const AtomValue Kind, const id_t Id,
const std::string &Name, MessagingSystem &S,
std::function<void(std::pair<MT, bool>)> &&MF,
std::function<T(void)> &&F) noexcept
: Agent(Kind, Id, Name, S, THISMEMBER(handleTrigger),
DSMASTERHANDLERREF(AtomValue), DSMASTERHANDLERREF(int16_t),
DSMASTERHANDLERREF(int32_t), DSMASTERHANDLERREF(int64_t),
DSMASTERHANDLERREF(int8_t), DSMASTERHANDLERREF(long_double),
DSMASTERHANDLERREF(std__string), DSMASTERHANDLERREF(uint16_t),
DSMASTERHANDLERREF(uint32_t), DSMASTERHANDLERREF(uint64_t),
DSMASTERHANDLERREF(uint8_t), DSMASTERHANDLERREF(unit_t),
DSMASTERHANDLERREF(bool), DSMASTERHANDLERREF(double),
DSMASTERHANDLERREF(float)),
ExecutionPolicy(DeluxeExecutionPolicy::decimation(1)), OutputType(T::TT),
- MasterInputType(MT::TT), MasterInputChanged(false),
+ MasterInputType(MT::TT), MasterInputNextPos(0), MasterInputChanged(false),
MasterInputValue(new typename TokenizedStorageForTypeList<
typename UnwrapDeluxeTuple<MT>::Type>::Type()),
MFP(triggerHandlerFromProcessingFunction(std::move(MF),
seq_t<MT::Length>())),
FP(triggerHandlerFromDataSource(std::move(F), false)), SFP(nullptr) {
ASSERT(Kind == atoms::SensorKind);
LOG_TRACE_STREAM << "DeluxeSensor " << FullName << " is created."
<< std::endl;
ASSERT(inv());
}
template <typename... Ts>
void DeluxeSensor::registerSimulationDataSource(
std::function<DeluxeTuple<Ts...>(void)> &&SF) noexcept {
ASSERT(OutputType == TypeToken<Ts...>::Value);
SFP = triggerHandlerFromDataSource(std::move(SF), true);
ASSERT(inv());
}
template <typename... Ts, size_t... S0>
void DeluxeSensor::sendToMaster(const DeluxeTuple<Ts...> &Value,
Seq<S0...>) noexcept {
STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments");
ASSERT(OutputType == TypeToken<Ts...>::Value);
// The assert must hold if \p this object was successfuuly constructed.
ASSERT((true && ... &&
(static_cast<size_t>(static_cast<token_size_t>(S0)) == S0)));
// Create a static constant array for these indices to be available as lvalue
// references when creating messages below. \c S0... when used directly in a
// fold expression is a temporary value, which would result in \c
// rosa::Message instances being created with rvalue references. Further, all
// other values would to copied into a temporary variable for making them
/// available as rvalue references (they are constant lvalue references here).
static constexpr std::array<token_size_t, sizeof...(S0)> Indices{{S0...}};
LOG_TRACE_STREAM << "DeluxeSensor " << FullName << "(" << Id
<< ") sends to master("
<< static_cast<bool>(Master && *Master) << "): " << Value
<< std::endl;
// There is a handle and the referred *master* is in a valid state.
if (Master && *Master) {
// Handle each element of the tuple in a fold expression.
(Master->sendMessage(Message::create(atoms::Slave::Value, Id, Indices[S0],
std::get<S0>(Value))),
...);
}
ASSERT(inv());
}
template <typename T>
void DeluxeSensor::saveMasterInput(id_t Id, token_size_t Pos,
T Value) noexcept {
ASSERT(Master && masterId() == Id && Pos == MasterInputNextPos &&
typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf<T>::Value);
LOG_TRACE_STREAM << "DeluxeSensor " << FullName << "(" << Id
<< ") saves value from master: (" << static_cast<size_t>(Pos)
<< ") " << Value << std::endl;
// Save value.
*static_cast<T *>(MasterInputValue->pointerTo(Pos)) = Value;
// Update position of next value.
if (++MasterInputNextPos == lengthOfToken(MasterInputType)) {
MasterInputNextPos = 0;
}
// Set flag.
MasterInputChanged = true;
}
} // End namespace deluxe
} // End namespace rosa
#undef DSMASTERHANDLEREF
#undef DSMASTERHANDLEDEF
#undef DSMASTERHANDLEDEFN
#undef DSMASTERHANDLENAME
#endif // ROSA_DELUXE_DELUXESENSOR_HPP
diff --git a/include/rosa/deluxe/DeluxeSystem.hpp b/include/rosa/deluxe/DeluxeSystem.hpp
old mode 100755
new mode 100644
index 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<DeluxeSystem>
createSystem(const std::string &Name) noexcept;
protected:
/// Creates a new instance.
///
/// \note Protected constructor restricts instantiation for subclasses.
DeluxeSystem(void) noexcept = default;
public:
/// Creates a \c rosa::deluxe::DeluxeSensor instance owned by \p this object
/// and returns a \p rosa::AgentHandle for it.
///
/// \tparam MT type of master-input the new \c rosa::deluxe::DeluxeSensor
/// receives
/// \tparam T type of data the new \c rosa::deluxe::DeluxeSensor operates on
///
/// \note Type arguments \p MT and \p T must be instances of \c
/// rosa::deluxe::DeluxeTuple.
///
/// \param Name name of the new \c rosa::deluxe::DeluxeSensor
/// \param MF function to process master-input values
/// \param F function to generate the next value with during normal operation
///
/// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor.
///
/// \return \c rosa::AgentHandle for new \c rosa::deluxe::DeluxeSensor
template <typename MT, typename T>
AgentHandle createSensor(const std::string &Name,
std::function<void(std::pair<MT, bool>)> &&MF,
std::function<T(void)> &&F) noexcept;
/// Creates a \c rosa::deluxe::DeluxeAgent instance owned by \p this object
/// and returns a \c rosa::AgentHandle for it.
///
/// \tparam MT type of master-input the new \c rosa::deluxe::DeluxeAgent
/// receives
/// \tparam T type of data the new \c rosa::deluxe::DeluxeAgent outputs
/// \tparam Ts types of master-output the new \c rosa::deluxe::DeluxeAgent
/// produces
/// \tparam As types of inputs the new \c rosa::deluxe::DeluxeAgent takes
///
/// \note Type arguments \p MT, \p T, \p Ts..., and \p As... must be
/// instances of \c rosa::deluxe::DeluxeTuple.
///
/// \param Name name of the new \c rosa::deluxe::DeluxeAgent
/// \param MF function for the new \c rosa::deluxe::DeluxeAgent to process
/// master-input values and generate master-output with
/// \param F function for the new \c rosa::deluxe::DeluxeAgent to process
/// input values and generate output and master-output with
///
/// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
///
/// \return \c rosa::AgentHandle for new \c rosa::deluxe::DeluxeAgent
template <typename MT, typename T, typename... Ts, typename... As>
AgentHandle createAgent(
const std::string &Name,
std::function<std::tuple<Optional<Ts>...>(std::pair<MT, bool>)> &&MF,
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)> &&F) noexcept;
protected:
/// Tells whether a \c rosa::AgentHandle refers to a
/// \c rosa::deluxe::DeluxeSensor owned by \p this object.
///
/// \param H \c rosa::AgentHandle to check
///
/// \return whether \p H refers to a \c rosa::deluxe::DeluxeSensor owned by
/// \p this object
virtual bool isDeluxeSensor(const AgentHandle &H) const noexcept = 0;
/// Extracts a const qualified \c rosa::deluxe::DeluxeSensor reference from a
/// const qualified \c rosa::AgentHandle if possible.
///
/// The function returns a \c rosa::Optional object containing a const
/// qualified reference to a \c rosa::deluxe::DeluxeSensor object extracted
/// from a const qualified \c rosa::AgentHandle instance if the referred
/// object is of type \c rosa::deluxeDeluxeSensor and owned by \p this object.
/// The returned \c rosa::Optional object is empty otherwise.
///
/// \see rosa::deluxe::DeluxeSystem::isDeluxeSensor
///
/// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeSensor
/// from
///
/// \return const qualified reference to \c rosa::deluxe::DeluxeSensor if
/// \p H refers to an object which is of that type and is owned by \p this
/// object
Optional<const DeluxeSensor &> getDeluxeSensor(const AgentHandle &H) const
noexcept;
/// Extracts a \c rosa::deluxe::DeluxeSensor reference from a
/// \c rosa::AgentHandle if possible.
///
/// The function returns a \c rosa::Optional object containing a reference to
/// a \c rosa::deluxe::DeluxeSensor object extracted from a
/// \c rosa::AgentHandle instance if the referred object is of type
/// \c rosa::deluxeDeluxeSensor and owned by \p this object. The returned
/// \c rosa::Optional object is empty otherwise.
///
/// \see rosa::deluxe::DeluxeSystem::isDeluxeSensor
///
/// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeSensor
/// from
///
/// \return reference to \c rosa::deluxe::DeluxeSensor if \p H refers to an
/// object which is of that type and is owned by \p this object
Optional<DeluxeSensor &> getDeluxeSensor(AgentHandle &H) const noexcept;
/// Tells whether a \c rosa::AgentHandle refers to a
/// \c rosa::deluxe::DeluxeAgent owned by \p this object.
///
/// \param H \c rosa::AgentHandle to check
///
/// \return whether \p H refers to a \c rosa::deluxe::DeluxeAgent owned by
/// \p this object
virtual bool isDeluxeAgent(const AgentHandle &H) const noexcept = 0;
/// Extracts a const qualified \c rosa::deluxe::DeluxeAgent reference from a
/// const qualified \c rosa::AgentHandle if possible.
///
/// The function returns a \c rosa::Optional object containing a const
/// qualified reference to a \c rosa::deluxe::DeluxeAgent object extracted
/// from a const qualified \c rosa::AgentHandle instance if the referred
/// object is of type \c rosa::deluxeDeluxeAgent and owned by \p this object.
/// The returned \c rosa::Optional object is empty otherwise.
///
/// \see rosa::deluxe::DeluxeSystem::isDeluxeAgent
///
/// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeAgent
/// from
///
/// \return const qualified reference to \c rosa::deluxe::DeluxeAgent if \p H
/// refers to an object which is of that type and is owned by \p this object
Optional<const DeluxeAgent &> getDeluxeAgent(const AgentHandle &H) const
noexcept;
/// Extracts a \c rosa::deluxe::DeluxeAgent reference from a
/// \c rosa::AgentHandle if possible.
///
/// The function returns a \c rosa::Optional object containing a reference to
/// a \c rosa::deluxe::DeluxeAgent object extracted from a
/// \c rosa::AgentHandle instance if the referred object is of type
/// \c rosa::deluxeDeluxeAgent and owned by \p this object. The returned
/// \c rosa::Optional object is empty otherwise.
///
/// \see rosa::deluxe::DeluxeSystem::isDeluxeAgent
///
/// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeAgent
/// from
///
/// \return reference to \c rosa::deluxe::DeluxeAgent if \p H refers to an
/// object which is of that type and is owned by \p this object
Optional<DeluxeAgent &> getDeluxeAgent(AgentHandle &H) const noexcept;
};
template <typename MT, typename T>
AgentHandle
DeluxeSystem::createSensor(const std::string &Name,
std::function<void(std::pair<MT, bool>)> &&MF,
std::function<T(void)> &&F) noexcept {
Agent &DS = createUnit<DeluxeSensor, MessagingSystem>(
[&](const id_t Id, MessagingSystem &S) {
return new DeluxeSensor(atoms::SensorKind, Id, Name, S, std::move(MF),
std::move(F));
});
return {DS};
}
template <typename MT, typename T, typename... Ts, typename... As>
AgentHandle DeluxeSystem::createAgent(
const std::string &Name,
std::function<std::tuple<Optional<Ts>...>(std::pair<MT, bool>)> &&MF,
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)> &&F) noexcept {
Agent &DA = createUnit<DeluxeAgent, DeluxeSystem>(
[&](const id_t Id, DeluxeSystem &S) {
return new DeluxeAgent(atoms::AgentKind, Id, Name, S, std::move(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 <ostream>
#include <tuple>
namespace rosa {
namespace deluxe {
/// A tuple to manage multiple input/output values in the *deluxe interface*.
///
/// \tparam Ts types of elements of the tuple
///
/// \note The template may be instantiated only with built-in types and the
/// number of those type may not exceed the capacity of a \c rosa::Token.
template <typename... Ts>
struct DeluxeTuple : public std::tuple<Ts...> {
// Statically enforce that the class template is instantiated only with
// built-in types.
STATIC_ASSERT((TypeListSubsetOf<TypeList<Ts...>, BuiltinTypes>::Value),
"not built-in types");
// Statically enforce that the class template is instantiated with not too
// many types.
// \note Instantiation would fail on \c rosa::deluxe::DeluxeTuple::TT if there
- // are too any types; this assertion is for more readable error reporting.
+ // are too many types; this assertion is for more readable error reporting.
STATIC_ASSERT(sizeof...(Ts) <= token::MaxTokenizableListSize,
"Too many types");
/// How many elements the instance has.
static constexpr token_size_t Length = sizeof...(Ts);
/// What types the class contains.
///
/// Type information encoded as \c rosa::Token.
static constexpr Token TT = TypeToken<Ts...>::Value;
/// Default constructor, zero-initializes elements.
DeluxeTuple(void) = default;
/// Constructor, initializes the underlying \c std::tuple with lvalue
/// references.
///
/// \param Args value references to the values to store
DeluxeTuple(const std::decay_t<Ts> &... Args) : std::tuple<Ts...>(Args...) {}
/// Constructor, initializes the underlying \c std::tuple with rvalue
/// references.
///
/// \param Args rvalue references to the values to store
DeluxeTuple(std::decay_t<Ts> &&... Args)
: std::tuple<Ts...>(std::move(Args)...) {}
+ /// Contructor, initializes the underlying \c std::tuple from another matching
+ /// \c std::tuple.
+ DeluxeTuple(const std::tuple<Ts...> &Args) : std::tuple<Ts...>(Args) {}
+
/// Default copy-constructor.
DeluxeTuple(const DeluxeTuple &) = default;
/// Default move-constructor.
DeluxeTuple(DeluxeTuple &&) = default;
/// Default copy-assignment.
DeluxeTuple &operator=(const DeluxeTuple &) = default;
/// Default move-assignment.
DeluxeTuple &operator=(DeluxeTuple &&) = default;
private:
/// Dumps \p this object to a given \c std::ostream.
///
/// \note Provides implementation for \c rosa::deluxe::DeluxeTuple::dump.
///
/// \tparam S0 Indices for accessing elements.
///
/// \param [in,out] OS output stream to dump to
///
/// \note The second argument provides indices statically as template
/// arguments \p S0..., so its actual value is ignored.
///
/// \pre Statically, \p S0... matches number of types \p this object was
/// created: \code
/// sizeof...(S0) == sizeof...(Ts)
/// \endcode
template <size_t... S0>
void dump(std::ostream &OS, Seq<S0...>) const noexcept;
public:
/// Dumps \p this object to a given \c std::ostream.
///
/// \param [in,out] OS output stream to dump to
void dump(std::ostream &OS) const noexcept;
};
template <typename... Ts>
template <size_t... S0>
void DeluxeTuple<Ts...>::dump(std::ostream &OS, Seq<S0...>) const noexcept {
STATIC_ASSERT(sizeof...(S0) == sizeof...(Ts), "inconsistent type arguments");
// Convert value to std::string with std::to_string except for a value of
// std::string that does not need conversion.
auto dump_to_string = [](const auto &V) {
if constexpr (std::is_same<std::decay_t<decltype(V)>, std::string>::value) {
return V;
} else {
return std::to_string(V);
}
};
OS << "{";
(OS << ... << (" " + dump_to_string(std::get<S0>(*this))));
OS << " }";
}
template <typename... Ts>
void DeluxeTuple<Ts...>::dump(std::ostream &OS) const noexcept {
dump(OS, seq_t<sizeof...(Ts)>());
}
/// Type alias for a \c rosa::deluxe::DeluxeTuple that contains no elements.
using EmptyDeluxeTuple = DeluxeTuple<>;
/// Template specialization for \c rosa::deluxe::EmptyDeluxeTuple.
template <> struct DeluxeTuple<> : public std::tuple<> {
/// How many elements the instance has.
static constexpr token_size_t Length = 0;
/// What types the class contains.
///
/// Type information encoded as \c rosa::Token.
static constexpr Token TT = TypeToken<>::Value;
/// Constructor, initializes the underlying \c std::tuple.
DeluxeTuple(void) : std::tuple<>() {}
/// Default copy-constructor.
DeluxeTuple(const DeluxeTuple &) = default;
// Default move-constructor.
DeluxeTuple(DeluxeTuple &&) = default;
/// Default copy-assignment.
DeluxeTuple &operator=(const DeluxeTuple &) = default;
// Default move-assignment,
DeluxeTuple &operator=(DeluxeTuple &&) = default;
/// Dumps \p this object to a given \c std::ostream.
///
/// \param [in,out] OS output stream to dump to
static void dump(std::ostream &OS) noexcept;
};
/// Creates a \c rosa::deluxe::DeluxeTuple instance from the given lvalues
/// references.
///
/// \tparam Ts types of elements of the tuple
///
/// \see \c rosa::deluxe::DeluxeTuple
///
/// \param Args values to store in the tuple
///
/// \return an instance of \c rosa::deluxe::DeluxeTuple<Ts...> with \p Args as
/// elements
template <typename... Ts>
inline DeluxeTuple<Ts...> make_deluxe_tuple(const Ts &... Args) noexcept {
return DeluxeTuple<Ts...>(Args...);
}
/// Creates a \c rosa::deluxe::DeluxeTuple instance from the given rvalue
/// references.
///
/// \tparam Ts types of elements of the tuple
///
/// \see \c rosa::deluxe::DeluxeTuple
///
/// \param Args values to store in the tuple
///
/// \return an instance of \c rosa::deluxe::DeluxeTuple<Ts...> with \p Args as
/// elements
template <typename... Ts>
inline DeluxeTuple<Ts...> make_deluxe_tuple(Ts&&... Args) noexcept {
return DeluxeTuple<Ts...>(std::move(Args)...);
}
+/// Creates a \c rosa::deluxe::DeluxeTuple instance from the given \c std::tuple
+/// reference.
+///
+/// \tparam Ts types of elements of the tuple
+///
+/// \see \c rosa::deluxe::DeluxeTuple
+///
+/// \param Args values to store in the tuple
+///
+/// \return an instance of \c rosa::deluxe::DeluxeTuple<Ts...> with \p Args as
+/// elements
+template <typename... Ts>
+inline DeluxeTuple<Ts...>
+make_deluxe_tuple(const std::tuple<Ts...> &Args) noexcept {
+ return DeluxeTuple<Ts...>(Args);
+}
+
/// \defgroup UnwrapDeluxeTuple Implementation of
/// rosa::deluxe::UnwrapDeluxeTuple
///
/// \brief Unwraps element types from an instance of \c
/// rosa::deluxe::DeluxeTuple into a \c rosa::TypeList
///
/// Types can be unwrapped from a \c rosa::deluxe::DeluxeTuple instance as \code
/// typename UnwrapDeluxeTuple<List>::Type
/// \endcode
///
/// For example, the following expression evaluates to `true`: \code
/// std::is_same<typename UnwrapDeluxeTuple<DeluxeTuple<T1, T2>>::Type,
/// TypeList<T1, T2>>::value
/// \endcode
///@{
/// Declaration of the template.
///
/// \tparam Tuple \c rosa::deluxe::DeluxeTuple to unwrap
template <typename Tuple> struct UnwrapDeluxeTuple;
/// Implementation of the template for \c rosa::deluxe::DeluxeTuple instances.
template <typename... Ts> struct UnwrapDeluxeTuple<DeluxeTuple<Ts...>> {
using Type = TypeList<Ts...>;
};
///@}
-/// \defgroup TypeListUnwrapDeluxeTuple Implementation of
-/// \c rosa::deluxe::TypeListUnwrapDeluxeTuple
+///@}
+
+/// \defgroup IsTuple Implementation of \c rosa::deluxe::IsTuple
///
-/// \brief Unwraps element types from instances of \c
-/// rosa::deluxe::DeluxeTuple in a \c rosa::TypeList.
+/// \brief Tells if a type is a tuple as in it can be converted to \c
+/// rosa::deluxe::DeluxeTuple.
///
-/// Types can be unwrapped from \c rosa::deluxe::DeluxeTuple instances as \code
-/// typename TypeListUnwrapDeluxeTuple<List>::Type
-/// \endcode
+/// \see \c rosa::deluxe::MatchingDeluxeTuple
///
-/// For example, the following expression evaluates to `true`: \code
-/// std::is_same<
-/// typename TypeListUnwrapDeluxeTuple<TypeList<T0,
-/// DeluxeTuple<T1, T2>,
-/// T3>>::Type,
-/// TypeList<T0, T1, T2, T3>
-/// >::value
+/// Whether a type \c T is a tuple can be checked as \code
+/// IsTuple<T>::Value
/// \endcode
///@{
/// Declaration of the template.
///
-/// \tparam List \c rosa::TypeList to check
-template <typename List> struct TypeListUnwrapDeluxeTuple;
+/// \tparam T type to check
+template <typename T> struct IsTuple;
-/// Specialization for \c rosa::EmptyTypeList.
-template <> struct TypeListUnwrapDeluxeTuple<EmptyTypeList> {
- using Type = EmptyTypeList;
+/// Specialization for the case when the type is an instance of \c std::tuple.
+template <typename... Ts> struct IsTuple<std::tuple<Ts...>> {
+ static constexpr bool Value = true;
};
-/// Specialization for the case when the first type in \p List is an instance of
-/// \c rosa::deluxe::DeluxeTuple.
-template <typename... As, typename... Ts>
-struct TypeListUnwrapDeluxeTuple<TypeList<DeluxeTuple<As...>, Ts...>> {
- using Type = typename TypeListConcat<
- typename UnwrapDeluxeTuple<DeluxeTuple<As...>>::Type,
- typename TypeListUnwrapDeluxeTuple<TypeList<Ts...>>::Type>::Type;
+/// Specialization for the case when the type is an instance of \c std::tuple.
+template <typename... Ts> struct IsTuple<DeluxeTuple<Ts...>> {
+ static constexpr bool Value = true;
};
-/// Implementation for a general first type in \p List.
-template <typename T, typename... Ts>
-struct TypeListUnwrapDeluxeTuple<TypeList<T, Ts...>> {
- using Type = typename TypeListPush<
- T, typename TypeListUnwrapDeluxeTuple<TypeList<Ts...>>::Type>::Type;
-};
+/// Implementation for a general case of type \p T.
+template <typename T> struct IsTuple { static constexpr bool Value = false; };
///@}
/// \defgroup IsDeluxeTuple Implementation of \c rosa::deluxe::IsDeluxeTuple
///
/// \brief Tells if a type is an instance of \c rosa::deluxe::DeluxeTuple.
///
/// Whether a type \c T is an instance of \c rosa::deluxe::DeluxeTuple can be
/// checked as \code
/// IsDeluxeTuple<T>::Value
/// \endcode
+///
+/// \note `!IsDeluxeTuple<T>::Value || IsTuple<T>::Value`
///@{
/// Declaration of the template.
///
/// \tparam T type to check
template <typename T> struct IsDeluxeTuple;
/// Specialization for the case when the type is an instance of \c
/// rosa::deluxe::DeluxeTuple.
template <typename... Ts>
struct IsDeluxeTuple<DeluxeTuple<Ts...>> {
static constexpr bool Value = true;
};
/// Implementation for a general case of type \p T.
template <typename T>
struct IsDeluxeTuple {
static constexpr bool Value = false;
};
///@}
+/// \defgroup MatchingDeluxeTuple Implementation of \c
+/// rosa::deluxe::MatchingDeluxeTuple
+///
+/// \brief Gives the \c rosa::deluxe::DeluxeTuple type that matches a given
+/// tuple type.
+///
+/// The matching \c rosa::deluxe::DeluxeTuple type for a tuple type \p T can be
+/// obtained as \code
+/// typename MatchingDeluxeTuple<T>::Type
+/// \endcode
+/// If \p T is \c rosa::deluxe::DeluxeTuple, the matching type is \p T itself.
+/// If \p T is \c std::tuple, the matching type if \c rosa::deluxe::DeluxeTuple
+/// with the same type parameters as \p T.
+/// \c rosa::deluxe::MatchingDeluxeTuple is not defined for other type
+/// parameters.
+///
+/// \note The template is defined for type \p T only if
+/// `rosa::deluxe::IsTuple<T>::Value`. Values of such types can be used to
+/// construct a new instance of the class \c rosa::deluxe::DeluxeTuple.
+///
+///\see \c rosa::deluxe::IsTuple
+///@{
+
+/// Declaration of the template.
+///
+/// \tparam T type to check
+template <typename T> struct MatchingDeluxeTuple;
+
+/// Specialization for the case when the type is an instance of \c
+/// rosa::deluxe::DeluxeTuple.
+template <typename... Ts> struct MatchingDeluxeTuple<DeluxeTuple<Ts...>> {
+ using Type = DeluxeTuple<Ts...>;
+};
+
+/// Specialization for the case when the type is an instance of \c
+/// std::tuple.
+template <typename... Ts> struct MatchingDeluxeTuple<std::tuple<Ts...>> {
+ using Type = DeluxeTuple<Ts...>;
+};
+
+///@}
+
+/// Convenience template type alias for easy use of \c
+/// rosa::deluxe::MatchingDeluxeTuple.
+///
+/// Converts a tuple type to the matching \c rosa::deluxe::DeluxeTuple type.
+///
+/// \tparam Tuple type to convert
+template <typename Tuple>
+using matching_deluxe_tuple_t = typename MatchingDeluxeTuple<Tuple>::Type;
+
/// \defgroup TypeListAllDeluxeTuple Implementation of
/// \c rosa::deluxe::TypeListAllDeluxeTuple
///
/// \brief Tells if all types in a \c rosa::TypeList is an instance of \c
/// rosa::deluxe::DeluxeTuple.
///
/// Whether a \c rosa::TypeList \c List contains instances of \c
/// rosa::deluxe::DeluxeTuple only can be checked as \code
/// TypeListAllDeluxeTuple<List>::Value
/// \endcode
///@{
/// Declaration of the template.
///
/// \tparam List \c rosa::TypeList to check
template <typename List> struct TypeListAllDeluxeTuple;
/// Specialization for \c rosa::EmptyTypeList.
template <> struct TypeListAllDeluxeTuple<EmptyTypeList> {
static constexpr bool Value = true;
};
/// Implementation for the general case when there is at leasst one element in
/// the list.
template <typename T, typename... Ts>
struct TypeListAllDeluxeTuple<TypeList<T, Ts...>> {
static constexpr bool Value =
IsDeluxeTuple<T>::Value && TypeListAllDeluxeTuple<TypeList<Ts...>>::Value;
};
///@}
} // End namespace deluxe
} // End namespace rosa
namespace std {
/// Dumps a \c rosa::deluxe::Deluxe instance to a given \c std::ostream.
///
/// \param [in,out] OS output stream to dump to
/// \param Tuple \c rosa::deluxe::Deluxe to dump
///
/// \return \p OS after dumping \p Tuple to it
template <typename... Ts>
ostream &operator<<(ostream &OS,
const rosa::deluxe::DeluxeTuple<Ts...> &Tuple) {
Tuple.dump(OS);
return OS;
}
} // End namespace std
#endif // ROSA_DELUXE_DELUXETUPLE_HPP
diff --git a/include/rosa/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<NameValue>;
///
/// [](NameAtom){ std::cout << "Argument of type NameAtom"; }(NameAtom::Value)
/// \endcode
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_SUPPORT_ATOM_HPP
#define ROSA_SUPPORT_ATOM_HPP
#include "rosa/support/debug.hpp"
namespace rosa {
/// Maximal length of valid atom strings.
constexpr size_t MaxAtomLength = 10;
/// Underlying integer type of atom values.
using atom_t = uint64_t;
/// Turn \c rosa::atom_t into a strongly typed enumeration.
///
/// Values of \c rosa::atom_t casted to \c rosa::AtomValue may be used in a
/// type-safe way.
enum class AtomValue : atom_t {};
/// Anonymous namespace with implementational details, consider it private.
namespace {
// clang-format off
/// Encodes ASCII characters to 6-bit encoding.
constexpr unsigned char AtomEncodingTable[] = {
/* ..0 ..1 ..2 ..3 ..4 ..5 ..6 ..7 ..8 ..9 ..A ..B ..C ..D ..E ..F */
/* 0.. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 1.. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 2.. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 3.. */ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0, 0,
/* 4.. */ 0, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
/* 5.. */ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 0, 0, 0, 0, 37,
/* 6.. */ 0, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
/* 7.. */ 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 0, 0, 0, 0, 0};
// clang-format on
/// Decodes 6-bit characters to ASCII
constexpr char AtomDecodingTable[] = " 0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ_"
"abcdefghijklmnopqrstuvwxyz";
/// Encodes one character and updates the integer representation.
///
/// \param Current an encoded value
/// \param CharCode a character to add to \p Current
///
/// \return \p Current updated with \p CharCode
constexpr atom_t nextInterim(atom_t Current, size_t CharCode) {
return (Current << 6) | AtomEncodingTable[(CharCode <= 0x7F) ? CharCode : 0];
}
/// Encodes a C-string into an integer value to be used as \c rosa::AtomValue.
///
/// \param CStr a string to encode
/// \param Interim encoded value to add \p CStr to it
///
/// \return \p Interim updated with \p CStr
constexpr atom_t atomValue(const char *CStr, atom_t Interim = 0xF) {
return (*CStr == '\0')
? Interim
: atomValue(CStr + 1,
nextInterim(Interim, static_cast<size_t>(*CStr)));
}
} // End namespace
/// Converts a \c std::string into a \c rosa::AtomValue.
///
/// \param S \c std::string to convert
///
/// \return \c rosa::AtomValue representing \p S
AtomValue atom_from_string(const std::string &S);
/// Converts a string-literal into a \c rosa::AtomValue.
///
/// \tparam Size the length of \p Str
///
/// \param Str the string-literal to convert
///
/// \return \c rosa::AtomValue representating \p Str
///
/// \pre \p Str is not too long:\code
/// Size <= MaxAtomLength + 1
/// \endcode
template <size_t Size> constexpr AtomValue atom(char const (&Str)[Size]) {
// Last character is the NULL terminator.
STATIC_ASSERT(Size <= MaxAtomLength + 1,
"Too many characters in atom definition");
return static_cast<AtomValue>(atomValue(Str));
}
/// Lifts a \c rosa::AtomValue to a compile-time constant.
///
/// \tparam V \c rosa::AtomValue to lift
template <AtomValue V> struct AtomConstant {
/// Constructor has to do nothing.
constexpr AtomConstant(void) {}
/// Returns the wrapped value.
///
/// \return \p V
constexpr operator AtomValue(void) const { return V; }
/// Returns the wrapped value as of type \c rosa::atom_t.
///
/// \return \c rosa::atom_t value from \p V
static constexpr atom_t value() { return static_cast<atom_t>(V); }
/// An instance *of this constant* (*not* a \c rosa::AtomValue).
static const AtomConstant Value;
};
// Implementation of the static member field \c rosa::AtomConstant::Value.
template <AtomValue V>
const AtomConstant<V> AtomConstant<V>::Value = AtomConstant<V>{};
} // End namespace rosa
namespace std {
/// Converts a \c rosa::AtomValue into \c std::string.
///
/// \param What value to convert
///
/// \return \c std::string encoded in \p What
string to_string(const rosa::AtomValue &What);
/// Dumps a \c rosa::AtomValue to a given \c std::ostream.
///
/// \param [in,out] OS output stream to dump to
/// \param A \c rosa::AtomValue to dump
///
/// \return \p OS after dumping \p N to it
inline ostream &operator<<(ostream &OS, const rosa::AtomValue &A) {
OS << to_string(A);
return OS;
}
/// Converts a \c rosa::AtomConstant into \c std::string.
///
/// \tparam V \c rosa::AtomValue to convert
///
/// \note The actual argument of type `const rosa::AtomConstant<V>` is ignored
/// because the \c rosa::AtomValue to convert is encoded in the type itself.
///
/// \return the original string encoded in \p V
template <rosa::AtomValue V> string to_string(const rosa::AtomConstant<V> &) {
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 <rosa::AtomValue V>
inline ostream &operator<<(ostream &OS, const rosa::AtomConstant<V> &A) {
(void)A; // Shut compiler about unused parameter.
OS << to_string(V);
return OS;
}
} // End namespace std
#endif // ROSA_SUPPORT_ATOM_HPP
diff --git a/include/rosa/support/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 <istream>
#include <sstream>
#include <vector>
+#include <map>
+#include <algorithm>
+#include <set>
namespace rosa {
namespace csv {
+/// Indicating it the CSV file contains any header or not
+enum class HeaderInformation {
+ HasHeader,
+ HasNoHeader
+};
+
/// Anonymous namespace providing implementation details for
/// \c rosa::csv::CSVIterator, consider it private.
namespace {
-/// Provides facility for parsing values from one row CSV data.
+/// Provides facility for parsing one value from a string.
///
-/// \tparam T type of values to parse from the line
+/// \tparam T type of value to parse
/// \tparam IsSignedInt if \p T is a signed integral type, always use default
/// \tparam IsUnsignedInt if \p T is an unsigned integral type, always use
/// default
/// \tparam IsFloat if \p T is a floating-point type, always use default
/// \tparam IsString if \p T is \c std::string, always use default
///
-/// \note Specializations of this `struct` are provided for arithmentic types
+/// \note Specializations of this struct are provided for arithmentic types
/// and \c std::string.
-template <typename T, bool IsSignedInt = (std::is_integral<T>::value &&
- std::is_signed<T>::value),
+template <typename T,
+ bool IsSignedInt =
+ (std::is_integral<T>::value && std::is_signed<T>::value),
bool IsUnsignedInt =
(std::is_integral<T>::value && std::is_unsigned<T>::value),
bool IsFloat = std::is_floating_point<T>::value,
bool IsString = std::is_same<T, std::string>::value>
-struct CSVRowParser;
-
-/// Specialization for signed integral types.
-///
-/// \tparam T type of values to parse from the line
-///
-/// \pre \p T is a signed integral type:\code
-/// std::is_integral<T>::value && std::is_signed<T>::value
-/// \endcode
-template <typename T> struct CSVRowParser<T, true, false, false, false> {
- STATIC_ASSERT((std::is_integral<T>::value && std::is_signed<T>::value),
- "wrong type"); // Sanity check.
+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<T> &Data) {
- std::string Cell;
- Data.clear();
- while (std::getline(LineStream, Cell, ',')) {
- Data.push_back(static_cast<T>(std::stoll(Cell)));
- }
- // This checks for a trailing comma with no data after it.
- if (!LineStream && Cell.empty()) {
- // If there was a trailing comma then add an empty element.
- Data.push_back(0);
- }
+ /// \return the parsed value
+ ///
+ /// \note The function silently fails if cannot parse \p Cell for type \p T.
+ static T parse(const std::string &Cell) noexcept;
+};
+
+template <typename T>
+struct ValueParser<T, true, false, false, false> {
+ STATIC_ASSERT((std::is_integral<T>::value && std::is_signed<T>::value),
+ "wrong type"); // Sanity check.
+ static T parse(const std::string &Cell) noexcept {
+ return static_cast<T>(std::stoll(Cell));
}
};
-/// Specialization for unsigned integral types.
-///
-/// \tparam T type of values to parse from the line
-///
-/// \pre \p T is an unsigned integral type:\code
-/// std::is_integral<T>::value && std::is_unsigned<T>::value
-/// \endcode
-template <typename T> struct CSVRowParser<T, false, true, false, false> {
+template <typename T>
+struct ValueParser<T, false, true, false, false> {
STATIC_ASSERT((std::is_integral<T>::value && std::is_unsigned<T>::value),
"wrong type"); // Sanity check.
-
- /// Parses a given row of CSV data into a given container.
- ///
- /// \p Data is cleared and then filled with values parsed from \p LineStream.
- /// Entries in the line are to be separated by commas, the character `,`. A
- /// trailing comma results in an empty entry at the end of the line. No empty
- /// entry should be present otherwise.
- ///
- /// \note Parsed values are silently converted to type \p T.
- ///
- /// \param [in,out] LineStream the line to parse
- /// \param [in,out] Data the container to store the parsed values
- static void parse(std::stringstream &LineStream, std::vector<T> &Data) {
- std::string Cell;
- Data.clear();
- while (std::getline(LineStream, Cell, ',')) {
- Data.push_back(static_cast<T>(std::stoull(Cell)));
- }
- // This checks for a trailing comma with no data after it.
- if (!LineStream && Cell.empty()) {
- // If there was a trailing comma then add an empty element.
- Data.push_back(0);
- }
+ static T parse(const std::string &Cell) noexcept {
+ return static_cast<T>(std::stoull(Cell));
}
};
-/// Specialization for floating-point types.
-///
-/// \tparam T type of values to parse from the line
-///
-/// \pre \p T is a floating-point type:\code
-/// std::is_floating_point<T>::value
-/// \endcode
-template <typename T> struct CSVRowParser<T, false, false, true, false> {
+template <typename T>
+struct ValueParser<T, false, false, true, false> {
STATIC_ASSERT((std::is_floating_point<T>::value),
"wrong type"); // Sanity check.
-
- /// Parses a given row of CSV data into a given container.
- ///
- /// \p Data is cleared and then filled with values parsed from \p LineStream.
- /// Entries in the line are to be separated by commas, the character `,`. A
- /// trailing comma results in an empty entry at the end of the line. No empty
- /// entry should be present otherwise.
- ///
- /// \note Parsed values are silently converted to type \p T.
- ///
- /// \param [in,out] LineStream the line to parse
- /// \param [in,out] Data the container to store the parsed values
- static void parse(std::stringstream &LineStream, std::vector<T> &Data) {
- std::string Cell;
- Data.clear();
- while (std::getline(LineStream, Cell, ',')) {
- Data.push_back(static_cast<T>(std::stold(Cell)));
- }
- // This checks for a trailing comma with no data after it.
- if (!LineStream && Cell.empty()) {
- // If there was a trailing comma then add an empty element.
- Data.push_back(0);
- }
+ static T parse(const std::string &Cell) noexcept {
+ return static_cast<T>(std::stold(Cell));
}
};
-/// Specialization for \c std::string.
-///
-/// \tparam T type of values to parse from the line
-///
-/// \pre \p T is \c std::string:\code
-/// std::is_same<T, std::string>::value
-/// \endcode
-template <typename T> struct CSVRowParser<T, false, false, false, true> {
+template <typename T>
+struct ValueParser<T, false, false, false, true> {
STATIC_ASSERT((std::is_same<T, std::string>::value),
"wrong type"); // Sanity check.
-
- /// Parses a given row of CSV data into a given container.
- ///
- /// \p Data is cleared and then filled with values parsed from \p LineStream.
- /// Entries in the line are to be separated by commas, the character `,`. A
- /// trailing comma results in an empty entry at the end of the line. No empty
- /// entry should be present otherwise.
- ///
- /// \param [in,out] LineStream the line to parse
- /// \param [in,out] Data the container to store the parsed values
- static void parse(std::stringstream &LineStream, std::vector<T> &Data) {
- std::string Cell;
- Data.clear();
- while (std::getline(LineStream, Cell, ',')) {
- Data.push_back(Cell);
- }
- // This checks for a trailing comma with no data after it.
- if (!LineStream && Cell.empty()) {
- // If there was a trailing comma then add an empty element.
- Data.push_back("");
- }
- }
+ static T parse(const std::string &Cell) noexcept { return Cell; }
};
/// Parses and stores entries from a row of CSV data.
///
-/// \tparam T type of values to parse and store, i.e. entries in the row
+/// \tparam Ts types of values to parse and store, i.e. entries in the row
///
/// \note The implementation relies on \c rosa::csv::CSVRowParser, which is
-/// implemented only for `arithmetic` types -- signed and unsigned integral and
-/// floating-point types -- and for \c std::string. Those are the valid values
-/// for \p T.
-template <typename T>
-class CSVRow {
-public:
- /// Gives a constant reference for an entry at a given position of the row.
+/// implemented only for `arithmetic` types -- signed and unsigned integral
+/// and floating-point types -- and for \c std::string. Those are the valid
+/// values for \p Ts.
+template <typename... Ts> class CSVRow {
+private:
+ /// Parses a given row of CSV data into \c CSVRow::Data.
+ ///
+ /// \ 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 <size_t... S0>
+ void parseRow(std::stringstream &LineStream, char Delimeter, Seq<S0...>) {
+ STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0),
+ "Not matching template arguments.");
+ std::string Cell;
+ // Get fields and parse the values into the proper element of the tuple
+ // one by one in a fold expression.
+ ((std::getline(LineStream, Cell, Delimeter),
+ std::get<S0>(Data) = ValueParser<Ts>::parse(Cell)),
+ ...);
+
+ }
- /// 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<T>::parse(LineStream, Data);
+
+ if(Line.size() > 0){
+ std::stringstream LineStream(Line);
+ parseRow(LineStream, Delimeter, seq_t<sizeof...(Ts)>());
+
+ RowNumber = RowNumber + 1;
+ }
+
}
+ /// Read header row and stores it as \p std::string.
+ ///
+ /// The function reads the first line of the csv file and stores the entries
+ /// in a vector.
+ ///
+ /// \param [in,out] Str input stream of a CSV file
+ void readHeader(std::istream &Str) noexcept {
+ std::string Line;
+ std::getline(Str, Line);
+ std::stringstream LineStream(Line);
+ std::string Value;
+
+ while( getline(LineStream, Value, Delimeter) ){
+ Header.push_back(Value);
+ }
+
+ IsHeaderRead = true;
+ }
+
+ /// The number of rows to skip once.
+ ///
+ /// This function returns the number of data rows to skip
+ /// at the beginning of the file.
+ ///
+ /// \return The number of rows to skip at the beginning of a csv file.
+ inline size_t SkipNumRows() const noexcept {
+ return this->SkipRows;
+ }
+
+ /// The current row number within the csv file.
+ ///
+ /// This function returns the current row number. The header
+ /// row is not counted as a row.
+ ///
+ /// \returns the current row number within the csv file.
+ inline size_t CurRow() const noexcept {
+ return this->RowNumber;
+ }
+
+ /// Indiciates if the header was already read.
+ ///
+ /// This function returns true, if the header of a csv file which contains
+ /// a header file is already read.
+ /// The user has to pass in the attribute HeaderInfo the information if the
+ /// file has in the first row the header row or not.
+ ///
+ /// \return if the header of a file is already read.
+ inline bool IsHeaderReadDone() const noexcept{
+ return this->IsHeaderRead;
+ }
+
+
+ /// Indicates if the file contains a header row in the first row.
+ ///
+ /// This function returns if the file contains a header row.
+ /// The information if the file contains a header row or not, has to be passed by the user.
+ /// The standard value is HeaderInformation::HasHeader
+ ///
+ /// \return if the csv file contains a header row in the first line of the file.
+ inline HeaderInformation HasFileHeader() const noexcept {
+ return this->HeaderInfo;
+ }
+
+ /// Set the number of rows to skip.
+ ///
+ /// This function sets the number of rows to skip at the beginning of
+ /// the reading of the file.
+ ///
+ /// \param 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<Ts...> &tuple(void) const noexcept { return Data; }
+
private:
- std::vector<T> Data; ///< Stores parsed entries
+ std::tuple<Ts...> Data; ///< Stores parsed entries
+ size_t SkipRows; ///< The number of rows to skip at the very beginning of the file.
+ ///< This number only applies on the number of data rows.
+ ///< If your file contains a header row and data rows, the skiping
+ ///< of the header row is not taken into account.
+ HeaderInformation HeaderInfo; ///< If the file contains a header row or not.
+ char Delimeter; ///< The seperator between the data entries.
+ size_t RowNumber; ///< Current row number, counts all row numbers including the header row.
+ bool IsHeaderRead; ///< Was the header read or not.
+ std::vector<std::string> Header; ///< The content of the header row.
+
};
/// Reads a row of CSV data into \c rosa::csv::CSVRow.
///
/// The next line is read from \p Str by calling
-/// \c rosa::csv::CSVRow::readNextRow on \p Data.
+/// \c rosa::csv::CSVRow::readNextRow on \p Data until all lines are
+/// skipped.
///
-/// \note A CSV file should contain no empty lines.
+/// If the function is called for the first time and the file contains
+/// a header than is the header and the first data row read in after the
+/// number of rows that the user wants to skip.
+///
+/// \tparam Ts type of values to read from the row
+///
+/// \note The CSV file should contain a line with fields matching \p Ts...
///
/// \param [in,out] Str input stream of a CSV file
/// \param [in,out] Data object to read the next line into
///
/// \return \p Str after reading one line from it
-template <typename T>
-std::istream &operator>>(std::istream &Str, CSVRow<T> &Data) {
+template <typename... Ts>
+std::istream &operator>>(std::istream &Str, CSVRow<Ts...> &Data) {
+
+ if( Data.HasFileHeader() == HeaderInformation::HasHeader && !Data.IsHeaderReadDone() ) {
+ Data.readHeader(Str);
+ }
+
+ while(Data.CurRow() < (Data.SkipNumRows())){
+ Data.readNextRow(Str);
+ }
+
+ //read the lines after you skipped the number of rows you want to skip
Data.readNextRow(Str);
+
return Str;
}
} // End namespace
-/// Provides `InputIterator` features for iterating over a CSV file in a
-/// flat way.
+/// Provides `InputIterator` features for iterating over a CSV file.
///
-/// The iterator hides rows of the CSV file, and iterates over the entries
-/// row-by-row.
+/// The iterator parses rows into `std::tuple` values and iterates over the
+/// file row by row.
///
-/// \note A CSV file should contain no empty lines.
+/// \tparam Ts types of values stored in one row of the CSV file
///
-/// \tparam T type of values to iterate over, i.e. entries in the CSV file.
+/// \note The iterator expects each row to consists of fields matching \p Ts.
///
/// \note The implementation relies on \c rosa::csv::CSVRow, which in turn
/// relies on \c rosa::csv::CSVRowParser, which is implemented only for
/// `arithmetic` types -- signed and unsigned integral types and floating-point
-/// types -- and for \c std::string. Those are the valid values for \p T.
-template <typename T>
-class CSVFlatIterator {
+/// types -- and for \c std::string. Those are the valid values for \p Ts
+template <typename... Ts> class CSVIterator {
public:
- /// \defgroup CSVFlatIteratorTypedefs Typedefs of rosa::csv::CSVFlatIterator
+ /// \defgroup CSVIteratorTypedefs Typedefs of rosa::csv::CSVIterator
///
/// Standard `typedef`s for iterators.
///
///@{
typedef std::input_iterator_tag
- iterator_category; ///< Category of the iterator.
- typedef T value_type; ///< Type of values iterated over.
- typedef std::size_t difference_type; ///< Type to identify distance.
- typedef T *pointer; ///< Pointer to the type iterated over.
- typedef T &reference; ///< Reference to the type iterated over.
+ iterator_category; ///< Category of the iterator.
+ typedef std::tuple<Ts...> value_type; ///< Type of values iterated over.
+ typedef std::size_t difference_type; ///< Type to identify distance.
+ typedef std::tuple<Ts...> *pointer; ///< Pointer to the type iterated over.
+ typedef std::tuple<Ts...>
+ &reference; ///< Reference to the type iterated over.
///@}
/// Creates a new instance.
///
/// \param [in,out] S input stream to iterate over
- CSVFlatIterator(std::istream &S)
- : Str(S.good() ? &S : nullptr), Pos((size_t)(-1)) {
- // \c rosa::csv::CSVFlatIterator::Pos is initialized to `-1` so the first
- // incrementation here will set it properly.
+ /// \param SkipRows the number of rows you want to skip only once at the beginning of the file.
+ /// If you have an header in the file, it is supposed to be the first row, and it will be always read out.
+ /// But after this header the next number of Rows will be skipped.
+ /// \param HeaderInfo is used to know wheter the file contains an header row or not.
+ /// The header has to be in the first row.
+ /// \param Delimeter is the separator between the differnt values of the csv file.
+ CSVIterator(std::istream &S, const size_t SkipRows = 0,
+ const HeaderInformation HeaderInfo = HeaderInformation::HasHeader,
+ const char Delimeter = ',') : Str(S.good() ? &S : nullptr),
+ SkipRows(SkipRows), HeaderInfo(HeaderInfo), Delimeter(Delimeter), Row(){
+
+ Row.SetSkipRows(SkipRows);
+ Row.SetHeaderInfo(HeaderInfo);
+ Row.SetDelimeter(Delimeter);
+
+ // \c rosa::csv::CSVIterator::Row is initialized empty so the first
+ // incrementation here will read the first row.
++(*this);
}
/// Creates an empty new instance.
- CSVFlatIterator(void) noexcept : Str(nullptr) {}
+ CSVIterator(void) noexcept
+ : Str(nullptr), 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<Ts...> &operator*(void)const noexcept { return Row.tuple(); }
/// Returns a constant pointer to the current entry.
///
/// \note Should not dereference the iterator when it is empty.
///
/// \return constant pointer to the current entry.
- const T *operator->(void)const noexcept { return &Row[Pos]; }
+ const std::tuple<Ts...> *operator->(void)const noexcept {
+ return &Row.tuple();
+ }
/// Tells if \p this object is equal to another one.
///
/// Two \c rosa::csv::CSVReader instances are equal if and only if they are
/// the same or both are empty.
///
/// \param RHS other object to compare to
///
/// \return whether \p this object is equal with \p RHS
- bool operator==(const CSVFlatIterator &RHS) const noexcept {
+ bool operator==(const CSVIterator &RHS) const noexcept {
return ((this == &RHS) || ((this->Str == nullptr) && (RHS.Str == nullptr)));
}
/// Tells if \p this object is not equal to another one.
///
/// \see rosa::csv::CSVReader::operator==
///
/// \param RHS other object to compare to
///
/// \return whether \p this object is not equal with \p RHS.
- bool operator!=(const CSVFlatIterator &RHS) const noexcept {
+ bool operator!=(const CSVIterator &RHS) const noexcept {
return !((*this) == RHS);
}
+ /// Set the delimeter used in the csv file.
+ /// \param 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<T> Row; ///< Content of the current row iterating over.
- size_t Pos; ///< Current position within the current row.
+ std::istream *Str; ///< Input stream of a CSV file to iterate over.
+ size_t SkipRows; ///< Number of Rows to skip only once at the beginning of the file.
+ HeaderInformation HeaderInfo; ///< does the csv file contain a header or not, if this information is
+ ///< not given correclty, the reading of the header would result in
+ ///< in an error.
+ char Delimeter; ///< Delimeter between the entries in the csv file.
+ CSVRow<Ts...> Row; ///< Content of the current row
+
};
} // End namespace csv
} // End namespace rosa
#endif // ROSA_SUPPORT_CSV_CSVREADER_HPP
diff --git a/include/rosa/support/csv/CSVWriter.hpp b/include/rosa/support/csv/CSVWriter.hpp
index b67de6a..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 <iostream>
#include <ostream>
+#include <tuple>
+#include <vector>
+#include <array>
+
+#include "rosa/support/log.h"
+
namespace rosa {
namespace csv {
/// Provides facilities to write values into a CSV file.
///
/// The writer emits a comma, the character `,`, between each written values.
/// The resulted stream is a flat CSV file as it consists of onlyone row, no new
/// line is emitted.
///
/// \tparam T type of values to write
template <typename T>
class CSVWriter {
public:
/// Creates a new instance.
///
/// \param [in,out] S output stream to write to
///
/// \note The writer operates on non-binary outputs as long as \p S is in
/// good state.
CSVWriter(std::ostream &S)
: Str(S.good() && !(S.flags() & std::ios::binary) ? &S : nullptr),
IsFirst(true) {}
/// Tells if the last operation was successful.
///
/// \return if the last operation was successful
bool good(void) const noexcept {
return Str != nullptr;
}
/// Writes an entry to the output stream.
///
/// The implementation does anything only if the last operation was
/// successful. If so, \p V is written to \c rosa::csv::CSVWriter::Str.
/// The emitted value is preceded with a comma if the actual call is not the
/// first one for \p this object. Success of the operation is checked at the
/// end.
///
/// \param V value to write
void write(const T &V) {
if (Str) {
if (!IsFirst) {
*Str << ',';
} else {
IsFirst = false;
}
*Str << V;
if (!Str->good()) {
Str = nullptr;
}
}
}
private:
std::ostream *Str; ///< Output stream to write to.
bool IsFirst; ///< Denotes if the next write would be the first one.
};
+/// Writes a tuple of values into a CSV file
+///
+/// \tparam Ts types of values to write
+template <typename... Ts> class CSVTupleWriter {
+
+public:
+
+// typedef <Ts...> value_type ; ///< Type of values written.
+ typedef std::tuple<Ts...> value_type;
+
+ /// Creates a new instance.
+ ///
+ /// \param [in,out] S output stream to write to
+ ///
+ /// \note The writer operates on non-binary outputs as long as \p S is in
+ /// good state.
+ CSVTupleWriter(std::ostream &S)
+ : Str(S.good() && !(S.flags() & std::ios::binary) ? &S : nullptr),
+ IsHeaderWritten(false), IsDataWritten(false) {}
+
+ /// Tells if the last operation was successful.
+ ///
+ /// \return if the last operation was successful
+ bool good(void) const noexcept {
+ return Str != nullptr;
+ }
+
+
+ /// Write the values of a tuple to a CSV file with \c rosa::csv::CSVTupleWriter.
+ ///
+ /// \see rosa::csv::CSVTupleWriter
+ ///
+ ///
+ /// \param [in,out] values tuple, which values are written in a recusive fashion into a stream.
+ template<size_t i = 0>
+ void write(const std::tuple<Ts...> &values) {
+ constexpr size_t size = sizeof...(Ts);
+
+ LOG_TRACE_STREAM << "Writing tuple values into file \n";
+ LOG_TRACE_STREAM << " Tuple has " << std::to_string(size) << " elements. \n";
+
+ LOG_TRACE_STREAM << " Value is " << std::get<i>(values);
+
+ if(Str){
+ /// Write the current element of the tuple into the stream and add a separtor after it,
+ /// and call the function for the next element in the tuple.
+ if constexpr(i+1 != sizeof...(Ts)){
+ *Str << std::get<i>(values) << ", ";
+ write<i+1>(values);
+ /// If the last element is written into the stream than begin a new line.
+ }else if constexpr(i + 1 == sizeof...(Ts)){
+ *Str << std::get<i>(values) << '\n';
+ /// every time the last data value of a line is written, the flag indicates that data was already written into the file.
+ IsDataWritten = true;
+ }
+ }
+ }
+
+ /// Write the header values to a CSV file with \c rosa::csv::CSVTupleWriter.
+ ///
+ /// \note The function has no effect if anything has already been written
+ /// to the output stream either by \c
+ /// rosa::csv::CSVTupleWriter::writeHeader() or \c
+ /// rosa::csv::CSVTupleWriter::write().
+ ///
+ /// \see rosa::csv::CSVTupleWriter
+ ///
+ /// \param header the content of the header line.
+ void writeHeader(const std::array<std::string, sizeof...(Ts)> &header){
+ size_t index = 0;
+ /// write into the stream only, if it is not a nullptr, and if no data and no header was already written into it.
+ if(Str && IsDataWritten == false && IsHeaderWritten == false){
+ index = 0;
+ for (auto i = header.begin(); i != header.end(); ++i){
+ index = index + 1;
+ /// write into the stream every entry with a delimeter, in this case ", " until
+ /// the last entry
+ if(index != header.size()){
+ *Str << *i << ", ";
+ /// write the last entry into the stream, without any delimeter
+ }else {
+ *Str << *i;
+ }
+ }
+ /// finish the header line and start a new line.
+ *Str << '\n';
+ /// now it is not possible to write additional header lines.
+ IsHeaderWritten = true;
+ }
+ }
+
+private:
+ std::ostream *Str; ///< Output stream to write to.
+ bool IsHeaderWritten; ///< If an header line was already written into the stream. If set than no additional header could be written.
+ bool IsDataWritten; ///< If one line of data has already been written into the stream, than no headerline could be added.
+};
+
+/// Writes all values of a tuple to a CSV file with \c rosa::csv::CSVTupleWriter.
+///
+/// \see rosa::csv::CSVTupleWriter
+///
+/// \tparam Ts types of values to write
+///
+/// \param [in,out] W object to write with
+/// \param V values to write
+///
+/// \return \p W after writing \p V with it
+template <typename... Ts>
+CSVTupleWriter<Ts...> &operator<<(CSVTupleWriter<Ts...> &W, const std::tuple<Ts...> &V)
+{
+ W.write(V);
+ return W;
+}
+
/// Writes a value to a CSV file with \c rosa::csv::CSVWriter.
///
/// \see rosa::csv::CSVWriter
///
/// \tparam T type of value to write
///
/// \param [in,out] W object to write with
/// \param V value to write
///
/// \return \p W after writing \p V with it
template <typename T>
CSVWriter<T> &operator<<(CSVWriter<T> &W, const T& V) {
W.write(V);
return W;
}
} // End namespace csv
} // End namespace rosa
#endif // ROSA_SUPPORT_CSV_CSVWRITER_HPP
diff --git a/include/rosa/support/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 <array>
#include <ostream>
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 <typename T, size_t Size>
std::ostream &operator<<(std::ostream &OS, const std::array<T, Size> &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<bool>(stmt) == false) { \
rosa::dbgs() << rosa::terminal::Color::Default << __FILENAME__ << ":" \
<< __LINE__ << ": requirement failed: '" << #stmt << "'" \
<< std::endl; \
::abort(); \
} \
ROSA_VOID_STMT
#else // defined(ROSA_LINUX)
#include <execinfo.h>
#define ASSERT(stmt) \
if (static_cast<bool>(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 <memory>
+#include <tuple>
+#include <type_traits>
+#include <queue>
+
+namespace rosa {
+namespace iterator {
+
+/// Anonymous namespace providing implementation for splitting tuple iterators;
+/// consider it private.
+namespace {
+
+/// Iterator of values provided by a buffer based on an index
+///
+/// \tparam TB buffer providing values to iterate
+/// \tparam I index of the values to iterator from \p TB
+///
+/// \note \p TB is expected to implemnet an interface matching that of \c
+/// TupleIteratorBuffer.
+template <typename TB, size_t I>
+class SplittedElementIterator {
+public:
+ /// Type alias for the values the iterator iterates.
+ using T = typename TB::template element_type<I>;
+
+ /// \defgroup SplittedElementIteratorTypedefs Typedefs of
+ /// \c SplittedElementIterator
+ ///
+ /// Standard `typedef`s for iterators.
+ ///
+ ///@{
+ typedef std::input_iterator_tag
+ iterator_category; ///< Category of the iterator.
+ typedef T value_type; ///< Type of values iterated over.
+ typedef std::size_t difference_type; ///< Type to identify distance.
+ typedef T *pointer; ///< Pointer to the type iterated over.
+ typedef T &reference; ///< Reference to the type iterated ver.
+ ///@}
+
+ /// Creates a new instance.
+ ///
+ /// \param Buffer buffer providing values for \p this object
+ ///
+ /// \note \p Buffer is captured as a \c std::shared_ptr because it is shared
+ /// among iterators for its element positions.
+ SplittedElementIterator(std::shared_ptr<TB> Buffer)
+ : Buffer(Buffer), Value() {
+ // \c Value is initialized empty so the first incrementation here will read
+ // the first value.
+ ++(*this);
+ }
+
+ /// Creates an empty new instance.
+ SplittedElementIterator(void) noexcept : Buffer(), Value() {}
+
+ /// Pre-increment operator.
+ ///
+ /// The implementation reads the next value from \c Buffer. If no more values
+ /// can be provided by \c Buffer, \p this object becomes empty and the
+ /// operator has no further effect.
+ ///
+ /// \return \p this object after incrementing it.
+ SplittedElementIterator &operator++() {
+ if (Buffer->template hasMore<I>()) {
+ Value = Buffer->template next<I>();
+ } else {
+ // Reached end of range, remove reference of \c Buffer.
+ Buffer.reset();
+ }
+ return *this;
+ }
+
+ /// Post-increment operator.
+ ///
+ /// The implementation uses the pre-increment operator and returns a copy of
+ /// the original state of \p this object.
+ ///
+ /// \return \p this object before incrementing it.
+ SplittedElementIterator operator++(int) {
+ SplittedElementIterator<TB, I> Tmp(*this);
+ ++(*this);
+ return Tmp;
+ }
+
+ /// Returns a constant reference to the current value.
+ ///
+ /// \note Should not dereference the iterator when it is empty.
+ ///
+ /// \return constant reference to the current value.
+ const T &operator*(void)const noexcept { return Value; }
+
+ /// Returns a constant pointer to the current value.
+ ///
+ /// \note Should not dereference the iterator when it is empty.
+ ///
+ /// \return constant pointer to the current value.
+ const T *operator->(void)const noexcept { return &Value; }
+
+ /// Tells if \p this object is equal to another one.
+ ///
+ /// Two \c SplittedElementIterator instances are equal if and only if they are
+ /// the same or both are empty.
+ ///
+ /// \param RHS other object to compare to
+ ///
+ /// \return whether \p this object is equal with \p RHS
+ bool operator==(const SplittedElementIterator &RHS) const noexcept {
+ return ((this == &RHS) || (!Buffer && !RHS.Buffer));
+ }
+
+ /// Tells if \p this object is not equal to another one.
+ ///
+ /// \see SplittedElementIterator::operator==
+ ///
+ /// \param RHS other object to compare to
+ ///
+ /// \return whether \p this object is not equal with \p RHS.
+ bool operator!=(const SplittedElementIterator &RHS) const noexcept {
+ return !((*this) == RHS);
+ }
+
+private:
+ std::shared_ptr<TB> Buffer; ///< Buffer providing values for \p this object
+ T Value; ///< The current value of the iterator
+};
+
+///\defgroup TupleBufferContainer Type converter turning a \c std::tuple of
+///types into a \c std::tuple of container of those types.
+///
+/// The new type is used for buffering elements of tuples separately.
+///
+///@{
+
+/// Template declaration.
+///
+/// \tparam T type to convert
+///
+/// \note The template is defined only when \p T is a \c std::tuple.
+///
+/// Usage for a type \c Tuple as:\code
+/// typename TupleBufferContainer<Tuple>::Type
+/// \endcode
+template <typename T> struct TupleBufferContainer;
+
+/// Template definition for \c std::tuple.
+template <typename... Ts> struct TupleBufferContainer<std::tuple<Ts...>> {
+ /// The converted type.
+ using Type = std::tuple<std::queue<Ts>...>;
+};
+
+///@}
+
+/// Convenience template type alias for easy use of \c TupleBufferContainer.
+///
+/// Converts a \c std::tuple of types into a \c std::tuple of container of those
+/// types.
+///
+/// \tparam Tuple type to convert
+template <typename Tuple>
+using tuple_buffer_container_t = typename TupleBufferContainer<Tuple>::Type;
+
+/// Buffer for element values for iterator of \c std::tuple.
+///
+/// The class can be instantiated with a range of iterator of \c std::tuple and
+/// provides an interface for accessing elements of the iterated tuples one by
+/// one.
+///
+/// \note The class is utilized by \c SplittedElementIterator.
+///
+/// \tparam TupleIterator the iterator type that provides values of \c
+/// std::tuple
+///
+/// \todo Consider thread safety of the class.
+template <typename TupleIterator>
+class TupleIteratorBuffer {
+public:
+ /// Type alias for the value type of \p TupleIterator.
+ /// \note The type is expected to be \c std::tuple.
+ using iterator_value_type = typename TupleIterator::value_type;
+
+ /// The number of elements of \c iterator_value_type.
+ static constexpr size_t iterator_value_size =
+ std::tuple_size_v<iterator_value_type>;
+
+ /// Template type alias to get element types of \c iterator_value_type by
+ /// index.
+ /// \tparam I the index of the element
+ template <size_t I>
+ using element_type =
+ typename std::tuple_element<I, iterator_value_type>::type;
+
+ /// Type alias for index sequence for accessing elements of \c
+ /// iterator_value_type.
+ using element_idx_seq_t = seq_t<iterator_value_size>;
+
+ /// Creates a new instance.
+ ///
+ /// \param Begin the beginning of the iterator range to buffer
+ /// \param End the end of the iterator range to buffer
+ TupleIteratorBuffer(TupleIterator &&Begin, const TupleIterator &End) noexcept
+ : Iterator(Begin), End(End), Buffer() {}
+
+ /// Tells whether there is any more value in the handled range for index \p I.
+ ///
+ /// \tparam I the index of the element to check
+ ///
+ /// \return whether there is more value for index \p I
+ template <size_t I> bool hasMore(void) const noexcept {
+ const auto &ElementBuffer = std::get<I>(Buffer);
+ // There is more value if some has already been buffered or the end of the
+ // handled range is not reached yet.
+ return !ElementBuffer.empty() || Iterator != End;
+ }
+
+ /// Gives the next value from the handled range for index \p I.
+ ///
+ /// \note The function should not be called if there is no more value for
+ /// index \p I (i.e., \c hasMore<I>() returns \c false). If it is called in
+ /// that situation, it returns the default value of \c element_type<I>.
+ ///
+ /// \tparam I the index of the element to fetch next value for
+ ///
+ /// \return the next value for index \p I
+ template <size_t I> element_type<I> next(void) noexcept {
+ auto &ElementBuffer = std::get<I>(Buffer);
+ if (ElementBuffer.empty()) {
+ // This position needs a new value from Iterator if there is more.
+ if (Iterator != End) {
+ // Push next value for all elements.
+ push(*Iterator++, element_idx_seq_t());
+ } else {
+ // No more values; called when hasMore<I>() is false.
+ // Return default value.
+ return element_type<I>();
+ }
+ }
+ // Buffer is not empty here, return the next value.
+ ASSERT(!ElementBuffer.empty() && "Unexpected condition");
+ // Get the next value and also pop it from the buffer.
+ const element_type<I> Elem = ElementBuffer.front();
+ ElementBuffer.pop();
+ return Elem;
+ }
+
+private:
+ /// Buffers next tuple value elementwise into element containers.
+ ///
+ /// \tparam S0 indices for accessing elements of \c std::tuple
+ ///
+ /// \param Tuple the values to buffer
+ ///
+ /// \note The second parameter provides indices as template parameter \p S0
+ /// and its actual value is ignored.
+ template <size_t... S0>
+ void push(const iterator_value_type &Tuple, Seq<S0...>) noexcept {
+ (std::get<S0>(Buffer).push(std::get<S0>(Tuple)), ...);
+ }
+
+ TupleIterator Iterator; ///< Current input iterator
+ const TupleIterator End; ///< End of iterator range to handle
+ tuple_buffer_container_t<iterator_value_type>
+ Buffer; ///< Container for elementwise buffering
+};
+
+/// Template type alias for iterator of an element based on buffered iterator of
+/// \c std::tuple.
+///
+/// The alias utilizes \c SplittedElementIterator for iterating over the values
+/// provided by \p TB.
+///
+/// \tparam TB buffer providing values to iterate
+/// \tparam I index of the values to iterator from \p TB
+template <typename TB, size_t I>
+using element_iterator_t = SplittedElementIterator<TB, I>;
+
+/// Template type alias for iterator-range of an element based on buffered
+/// iterator of \c std::tuple.
+///
+/// \tparam TB buffer providing values to iterate
+/// \tparam I index of the values to iterator from \p TB
+template <typename TB, size_t I>
+using element_iterator_range_t =
+ std::pair<element_iterator_t<TB, I>, element_iterator_t<TB, I>>;
+
+///\defgroup ElementIteratorRanges Type converter turning a buffer of \c
+/// std::tuple into a \c std::tuple of corresponding \c
+/// element_iterator_range_t.
+///
+///@{
+
+/// Template declaration.
+///
+/// \tparam TB buffer providing values
+/// \tparam S type providing indices for accessing elements of \c std::tuple
+///
+/// \note \p TB is expected to implement an interface matching that of \c
+/// TupleIteratorBuffer.
+///
+/// \note The template is defined only when \p S is a \c rosa::Seq.
+///
+/// Usage for a proper buffer type \c TB:\code
+/// typename ElementIteratorRanges<TB, typename TB::element_idx_seq_t>::Type
+/// \endcode
+template <typename TB, typename S> struct ElementIteratorRanges;
+
+/// Template definition.
+template <typename TB, size_t... S0>
+struct ElementIteratorRanges<TB, Seq<S0...>> {
+ /// The converted type.
+ using Type = std::tuple<element_iterator_range_t<TB, S0>...>;
+};
+
+///@}
+
+/// Convenience template type alias for easy use of \c ElementIteratorRanges.
+///
+/// Converts a buffer of \c std::tuple into a \c std::tuple of corresponding \c
+/// element_iterator_range_t.
+///
+/// \tparam TB buffer providing values
+///
+/// \note \p TB is expected to implement an interface matching that of \c
+/// TupleIteratorBuffer.
+template <typename TB>
+using element_iterator_ranges_t =
+ typename ElementIteratorRanges<TB, typename TB::element_idx_seq_t>::Type;
+
+/// Template type alias for turning an iterator of \c std::tuple into
+/// corresponding \c element_iterator_ranges_t.
+///
+/// The alias utilizes \c TupleIteratorBuffer for buffering the values provided
+/// by \p TupleIterator. The elementwise iterators are \c
+/// SplittedElementIterator.
+///
+/// \tparam TupleIterator iterator of \c std::tuple
+template <typename TupleIterator>
+using splitted_tuple_iterator_ranges_t =
+ element_iterator_ranges_t<TupleIteratorBuffer<TupleIterator>>;
+
+/// Creates elementwise iterator ranges for an iterator range of \c std::tuple.
+///
+/// \note Implementation for \c rosa::iterator::splitTupleIterator.
+///
+/// \tparam TupleIterator iterator of \c std::tuple
+/// \tparam S0 indices for accessing elements of \c std::tuple
+///
+/// \param Begin the beginning of the iterator range to handle
+/// \param End the end of the iterator range to handle
+///
+/// \note The last parameter provides indices as template parameter \p S0 and
+/// its actual value is ignored.
+///
+/// \return \c std::tuple of elementwise iterator ranges corresponding to \p
+/// Begin and \p End
+template <typename TupleIterator, size_t... S0>
+splitted_tuple_iterator_ranges_t<TupleIterator>
+splitTupleIteratorImpl(TupleIterator &&Begin, const TupleIterator &End,
+ Seq<S0...>) noexcept {
+ using TB = TupleIteratorBuffer<TupleIterator>;
+ // Create a buffer in shared pointer. The buffer will be destructed once
+ // all corresponding element iterators (created below) have reached the end of
+ // the handled range or have been destructed.
+ auto Buffer = std::make_shared<TB>(std::move(Begin), End);
+ return {std::make_pair(element_iterator_t<TB, S0>(Buffer),
+ element_iterator_t<TB, S0>())...};
+}
+
+} // End namespace
+
+/// Gives the beginning of a range created by \c splitTupleIterator().
+///
+/// \see rosa::iterator::splitTupleIterator()
+///
+/// \note The signature of the template function uses implementation details.
+///
+/// \tparam TB buffer providing values to iterate
+/// \tparam I index of the values to iterator from \p TB
+///
+/// \param Range range to get the beginning of
+///
+/// \return beginning of \p Range
+template <typename TB, size_t I>
+element_iterator_t<TB, I> &begin(element_iterator_range_t<TB, I> &Range) {
+ return Range.first;
+}
+
+/// Gives the end of a range created by \c splitTupleIterator().
+///
+/// \see rosa::iterator::splitTupleIterator()
+///
+/// \note The signature of the template function uses implementation details.
+///
+/// \tparam TB buffer providing values to iterate
+/// \tparam I index of the values to iterator from \p TB
+///
+/// \param Range range to get the end of
+///
+/// \return end of \p Range
+template <typename TB, size_t I>
+element_iterator_t<TB, I> &end(element_iterator_range_t<TB, I> &Range) {
+ return Range.second;
+}
+
+/// Creates elementwise iterator ranges for an iterator range of \c std::tuple.
+///
+/// \note The implementation utilizes \c splitTupleIteratorImpl.
+///
+/// Obtain element iterator ranges for an iterator range \c Begin and \c End of
+/// iterator type \c TupleIterator of \c std::tuple<T1, T2, T3> as \code
+/// auto [T1Range, T2Range, T3Range] = splitTupleIterator(std::move(Begin), End);
+/// auto [T1Begin, T1End] = T1Range;
+/// auto [T2Begin, T2End] = T2Range;
+/// auto [T3Begin, T3End] = T3Range;
+/// \endcode
+/// One range can also be dissected by dedicated functions like \code
+/// auto T1Begin = begin(T1Range);
+/// auto T1End = end(T1Range);
+/// \endcode
+///
+/// The function returns a tuple of ranges (i.e., \c std::pair of iterators),
+/// which can be assigned to variables using structured binding. The ranges can
+/// be dissected into begin and end iterators by separate structured bindings.
+///
+/// The function moves its first argument (i.e., \p Begin cannot be used
+/// directly after splitting it). If the first argument is a variable, it needs
+/// to be moved explicitly by \c std::move() as in the example.
+///
+/// \tparam TupleIterator iterator of \c std::tuple
+///
+/// \param Begin the beginning of the iterator range to handle
+/// \param End the end of the iterator range to handle
+///
+/// \return \c std::tuple of elementwise iterator ranges corresponding to \p
+/// Begin and \p End
+template <typename TupleIterator>
+splitted_tuple_iterator_ranges_t<TupleIterator>
+splitTupleIterator(TupleIterator &&Begin, const TupleIterator &End) noexcept {
+ return splitTupleIteratorImpl(
+ std::move(Begin), End,
+ seq_t<std::tuple_size_v<typename TupleIterator::value_type>>());
+}
+
+} // End namespace iterator
+} // End namespace rosa
+
+#endif // ROSA_SUPPORT_ITERATOR_SPLIT_TUPLE_ITERATOR_HPP
diff --git a/include/rosa/support/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 <ostream>
#include <string>
/* ****************************************************************************
* 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 <cmath>
#include <cstdlib>
#include <limits>
#include <type_traits>
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 <typename T,
typename = std::enable_if_t<std::is_floating_point<T>::value>>
T nextRepresentableFloatingPoint(const T V) {
return std::nextafter(V, std::numeric_limits<T>::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 <cstddef>
namespace rosa {
/// \defgroup Seq Implementation of rosa::Seq
///
/// Facility to statically generate sequences of numbers.
///
///@{
/// Template with an empty struct to store a sequence of numbers in compile time
/// as template arguments.
///
/// Generate a sequence of numbers from `0` up to (including) `(N - 1)` like
/// \code
/// typename GenSeq<N>::Type
/// \endcode
template <size_t...> struct Seq {};
/// Sequence generator, the general case when counting down by extending the
/// sequence.
template <size_t N, size_t... S> struct GenSeq : GenSeq<N - 1, N - 1, S...> {};
/// Sequence generator, the terminal case when storing the generated sequence
/// into \c Seq.
template <size_t... S> struct GenSeq<0, S...> { using Type = Seq<S...>; };
///@}
/// Convenience template alias for using \c rosa::GenSeq to obtain an instance
-/// of \c rosa::Seq.
-///
-/// \see \c rosa::Seq and \c rosa::GenSeq
-template <size_t... S> using seq_t = typename GenSeq<S...>::Type;
-
+ /// of \c rosa::Seq.
+ ///
+ /// \see \c rosa::Seq and \c rosa::GenSeq
+ template <size_t... S> using seq_t = typename GenSeq<S...>::Type;
} // End namespace rosa
#endif // ROSA_SUPPORT_SEQUENCE_HPP
diff --git a/include/rosa/support/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<int8_t, uint8_t>, // 1
TypePair<int16_t, uint16_t>, // 2
none_t, // 3
TypePair<int32_t, uint32_t>, // 4
none_t, // 5
none_t, // 6
none_t, // 7
TypePair<int64_t, uint64_t> // 8
>;
/// Squashes integral types (except \c bool) into \c [u]int_[8|16|32|64]_t
/// equivalents.
///
/// The squashed type for a type \c T can be obtained as \code
/// typename SquashedInt<T>::Type
/// \endcode
///
/// \tparam T the integral type to squash
///
/// \pre \p T is an integral type:\code
/// std::is_integral<T>::value
/// \endcode
template <typename T> struct SquashedInt {
STATIC_ASSERT((std::is_integral<T>::value && !std::is_same<T, bool>::value),
"squashing a non-integral type or bool");
using TPair = typename TypeListAt<IntegerTypesBySize, sizeof(T)>::Type;
using Type =
typename std::conditional<std::is_signed<T>::value, typename TPair::First,
typename TPair::Second>::type;
};
/// Convenience alias for obtaining a squashed integer type.
template <typename T> using squashed_int_t = typename SquashedInt<T>::Type;
/// \defgroup SquashedType Implementation for squashing types
///
/// \brief Squashes a type.
///
/// The squashed type for a type \c T can be obtained as \code
/// typename SquashedType<T>::Type
/// \endcode
/// The resulting type is squashed with \c rosa::SquashedInt if \c T is
/// integral but not \c bool, and remains \p T otherwise.
///@{
/// Definition for the general case, when squashing a non-integral type.
///
/// \tparam T the type to squash
/// \tparam IsIntegral Always use the default value!
template <typename T, bool IsIntegral = std::is_integral<T>::value>
struct SquashedType {
using Type = T;
};
/// Specialization for the case when squashing an integral type.
///
/// \tparam T the type to squash
template <typename T> struct SquashedType<T, true> {
using Type = squashed_int_t<T>;
};
/// Specialization for the type \c bool.
///
/// \note The type \c bool is an integral type and would be squashed by the
/// general case to \c uint8_t without this specialization.
template <> struct SquashedType<bool, true> { using Type = bool; };
///@}
/// Convenience alias for obtaining a squashed type.
template <typename T> using squashed_t = typename SquashedType<T>::Type;
/// \defgroup SquashedTypeList Implementation for squashing lists of types
///
/// \brief Squashes a \c rosa::TypeList elementwise.
///
/// Replaces all types in a \c rosa::TypeList with their corresponding squashed
/// types by using \c rosa::SquashedType. The squashed \c rosa::TypeList
/// corresponding to \c List can be obtained as \code
/// typename SquashedTypeList<List>::Type
/// \endcode
///@{
/// Declaration of the template.
///
/// \tparam List \c rosa::TypeList to squash
template <typename List> struct SquashedTypeList;
// Specialization for \c rosa::EmptyTypeList.
template <> struct SquashedTypeList<EmptyTypeList> {
using Type = EmptyTypeList;
};
/// Specialization for non-empty \c rosa::TypeList.
template <typename T, typename... Ts>
struct SquashedTypeList<TypeList<T, Ts...>> {
using Type = typename TypeListPush<
squashed_t<T>, typename SquashedTypeList<TypeList<Ts...>>::Type>::Type;
};
///@}
} // End namespace rosa
#endif // ROSA_SUPPORT_SQUASHED_INT_HPP
diff --git a/include/rosa/support/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 <ostream>
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 <memory>
#include <vector>
namespace rosa {
/// Defines a simple interface for storing and accessing values of different
/// types.
///
/// While the interface provides features to access values and know their
/// types, it is the users responsibility to use particular values according to
/// their actual types. No facilities for type-safe access of values is
/// provided by the class.
///
/// \see \c rosa::TokenizedStorage for a type-safe specialization of the
/// interface.
class AbstractTokenizedStorage {
protected:
/// Protected constructor restricts instantiation for derived classes.
AbstractTokenizedStorage(void) noexcept = default;
public:
/// No copying and moving of \c rosa::AbstractTokenizedStorage instances.
///@{
AbstractTokenizedStorage(const AbstractTokenizedStorage&) = delete;
AbstractTokenizedStorage &operator=(const AbstractTokenizedStorage&) = delete;
AbstractTokenizedStorage(AbstractTokenizedStorage&& Other) = delete;
AbstractTokenizedStorage &operator=(AbstractTokenizedStorage&&) = delete;
///@}
/// Destroys \p this object.
virtual ~AbstractTokenizedStorage(void) noexcept = default;
/// Tells how many values are stored in \p this object.
///
/// \return number of values stored in \p this object
virtual size_t size(void) const noexcept = 0;
/// Tells the type of the value stored at a position.
///
/// \param Pos the index of the value whose type is to returned
///
/// \return \c rosa::TypeNumber for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index:\code
/// Pos < size()
/// \endcode
virtual TypeNumber typeAt(const 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 <typename... Types> class TokenizedStorage;
/// \defgroup TokenizedStorageForTypeList Implementation of
/// rosa::TokenizedStorageForTypeList
///
/// \brief Transforms a \c rosa::TypeList instance to the corresponding
/// \c rosa::TokenizedStorage instance.
///
/// A \c rosa::TypeList \c List instance can be turned into a corresponding \c
/// rosa::TokenizedStorage instance as \code
/// typename TokenizedStorageForTypeList<List>::Type
/// \endcode
///
/// For example, the following expression evaluates to `true`: \code
/// std::is_same<typename TokenizedStorageForTypeList<TypeList<T1, T2>>::Type,
/// TokenizedStorage<T1, T2>>::value
/// \endcode
///@{
/// Declaration of the template.
///
/// \tparam List \c rosa::TypeList to transform
template <typename List> struct TokenizedStorageForTypeList;
/// Implementation of the template for \c rosa::TypeList instances.
template <typename... Ts>
struct TokenizedStorageForTypeList<TypeList<Ts...>> {
using Type = TokenizedStorage<Ts...>;
};
///@}
/// Nested namespace with implementation for \c rosa::TokenizedStorage, consider
/// it private.
namespace {
/// Initializes a pre-allocated memory area with values from constant lvalue
/// references.
///
/// \tparam Types types whose values are to be stored
///
/// \param Arena pre-allocated memory area to store values to
/// \param Ts the values to store in \p Arena
///
/// \note \p Arena needs to be a valid pointer to a memory area big enough for
/// values of \p Types.
template <typename... Types>
inline void createArenaElements(void *const Arena,
const Types &... Ts) noexcept;
/// \defgroup createLvalueArenaElement Implementation of creating lvalue arena elements
///
/// Stores values from constant lvalue references into a pre-allocated memory
/// area.
///
/// \note To be used by the implementation of \c createArenaElements.
///
/// \todo Document these functions.
///@{
/// \note This terminal case is used for both constant lvalue references and
/// value references.
template <size_t Pos>
inline void createArenaElement(void *const,
const std::vector<size_t> &Offsets) {
ASSERT(Pos == Offsets.size());
}
template <size_t Pos, typename Type, typename... Types>
inline void createArenaElement(void *const Arena,
const std::vector<size_t> &Offsets,
const Type &T, const Types &... Ts) noexcept {
ASSERT(Arena != nullptr && Pos < Offsets.size());
new (static_cast<Type *>(static_cast<void *>(static_cast<uint8_t *>(Arena) +
Offsets[Pos]))) Type(T);
createArenaElement<Pos + 1>(Arena, Offsets, Ts...);
}
template <size_t Pos, AtomValue V, typename... Types>
inline void
createArenaElement(void *const Arena, const std::vector<size_t> &Offsets,
const AtomConstant<V> &, const Types &... Ts) noexcept {
ASSERT(Arena != nullptr && Pos < Offsets.size());
*static_cast<AtomValue *>(
static_cast<void *>(static_cast<uint8_t *>(Arena) + Offsets[Pos])) = V;
createArenaElement<Pos + 1>(Arena, Offsets, Ts...);
}
///@}
/// Implementation of the template.
///
/// \tparam 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 <typename... Types>
inline void createArenaElements(void *const Arena,
const Types &... Ts) noexcept {
ASSERT(Arena != nullptr);
createArenaElement<0>(Arena, TokenizedStorage<Types...>::Offsets, Ts...);
}
/// Initializes a pre-allocated memory area with values from rvalue references.
///
/// \tparam Types types whose values are to be stored
///
/// \param Arena pre-allocated memory area to store values to
/// \param Ts the values to store in \p Arena
///
/// \note \p Arena needs to be a valid pointer to a memory area big enough for
/// values of \p Types.
template <typename... Types>
inline void createArenaElements(void *const Arena, Types &&... Ts) noexcept;
/// \defgroup createRvalueArenaElement Implementation of creating rvalue arena elements
///
/// Stores values from rvalue references into a pre-allocated memory area.
///
/// \note To be used by the implementation of \c createArenaElements.
///
/// \todo Document these functions.
///@{
template <size_t Pos, typename Type, typename... Types>
inline void createArenaElement(void *const Arena,
const std::vector<size_t> &Offsets, Type &&T,
Types &&... Ts) noexcept {
ASSERT(Arena != nullptr && Pos < Offsets.size());
new (static_cast<Type *>(static_cast<void *>(
static_cast<uint8_t *>(Arena) + Offsets[Pos]))) Type(std::move(T));
createArenaElement<Pos + 1>(Arena, Offsets, std::move(Ts)...);
}
template <size_t Pos, AtomValue V, typename... Types>
inline void createArenaElement(void *const Arena,
const std::vector<size_t> &Offsets,
AtomConstant<V> &&, Types &&... Ts) noexcept {
ASSERT(Arena != nullptr && Pos < Offsets.size());
*static_cast<AtomValue *>(
static_cast<void *>(static_cast<uint8_t *>(Arena) + Offsets[Pos])) = V;
createArenaElement<Pos + 1>(Arena, Offsets, std::move(Ts)...);
}
///@}
/// Implementation of the template.
///
/// \tparam 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 <typename... Types>
inline void createArenaElements(void *const Arena, Types &&... Ts) noexcept {
ASSERT(Arena != nullptr);
createArenaElement<0>(Arena, TokenizedStorage<Types...>::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 <typename... Types>
inline void destroyArenaElements(void *const Arena) noexcept;
/// \defgroup destroyArenaElement Implementation of destroying arena elements
///
/// Destroys values from a memory area.
///
/// \note To be used by the implementation of \c destroyArenaElements.
///
/// \todo Document these functions.
///@{
template <size_t Pos>
inline void destroyArenaElement(void *const,
const std::vector<size_t> &Offsets) noexcept {
ASSERT(Pos == Offsets.size());
}
template <size_t Pos, typename Type, typename... Types>
inline void destroyArenaElement(void *const Arena,
const std::vector<size_t> &Offsets) noexcept {
ASSERT(Arena != nullptr && Pos < Offsets.size());
static_cast<Type *>(
static_cast<void *>(static_cast<uint8_t *>(Arena) + Offsets[Pos]))
->~Type();
destroyArenaElement<Pos + 1, Types...>(Arena, Offsets);
}
///@}
/// Implementation of the template.
///
/// \tparam Types types of values to destroy
///
/// \param Arena the memory area to destroy values from
///
/// \pre \p Arena is not \c nullptr.
template <typename... Types>
inline void destroyArenaElements(void *const Arena) noexcept {
ASSERT(Arena != nullptr);
destroyArenaElement<0, Types...>(Arena, TokenizedStorage<Types...>::Offsets);
}
} // End namespace
/// Implementation of the template \c rosa::TokenizedStorage as a
/// specialization of \c rosa::AbstractTokenizedStorage.
///
/// The class provides facilities for storing values and providing type-safe
/// access to them.
///
/// \tparam Types types of values to store
template <typename... Types>
class TokenizedStorage : public AbstractTokenizedStorage {
public:
/// \c rosa::Token for the stored values.
static constexpr Token ST = TypeToken<std::decay_t<Types>...>::Value;
/// Byte offsets to access stored values in \c rosa::TokenizedStorage::Arena.
static const std::vector<size_t> Offsets;
private:
/// A BLOB storing all the values one after the other.
void *const Arena;
/// Generates byte offsets for accessing values stored in
/// \c rosa::TokenizedStorage::Arena.
///
/// \return \c std::vector containing byte offsets for accessing values stored
/// in \c rosa::TokenizedStorage::Arena
static std::vector<size_t> offsets(void) noexcept {
Token T = ST; // Need a mutable copy.
const token_size_t N = lengthOfToken(T); // Number of types encoded in \c T.
std::vector<size_t> 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<Types> &... 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<Types> &&... 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<std::decay_t<Types>...>(Arena);
::operator delete(Arena);
}
/// Tells how many values are stored in \p this object.
///
/// \return number of values stored in \p this object
size_t size(void) const noexcept override {
return Offsets.size();
}
/// Tells the type of the value stored at a position.
///
/// \param Pos the index of the value whose type is to returned
///
/// \return \c rosa::TypeNumber for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index:\code
/// Pos < size()
/// \endcode
TypeNumber typeAt(const 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<uint8_t *>(Arena) + Offsets[Pos];
}
/// Provides a constant untyped pointer for the value stored at a position.
///
/// \param Pos the index of the value to return an untyped pointer for
///
/// \return constant untyped pointer for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index:\code
/// Pos < Offsets.size()
/// \endcode
const void *pointerTo(const token_size_t Pos) const noexcept override {
ASSERT(Pos < size());
return static_cast<const uint8_t *>(Arena) + Offsets[Pos];
}
/// Tells if the value stored at a given index is of a given type.
///
/// \note Any \c rosa::AtomConstant is encoded in \c rosa::Token as
/// the \c rosa::AtomValue wrapped into it.
///
/// \tparam T type to match against
///
/// \param Pos index the type of the value at is to be matched against \p Type
///
/// \return if the value at index \p Pos of type \p T
///
/// \pre \p Pos is a valid index:\code
/// Pos < Offsets.size()
/// \endcode
template <typename T> bool isTypeAt(const size_t Pos) const noexcept {
ASSERT(Pos < size());
Token TT = ST;
dropNOfToken(TT, Pos);
return isHeadOfTokenTheSameType<T>(TT);
}
/// Gives a reference of a value of a given type stored at a given index.
///
/// \note The constant variant of the function relies on this implementation,
/// the function may not modify \p this object!
///
/// \tparam T type to give a reference of
///
/// \param Pos index to set the reference for
///
/// \return reference of \p T for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index and the value at index \p Pos is of type
/// \p T:
/// \code
/// Pos < Size && isTypeAt<T>(Pos)
/// \endcode
template <typename T> T &valueAt(const token_size_t Pos) noexcept {
ASSERT(Pos < size() && isTypeAt<T>(Pos));
return *static_cast<T *>(pointerTo(Pos));
}
/// Gives a constant reference of a value of a given type stored at a given
/// index.
///
/// \tparam T type to give a reference of
///
/// \param Pos index to set the reference for
///
/// \return constant reference of \p T for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index and the value at index \p Pos is of type
/// \p T:
/// \code
/// Pos < Size && isTypeAt<T>(Pos)
/// \endcode
template <typename T>
const T &valueAt(const token_size_t Pos) const noexcept {
// \note Just use the non-const implementation as that does not modify
// \p this object.
return const_cast<TokenizedStorage *>(this)->valueAt<T>(Pos);
}
};
// Implementation of the static member field \c rosa::TokenizedStorage::Offsets.
template <typename... Types>
const std::vector<size_t>
TokenizedStorage<Types...>::Offsets = TokenizedStorage<Types...>::offsets();
/// Specialization of the template \c rosa::TokenizedStorage for storing
/// nothing.
///
/// \note The specialization implements the interface defined by \c
/// rosa::AbstractTokenizedStorage but most of the functions cannot be called
/// because nothing is stored in instances of the class.
template <> class TokenizedStorage<> : public AbstractTokenizedStorage {
public:
/// \c rosa::Token for the stored values.
static constexpr Token ST = TypeToken<>::Value;
/// Byte offsets to access stored values in \c rosa::TokenizedStorage::Arena.
static const std::vector<size_t> Offsets;
/// Creates an instance.
TokenizedStorage(void) noexcept {}
/// No copying and moving of \c rosa::TokenizedStorage instances.
///
/// \note This restriction may be relaxed as moving should be easy to
/// implement, only requires the possiblity to validate Arena pointer.
///@{
TokenizedStorage(const TokenizedStorage &) = delete;
TokenizedStorage &operator=(const TokenizedStorage &) = delete;
TokenizedStorage(TokenizedStorage &&Other) = delete;
TokenizedStorage &operator=(TokenizedStorage &&) = delete;
///@}
// Destroys \p this object.
~TokenizedStorage(void) {}
/// Tells how many values are stored in \p this object.
///
/// \return `0`
size_t size(void) const noexcept override { return 0; }
/// Tells the type of the value stored at a position.
///
/// \pre Do not call.
TypeNumber typeAt(const token_size_t) const noexcept override {
ASSERT(false);
return TypeNumber(0);
}
/// Provides an untyped pointer for the value stored at a position.
///
/// \pre Do not call.
void *pointerTo(const token_size_t) noexcept override {
ASSERT(false);
return nullptr;
}
/// Provides a constant untyped pointer for the value stored at a position.
///
/// \pre Do not call.
const void *pointerTo(const token_size_t) const noexcept override {
ASSERT(false);
return nullptr;
}
/// Tells if the value stored at a given index is of a given type.
///
/// \pre Do not call.
template <typename> bool isTypeAt(const size_t) const noexcept {
ASSERT(false);
return false;
}
/// Gives a reference of a value of a given type stored at a given index.
///
/// \tparam T type to give a reference of
/// \pre Do not call.
template <typename T> T &valueAt(const token_size_t) noexcept {
ASSERT(false);
return *static_cast<T *>(nullptr);
}
/// Gives a constant reference of a value of a given type stored at a given
/// index.
///
/// \tparam T type to give a reference of
///
/// \pre Do not call.
template <typename T> const T &valueAt(const token_size_t) const noexcept {
// \note Just use the non-const implementation as that does not modify
// \p this object.
return *static_cast<T *>(nullptr);
}
};
} // End namespace rosa
#endif // ROSA_SUPPORT_TOKENIZED_STORAGES_HPP
diff --git a/include/rosa/support/type_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 <cstdint>
#include <type_traits>
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<T>::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 <typename T, bool IsReference = std::is_reference<T>::value,
bool IsConst = std::is_const<T>::value,
bool IsEnum = std::is_enum<T>::value>
struct PrintableType {
using Type = T;
};
/// Specialization for reference types.
template <typename T, bool IsConst, bool IsEnum>
struct PrintableType<T, true, IsConst, IsEnum> {
using Type =
typename PrintableType<typename std::remove_reference<T>::type>::Type;
};
/// Specialization for non-reference, non-const types.
template <typename T, bool IsEnum>
struct PrintableType<T, false, false, IsEnum> {
using Type = typename PrintableType<const T>::Type;
};
/// Specialization for non-reference, const, enum types.
template <typename T> struct PrintableType<T, false, true, true> {
using Type =
typename PrintableType<typename std::underlying_type<T>::type>::Type;
};
/// Specialization for \c const uint8_t.
template <> struct PrintableType<const uint8_t, false, true, false> {
using Type = const unsigned int;
};
/// Specialization for \c const int8_t.
template <> struct PrintableType<const int8_t, false, true, false> {
using Type = const int;
};
///@}
/// Convenience template alias for using \c rosa::PrintableType.
template <typename T> using printable_t = typename PrintableType<T>::Type;
/// Casts values to their corresponding printable types.
///
/// \param V value to cast
#define PRINTABLE(V) static_cast<printable_t<decltype(V)>>(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<T>::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 <typename T, bool IsIntegral = std::is_integral<T>::value>
struct Unsigned {
using Type = T;
};
/// Specialization for the case when converting an integral type.
template <typename T> struct Unsigned<T, true> {
using Type = typename std::make_unsigned<T>::type;
};
///@}
/// Convenience template alias for using \c rosa::Unsigned.
template <typename T> using unsigned_t = typename Unsigned<T>::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 <type_traits>
namespace rosa {
/// A list of types.
///
/// \tparam Ts types to make a list of
template <typename... Ts> struct TypeList {
/// Constructor, needs to do nothing.
constexpr TypeList(void) {}
};
/// The empty \c rosa::Typelist.
using EmptyTypeList = TypeList<>;
/// \defgroup TypeListAtImpl Implementation of rosa::TypeListAt
///
/// \brief Gets the type at index \p Pos from a list of types.
///
/// \note Only to be used by the implementation of \c rosa::TypeListAt.
///@{
/// Declaration of the template.
///
/// \tparam Pos index to take the element from
/// \tparam Ts types
template <size_t Pos, typename... Ts> struct TypeListAtImpl;
/// Definition for the general case when \p Pos is not \c 0 and there is type in
/// the list.
template <size_t Pos, typename T, typename... Ts>
struct TypeListAtImpl<Pos, T, Ts...> {
using Type = typename TypeListAtImpl<Pos - 1, Ts...>::Type;
};
/// Specialization for the case when \p Pos is \c 0.
template <typename T, typename... Ts> struct TypeListAtImpl<0, T, Ts...> {
using Type = T;
};
/// Specialization for the case when there is no more type.
///
/// In this case, the found type is \c rosa::none_t.
template <size_t Pos> struct TypeListAtImpl<Pos> { using Type = none_t; };
///@}
/// \defgroup TypeListAt Definition of rosa::TypeListAt
///
/// \brief Gets the element at index \p Pos of \p List.
///
///
/// The type at index \c Pos in a \c rosa::TypeList \c List can be obtained as
/// \code
/// typename TypeListAt<List, Pos>::Type
/// \endcode
///
/// \note The resulting type is \c rosa::none_t if \code
/// TypeListSize<List>::Value < Pos
/// \endcode
///@{
/// Declaration of the template.
///
/// \tparam List \c rosa::TypeList to take an element from
/// \tparam Pos index to take the element from
template <typename List, size_t Pos> struct TypeListAt;
/// Implementation using \c rosa::TypeListAtImpl.
template <size_t Pos, typename... Ts> struct TypeListAt<TypeList<Ts...>, Pos> {
using Type = typename TypeListAtImpl<Pos, Ts...>::Type;
};
///@}
/// \defgroup TypeListIndexOfImpl Implementation of rosa::TypeListIndexOf
///
/// \brief Tells the index of the first occurence of a type in a list of types.
///
/// \note Only to be used by the implementation of \c rosa::TypeListIndexOf.
///@{
/// Declaration of the template.
///
/// \tparam Pos the number types already being checked from the beginning of the
/// list
/// \tparam X type to search for
/// \tparam Ts remaining list of types
template <size_t Pos, typename X, typename... Ts> struct TypeListIndexOfImpl;
/// Specialization for the case when the list is over.
///
/// In this case, the found index is \c -1.
template <size_t Pos, typename X> struct TypeListIndexOfImpl<Pos, X> {
static constexpr int Value = -1;
};
/// Specialization for the case when the first type in the remaining list
/// is a match.
template <size_t Pos, typename X, typename... Ts>
struct TypeListIndexOfImpl<Pos, X, X, Ts...> {
static constexpr int Value = Pos;
};
/// Implementation for the general case when need to continue looking.
template <size_t Pos, typename X, typename T, typename... Ts>
struct TypeListIndexOfImpl<Pos, X, T, Ts...> {
static constexpr int Value = TypeListIndexOfImpl<Pos + 1, X, Ts...>::Value;
};
///@}
/// \defgroup TypeListIndexOf Definition of rosa::TypeListIndexOf
///
/// \brief Tells the index of the first occurence of type in a
/// \c rosa::TypeList.
///
/// The index of the first occurence of type \c T in \c rosa::TypeList \c List
/// can be obtained as \code
/// TypeListIndexOf<List, T>::Value
/// \endcode
///
/// \note The resulting index is \c -1 if \c T is not present in \c List.
///@{
/// Declaration of the template.
///
/// \tparam List \c rosa::TypeList to search in
/// \tparam T type to search for
template <typename List, typename T> struct TypeListIndexOf;
/// Implementation of the template using \c rosa::TypeListIndexOfImpl.
template <typename... Ts, typename T>
struct TypeListIndexOf<TypeList<Ts...>, T> {
static constexpr int Value = TypeListIndexOfImpl<0, T, Ts...>::Value;
};
///@}
/// \defgroup TypeListHead Implementation of rosa::TypeListHead
///
/// \brief Gets the first element of a \c rosa::TypeList.
///
/// The first element of a \c rosa::TypeList \c List can be obtained as \code
/// typename TypeListHead<List>::Type
/// \endcode
///
/// \note The resulting type is \c rosa::none_t if \c List is
/// \c rosa::EmptyTypeList.
///@{
/// Declaration of the template.
///
/// \tparam List \c rosa::TypeList to get the first element of
template <typename List> struct TypeListHead;
/// Specialization for \c rosa::EmptyTypeList.
///
/// In this case, the found type is \c rosa::none_t.
template <> struct TypeListHead<EmptyTypeList> { using Type = none_t; };
/// Implementation for a non-empty \c rosa::TypeList.
template <typename T, typename... Ts> struct TypeListHead<TypeList<T, Ts...>> {
using Type = T;
};
///@}
/// \defgroup TypeListTail Implementation of rosa::TypeListTail
///
/// \brief Gets the tail of a \c rosa::TypeList.
///
/// The tail of a \c rosa::TypeList \c List, that is \c List except for its
/// first element, can be obtained as \code
/// typename TypeListTail<List>::Type
/// \endcode
///
/// \note If \c List is \c rosa::EmptyTypeList, then the resulting type is also
/// \c rosa::EmptyTypeList.
///@{
/// Declaration of the template.
///
/// \tparam List \c rosa::TypeList to take the tail of
template <typename List> struct TypeListTail;
/// Specialization for \c rosa::EmptyTypeList.
///
/// In this case, the resulting type is \c rosa::EmptyTypeList.
template <> struct TypeListTail<EmptyTypeList> { using Type = EmptyTypeList; };
/// Implementation for a non-empty \c rosa::TypeList.
template <typename T, typename... Ts> struct TypeListTail<TypeList<T, Ts...>> {
using Type = TypeList<Ts...>;
};
///@}
/// \defgroup TypeListPush Implementation of rosa::TypeListPush
///
/// \brief Extends a \c rosa::TypeList with a type.
///
/// Whether the new type is pushed in the front or in the back of the
/// \c rosa::TypeList depends on the order of template arguments, as shown in
/// the following example: \code
/// using List = TypeList<A>
/// typename TypeListPush<List, T>::Type; // TypeList<A, T>
/// typename TypeListPush<T, List>::Type; // TypeList<T, A>
/// \endcode
///@{
/// Declaration of the template.
///
/// \tparam P a type if \p Q is a \c rosa::TypeList, a \c rosa::TypeList
/// otherwise
/// \tparam Q a type if \p P is a \c rosa::TypeList, a \c rosa::TypeList
/// otherwise
template <typename P, typename Q> struct TypeListPush;
/// Implementation for the case when pushing at the back of the
/// \c rosa::TypeList.
template <typename... Ts, typename T> struct TypeListPush<TypeList<Ts...>, T> {
using Type = TypeList<Ts..., T>;
};
/// Implementation for the case when pushing to the front of the
/// \c rosa::TypeList.
template <typename T, typename... Ts> struct TypeListPush<T, TypeList<Ts...>> {
using Type = TypeList<T, Ts...>;
};
///@}
/// \defgroup TypeListDrop Implementation of rosa::TypeListDrop
///
/// \brief Drops some elements from the beginning of a \c rosa::TypeList.
///
/// The first \c N types of a \c rosa::TypeList \c List can be dropped as \code
/// typename TypeListDrop<N, List>::Type
/// \endcode
///@{
/// Declaration of the template.
///
/// \tparam N number of types to drop
/// \tparam List \c rosa::TypeList to drop the first \p N element of
template <size_t N, typename List> struct TypeListDrop;
/// Specialization for \c rosa::EmptyTypeList.
template <size_t N> struct TypeListDrop<N, EmptyTypeList> {
using Type = EmptyTypeList;
};
/// Implementation for a non-empty \c rosa::TypeList.
template <size_t N, typename T, typename... Ts>
struct TypeListDrop<N, TypeList<T, Ts...>> {
using Type = typename std::conditional<
N == 0, TypeList<T, Ts...>,
typename TypeListDrop<N - 1, TypeList<Ts...>>::Type>::type;
};
///@}
/// \defgroup TypeListConcat Implementation of rosa::TypeListConcat
///
/// \brief Concatenates two \c rosa::TypeList instances.
///
/// Two instances of \c rosa::TypeList \c List1 and \c List2 can be
/// concatenated as \code
/// typename TypeListConcat<List1, List2>::Type
/// \endcode
///@{
/// Declaration of the template
///
/// \tparam List1 the first instance of \c rosa::TypeList
/// \tparam List2 the second instance of \c rosa::TypeList
template <typename List1, typename List2> struct TypeListConcat;
/// Implementation of the template for \c rosa::TypeList instances.
template <typename... As, typename... Bs>
struct TypeListConcat<TypeList<As...>, TypeList<Bs...>> {
using Type = TypeList<As..., Bs...>;
};
///@}
/// \defgroup TypeListSize Implementation of rosa::TypeListSize
///
/// \brief Tells the number of types stored in a \c rosa::TypeList.
///
/// The size of a \c rosa::TypeList \c List can be obtained as \code
/// TypeListSize<List>::Value
/// \endcode
///@{
/// Declaration of the template.
///
/// \tparam List \c rosa::TypeList to get the size of
template <typename List> struct TypeListSize;
/// Implementation of the template.
template <typename... Ts> struct TypeListSize<TypeList<Ts...>> {
static constexpr size_t Value = sizeof...(Ts);
};
template <typename... Ts> constexpr size_t TypeListSize<TypeList<Ts...>>::Value;
///@}
/// Tests whether a \c rosa::TypeList is empty.
///
/// \tparam List \c rosa::TypeList to check
template <typename List> struct TypeListEmpty {
/// Denotes whether \p List is an empty \c rosa::TypeList or not.
static constexpr bool Value = std::is_same<EmptyTypeList, List>::value;
};
/// \defgroup TypeListContains Implementation of rosa::TypeListContains
///
/// \brief Tells if a \c rosa::TypeList contains a given type.
///
/// Whether a \c rosa::TypeList \c List contains the type \c T can be checked as
/// \code
/// TypeListContains<List, T>::Value
/// \endcode
///@{
/// Declaration of the template.
///
/// \tparam List \c rosa::TypeList to search in
/// \tparam T type to search for
template <typename List, typename T> struct TypeListContains;
/// Implementation of the template.
template <typename... Ts, typename T>
struct TypeListContains<TypeList<Ts...>, T> {
static constexpr bool Value =
std::conditional<TypeListIndexOf<TypeList<Ts...>, T>::Value == -1,
std::false_type, std::true_type>::type::value;
};
///@}
/// \defgroup TypeListSubsetOf Implementation of rosa::TypeListSubsetOf
///
/// \brief Tells if a \c rosa::TypeList is a subset of another one.
///
/// Whether a \c rosa::TypeList \c ListA is a subset of another
/// \c rosa::TypeList \c ListB can be checked as \code
/// TypeListSubsetOf<ListA, ListB>::Value
/// \endcode
///@{
/// Declaration of the template.
///
/// \tparam ListA \c rosa::TypeList to check if is a subset of \p ListB
/// \tparam ListB \c rosa::TypeList to check if is a superset of \p ListA
/// \tparam Fwd always use the default value!
template <typename ListA, typename ListB, bool Fwd = true>
struct TypeListSubsetOf;
/// Specialization for the case when all the elements of the original \p ListA
/// was found in \p ListB.
template <typename List> struct TypeListSubsetOf<EmptyTypeList, List, true> {
static constexpr bool Value = true;
};
/// Specializaton for the case when an element of the original \p ListA cannot
/// be found in \p ListB.
template <typename ListA, typename ListB>
struct TypeListSubsetOf<ListA, ListB, false> {
static constexpr bool Value = false;
};
/// Definition for the general case.
template <typename T, typename... Ts, typename List>
struct TypeListSubsetOf<TypeList<T, Ts...>, List>
: TypeListSubsetOf<TypeList<Ts...>, List,
TypeListContains<List, T>::Value> {};
///@}
/// \defgroup TypeListFindImpl Implementation of rosa::TypeListFind
///
/// \brief Finds the first type in a list of types that satisfies a predicate.
///
/// \note Only to be used by the implementation of \c rosa::TypeListFind.
///@{
/// Declaration of the template.
///
/// \tparam Pred the predicate to check types against
/// \tparam Ts list of types to check
template <template <typename> class Pred, typename... Ts>
struct TypeListFindImpl;
/// Specialization for the case when no more types remain to check.
template <template <typename> class Pred> struct TypeListFindImpl<Pred> {
using Type = none_t;
};
/// Implementation for the general case when there is a type to check.
template <template <typename> class Pred, typename T, typename... Ts>
struct TypeListFindImpl<Pred, T, Ts...> {
using Type = typename std::conditional<
Pred<T>::Value, T, typename TypeListFindImpl<Pred, Ts...>::Type>::type;
};
///@}
/// \defgroup TypeListFind Definition of rosa::TypeListFind
///
/// \brief Finds the first element satisfying a predicate in a
/// \c rosa::TypeList.
///
/// The first type satisfying a predicate \c Pred in a \c rosa::TypeList
/// \c List can be obtained as \code
/// typename TypeListFind<List, Pred>::Type
/// \endcode
///
/// \note The resulting type is \c rosa::none_t if no type in \c List satisfies
/// \c Pred.
///@{
/// Declaration of the template.
///
/// \tparam List \c rosa::TypeList to search in
/// \tparam Pred predicate to check elements against
template <typename List, template <typename> class Pred> struct TypeListFind;
/// Implementation of the template using \c rosa::TypeListFindImpl.
template <typename... Ts, template <typename> class Pred>
struct TypeListFind<TypeList<Ts...>, Pred> {
using Type = typename TypeListFindImpl<Pred, Ts...>::Type;
};
///@}
} // End namespace rosa
#endif // ROSA_SUPPORT_TYPE_LIST_HPP
diff --git a/include/rosa/support/type_numbers.hpp b/include/rosa/support/type_numbers.hpp
index cc85cc7..99bf92b 100644
--- a/include/rosa/support/type_numbers.hpp
+++ b/include/rosa/support/type_numbers.hpp
@@ -1,240 +1,245 @@
//===-- rosa/support/type_numbers.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_numbers.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2019
///
/// \brief Facilities for registering supported types and representing them with
/// numbers.
///
/// \note This implementation is partially based on the \c type_number
/// implementation of CAF.
-/// \todo Check license.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_SUPPORT_TYPE_NUMBERS_HPP
#define ROSA_SUPPORT_TYPE_NUMBERS_HPP
#include "rosa/support/atom.hpp"
#include "rosa/support/math.hpp"
#include "rosa/support/squashed_int.hpp"
#include "rosa/support/type_helper.hpp"
#include "rosa/support/types.hpp"
#include <array>
#include <string>
namespace rosa {
/// Compile-time list of all built-in types.
/// \note Appending new types to the end of this list maintains backward
/// compatibility in the sense that old builtin types have the same type number
/// associated to them in both the old and new versions. But changing any of
/// the already present types in the list breaks that backward compatibility.
/// Should compatibility be broken, step \c rosa::TypeNumberVersion below!
/// \note Keep this list in sync with the definition of
/// \c rosa::NumberedTypeNames.
/// \note The built-in types are explicitly listed in the definition of
/// rosa::deluxe::DeluxeAgent. Keep those definitions in sync with this list.
using BuiltinTypes = TypeList<AtomValue, ///< atom
int16_t, ///< i16
int32_t, ///< i32
int64_t, ///< i64
int8_t, ///< i8
long double, ///< ldouble
std::string, ///< str
uint16_t, ///< u16
uint32_t, ///< u32
uint64_t, ///< u64
uint8_t, ///< u8
unit_t, ///< unit
bool, ///< bool
double, ///< double
float ///< float
>;
/// Indicates the version number of \c rosa::BuiltinTypes.
///
/// Software with the same version number are supposed to have backward
/// compatible type numbering.
///
/// \sa \c rosa::BultinTypes on backward compatibility.
constexpr size_t TypeNumberVersion = 0;
/// The number of built-in types.
static constexpr size_t NumberOfBuiltinTypes =
TypeListSize<BuiltinTypes>::Value;
/// Anonymous namespace for helper facilities, consider it private.
namespace {
/// Tells if a type is not \c rosa::NoneType.
///
/// \tparam T the type to check
template <typename T> struct IsNotNoneType {
/// Denotes if \p T is the \c rosa::NoneType or not.
static constexpr bool Value = !std::is_same<T, NoneType>::value;
};
} // End namespace
/// Integer type to store type numbers.
/// \note The narrowest unsigned integer type that is wide enough to represent
/// \c NumberOfBuiltinTypes different values.
using type_nr_t = typename TypeListFind<
typename TypeListDrop<log2(NumberOfBuiltinTypes) / 8 + 1,
IntegerTypesBySize>::Type,
IsNotNoneType>::Type::Second;
/// Turn \c rosa::type_nr_t into a strongly typed enumeration.
///
/// Values of \c rosa::type_nr_t casted to \c rosa::TypeNumbers can be used in a
/// type-safe way.
enum class TypeNumber : type_nr_t {};
/// A type to cast type numbers into in order to output them to streams as
/// numbers and not ASCII-codes.
///
/// \note Use it for safety, necessary for printing \c uint8_t values.
using printable_tn_t = printable_t<type_nr_t>;
/// Casts a \c rosa::TypeNumber into \c rosa::printable_tn_t.
///
/// \param TN \c rosa::TypeNumber to cast.
#define PRINTABLE_TN(TN) static_cast<printable_tn_t>(TN)
/// \name TypeNumberOf
/// \brief Computes \c rosa::TypeNumber for a type.
///
/// The \c rosa::TypeNumber for a type \c T can be obtained as \code
/// TypeNumberOf<T>::Value
/// \endcode
///
/// \note \c rosa::TypeNumber for a type is based on the corresponding squashed
/// type, except for \c rosa::AtomConstant types.
///
/// \sa \c rosa::SquashedType
///
/// \note \c rosa::TypeNumber is the index of the type in \c rosa::BuiltinTypes
/// starting from \c 1; index \c 0 indicates a non-builtin type.
///@{
/// Definition of the template for the general case.
///
/// \tparam T type to get \c rosa::TypeNumber for
template <typename T> struct TypeNumberOf {
static constexpr TypeNumber Value = static_cast<TypeNumber>(
TypeListIndexOf<BuiltinTypes, squashed_t<T>>::Value + 1);
};
/// Specialization for \c rosa::AtomConstant.
///
/// \note For a \c rosa::AtomConstant type, \c rosa::TypeNumber is based on the
/// \c rosa::AtomValue wrapped into the actual \c rosa::AtomConstant.
template <AtomValue V> struct TypeNumberOf<AtomConstant<V>> {
static constexpr TypeNumber Value = TypeNumberOf<AtomValue>::Value;
};
///@}
// clang-format off
/// List of type names for all builtin-types, indexed via \c rosa::TypeNumber.
///
/// \note Keep this definition in sync with \c rosa::BuiltinTypes.
constexpr std::array<const char *, NumberOfBuiltinTypes> NumberedTypeNames {{
"atom",
"i16",
"i32",
"i64",
"i8",
"ldouble",
"str",
"u16",
"u32",
"u64",
"u8",
"unit",
"bool",
"double",
"float"
}};
// clang-format on
/// Tells if a \c rosa::TypeNumber is valid in the software.
///
/// \note A \c rosa::TypeNumber generated by an incompatible version may be
/// valid but may denote a type that is different from the \c rosa::TypeNumber
/// denotes in the current software. That is why this validation needs to be
/// done in connection to checking \c rosa::TypeNumberVersion as well.
///
/// \param TN \c rosa::TypeNumber to validate in the context of the current
/// software
///
/// \return Whether \p TN is valid in the current software
constexpr bool validTypeNumber(const TypeNumber TN) {
// \todo Duplication of static_cast into a const variable would be
// possible in C++14.
return 0 < static_cast<type_nr_t>(TN) &&
static_cast<type_nr_t>(TN) <= NumberOfBuiltinTypes;
}
/// Provides information about the type corresponding to a \c rosa::TypeNumber.
///
/// \tparam TN \c rosa::TypeNumber to get information for
///
/// \pre Statically, \p TN is a valid \c rosa::TypeNumber:
/// \code
/// validTypeNumber(TN)
/// \endcode
template <TypeNumber TN> struct TypeForNumber {
STATIC_ASSERT(validTypeNumber(TN), "not a valid type number");
/// \p TN as \c rosa::type_nr_t.
static constexpr type_nr_t TNI = static_cast<type_nr_t>(TN);
/// The builtin-type corresponding to \p TN.
using Type = typename TypeListAt<BuiltinTypes, TNI - 1>::Type;
/// The size of \c Type.
static constexpr size_t Size = sizeof(Type);
/// Textual representation of the builtin-type.
static constexpr const char *Name = NumberedTypeNames[TNI - 1];
};
} // End namespace rosa
namespace std {
/// Converts a \c rosa::TypeNumber into \c std::string.
///
/// \param TN \c rosa::TypeNumber to convert
///
/// \return \c std::string representing \p TN
inline string to_string(const rosa::TypeNumber TN) {
return to_string(static_cast<rosa::type_nr_t>(TN));
}
/// Dumps a \c rosa::TypeNumber to a given \c std::ostream.
///
/// \param [in,out] OS output stream to dump to
/// \param TN \c rosa::TypeNumber to dump
///
/// \return \p OS after dumping \p TN to it
inline ostream &operator<<(ostream &OS, const rosa::TypeNumber &TN) {
OS << to_string(TN);
return OS;
}
} // End namespace std
#endif // ROSA_SUPPORT_TYPE_NUMBERS_HPP
diff --git a/include/rosa/support/type_pair.hpp b/include/rosa/support/type_pair.hpp
index d22ef45..5ab0bb7 100644
--- a/include/rosa/support/type_pair.hpp
+++ b/include/rosa/support/type_pair.hpp
@@ -1,50 +1,55 @@
//===-- rosa/support/type_pair.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_pair.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Facilities for types representing pairs of types.
///
/// \note This implementation is based on the \c type_pair implementation of
/// CAF.
-/// \todo Check license.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_SUPPORT_TYPE_PAIR_HPP
#define ROSA_SUPPORT_TYPE_PAIR_HPP
namespace rosa {
/// A pair of types.
///
/// \tparam F the first type
/// \tparam S the second type
template <typename F, typename S> struct TypePair {
using First = F; ///< The first type.
using Second = S; ///< The second type.
};
/// Turns two types into a \c rosa::TypePair.
///
/// Turning two types \c F and \c S into \c rosa::TypePair<F, S> is as simple as
/// \code
/// typename ToTypePair<F, S>::Type
/// \endcode
///
/// \tparam F the fist type
/// \tparam S the second type
template <typename F, typename S> struct ToTypePair {
using Type = TypePair<F, S>; ///< The \c rosa::TypePair.
};
} // End namespace rosa
#endif // ROSA_SUPPORT_TYPE_PAIR_HPP
diff --git a/include/rosa/support/type_token.hpp b/include/rosa/support/type_token.hpp
index 99f2cec..57a860c 100644
--- a/include/rosa/support/type_token.hpp
+++ b/include/rosa/support/type_token.hpp
@@ -1,321 +1,327 @@
//===-- rosa/support/type_token.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_token.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2019
///
/// \brief Facilities for encoding TypeLists as unsigned integer values.
///
/// \note **On the compatibility between different versions of the type token
/// implementation:**
/// Different software versions produce compatible type tokens as long as
/// *backward compatibility* of \c rosa::BuiltinTypes is maintained (denoted by
/// \c rosa::TypeNumberVersion) and the type token implementation uses the same
/// type as \c rosa::token_t (boiling down to the same \c rosa::token::TokenBits
/// value) and the same \c rosa::token::RepresentationBits value. Thus,
/// interacting software need to cross-validate the aforementioned values to
/// check compatibility. Interoperation between compatible sofware is limited
/// to backward compatiblity, that is builtin types defined in both software
/// versions are handled correctly but a newer system may produce a type token
/// which is invalid in an old one. Therefore, tokens obtained from a compatible
/// remote system need to be validated.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_SUPPORT_TYPE_TOKEN_HPP
#define ROSA_SUPPORT_TYPE_TOKEN_HPP
#include "rosa/support/type_numbers.hpp"
namespace rosa {
/// Integer type to store type tokens.
///
/// \note The trade-off between the binary overhead of type encoding and the
/// maximal size of encodable lists can be tuned by using unsigned integer types
/// of different widths as \c rosa::token_t.
using token_t = uint64_t;
/// Sanity check in case someone would change \c rosa::token_t.
STATIC_ASSERT(std::is_unsigned<token_t>::value,
"token_t is not an unsigned integer");
/// Turn \c rosa::token_t into a strongly typed enumeration.
///
/// Values of \c rosa::token_t casted to \c rosa::Token can be used in a
/// type-safe way.
enum class Token : token_t {};
/// A type to cast tokens into in order to output them to streams as
/// numbers and not ASCII-codes.
///
/// \note Use it for safety, necessary for printing \c uint8_t values.
using printable_token_t = printable_t<token_t>;
/// Casts a \c rosa::Token into \c rosa::printable_token_t.
///
/// \param T \c rosa::Token to cast
#define PRINTABLE_TOKEN(T) static_cast<printable_token_t>(T)
/// Encloses constants related to the implementation of \c rosa::Token.
namespace token {
/// The number of bits in one \c rosa::Token.
constexpr size_t TokenBits = sizeof(Token) * 8;
/// The number of bits a builtin type can be uniquely encoded into, that is any
/// valid \c rosa::TypeNumber can fit into.
///
/// \note There is one extra bit position added for encoding so that providing a
/// better chance to maintain backward comaptibility when \c rosa::BuiltinTypes
/// is extended.
constexpr size_t RepresentationBits = log2(NumberOfBuiltinTypes) + 1;
/// Maximal size of uniquely tokenizable \c rosa::TypeList.
constexpr size_t MaxTokenizableListSize = TokenBits / RepresentationBits;
} // End namespace token
/// Integer type to store length of a \c rosa::Token.
///
/// \note The narrowest unsigned integer type that is wide enough to
/// represent \c rosa::token::MaxTokenizableListSize different values.
using token_size_t = typename TypeListFind<
typename TypeListDrop<log2(token::MaxTokenizableListSize) / 8 + 1,
IntegerTypesBySize>::Type,
IsNotNoneType>::Type::Second;
/// \defgroup TypeListTokenImpl Implementation of rosa::TypeListToken
///
/// \brief Generates a \c rosa::Token for a squashed \c rosa::TypeList.
///
/// \note Only to be used by the implementation of \c rosa::TypeListToken.
///@{
/// Declaration of the template.
///
/// \tparam List \c rosa::TypeList to generate \c rosa::Token for
template <typename List> struct TypeListTokenImpl;
/// Specialization for \c rosa::EmptyTypeList.
template <> struct TypeListTokenImpl<EmptyTypeList> {
static constexpr Token Value = static_cast<Token>(0);
};
/// Specialization for a non-empty \c rosa::TypeList.
template <typename T, typename... Ts>
struct TypeListTokenImpl<TypeList<T, Ts...>> {
static constexpr TypeNumber TN = TypeNumberOf<T>::Value;
// Check if the generated type number is valid.
STATIC_ASSERT(validTypeNumber(TN), "non-builtin type");
static constexpr Token Value = static_cast<Token>(
(static_cast<token_t>(TypeListTokenImpl<TypeList<Ts...>>::Value)
<< token::RepresentationBits) |
static_cast<type_nr_t>(TN));
};
///@}
/// \name TypeListToken
///
/// \brief Generates a \c rosa::Token for a \c rosa::TypeList.
///
/// \c rosa::Token for a \c rosa::TypeList \c List can be obtained as \code
/// TypeListToken<List>::Value
/// \endcode
///
/// \note The \c rosa::TypeList cannot have more than
/// \c rosa::token::MaxTokenizableListSize elements and must be a subset of
/// \c rosa::BuiltinTypes with respect to squashed integers.
///
/// \note A generated \c rosa::Token uniquely represents a list of types, except
/// for \c rosa::AtomConstant types. Observe that any \c rosa::AtomConstant is
/// encoded as the type \c rosa::AtomValue. The type information on all separate
/// \c rosa::AtomConstant types are lost and replaced by the \c rosa::AtomValue
/// type whose actual value needs to be used in order to obtain the original
/// \c rosa::AtomConstant type and so the full type information on the encoded
/// \c rosa::TypeList.
///
///@{
/// Declaration of the template.
///
/// \tparam List \c rosa::TypeList to generate \c rosa::Token for
template <typename List> struct TypeListToken;
/// Implementation using \c rosa::TypeListTokenImpl.
template <typename... Ts> struct TypeListToken<TypeList<Ts...>> {
/// \note \c rosa::TypeNumber is computed against \c rosa::squased_t, so let's
/// do the same here.
using List = typename SquashedTypeList<TypeList<Ts...>>::Type;
/// Check the length of the list here.
/// \note Type validation is done one-by-one in \c rosa::TypeListTokenImpl.
STATIC_ASSERT((TypeListSize<List>::Value <= token::MaxTokenizableListSize),
"too long list of types");
/// The \c rosa::Token for \p List.
static constexpr Token Value = TypeListTokenImpl<List>::Value;
};
///@}
/// Convenience template to generate \c rosa::Token for a list of types.
template <typename... Ts> using TypeToken = TypeListToken<TypeList<Ts...>>;
/// Tells if a given \c rosa::Token is valid.
///
/// A \c rosa::Token is considered valid if it can be decoded by the current
/// software.
///
/// \note Validation gives a correct result only when \p T was generated by a
/// compatible software.
///
/// \sa Note for type_token.hpp on compatibility.
///
/// \param T \c rosa::Token to validate
///
/// \return if \p T is valid
bool validToken(const Token T);
/// Tells if a \c rosa::Token does encode an empty list of types.
///
/// \param T \c rosa::Token to check
///
/// \return if \p T encodes an empty list of types
bool emptyToken(const Token T);
/// Tells how many types are encoded in a \c rosa::Token.
///
/// \param T \c rosa::Token to check
///
/// \return how many types are encoded in \p T
token_size_t lengthOfToken(const Token T);
/// Tells the full memory size of the types encoded in a \c rosa::Token.
///
/// The full memory size of the types encoded in a \c rosa::Token is the sum of
/// the separate memory sizes of all the types encoded in the \c rosa::Token.
///
/// \param T \c rosa::Token to check
///
/// \return full memory size of the types encoded in \p T
///
/// \pre \p T is valid:\code
/// validToken(T)
/// \endcode
size_t sizeOfValuesOfToken(const Token T);
/// Extracts the \c rosa::TypeNumber of the first encoded type of a
/// \c rosa::Token.
///
/// \note The returned type number is not validated.
///
/// \param T \c rosa::Token to take the first \c rosa::TypeNumber from
///
/// \return the first \c rosa::TypeNumber encoded in \p T
inline TypeNumber headOfToken(const Token T) {
return static_cast<TypeNumber>(static_cast<token_t>(T) &
((1 << token::RepresentationBits) - 1));
}
/// Tells the memory size of the first type encoded in a \c rosa::Token.
///
/// \param T \c rosa::Token to check the first encoded type of
///
/// \return memory size of the first type encoded in \p T
///
/// \pre \p T is not empty and valid:\code
/// !empty(T) && validToken(T)
/// \endcode
size_t sizeOfHeadOfToken(const Token T);
/// Gives the textual representation of the first type encoded in a
/// \c rosa::Token.
///
/// \param T \c rosa::Token to take the name of its first encoded type
///
/// \return textual representation of the first type encoded in \p T
///
/// \pre \p T is not empty and valid:\code
/// !empty(T) && validToken(T)
/// \endcode
const char *nameOfHeadOfToken(const Token T);
/// Updates a \c rosa::Token by dropping its first encoded type.
///
/// \param [in,out] T \c rosa::Token to drop the first encoded type of
void dropHeadOfToken(Token &T);
/// Updates a \c rosa::Token by dropping a number of its first encoded types
///
/// \param [in,out] T \c rosa::Token to drop the first \p N encoded types of
/// \param N the number of types to drop from \p T
void dropNOfToken(Token &T, const size_t N);
/// Tells what type is encoded at Position \p Pos in the \c rosa::Token \p T
///
/// \param T \c rosa::Token to check
/// \param Pos the position to check
///
/// \return \c rosa::TypeNumber of the type encodeded at position \p Pos of \c
/// rosa::Token \p T
///
/// \pre \p T is valid and it encodes \p Pos types: \code
/// validToken(T) && Pos < static_cast<size_t>(lengthOfToken(T))
/// \endcode
inline TypeNumber typeAtPositionOfToken(const Token T, const size_t Pos) {
ASSERT(validToken(T) && Pos < static_cast<size_t>(lengthOfToken(T)));
Token TT = T;
dropNOfToken(TT, Pos);
return headOfToken(TT);
}
/// Tells if the first encoded type of a \c rosa::Token is a given type.
///
/// \tparam Type type to match the first encoded type of \p T against
///
/// \param T \c rosa::Token whose first encoded type is to be matched against
/// \p Type
///
/// \return if the first encoded type of \p T is \p Type
///
/// \pre \p T is not empty and valid:\code
/// !empty(T) && validToken(T)
/// \endcode
template <typename Type> bool isHeadOfTokenTheSameType(const Token T) {
ASSERT(!emptyToken(T) && validToken(T));
return TypeNumberOf<Type>::Value == headOfToken(T);
}
} // End namespace rosa
namespace std {
/// Converts a \c rosa::Token into \c std::string.
///
/// \param T \c rosa::Token to convert
///
/// \return \c std::string representing \p T
inline string to_string(const rosa::Token T) {
return to_string(static_cast<rosa::token_t>(T));
}
/// Dumps a \c rosa::Token to a given \c std::ostream.
///
/// \param [in,out] OS output stream to dump to
/// \param T \c rosa::Token to dump
///
/// \return \p OS after dumping \p N to it
inline ostream &operator<<(ostream &OS, const rosa::Token &T) {
OS << to_string(T);
return OS;
}
} // End namespace std
#endif // ROSA_SUPPORT_TYPE_TOKEN_HPP
diff --git a/include/rosa/support/types.hpp b/include/rosa/support/types.hpp
index eb6a3b2..afeb508 100644
--- a/include/rosa/support/types.hpp
+++ b/include/rosa/support/types.hpp
@@ -1,550 +1,555 @@
//===-- rosa/support/types.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/types.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2019
///
/// \brief Implementation of some basic convenience types.
///
/// \note This implementation is partially based on the implementation of
/// corresponding parts of CAF.
-/// \todo Check license.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_SUPPORT_TYPES_HPP
#define ROSA_SUPPORT_TYPES_HPP
#include "rosa/support/debug.hpp"
#include <string>
namespace rosa {
/* ************************************************************************** *
* Unit *
* ************************************************************************** */
/// A safe type to replace \c void.
///
/// \c rosa::UnitType is analogous to \c void, but can be safely returned,
/// stored, etc. to enable higher-order abstraction without cluttering code with
/// exceptions for \c void (which can't be stored, for example).
struct UnitType {
/// Constructor, needs to do nothing.
constexpr UnitType() noexcept {}
/// Copy-constructor, needs to do nothing.
constexpr UnitType(const UnitType &) noexcept {}
};
/// Aliasing \c rosa::UnitType as \c rosa::unit_t.
using unit_t = UnitType;
/// The value of \c rosa::unit_t.
///
/// \note Since a value of \c rosa::UnitType has no state, all instances of
/// \c rosa::UnitType is equal and considered *the \c rosa::unit_t value*.
static constexpr unit_t unit = unit_t{}; // NOLINT
/// \name LiftVoid
/// \brief Lifts a type to avoid \c void.
///
/// A type \c T can be lifted as \code
/// typename LiftVoid<T>::Type
/// \endcode
/// The resulted type is \c rosa::unit_t if \c T is \c void, and \c T itself
/// otherwise.
///@{
/// Definition for the general case.
///
/// \tparam T type to lift
template <typename T> struct LiftVoid { using Type = T; };
/// Specialization for \c void.
template <> struct LiftVoid<void> { using Type = unit_t; };
///@}
/// \name UnliftVoid
/// \brief Unlifts a type already lifted by \c rosa::LiftVoid.
///
/// A type \c T can be unlifted as \code
/// typename UnliftVoid<T>::Type
/// \endcode
/// The resulted type is \c void if \c T is \c rosa::unit_t -- that is \c void
/// lifted by \c rosa::LiftVoid --, and \c T itself otherwise.
///
///@{
/// Definition for the general case.
///
/// \tparam T type to unlift
template <typename T> struct UnliftVoid { using Type = T; };
/// Specialization for \c rosa::unit_t.
template <> struct UnliftVoid<unit_t> { using Type = void; };
///@}
/* ************************************************************************** *
* None *
* ************************************************************************** */
/// Represents *nothing*.
///
/// An instance of the type represents *nothing*, that can be used, e.g., for
/// clearing an instance of \c rosa::Optional by assigning an instance of
/// \c rosa::NoneType to it.
struct NoneType {
/// Constructor, needs to do nothing.
constexpr NoneType(void) {}
/// Evaluates the instance to \c bool.
///
/// A "nothing" is always evaluates to \c false.
constexpr explicit operator bool(void) const { return false; }
};
/// Aliasing type \c rosa::NoneType as \c rosa::none_t.
using none_t = NoneType;
/// The value of \c rosa::none_t.
///
/// \note Since a value of \c rosa::NoneType has no state, all instances of
/// \c rosa::NoneType is equal and considered *the \c rosa::none_t value*.
static constexpr none_t none = none_t{}; // NOLINT
/* ************************************************************************** *
* Optional *
* ************************************************************************** */
/// \defgroup Optional Specializations of rosa::Optional
///
/// \brief Represents an optional value.
///
/// \note This implementation is compatible with \c std::optional of C++17.
///@{
/// Definition for the general case, optionally storing a value.
///
/// \tparam T type of the optional value
template <class T> class Optional {
public:
using Type = T;
/// Creates an instance without value.
///
/// \note Use it with its default parameter.
Optional(const none_t & = none) : Valid(false) {}
/// Creates a valid instance with value.
///
/// \tparam U type of the \p X
/// \tparam E always use it with default value!
///
/// \param X value to store in the object
///
/// \note The constructor is available for types that are convertible to \p T.
template <class U, class E = typename std::enable_if<
std::is_convertible<U, T>::value>::type>
Optional(U X) : Valid(false) {
cr(std::move(X));
}
/// Creates an instance as a copy of another one.
///
/// \param Other the instance whose state to copy
Optional(const Optional &Other) : Valid(false) {
if (Other.Valid) {
cr(Other.Value);
}
}
/// Creates an instance by moving the state of another one.
///
/// \param Other the instance whose state to obtain
Optional(Optional &&Other) noexcept(
std::is_nothrow_move_constructible<T>::value)
: Valid(false) {
if (Other.Valid) {
cr(std::move(Other.Value));
}
}
/// Destroys \p this object.
~Optional(void) { destroy(); }
/// Updates \p this object by copying the state of another one.
///
/// \param Other the instance whose state to copy
///
/// \return reference of the updated instance
Optional &operator=(const Optional &Other) {
if (Valid) {
if (Other.Valid) {
Value = Other.Value;
} else {
destroy();
}
} else if (Other.Valid) {
cr(Other.Value);
}
return *this;
}
/// Updates \p this object by moving the state of another one.
///
/// \param Other the instance whose state to obtain
///
/// \return reference of the updated instance
Optional &operator=(Optional &&Other) noexcept(
std::is_nothrow_destructible<T>::value
&&std::is_nothrow_move_assignable<T>::value) {
if (Valid) {
if (Other.Valid) {
Value = std::move(Other.Value);
} else {
destroy();
}
} else if (Other.Valid) {
cr(std::move(Other.Value));
}
return *this;
}
/// Checks whether \p this object contains a value.
///
/// \return if \p this object contains a value
explicit operator bool(void) const { return Valid; }
/// Checks whether \p this object does not contain a value.
///
/// \return if \p this object does not contain a value
bool operator!(void)const { return !Valid; }
/// Returns the value stored in \p this object.
///
/// \return reference of the stored value
///
/// \pre \p this object contains a value
T &operator*(void) {
ASSERT(Valid);
return Value;
}
/// Returns the value stored in \p this object.
///
/// \return reference of the stored value
///
/// \pre \p this object contains a value
const T &operator*(void)const {
ASSERT(Valid);
return Value;
}
/// Returns the value stored in \p this object.
///
/// \return pointer to the stored value
///
/// \pre \p this object contains a value
const T *operator->(void)const {
ASSERT(Valid);
return &Value;
}
/// Returns the value stored in \p this object.
///
/// \return pointer of the stored value
///
/// \pre \p this object contains a value
T *operator->(void) {
ASSERT(Valid);
return &Value;
}
/// Returns the value stored in \p this object.
///
/// \return reference of the stored value
///
/// \pre \p this object contains a value
T &value(void) {
ASSERT(Valid);
return Value;
}
/// Returns the value stored in \p this object.
///
/// \return reference of the stored value
///
/// \pre \p this object contains a value
const T &value(void) const {
ASSERT(Valid);
return Value;
}
/// Returns the stored value or a default.
///
/// If \p this object contains a value, then the stored value is returned. A
/// given default value is returned otherwise.
///
/// \param DefaultValue the value to return if \p this object does not contain
/// a value
///
/// \return reference to either the stored value or \p DefaultValue if \p this
/// object does not contain a value
const T &valueOr(const T &DefaultValue) const {
return Valid ? Value : DefaultValue;
}
private:
/// Deallocates the stored value if any.
void destroy(void) {
if (Valid) {
Value.~T();
Valid = false;
}
}
/// Updates the state of \p this object by moving a value into it.
///
/// \tparam V type of \p X
///
/// \param X value to move
///
/// \pre \p this object does not contain a value
template <class V> void cr(V &&X) {
ASSERT(!Valid);
Valid = true;
new (&Value) T(std::forward<V>(X));
}
/// Denotes if \p this object contains a value.
bool Valid;
/// Holds the stored value if any.
union {
T Value; ///< The stored value.
};
};
/// Specialization storing a reference.
///
/// The specialization allows \p rosa::Optional to hold a reference
/// rather than an actual value with minimal overhead.
///
/// \tparam T the base type whose reference is to be stored
template <typename T> class Optional<T &> {
public:
using Type = T;
/// Creates an instance without reference
///
/// \note Use it with its default parameter.
Optional(const none_t & = none) : Value(nullptr) {}
/// Creates a valid instance with reference.
///
/// \param X reference to store in the object
Optional(T &X) : Value(&X) {}
/// Creates a valid instance with reference.
///
/// \param X pointer to store in the object as reference
Optional(T *X) : Value(X) {}
/// Creates an instance as a copy of another one.
///
/// \param Other the instance whose state to copy
Optional(const Optional &Other) = default;
/// Updates \p this object by copying the state of another one.
///
/// \param Other the instance whose state to copy
///
/// \return reference of the updated instance
Optional &operator=(const Optional &Other) = default;
/// Checks whether \p this object contains a reference.
///
/// \return if \p this object contains a reference
explicit operator bool(void) const { return Value != nullptr; }
/// Checks whether \p this object does not contain a reference.
///
/// \return if \p this object does not contain a reference
bool operator!(void)const { return !Value; }
/// Returns the reference stored in \p this object.
///
/// \return the stored reference
///
/// \pre \p this object contains a reference
T &operator*(void) {
ASSERT(Value);
return *Value;
}
/// Returns the value stored in \p this object.
///
/// \return the stored reference
///
/// \pre \p this object contains a reference
const T &operator*(void)const {
ASSERT(Value);
return *Value;
}
/// Returns the value stored in \p this object.
///
/// \return the stored reference
///
/// \pre \p this object contains a reference
T *operator->(void) {
ASSERT(Value);
return Value;
}
/// Returns the value stored in \p this object.
///
/// \return the stored reference
///
/// \pre \p this object contains a reference
const T *operator->(void)const {
ASSERT(Value);
return Value;
}
/// Returns the value stored in \p this object.
///
/// \return the stored reference
///
/// \pre \p this object contains a reference
T &value(void) {
ASSERT(Value);
return *Value;
}
/// Returns the value stored in \p this object.
///
/// \return the stored reference
///
/// \pre \p this object contains a reference
const T &value(void) const {
ASSERT(Value);
return *Value;
}
/// Returns the stored reference or a default.
///
/// If \p this object contains a reference, then the stored reference is
/// returned. A given default value is returned otherwise.
///
/// \param DefaultValue the value to return if \p this object does not contain
/// a reference
///
/// \return either the stored reference or \p DefaultValue if \p this object
/// does not contain a reference
const T &valueOr(const T &DefaultValue) const {
return Value ? Value : DefaultValue;
}
private:
/// The stored reference as a pointer.
T *Value;
};
/// Specialization storing \c void.
///
/// The specialization allows \c rosa::Optional to implement a flag for \c void.
template <> class Optional<void> {
public:
using Type = unit_t;
/// Creates an instance with a \c false flag.
///
/// \note Use it with its default parameter.
Optional(none_t = none) : Value(false) {}
/// Creates an instance with a \c true flag.
///
/// \note The only argument is ignored because it can be *the \c rosa::unit_t
/// value* only.
Optional(unit_t) : Value(true) {}
/// Creates an instance as a copy of another one.
///
/// \param Other the instance whose state to copy
Optional(const Optional &Other) = default;
/// Updates \p this object by copying the state of another one.
///
/// \param Other the instance whose state to copy
///
/// \return reference of the updated instance
Optional &operator=(const Optional &Other) = default;
/// Checks whether \p this object contains a \p true flag.
///
/// \return if \p this object contains a \p true flag.
explicit operator bool(void) const { return Value; }
/// Checks whether \p this object contains a \p false flag.
///
/// \return if \p this object contains a \p false flag.
bool operator!(void)const { return !Value; }
private:
/// The stored flag.
bool Value;
};
///@}
} // End namespace rosa
namespace std {
/// Returns the textual representation of any value of \c rosa::unit_t.
///
/// \return textual representation of \c rosa::UnitType.
inline std::string to_string(const rosa::unit_t &) { return "unit"; }
/// Dumps a \c rosa::Unit to a given \c std::ostream.
///
/// \param [in,out] OS output stream to dump to
/// \param U \c rosa::Unit to dump
///
/// \return \p OS after dumping \p U to it
inline ostream &operator<<(ostream &OS, const rosa::unit_t &U) {
OS << to_string(U);
return OS;
}
/// Returns the textual representation of any value of \c rosa::none_t.
///
/// \return textual representation of \c rosa::NoneType.
inline std::string to_string(const rosa::none_t &) { return "none"; }
/// Dumps a \c rosa::none_t to a given \c std::ostream.
///
/// \param [in,out] OS output stream to dump to
/// \param N \c rosa::none_t to dump
///
/// \return \p OS after dumping \p N to it
inline ostream &operator<<(ostream &OS, const rosa::none_t &N) {
OS << to_string(N);
return OS;
}
} // End namespace std
#endif // ROSA_SUPPORT_TYPES_HPP
diff --git a/include/rosa/support/writer/namespace.h b/include/rosa/support/writer/namespace.h
new file mode 100644
index 0000000..3349a93
--- /dev/null
+++ b/include/rosa/support/writer/namespace.h
@@ -0,0 +1,25 @@
+//===-- rosa/support/writer/namespace.h --------------------------*- C++ -*-===/
+//
+// The RoSA Framework
+//
+//===----------------------------------------------------------------------===/
+///
+/// \file rosa/support/writer/namespace.h
+///
+/// \author David Juhasz (david.juhasz@tuwien.ac.at)
+///
+/// \date 2019
+///
+/// \brief Documentation for the namespace \c rosa::writer.
+///
+//===----------------------------------------------------------------------===/
+
+#ifndef ROSA_SUPPORT_WRITER_NAMESPACE_H
+#define ROSA_SUPPORT_WRITER_NAMESPACE_H
+
+namespace rosa {
+/// Provides facilities to work with writers.
+namespace writer {}
+} // End namespace rosa
+
+#endif // ROSA_SUPPORT_WRITER_NAMESPACE_H
diff --git a/include/rosa/support/writer/split_tuple_writer.hpp b/include/rosa/support/writer/split_tuple_writer.hpp
new file mode 100644
index 0000000..d98ace2
--- /dev/null
+++ b/include/rosa/support/writer/split_tuple_writer.hpp
@@ -0,0 +1,562 @@
+//===-- rosa/support/writer/split_tuple_writer.hpp ---------------*- C++ -*-===/
+//
+// The RoSA Framework
+//
+//===----------------------------------------------------------------------===/
+///
+/// \file rosa/support/writer/split_tuple_writer.hpp
+///
+/// \authors David Juhasz (david.juhasz@tuwien.ac.at)
+///
+/// \date 2019
+///
+/// \brief Facilities to split writers of \c std::tuple into writers of
+/// their elements.
+///
+//===----------------------------------------------------------------------===/
+
+#ifndef ROSA_SUPPORT_WRITER_SPLIT_TUPLE_WRITER_HPP
+#define ROSA_SUPPORT_WRITER_SPLIT_TUPLE_WRITER_HPP
+
+#include "rosa/support/debug.hpp"
+#include "rosa/support/sequence.hpp"
+
+#include <memory>
+#include <tuple>
+#include <queue>
+
+namespace rosa {
+namespace writer {
+
+/// What to do when splitted element writers got destructed so that some
+/// incomplete tuples remain in the buffer.
+enum IncompleteTuplePolicy {
+ Ignore, ///< ignore incomplete tuples, data discarded
+ Flush, ///< flush incomplete tuples, use default value for missing elements
+#ifndef NDEBUG
+ DbgFail ///< fail with an assertion, available only in debug build
+#endif // !defined NDEBUG
+};
+
+/// Anonymous namespace providing implementation for splitting tuple writers;
+/// consider it private.
+namespace {
+
+/// Writes values into a buffer based on an index.
+///
+/// The values are first buffered and flushed out later once a corresponding
+/// tuple gets complete (i.e., other elementwise writers put the corresponding
+/// values into the buffer). Since the underlying buffer waits for tuples to
+/// become complete, the elementwise writers are expected to provide the same
+/// number of values for the buffer (i.e., completing all tuples that have an
+/// element). That is, however, not enforced in any ways.
+///
+/// Once all elementwise writers are destructed, the underlying buffer gets
+/// destructed as well. Should the buffer contain some values, which are
+/// elements of incomplete tuples, the destructor of the buffer handles the
+/// situation according to the \c rosa::writer::IncompleteTuplePolicy set when
+/// the elementwise writers were created by \c rosa::writer::splitTupleWriter().
+///
+/// \tparam TB buffer to write into
+/// \tparam I index of the values to write to \p TB
+///
+/// \note \p TB is expected to implemnet an interface matching that of \c
+/// TupleWriterBuffer.
+///
+/// \note the
+template <typename TB, size_t I>
+class SplittedElementWriter {
+public:
+ /// Type alias for the values the writer writes.
+ using T = typename TB::template element_type<I>;
+
+ /// \defgroup SplittedElemenWriterTypedefs Typedefs of
+ /// \c SplittedElementWriter
+ ///
+ /// Useful `typedef`s for writers.
+ ///
+ ///@{
+ typedef T value_type; ///< Type of values written.
+ typedef T &reference; ///< Reference to the type written
+ ///@}
+
+ /// Creates a new instance.
+ ///
+ /// \param Buffer buffer \p this object writes values into
+ ///
+ /// \note \p Buffer is captured as a \c std::shared_ptr because it is shared
+ /// among writers for its element positions.
+ SplittedElementWriter(std::shared_ptr<TB> Buffer) : Buffer(Buffer) {}
+
+ /// Tells how many values are still in the buffer.
+ ///
+ /// Values are buffered as long as all elements of their corresponding tuples
+ /// are written into the buffer by other elementwise writers and in turn
+ /// complete tuples written by the buffer itself. This function tells how many
+ /// values are still in the buffer waiting for their containing tuples to be
+ /// complete and written out from the buffer.
+ ///
+ /// \note The function simply calles the corresponding function of \p TB.
+ ///
+ /// \return how many values are still in the buffer.
+ size_t buffered(void) const noexcept {
+ return Buffer->template buffered<I>();
+ }
+
+ /// Tells if the last write operation of the buffer was successful.
+ ///
+ /// Values are buffered until their corresponding tuples gets complete. The
+ /// buffer writes complete tuples by the underlying writer. This function
+ /// tells if the buffer successfully has written the latest complete tuple by
+ /// the underlying writer.
+ ///
+ /// Once this function returns \c false, further \c write() calls has no
+ /// effect and the last \c buffered() values will not be written to the
+ /// underlying writer of \c std::tuple.
+ ///
+ /// \note The function simply calles the corresponding function of \p TB.
+ ///
+ /// \return if the last write operation was successful
+ bool good(void) const noexcept { return Buffer->good(); }
+
+ /// Writes an entry to the buffer.
+ ///
+ /// The function has no effect if an earlier write operation of the buffer has
+ /// failed, that is \c good() returns \c false.
+ ///
+ /// \note The function simply calles the corresponding function of \p TB.
+ ///
+ /// \param V value to write
+ void write(const T &V) { Buffer->template write<I>(V); }
+
+private:
+ std::shared_ptr<TB> Buffer; ///< Buffer \p this object writes into
+};
+
+/// Writes an element of a tuple.
+///
+/// \see SplittedElementWriter
+///
+/// \tparam TB type of the buffer \p W writes into
+/// \tparam I index of the values \p W write to \p TB
+///
+/// \param [in,out] W object to write with
+/// \param V value to write
+///
+/// \return \p W after writing \p V with it
+template <typename TB, size_t I>
+SplittedElementWriter<TB, I> &
+operator<<(SplittedElementWriter<TB, I> &W,
+ const typename SplittedElementWriter<TB, I>::value_type &V) {
+ W.write(V);
+ return W;
+}
+
+///\defgroup TupleBufferContainer Type converter turning a \c std::tuple of
+///types into a \c std::tuple of container of those types.
+///
+/// The new type is used for buffering elements of tuples separately.
+///
+///@{
+
+/// Template declaration.
+///
+/// \tparam T type to convert
+///
+/// \note The template is defined only when \p T is a \c std::tuple.
+///
+/// Usage for a type \c Tuple as:\code
+/// typename TupleBufferContainer<Tuple>::Type
+/// \endcode
+template <typename T> struct TupleBufferContainer;
+
+/// Template definition for \c std::tuple.
+template <typename... Ts> struct TupleBufferContainer<std::tuple<Ts...>> {
+ /// The converted type.
+ using Type = std::tuple<std::queue<Ts>...>;
+};
+
+///@}
+
+/// Convenience template type alias for easy use of \c TupleBufferContainer.
+///
+/// Converts a \c std::tuple of types into a \c std::tuple of container of those
+/// types.
+///
+/// \tparam Tuple type to convert
+template <typename Tuple>
+using tuple_buffer_container_t = typename TupleBufferContainer<Tuple>::Type;
+
+/// Buffer for element values for writer of \c std::tuple.
+///
+/// The class can be instantiated with a writer of \c std::tuple and provides
+/// an interface for writing elements into tuples one by one.
+///
+/// \note The class is utilized by \c SplittedElementWriter.
+///
+/// \tparam TupleWriter the writer type that handles values of \c std::tuple
+///
+/// \note The elements of the written \c std::tuple need to be default
+/// constructible because of \c rosa::writer::IncompleteTuplePolicy::Flush.
+///
+/// \todo Consider thread safety of the class.
+template <typename TupleWriter>
+class TupleWriterBuffer {
+public:
+ /// Type alias for the value type of \p TupleWriter.
+ /// \note The type is expected to be \c std::tuple.
+ using writer_value_type = typename TupleWriter::value_type;
+
+ /// The number of elements of \c writer_value_type.
+ static constexpr size_t writer_value_size =
+ std::tuple_size_v<writer_value_type>;
+
+ /// Template type alias to get element types of \c writer_value_type by
+ /// index.
+ /// \tparam I the index of the element
+ template <size_t I>
+ using element_type =
+ typename std::tuple_element<I, writer_value_type>::type;
+
+ /// Type alias for index sequence for accessing elements of \c
+ /// writer_value_size.
+ using element_idx_seq_t = seq_t<writer_value_size>;
+
+ /// Creates a new instance.
+ ///
+ /// \param Writer the writer \p this object uses to write tuples
+ /// \param Policy what to do with incomplete tuples when destructing \p
+ /// this object
+ TupleWriterBuffer(TupleWriter &&Writer,
+ const IncompleteTuplePolicy Policy) noexcept
+ : Writer(Writer), Policy(Policy), Buffer() {}
+
+ /// Destructor.
+ ///
+ /// Should \p this object has some values in \c Buffer (i.e., incomplete
+ /// tuples), the destructor takes care of them according to \c Policy.
+ ///
+ /// \note The destructor may flush the buffered values if the last write
+ /// operation was successful, that is \c good() returns \c true.
+ ~TupleWriterBuffer(void) noexcept {
+ if (good()) {
+ switch (Policy) {
+ default:
+ ASSERT(false && "Unexpected Policy");
+ case Ignore:
+ // Do not care about incomplete tuples in the buffer.
+ break;
+ case Flush:
+ // Whatever we have in the buffer, flush it out.
+ flush();
+ break;
+#ifndef NDEBUG
+ case DbgFail:
+ ASSERT(empty(element_idx_seq_t()) && "Non-empty buffer");
+ break;
+#endif // !defined NDEBUG
+ }
+ }
+ }
+
+ /// Tells how many values are in the buffer for index \p I.
+ ///
+ /// \tparam I the index of the element to check
+ ///
+ /// \return the number of values in buffer for index \p I
+ template <size_t I> size_t buffered(void) const noexcept {
+ return std::get<I>(Buffer).size();
+ }
+
+ /// Tells if the last write operation was successful.
+ ///
+ /// Values are buffered until their corresponding tuples gets complete.
+ /// Once a tuple becomes complete, it is written by \c Writer. This function
+ /// tells if writing the latest complete tuple was successful.
+ ///
+ /// Once this function returns \c false, further \c write<I>() calls has no
+ /// effect and the last \c buffered<I>() values will not be written to the
+ /// underlying writer of \c std::tuple.
+ ///
+ /// \return if the last write operation was successful
+ bool good(void) const noexcept { return Writer.good(); }
+
+ /// Writes an entry to the buffer for index \p I.
+ ///
+ /// The function has no effect if an earlier write operation of the buffer has
+ /// failed, that is \c good() returns \c false.
+ ///
+ /// The value is buffered first and written by \p Writer once its
+ /// corresponding tuple becomes complete (i.e., all other elements of the
+ /// tuple are written into \p this object by corresponding elementwise
+ /// writers).
+ ///
+ /// \tparam I the index of the element to write
+ ///
+ /// \param V value to write
+ template <size_t I> void write(const element_type<I> &V) {
+ ASSERT(!hasCompleteTuple(element_idx_seq_t()));
+ if (good()) {
+ std::get<I>(Buffer).push(V);
+ if (hasCompleteTuple(element_idx_seq_t())) {
+ writeTuple(false);
+ }
+ }
+ ASSERT(!hasCompleteTuple(element_idx_seq_t()));
+ }
+
+private:
+ /// Tells whether \c Buffer is completely empty, all elements of it are empty.
+ ///
+ /// \tparam S0 indices for accessing elements of \c std::tuple
+ ///
+ /// \note The parameter provides indices as template parameter \p S0 and its
+ /// actual value is ignored.
+ ///
+ /// \return whether all elements of \c Buffer are empty
+ template <size_t... S0> bool empty(Seq<S0...>) const noexcept {
+ return (true && ... && std::get<S0>(Buffer).empty());
+ }
+
+ /// Tells whether \c Buffer contains at least one complete tuple.
+ ///
+ /// \tparam S0 indices for accessing elements of \c std::tuple
+ ///
+ /// \note The parameter provides indices as template parameter \p S0 and its
+ /// actual value is ignored.
+ ///
+ /// \return whether there is at least one complete tuple in \c Buffer
+ template <size_t... S0> bool hasCompleteTuple(Seq<S0...>) const noexcept {
+ return (true && ... && !std::get<S0>(Buffer).empty());
+ }
+
+ /// Makes sure \c Buffer has one complete tuple in front.
+ ///
+ /// If the tuple in front of \c Buffer is not complete, missing elements are
+ /// default constructed in \c Buffer to complete the tuple.
+ ///
+ /// \tparam S0 indices for accessing elements of \c std::tuple
+ ///
+ /// \note The parameter provides indices as template parameter \p S0 and its
+ /// actual value is ignored.
+ template <size_t... S0> void makeCompleteTuple(Seq<S0...>) noexcept {
+ auto addDefaultIfEmpty = [](auto &Queue) {
+ if (Queue.empty()) {
+ Queue.emplace(); // default construct a value in the empty queue.
+ }
+ };
+ (addDefaultIfEmpty(std::get<S0>(Buffer)), ...);
+ }
+
+ /// Gives the first complete tuple from \c Buffer.
+ ///
+ /// \tparam S0 indices for accessing elements of \c std::tuple
+ ///
+ /// \note The parameter provides indices as template parameter \p S0 and its
+ /// actual value is ignored.
+ ///
+ /// \return the first complete tuple from \c Buffer
+ ///
+ /// \pre There is at least one complete tuple in \c Buffer:\code
+ /// hasCompleteTuple(element_idx_seq_t())
+ /// \endcode
+ template <size_t... S0> writer_value_type front(Seq<S0...>) const noexcept {
+ ASSERT(hasCompleteTuple(element_idx_seq_t()));
+ return {std::get<S0>(Buffer).front()...};
+ }
+
+ /// Removes the first complete tuple from \c Buffer.
+ ///
+ /// \tparam S0 indices for accessing elements of \c std::tuple
+ ///
+ /// \note The parameter provides indices as template parameter \p S0 and its
+ /// actual value is ignored.
+ ///
+ /// \pre There is at least one complete tuple in \c Buffer:\code
+ /// hasCompleteTuple(element_idx_seq_t())
+ /// \endcode
+ template <size_t... S0> void pop(Seq<S0...>) noexcept {
+ ASSERT(hasCompleteTuple(element_idx_seq_t()));
+ (std::get<S0>(Buffer).pop(), ...);
+ }
+
+ /// Takes the first tuple from \c Buffer and writes it with \c Writer.
+ ///
+ /// \c Buffer need to contain a complete tuple to write it with \c Writer. The
+ /// function makes sure that the tuple in front of \c Buffer is complete if \p
+ /// MakeComplete is true. The tuple in fron of \c Buffer are expected to be
+ /// complete otherwise.
+ ///
+ /// \param MakeComplete whether to make the first tuple complete
+ ///
+ /// \pre There is at least one complete tuple in \c Buffer if not \p
+ /// MakeComplete: \code
+ /// MakeComplete || hasCompleteTuple(element_idx_seq_t())
+ /// \endcode
+ void writeTuple(const bool MakeComplete) noexcept {
+ if (MakeComplete) {
+ makeCompleteTuple(element_idx_seq_t());
+ }
+ ASSERT(hasCompleteTuple(element_idx_seq_t()));
+ Writer.write(front(element_idx_seq_t()));
+ pop(element_idx_seq_t());
+ }
+
+ /// Empties \c Buffer by writing out all tuples from it so that missing
+ /// elements of incomplete tuples are extended with default constructed
+ /// values.
+ void flush(void) noexcept {
+ while (!empty(element_idx_seq_t())) {
+ ASSERT(!hasCompleteTuple(element_idx_seq_t())); // Sanity check.
+ writeTuple(true);
+ }
+ }
+
+ TupleWriter Writer; ///< The splitted writer
+ const IncompleteTuplePolicy Policy; ///< what to do with incomplete tuples
+ ///< when destructing \p this object
+ tuple_buffer_container_t<writer_value_type>
+ Buffer; ///< Container for elementwise buffering
+};
+
+/// Template type alias for writer of an element based on buffered writer of
+/// \c std::tuple.
+///
+/// The alias utilizes \c SplittedElementWriter for writing element values by
+/// \p TB.
+///
+/// \tparam TB buffer to write into
+/// \tparam I index of the values to write to \p TB
+template <typename TB, size_t I>
+using element_writer_t = SplittedElementWriter<TB, I>;
+
+///\defgroup ElementWriters Type converter turning a buffer of \c std::tuple
+///into a \c std::tuple of corresponding \c element_writer_t.
+///
+///@{
+
+/// Template declaration.
+///
+/// \tparam TB buffer to write into
+/// \tparam S type providing indices for accessing elements of \c std::tuple
+///
+/// \note \p TB is expected to implement an interface matching that of \c
+/// TupleWriterBuffer.
+///
+/// \note The template is defined only when \p S is a \c rosa::Seq.
+///
+/// Usage for a proper buffer type \c TB:\code
+/// typename ElementIteratorWriters<TB, typename TB::element_idx_seq_t>::Type
+/// \endcode
+template <typename TB, typename S> struct ElementWriters;
+
+/// Template definition.
+template <typename TB, size_t... S0>
+struct ElementWriters<TB, Seq<S0...>> {
+ /// The converted type.
+ using Type = std::tuple<element_writer_t<TB, S0>...>;
+};
+
+///@}
+
+/// Convenience template type alias for easy use of \c ElementIteratorWriters.
+///
+/// Converts a buffer of \c std::tuple into a \c std::tuple of corresponding \c
+/// element_writer_t.
+///
+/// \tparam TB buffer to write into
+///
+/// \note \p TB is expected to implement an interface matching that of \c
+/// TupleWriterBuffer.
+template <typename TB>
+using element_writers_t =
+ typename ElementWriters<TB, typename TB::element_idx_seq_t>::Type;
+
+/// Template type alias for turning a writer of \c std::tuple into
+/// corresponding \c element_writers_t.
+///
+/// The alias utilizes \c TupleWriterBuffer for buffering values before writing
+/// them by \p TupleWriter. The elementwise writers are \c
+/// SplittedElementWriter.
+///
+/// \tparam TupleWriter writer of \c std::tuple
+template <typename TupleWriter>
+using splitted_tuple_writers_t =
+ element_writers_t<TupleWriterBuffer<TupleWriter>>;
+
+/// Creates elementwise writers for a writer of \c std::tuple.
+///
+/// \note Implementation for \c rosa::iterator::splitTupleWriter.
+///
+/// \tparam TupleWriter writer of \c std::tuple
+/// \tparam S0 indices for accessing elements of \c std::tuple
+///
+/// \param Writer the writer to write tuples with
+/// \param Policy how to handle incomplete tuples when destructing the
+/// underlying buffer
+///
+/// \note The last parameter provides indices as template parameter \p S0 and
+/// its actual value is ignored.
+///
+/// \return \c std::tuple of elementwise writers corresponding to \p Writer
+template <typename TupleWriter, size_t... S0>
+splitted_tuple_writers_t<TupleWriter>
+splitTupleWriterImpl(TupleWriter &&Writer, const IncompleteTuplePolicy Policy,
+ Seq<S0...>) noexcept {
+ using TB = TupleWriterBuffer<TupleWriter>;
+ // Create a buffer in shared pointer. The buffer will be destructed once
+ // all corresponding element writers (created below) have been destructed.
+ auto Buffer = std::make_shared<TB>(std::move(Writer), Policy);
+ return {element_writer_t<TB, S0>(Buffer)...};
+}
+
+} // End namespace
+
+/// Creates elementwise writers for a writer of \c std::tuple.
+///
+/// \note The implementation utilizes \c splitTupleWriterImpl.
+///
+/// Obtain elementwise writers for a writer \p Writer of type \c TupleWriter of
+/// \c std::tuple<T1, T2, T3> as \code
+/// auto [T1Writer, T2Writer, T3Writer] = splitTupleWriter(std::move(Writer),
+/// Ignore);
+/// \endcode
+///
+/// The function returns a tuple of writers, which can be assigned to variables
+/// using structured binding.
+///
+/// The function moves its first argument (i.e., \p Writer cannot be used
+/// directly after splitting it). If the first argument is a variable, it needs
+/// to be moved explicitly by \c std::move() as in the example.
+///
+/// While a tuple could be written with \p Writer directly as \code
+/// Writer << std::make_tuple(T1(), T2(), T3());
+/// \endcode The same tuple is written with splitted writers as \code
+/// T1Writer << T1();
+/// T2Writer << T2();
+/// T3Writer << T3();
+/// \endcode
+///
+/// \tparam TupleWriter writer of \c std::tuple
+///
+/// \note The elements of the written \c std::tuple need to be default
+/// constructible because of \c rosa::writer::IncompleteTuplePolicy::Flush.
+///
+/// \param Writer the writer to write tuples with
+/// \param Policy what to do with incomplete tuples when destructing the
+/// underlying buffer
+///
+/// \return \c std::tuple of elementwise writers corresponding to \p Writer
+template <typename TupleWriter>
+splitted_tuple_writers_t<TupleWriter>
+splitTupleWriter(TupleWriter &&Writer,
+ const IncompleteTuplePolicy Policy) noexcept {
+ return splitTupleWriterImpl(
+ std::move(Writer), Policy,
+ seq_t<std::tuple_size_v<typename TupleWriter::value_type>>());
+}
+
+} // End namespace writer
+} // End namespace rosa
+
+#endif // ROSA_SUPPORT_WRITER_SPLIT_TUPLE_WRITER_HPP
diff --git a/lib/agent/Abstraction.cpp b/lib/agent/Abstraction.cpp
index 843b2ff..9d07fee 100644
--- a/lib/agent/Abstraction.cpp
+++ b/lib/agent/Abstraction.cpp
@@ -1,20 +1,26 @@
//===-- agent/Abstraction.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 agent/Abstraction.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation for rosa/agent/Abstraction.hpp.
///
/// \note Empty implementation, source file here to have a compile database
/// entry for rosa/agent/Abstraction.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/agent/Abstraction.hpp"
diff --git a/lib/agent/Confidence.cpp b/lib/agent/Confidence.cpp
index 1045652..f97657a 100644
--- a/lib/agent/Confidence.cpp
+++ b/lib/agent/Confidence.cpp
@@ -1,20 +1,26 @@
//===-- agent/Confidence.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 agent/Confidence.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation for rosa/agent/Confidence.hpp.
///
/// \note Empty implementation, source file here to have a compile database
/// entry for rosa/agent/Confidence.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/agent/Confidence.hpp"
diff --git a/lib/agent/FunctionAbstractions.cpp b/lib/agent/FunctionAbstractions.cpp
index 0206760..a48a9a1 100644
--- a/lib/agent/FunctionAbstractions.cpp
+++ b/lib/agent/FunctionAbstractions.cpp
@@ -1,20 +1,26 @@
//===-- agent/FunctionAbstractions.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 agent/FunctionAbstractions.cpp
///
/// \author Benedikt Tutzer (benedikt.tutzer@tuwien.ac.at)
///
/// \date 2019
///
/// \brief Implementation for rosa/agent/FunctionAbstractions.hpp.
///
/// \note Empty implementation, source file here to have a compile database
/// entry for rosa/agent/FunctionAbstractions.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/agent/FunctionAbstractions.hpp"
diff --git a/lib/agent/Functionality.cpp b/lib/agent/Functionality.cpp
index f6b60c4..3044e73 100644
--- a/lib/agent/Functionality.cpp
+++ b/lib/agent/Functionality.cpp
@@ -1,20 +1,26 @@
//===-- agent/Functionality.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 agent/Functionality.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation for rosa/agent/Functionality.h.
///
/// \note Empty implementation, source file here to have a compile database
/// entry for rosa/agent/Functionality.h.
///
//===----------------------------------------------------------------------===//
#include "rosa/agent/Functionality.h"
diff --git a/lib/agent/History.cpp b/lib/agent/History.cpp
index 7edb9dd..d2dbab5 100644
--- a/lib/agent/History.cpp
+++ b/lib/agent/History.cpp
@@ -1,20 +1,26 @@
//===-- agent/History.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 agent/History.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation for rosa/agent/History.hpp.
///
/// \note Empty implementation, source file here to have a compile database
/// entry for rosa/agent/History.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/agent/History.hpp"
diff --git a/lib/agent/RangeConfidence.cpp b/lib/agent/RangeConfidence.cpp
index 415f11f..d373ca6 100644
--- a/lib/agent/RangeConfidence.cpp
+++ b/lib/agent/RangeConfidence.cpp
@@ -1,20 +1,26 @@
//===-- agent/RangeConfidence.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 agent/RangeConfidence.cpp
///
/// \author Benedikt Tutzer (benedikt.tutzer@tuwien.ac.at)
///
/// \date 2019
///
/// \brief Implementation for rosa/agent/RangeConfidence.hpp.
///
/// \note Empty implementation, source file here to have a compile database
/// entry for rosa/agent/RangeConfidence.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/agent/RangeConfidence.hpp"
diff --git a/lib/agent/namespace.cpp b/lib/agent/namespace.cpp
index cfac9b9..db5c01c 100755
--- a/lib/agent/namespace.cpp
+++ b/lib/agent/namespace.cpp
@@ -1,20 +1,26 @@
//===-- agent/namespace.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 agent/namespace.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation for rosa/agent/namespace.h.
///
/// \note Empty implementation, source file here to have a compile database
/// entry for rosa/agent/namespace.h.
///
//===----------------------------------------------------------------------===//
#include "rosa/agent/namespace.h"
diff --git a/lib/config/config.cpp b/lib/config/config.cpp
index 2661ad3..37b170b 100644
--- a/lib/config/config.cpp
+++ b/lib/config/config.cpp
@@ -1,20 +1,26 @@
//===-- config/config.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 config/config.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation for rosa/config/config.h.
///
/// \note Empty implementation, source file here to have a compile database
/// entry for rosa/config/config.h.
///
//===----------------------------------------------------------------------===//
#include "rosa/config/config.h"
diff --git a/lib/config/namespace.cpp b/lib/config/namespace.cpp
index 2cd14c1..59c0648 100755
--- a/lib/config/namespace.cpp
+++ b/lib/config/namespace.cpp
@@ -1,20 +1,26 @@
//===-- config/namespace.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 config/namespace.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Placeholder for rosa/config/namespace.h.
///
/// \note Empty implementation, source file here to have a compile database
/// entry for rosa/config/namespace.h.
///
//===----------------------------------------------------------------------===//
#include "rosa/config/namespace.h"
diff --git a/lib/config/project_path.cpp b/lib/config/project_path.cpp
index 1f08352..b4f940e 100644
--- a/lib/config/project_path.cpp
+++ b/lib/config/project_path.cpp
@@ -1,20 +1,26 @@
//===-- config/project_path.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 config/project_path.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation for rosa/config/project_path.hpp.
///
/// \note Empty implementation, source file here to have a compile database
/// entry for rosa/config/project_path.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/config/project_path.hpp"
diff --git a/lib/config/version.cpp b/lib/config/version.cpp
index 73bc786..91115c1 100644
--- a/lib/config/version.cpp
+++ b/lib/config/version.cpp
@@ -1,38 +1,44 @@
//===-- config/version.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 config/version.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation for rosa/config/version.h.
///
//===----------------------------------------------------------------------===//
#include "rosa/config/version.h"
#include <sstream>
namespace rosa {
std::string library_string(void) { return PACKAGE_STRING; }
std::string version(void) { return PACKAGE_VERSION; }
std::string verbose_version(void) {
std::stringstream SS;
SS << PACKAGE_STRING << "\nBuilt by " << CMAKE_GENERATOR << " with "
<< CMAKE_CXX_COMPILER_ID << ' ' << CMAKE_CXX_COMPILER_VERSION
<< "\non a(n) " << CMAKE_SYSTEM << " system."
<< "\nBuild date: " << BUILD_DATE << "\nPackage name: " << PACKAGE_NAME
<< "\nPackage version: " << PACKAGE_VERSION
<< "\nReport issues: " << PACKAGE_BUGREPORT;
return SS.str();
}
} // End namespace rosa
diff --git a/lib/core/AbstractAgent.cpp b/lib/core/AbstractAgent.cpp
index e76803e..69c2d27 100644
--- a/lib/core/AbstractAgent.cpp
+++ b/lib/core/AbstractAgent.cpp
@@ -1,20 +1,26 @@
//===-- core/AbstractAgent.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 core/AbstractAgent.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation of rosa/core/AbstractAgent.hpp.
///
/// \note Empty implementation, source file here to have a compile database
/// entry for rosa/core/AbstractAgent.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/core/AbstractAgent.hpp"
diff --git a/lib/core/Agent.cpp b/lib/core/Agent.cpp
index ebfd576..8c267f5 100644
--- a/lib/core/Agent.cpp
+++ b/lib/core/Agent.cpp
@@ -1,55 +1,61 @@
//===-- core/Agent.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 core/Agent.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation of rosa/core/Agent.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/core/Agent.hpp"
namespace rosa {
Agent::~Agent(void) { LOG_TRACE("Destroying Agent..."); }
Agent::operator bool(void) const noexcept {
// An Agent itself is always valid.
return true;
}
bool Agent::operator==(const AgentHandle &H) const noexcept {
// Return if the \c rosa::Agent wrapped by \p H is this very object.
return this == &H.A;
}
bool Agent::operator<(const AgentHandle &H) const noexcept {
// Compare memory addresses.
return this < &H.A;
}
AgentHandle Agent::self(void) noexcept { return Self; }
void Agent::sendMessage(message_t &&M) noexcept {
system().send(Self, std::move(M));
}
std::string Agent::dump(void) const noexcept {
LOG_TRACE("Dumping Agent '" + FullName + "'");
return "[Agent] " + FullName;
}
MessagingSystem &Agent::system(void) const noexcept {
// \note The \c rosa::System the \c rosa::Unit is created with is a
// \c rosa::MessagingSystem.
return static_cast<MessagingSystem &>(Unit::system());
}
} // End namespace rosa
diff --git a/lib/core/AgentHandle.cpp b/lib/core/AgentHandle.cpp
index 5a15789..75a6e7b 100644
--- a/lib/core/AgentHandle.cpp
+++ b/lib/core/AgentHandle.cpp
@@ -1,55 +1,61 @@
//===-- core/AgentHandle.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 core/AgentHandle.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation of rosa/core/AgentHandle.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/core/AgentHandle.hpp"
#include "rosa/core/Agent.hpp"
namespace rosa {
AgentHandle::AgentHandle(Agent &A, bool) noexcept : A(A), S(A.system()) {}
AgentHandle::AgentHandle(Agent &A) : A(A), S(A.system()) {
ASSERT(S.isUnitRegistered(A));
}
AgentHandle::operator bool(void) const noexcept {
// \note The referred \c rosa::MessageSystem is supposed to be still alive.
return S.isUnitRegistered(A);
}
bool AgentHandle::operator==(const AgentHandle &H) const noexcept {
// Return if the referred \c rosa::Agent is the same object in both
// \c rosa::AgentHandler instances.
return &A == &H.A;
}
bool AgentHandle::operator<(const AgentHandle &H) const noexcept {
// Compare memory addresses of the referred \c rosa::Agent instances.
return &A < &H.A;
}
AgentHandle AgentHandle::self(void) noexcept {
// Return a copy of \p this object.
return *this;
}
void AgentHandle::sendMessage(message_t &&M) noexcept {
ASSERT(bool(*this));
S.send(*this, std::move(M));
}
} // End namespace rosa
diff --git a/lib/core/Invoker.cpp b/lib/core/Invoker.cpp
index c9d21dd..d9256ec 100644
--- a/lib/core/Invoker.cpp
+++ b/lib/core/Invoker.cpp
@@ -1,25 +1,31 @@
//===-- core/Invoker.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 core/Invoker.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation of rosa/core/Invoker.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/core/Invoker.hpp"
namespace rosa {
Invoker::Invoker(void) noexcept { LOG_TRACE("Creating Invoker"); }
Invoker::~Invoker(void) { LOG_TRACE("Destroying Invoker"); }
} // End namespace rosa
diff --git a/lib/core/Message.cpp b/lib/core/Message.cpp
index bc5f8fe..7e3b280 100644
--- a/lib/core/Message.cpp
+++ b/lib/core/Message.cpp
@@ -1,23 +1,29 @@
//===-- core/Message.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 core/Message.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation of rosa/core/Message.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/core/Message.hpp"
namespace rosa {
Message::~Message(void) { LOG_TRACE("Destroying Message"); }
} // End namespace rosa
diff --git a/lib/core/MessageHandler.cpp b/lib/core/MessageHandler.cpp
index ff44f3f..9df17d5 100644
--- a/lib/core/MessageHandler.cpp
+++ b/lib/core/MessageHandler.cpp
@@ -1,39 +1,45 @@
//===-- core/MessageHandler.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 core/MessageHandler.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation of rosa/core/MessageHandler.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/core/MessageHandler.hpp"
#include <algorithm>
namespace rosa {
MessageHandler::~MessageHandler(void) {
LOG_TRACE("Destroying MessageHandler");
}
bool MessageHandler::canHandle(const Message &Msg) const noexcept {
return std::any_of(Invokers.begin(), Invokers.end(),
[&Msg](const invoker_t &I) { return I->match(Msg); });
}
bool MessageHandler::operator()(const Message &Msg) const noexcept {
return std::any_of(Invokers.begin(), Invokers.end(),
[&Msg](const invoker_t &I) {
return (*I)(Msg) == Invoker::result_t::Invoked;
});
}
} // End namespace rosa
diff --git a/lib/core/MessageMatcher.cpp b/lib/core/MessageMatcher.cpp
index 77e8efd..1ec662b 100755
--- a/lib/core/MessageMatcher.cpp
+++ b/lib/core/MessageMatcher.cpp
@@ -1,20 +1,26 @@
//===-- core/MessageMatcher.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 core/MessageMatcher.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation of rosa/core/MessageMatcher.hpp.
///
/// \note Empty implementation, source file here to have a compile database
/// entry for rosa/core/MessageMatcher.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/core/MessageMatcher.hpp"
diff --git a/lib/core/MessagingSystem.cpp b/lib/core/MessagingSystem.cpp
index 30d5879..83dc020 100644
--- a/lib/core/MessagingSystem.cpp
+++ b/lib/core/MessagingSystem.cpp
@@ -1,34 +1,40 @@
//===-- core/MessagingSystem.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 core/MessagingSystem.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation of rosa/core/MessagingSystem.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/core/MessagingSystem.hpp"
#include "rosa/core/Agent.hpp"
#include "MessagingSystemImpl.hpp"
namespace rosa {
std::unique_ptr<MessagingSystem>
MessagingSystem::createSystem(const std::string &Name) noexcept {
return std::unique_ptr<MessagingSystem>(new MessagingSystemImpl(Name));
}
void MessagingSystem::destroyAgent(const AgentHandle &H) noexcept {
destroyUnit(unwrapAgent(H));
}
} // End namespace rosa
diff --git a/lib/core/MessagingSystemImpl.cpp b/lib/core/MessagingSystemImpl.cpp
index ab28444..be07339 100644
--- a/lib/core/MessagingSystemImpl.cpp
+++ b/lib/core/MessagingSystemImpl.cpp
@@ -1,34 +1,40 @@
//===-- core/MessageingSystemImpl.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 core/MessagingSystemImpl.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation of core/MessagingSystemImpl.hpp.
///
//===----------------------------------------------------------------------===//
#include "MessagingSystemImpl.hpp"
#include "rosa/core/Agent.hpp"
namespace rosa {
MessagingSystemImpl::MessagingSystemImpl(const std::string &Name) noexcept
: MessagingSystem(),
SystemImpl(Name) {
LOG_TRACE("System '" + Name + "' is a MessagingSystem");
}
void MessagingSystemImpl::send(const AgentHandle &H, message_t &&M) noexcept {
ASSERT(*this == unwrapSystem(H) && isUnitRegistered(unwrapAgent(H)));
unwrapAgent(H)(*M);
}
} // End namespace rosa
diff --git a/lib/core/MessagingSystemImpl.hpp b/lib/core/MessagingSystemImpl.hpp
index 485711a..fa0dbe8 100644
--- a/lib/core/MessagingSystemImpl.hpp
+++ b/lib/core/MessagingSystemImpl.hpp
@@ -1,109 +1,116 @@
//===-- core/MessagingSystemImpl.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 core/MessagingSystemImpl.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Declaration of a basic implementation of the \c rosa::MessagingSystem
/// interface.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_LIB_CORE_MESSAGINGSYSTEMIMPL_HPP
#define ROSA_LIB_CORE_MESSAGINGSYSTEMIMPL_HPP
#include "SystemImpl.hpp"
#include "rosa/core/MessagingSystem.hpp"
namespace rosa {
/// Implements \c rosa::MessagingSystem by extending \c rosa::SystemImpl with
/// adding a simple implementation of sending messages: directly invoking
/// \c rosa::Agent instances with given \c rosa::Message objects.
///
/// \note Keep in mind that sending a \c rosa::Message object with this
/// implementation translates into a direct function call.
class MessagingSystemImpl : public MessagingSystem, public SystemImpl {
/// Alies for the base-class \c rosa::SystemImpl.
using Base = SystemImpl;
public:
/// Creates an instance.
///
/// \param Name name of the new instance
MessagingSystemImpl(const std::string &Name) noexcept;
- /// \defgroup MessagingSystemImplCallForwarding Call forwardings of rosa::MessagingSystemImpl
+ /// \defgroup MessagingSystemImplCallForwarding Call forwardings of
+ /// rosa::MessagingSystemImpl
///
/// \c rosa::MessagingSystemImpl call forwardings
///
/// \note Simply forwarding calls to implementations provided by
/// \c rosa::MessagingSystem::Base for the \c rosa::System interface.
///
/// \todo How could we use the inherited implementations in a simpler way?
///@{
bool operator==(const System &Other) const noexcept override {
return Base::operator==(Other);
}
protected:
id_t nextId(void) noexcept override { return Base::nextId(); }
bool isSystemCleaned(void) const noexcept override {
return Base::isSystemCleaned();
}
void markCleaned(void) noexcept override { Base::markCleaned(); }
void registerUnit(Unit &U) noexcept override { Base::registerUnit(U); }
void destroyUnit(Unit &U) noexcept override { Base::destroyUnit(U); }
bool isUnitRegistered(const Unit &U) const noexcept override {
return Base::isUnitRegistered(U);
}
public:
const std::string &name(void) const noexcept override { return Base::name(); }
size_t numberOfConstructedUnits(void) const noexcept override {
return Base::numberOfConstructedUnits();
}
size_t numberOfLiveUnits(void) const noexcept override {
return Base::numberOfLiveUnits();
}
bool empty(void) const noexcept override { return Base::empty(); }
///@}
/// Sends a \c rosa::message_t instance to the \c rosa::Agent instance
/// referred by a \c rosa::AgentHandle -- by directly invoking the
/// \c rosa::Agent instance with the \c rosa::Message object.
///
/// \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
void send(const AgentHandle &H, message_t &&M) noexcept override;
};
} // End namespace rosa
#endif // ROSA_LIB_CORE_MESSAGINGSYSTEMIMPL_HPP
diff --git a/lib/core/System.cpp b/lib/core/System.cpp
index 89ed6a0..3768cc1 100644
--- a/lib/core/System.cpp
+++ b/lib/core/System.cpp
@@ -1,27 +1,33 @@
//===-- core/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 core/System.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation of rosa/core/System.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/core/System.hpp"
#include "SystemImpl.hpp"
namespace rosa {
std::unique_ptr<System> System::createSystem(const std::string &Name) noexcept {
return std::unique_ptr<System>(new SystemImpl(Name));
}
} // End namespace rosa
diff --git a/lib/core/SystemBase.cpp b/lib/core/SystemBase.cpp
index 2674c2b..a4a041f 100644
--- a/lib/core/SystemBase.cpp
+++ b/lib/core/SystemBase.cpp
@@ -1,62 +1,68 @@
//===-- core/SystemBase.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 core/SystemBase.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation of rosa/core/SystemBase.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/core/SystemBase.hpp"
namespace rosa {
SystemBase::SystemBase(const std::string &Name) noexcept
: Name(Name),
UnitCount(0),
SystemIsCleaned(false) {
LOG_TRACE("Creating System '" + Name + "'");
}
SystemBase::~SystemBase(void) {
if (!SystemIsCleaned) {
ROSA_CRITICAL("Trying to destroy an uncleaned System '" + Name + "'");
}
LOG_TRACE("Destroying System '" + Name + "'");
}
bool SystemBase::operator==(const System &Other) const noexcept {
return &Name == &Other.name();
}
const std::string &SystemBase::name() const noexcept { return Name; }
id_t SystemBase::nextId(void) noexcept { return ++UnitCount; }
bool SystemBase::isSystemCleaned(void) const noexcept {
return SystemIsCleaned;
}
void SystemBase::markCleaned(void) noexcept {
if (SystemIsCleaned) {
ROSA_CRITICAL("System '" + Name + "' has been already mark cleaned");
} else if (!empty()) {
ROSA_CRITICAL("Trying to mark a non-empty System '" + Name + "'");
} else {
SystemIsCleaned = true;
LOG_TRACE("System '" + Name + "' is marked cleaned");
}
}
size_t SystemBase::numberOfConstructedUnits(void) const noexcept {
return UnitCount;
}
} // End namespace rosa
diff --git a/lib/core/SystemImpl.cpp b/lib/core/SystemImpl.cpp
index 4815159..c7b7288 100644
--- a/lib/core/SystemImpl.cpp
+++ b/lib/core/SystemImpl.cpp
@@ -1,71 +1,77 @@
//===-- core/SystemImpl.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 core/SystemImpl.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation for core/SystemImpl.hpp.
///
//===----------------------------------------------------------------------===//
#include "SystemImpl.hpp"
#include "rosa/core/Unit.h"
namespace rosa {
SystemImpl::SystemImpl(const std::string &Name) noexcept : SystemBase(Name) {}
SystemImpl::~SystemImpl(void) {
if (!empty()) {
ROSA_CRITICAL("Trying to destroy a non-empty System '" + Name + "'");
} else {
markCleaned();
}
}
void SystemImpl::registerUnit(Unit &U) noexcept {
ASSERT(!isUnitRegistered(U) && "Unit already registered!");
// Obtain exclusive access and insert the \c rosa::Unit instance.
std::lock_guard<std::mutex> L(RegisterMutex);
auto R = Units.insert(&U);
if (!R.second) {
ROSA_CRITICAL("Could not register Unit");
}
}
void SystemImpl::destroyUnit(Unit &U) noexcept {
ASSERT(isUnitRegistered(U));
LOG_TRACE("Destroying Unit '" + U.FullName + "'");
// Scope protected container access.
{
// Obtain exclusive access and remove the Unit.
std::lock_guard<std::mutex> L(RegisterMutex);
auto R = Units.erase(&U);
// \note This case is catched by assertion when that is enabled.
if (!R) {
ROSA_CRITICAL("Trying to remove unregistered Unit");
}
}
delete &U;
}
bool SystemImpl::isUnitRegistered(const Unit &U) const noexcept {
// \note Casting away constness is safe here.
return Units.find(const_cast<Unit *>(&U)) != Units.cend();
}
size_t SystemImpl::numberOfLiveUnits(void) const noexcept {
return Units.size();
}
bool SystemImpl::empty(void) const noexcept { return Units.empty(); }
} // End namespace rosa
diff --git a/lib/core/SystemImpl.hpp b/lib/core/SystemImpl.hpp
index b3615fd..1b50900 100644
--- a/lib/core/SystemImpl.hpp
+++ b/lib/core/SystemImpl.hpp
@@ -1,104 +1,110 @@
//===-- core/SystemImpl.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 core/SystemImpl.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Declaration of a basic implementation of the \c rosa::System
/// interface.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_LIB_CORE_SYSTEMIMPL_HPP
#define ROSA_LIB_CORE_SYSTEMIMPL_HPP
#include "rosa/core/SystemBase.hpp"
#include <mutex>
#include <unordered_set>
namespace rosa {
/// A basic implementation of the \c rosa::System interface, which is based on
/// \c rosa::SystemBase and simply stores \c rosa::Unit instances in a set.
class SystemImpl : public SystemBase {
public:
/// Creates a new instance.
///
/// \param Name name of the new instance
SystemImpl(const std::string &Name) noexcept;
/// Destroys \p this object.
///
/// \note The destructor deallocates internal resources and marks \p this
/// object cleaned.
///
/// \pre No live \c rosa::Unit instances are managed by \p this object:\code
/// empty()
/// \endcode
~SystemImpl(void);
private:
/// Stores references for the registered \c rosa::Unit instances.
std::unordered_set<Unit *> Units;
/// Provides mutual exclusion when modifying \c rosa::Unit instances.
std::mutex RegisterMutex;
protected:
/// 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
void registerUnit(Unit &U) noexcept override;
/// 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:\code
/// !isUnitRegistered(U)
/// \endcode Moreover, \p U is destroyed.
void destroyUnit(Unit &U) noexcept override;
/// 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
bool isUnitRegistered(const Unit &U) const noexcept override;
public:
/// 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
size_t numberOfLiveUnits(void) const noexcept override;
/// Tells if \p this object has no live \c rosa::Unit instances.
///
/// \return whether \p this object has any live \c rosa::Unit instances
bool empty(void) const noexcept override;
};
} // End namespace rosa
#endif // ROSA_LIB_CORE_SYSTEMIMPL_HPP
diff --git a/lib/core/Unit.cpp b/lib/core/Unit.cpp
index 007ef55..397076f 100644
--- a/lib/core/Unit.cpp
+++ b/lib/core/Unit.cpp
@@ -1,53 +1,59 @@
//===-- core/Unit.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 core/Unit.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2019
///
/// \brief Implementation of rosa/core/Unit.h.
///
//===----------------------------------------------------------------------===//
#include "rosa/core/Unit.h"
#include "rosa/core/System.hpp" // NOLINT
#include "rosa/support/debug.hpp"
#include "rosa/support/log.h"
namespace rosa {
Unit::Unit(const AtomValue Kind, const id_t Id, const std::string &Name,
System &S) noexcept : Kind(Kind),
Id(Id),
Name(Name),
S(S),
FullName(Name + "@" + S.name()) {
ASSERT(!Name.empty());
LOG_TRACE("Constructing Unit '" + FullName + "' of kind '" +
std::to_string(Kind) + "'");
}
Unit::~Unit(void) { LOG_TRACE("Destroying Unit '" + FullName + "'"); }
/// The default implementation of \c rosa::Unit::dump emits
/// \c rosa::Unit::FullName.
std::string Unit::dump(void) const noexcept {
LOG_TRACE("Dumping Unit '" + FullName + "'");
return "[Unit] " + FullName;
}
System &Unit::system(void) const noexcept { return S; }
std::ostream &operator<<(std::ostream &OS, const Unit &U) {
OS << U.dump();
return OS;
}
} // End namespace rosa
diff --git a/lib/deluxe/DeluxeAgent.cpp b/lib/deluxe/DeluxeAgent.cpp
index 8f6669e..47685c6 100644
--- a/lib/deluxe/DeluxeAgent.cpp
+++ b/lib/deluxe/DeluxeAgent.cpp
@@ -1,313 +1,304 @@
//===-- deluxe/DeluxeAgent.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 deluxe/DeluxeAgent.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2019
///
/// \brief Implementation of rosa/deluxe/DeluxeAgent.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/deluxe/DeluxeAgent.hpp"
#include "rosa/deluxe/DeluxeSystem.hpp"
#include <algorithm>
namespace rosa {
namespace deluxe {
bool DeluxeAgent::inv(void) const noexcept {
// Check execution policy.
// \note The \c rosa::System the \c rosa::Unit is created with is a
// \c rosa::DeluxeSystem.
const DeluxeSystem &DS = static_cast<DeluxeSystem &>(Unit::system());
if (!ExecutionPolicy || !ExecutionPolicy->canHandle(Self, DS)) {
return false;
}
// Check number of inputs and master-outputs.
if (NumberOfInputs != NumberOfMasterOutputs) {
return false;
}
// Check container sizes.
if (!(InputTypes.size() == NumberOfInputs &&
InputNextPos.size() == NumberOfInputs &&
InputChanged.size() == NumberOfInputs &&
- InputStorageOffsets.size() == NumberOfInputs &&
- InputValues->size() ==
- InputStorageOffsets[NumberOfInputs - 1] +
- lengthOfToken(InputTypes[NumberOfInputs - 1]) &&
+ InputValues.size() == NumberOfInputs &&
MasterOutputTypes.size() == NumberOfInputs // == NumberOfMasterOutputs
&& Slaves.size() == NumberOfInputs)) {
return false;
}
- // Stores storage offset for the next slave position checked in the following
- // loop.
- token_size_t InputStorageOffset = 0;
-
// Check *slave* types and validate *slave* registrations and reverse lookup
// information.
std::map<id_t, size_t> RefIds; // Build up a reference of SlaveIds in this.
for (size_t I = 0; I < NumberOfInputs; ++I) {
- // First, validate the corresponding storage offset value.
- if (InputStorageOffsets[I] != InputStorageOffset) {
- return false;
- }
-
// Fetch type-related information for the input position.
const Token T = InputTypes[I];
const token_size_t TL = lengthOfToken(T);
- const size_t StorageOffset = InputStorageOffsets[I];
- // Update storage offset for the next position.
- InputStorageOffset += TL;
+ // Validate size of storage at position \c I.
+ if (InputValues[I]->size() != TL) {
+ return false;
+ }
// Validate input types at position \c I.
for (token_size_t TI = 0; TI < TL; ++TI) {
- const size_t ElemOffset = StorageOffset + TI;
- // The assert must hold if \p this object was successfuuly constructed.
- ASSERT(static_cast<size_t>(static_cast<token_size_t>(ElemOffset)) ==
- ElemOffset);
- if (InputValues->typeAt(static_cast<token_size_t>(ElemOffset)) !=
+ if (InputValues[I]->typeAt(static_cast<token_size_t>(TI)) !=
typeAtPositionOfToken(T, TI)) {
return false;
}
}
// Check the index of next expected element for position \c I.
if (InputNextPos[I] >= TL) {
return false;
}
// Check the registered *slave* at position \c I.
const auto &Slave = Slaves[I];
// If \c Slave is empty, nothing to check.
if (!Slave)
continue;
// Prepare master-output related info for the *slave*.
const Token MT = MasterOutputTypes[I];
const bool hasMT = !emptyToken(MT);
// \c Slave is not empty here.
// Check the `OutputType` and `MasterInputType` of the registered *slave*.
const auto &A = unwrapAgent(*Slave);
if (!((A.Kind == atoms::SensorKind &&
static_cast<const DeluxeSensor &>(A).OutputType == T &&
(!hasMT ||
static_cast<const DeluxeSensor &>(A).MasterInputType == MT)) ||
(A.Kind == atoms::AgentKind &&
static_cast<const DeluxeAgent &>(A).OutputType == T &&
(!hasMT ||
static_cast<const DeluxeAgent &>(A).MasterInputType == MT)))) {
return false;
}
// Validate that the *slave* is not registered more than once.
if (std::any_of(
Slaves.begin() + I + 1, Slaves.end(),
[&Slave](const Optional<AgentHandle> &O) { return O && *Slave == *O; })) {
return false;
}
// Build the content of \c RefIds.
RefIds.emplace(A.Id, I);
}
// Validate *slave* reverse lookup information against our reference.
if (RefIds != SlaveIds) {
return false;
}
// Check the size of the master-input storage.
if (MasterInputValue->size() != lengthOfToken(MasterInputType)) {
return false;
}
// Check the index of next expected element from the *master*.
const token_size_t MITL = lengthOfToken(MasterInputType);
if ((MITL != 0 && MasterInputNextPos >= MITL) ||
(MITL == 0 && MasterInputNextPos != 0)) {
return false;
}
// All checks were successful, the invariant is held.
return true;
}
DeluxeAgent::~DeluxeAgent(void) noexcept {
ASSERT(inv());
LOG_TRACE_STREAM << "Destroying DeluxeAgent " << FullName << "..."
<< std::endl;
// Make sure \p this object is not a registered *slave*.
if (Master) {
ASSERT(unwrapAgent(*Master).Kind == atoms::AgentKind); // Sanity check.
DeluxeAgent &M = static_cast<DeluxeAgent&>(unwrapAgent(*Master));
ASSERT(M.positionOfSlave(self()) != M.NumberOfInputs); // Sanity check.
M.registerSlave(M.positionOfSlave(self()), {});
Master = {};
}
// Also, make sure \p this object is no acting *master*.
for (size_t Pos = 0; Pos < NumberOfInputs; ++Pos) {
registerSlave(Pos, {});
}
// Now there is no connection with other entities, safe to destroy.
}
id_t DeluxeAgent::masterId(void) const noexcept {
ASSERT(inv() && Master);
return unwrapAgent(*Master).Id;
}
const DeluxeExecutionPolicy &DeluxeAgent::executionPolicy(void) const noexcept {
ASSERT(inv());
return *ExecutionPolicy;
}
bool DeluxeAgent::setExecutionPolicy(
std::unique_ptr<DeluxeExecutionPolicy> &&EP) noexcept {
ASSERT(inv());
LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " setting execution policy "
<< *EP << std::endl;
bool Success = false;
// \note The \c rosa::System the \c rosa::Unit is created with is a
// \c rosa::DeluxeSystem.
const DeluxeSystem &DS = static_cast<DeluxeSystem &>(Unit::system());
if (EP && EP->canHandle(self(), DS)) {
ExecutionPolicy.swap(EP);
Success = true;
} else {
LOG_TRACE_STREAM << "Execution policy " << *EP
<< " cannot handle DeluxeAgent " << FullName << std::endl;
}
ASSERT(inv());
return Success;
}
Optional<AgentHandle> DeluxeAgent::master(void) const noexcept {
ASSERT(inv());
return Master;
}
void DeluxeAgent::registerMaster(const Optional<AgentHandle> _Master) noexcept {
ASSERT(inv() && (!_Master || unwrapAgent(*_Master).Kind == atoms::AgentKind));
Master = _Master;
ASSERT(inv());
}
Token DeluxeAgent::inputType(const size_t Pos) const noexcept {
ASSERT(inv() && Pos < NumberOfInputs);
return InputTypes[Pos];
}
Token DeluxeAgent::masterOutputType(const size_t Pos) const noexcept {
ASSERT(inv() && Pos < NumberOfMasterOutputs);
return MasterOutputTypes[Pos];
}
Optional<AgentHandle> DeluxeAgent::slave(const size_t Pos) const noexcept {
ASSERT(inv() && Pos < NumberOfInputs);
return Slaves[Pos];
}
void DeluxeAgent::registerSlave(const size_t Pos,
const Optional<AgentHandle> Slave) noexcept {
ASSERT(inv() && Pos < NumberOfInputs &&
(!Slave ||
(unwrapAgent(*Slave).Kind == atoms::SensorKind &&
static_cast<const DeluxeSensor &>(unwrapAgent(*Slave)).OutputType ==
InputTypes[Pos] &&
(emptyToken(MasterOutputTypes[Pos]) ||
static_cast<const DeluxeSensor &>(unwrapAgent(*Slave))
.MasterInputType == MasterOutputTypes[Pos])) ||
(unwrapAgent(*Slave).Kind == atoms::AgentKind &&
static_cast<const DeluxeAgent &>(unwrapAgent(*Slave)).OutputType ==
InputTypes[Pos] &&
(emptyToken(MasterOutputTypes[Pos]) ||
static_cast<const DeluxeAgent &>(unwrapAgent(*Slave))
.MasterInputType == MasterOutputTypes[Pos]))));
// If registering an actual *slave*, not just clearing the slot, make sure
// the same *slave* is not registered to another slot.
if (Slave) {
auto It = SlaveIds.find(unwrapAgent(*Slave).Id);
if (It != SlaveIds.end()) {
Slaves[It->second] = {};//Optional<AgentHandle>();
SlaveIds.erase(It);
}
}
// Obtain the place whose content is to be replaced with \p Slave
auto &OldSlave = Slaves[Pos];
// If there is already a *slave* registered at \p Pos, clear reverse lookup
// information for it, and make sure it no longer has \p this object as
// *master*.
if (OldSlave) {
auto &A = unwrapAgent(*OldSlave);
ASSERT(SlaveIds.find(A.Id) != SlaveIds.end()); // Sanity check.
SlaveIds.erase(A.Id);
if (A.Kind == atoms::AgentKind) {
static_cast<DeluxeAgent &>(A).registerMaster({});
} else {
ASSERT(A.Kind == atoms::SensorKind); // Sanity check.
static_cast<DeluxeSensor &>(A).registerMaster({});
}
}
// Register \p Slave at \p Pos.
OldSlave = Slave;
// If registering an actual *slave*, not just clearing the slot, register
// reverse lookup information for the new *slave*.
if (Slave) {
SlaveIds.emplace(unwrapAgent(*Slave).Id, Pos);
}
ASSERT(inv());
}
size_t DeluxeAgent::positionOfSlave(const AgentHandle Slave) const noexcept {
ASSERT(inv());
bool Found = false;
size_t Pos = 0;
while (!Found && Pos < NumberOfInputs) {
auto &ExistingSlave = Slaves[Pos];
if (ExistingSlave && *ExistingSlave == Slave) {
Found = true;
} else {
++Pos;
}
}
ASSERT(Found || Pos == NumberOfInputs); // Sanity check.
return Pos;
}
void DeluxeAgent::handleTrigger(atoms::Trigger) noexcept {
ASSERT(inv());
FP();
ASSERT(inv());
}
} // End namespace deluxe
} // End namespace rosa
diff --git a/lib/deluxe/DeluxeAtoms.cpp b/lib/deluxe/DeluxeAtoms.cpp
index 7728c67..ba45b08 100755
--- a/lib/deluxe/DeluxeAtoms.cpp
+++ b/lib/deluxe/DeluxeAtoms.cpp
@@ -1,20 +1,26 @@
//===-- deluxe/DeluxeAtoms.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 deluxe/DeluxeAtoms.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation of rosa/deluxe/DeluxeAtoms.hpp.
///
/// \note Empty implementation, source file here to have a compile database
/// entry for rosa/deluxe/DeluxeAtoms.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/deluxe/DeluxeAtoms.hpp"
diff --git a/lib/deluxe/DeluxeContext.cpp b/lib/deluxe/DeluxeContext.cpp
index 7991c03..ae5bdba 100755
--- a/lib/deluxe/DeluxeContext.cpp
+++ b/lib/deluxe/DeluxeContext.cpp
@@ -1,201 +1,207 @@
//===-- deluxe/DeluxeContext.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 deluxe/DeluxeContext.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2019
///
/// \brief Implementation for rosa/deluxe/DeluxeContext.hpp.
///
//===----------------------------------------------------------------------===//
#define ROSA_LIB_DELUXE_DELUXECONTEXT_CPP // For including helper macros.
#include "rosa/deluxe/DeluxeContext.hpp"
#include <algorithm>
namespace rosa {
namespace deluxe {
std::unique_ptr<DeluxeContext>
DeluxeContext::create(const std::string &Name) noexcept {
return std::unique_ptr<DeluxeContext>(new DeluxeContext(Name));
}
DeluxeContext::DeluxeContext(const std::string &Name) noexcept
: System(DeluxeSystem::createSystem(Name)) {
LOG_TRACE("DeluxeContext for '" + System->name() + "' is created.");
}
DeluxeContext::~DeluxeContext(void) noexcept {
// \c rosa::deluxe::DeluxeContext::System is not used outside, just clean it.
for(auto U : DeluxeUnits) {
System->destroyAgent(U);
}
// \note \c System will be marked clean by SystemImpl::~SystemImpl.
LOG_TRACE("DeluxeContext for '" + System->name() +
"' prepared for destruction.");
}
Optional<const DeluxeExecutionPolicy &>
DeluxeContext::getExecutionPolicy(AgentHandle Unit) const noexcept {
if (System->isDeluxeSensor(Unit)) {
return {System->getDeluxeSensor(Unit)->executionPolicy()};
} else if (System->isDeluxeAgent(Unit)) {
return {System->getDeluxeAgent(Unit)->executionPolicy()};
} else {
return {};
}
}
DeluxeContext::ErrorCode DeluxeContext::setExecutionPolicy(
AgentHandle Unit,
std::unique_ptr<DeluxeExecutionPolicy> &&ExecutionPolicy) noexcept {
// Generate trace log.
auto &Trace = LOG_TRACE_STREAM;
Trace << "Setting execution policy of " << System->unwrapAgent(Unit).FullName
<< " to ";
if (ExecutionPolicy) {
Trace << "'" << ExecutionPolicy->dump() << "'\n";
} else {
Trace << "[]\n";
DCRETERROR(ErrorCode::UnsuitableExecutionPolicy);
}
if (System->isDeluxeSensor(Unit)) {
const bool Success = System->getDeluxeSensor(Unit)->setExecutionPolicy(
std::move(ExecutionPolicy));
if (!Success) {
DCRETERROR(ErrorCode::UnsuitableExecutionPolicy);
} else {
return ErrorCode::NoError;
}
} else if (System->isDeluxeAgent(Unit)) {
const bool Success = System->getDeluxeAgent(Unit)->setExecutionPolicy(
std::move(ExecutionPolicy));
if (!Success) {
DCRETERROR(ErrorCode::UnsuitableExecutionPolicy);
} else {
return ErrorCode::NoError;
}
} else {
DCRETERROR(ErrorCode::NotUnit);
}
}
DeluxeContext::ErrorCode
DeluxeContext::connectSensor(AgentHandle Agent, const size_t Pos,
AgentHandle Sensor,
const std::string &Description) noexcept {
// Generate trace log.
auto &Trace = LOG_TRACE_STREAM;
Trace << "Establishing connection";
if (!Description.empty()) {
Trace << " '" << Description << "'";
}
Trace << " between '" << System->unwrapAgent(Sensor).FullName << "' and '"
<< System->unwrapAgent(Agent).FullName << "'\n";
// Make sure preconditions are met.
if (!System->isDeluxeAgent(Agent)) {
DCRETERROR(ErrorCode::NotAgent);
} else if (!System->isDeluxeSensor(Sensor)) {
DCRETERROR(ErrorCode::NotSensor);
}
auto A = System->getDeluxeAgent(Agent);
auto S = System->getDeluxeSensor(Sensor);
ASSERT(A && S); // Sanity check.
if (Pos >= A->NumberOfInputs) {
DCRETERROR(ErrorCode::WrongPosition);
} else if (A->inputType(Pos) != S->OutputType ||
(!emptyToken(A->masterOutputType(Pos)) &&
A->masterOutputType(Pos) != S->MasterInputType)) {
DCRETERROR(ErrorCode::TypeMismatch);
} else if (A->slave(Pos)) {
DCRETERROR(ErrorCode::AlreadyHasSlave);
} else if (S->master()) {
DCRETERROR(ErrorCode::AlreadyHasMaster);
}
// Do register.
A->registerSlave(Pos, {Sensor});
S->registerMaster({Agent});
return ErrorCode::NoError;
}
DeluxeContext::ErrorCode
DeluxeContext::connectAgents(AgentHandle Master, const size_t Pos,
AgentHandle Slave,
const std::string &Description) noexcept {
// Generate trace log.
auto &Trace = LOG_TRACE_STREAM;
Trace << "Establishing connection";
if (!Description.empty()) {
Trace << " '" << Description << "'";
}
Trace << " between '" << System->unwrapAgent(Slave).FullName << "' and '"
<< System->unwrapAgent(Master).FullName << "'\n";
// Make sure preconditions are met.
if (!(System->isDeluxeAgent(Master) && System->isDeluxeAgent(Slave))) {
DCRETERROR(ErrorCode::NotAgent);
}
auto M = System->getDeluxeAgent(Master);
auto S = System->getDeluxeAgent(Slave);
ASSERT(M && S); // Sanity check.
if (Pos >= M->NumberOfInputs) {
DCRETERROR(ErrorCode::WrongPosition);
} else if (M->inputType(Pos) != S->OutputType ||
(!emptyToken(M->masterOutputType(Pos)) &&
M->masterOutputType(Pos) != S->MasterInputType)) {
DCRETERROR(ErrorCode::TypeMismatch);
} else if (M->slave(Pos)) {
DCRETERROR(ErrorCode::AlreadyHasSlave);
} else if (S->master()) {
DCRETERROR(ErrorCode::AlreadyHasMaster);
}
// Do register.
M->registerSlave(Pos, {Slave});
S->registerMaster({Master});
return ErrorCode::NoError;
}
std::weak_ptr<MessagingSystem> DeluxeContext::getSystem(void) const noexcept {
return std::weak_ptr<MessagingSystem>(System);
}
void DeluxeContext::initializeSimulation(void) noexcept {
// Clear simulation data sources from sensors.
for (auto U : DeluxeUnits) {
if (auto S = System->getDeluxeSensor(U)) {
S->clearSimulationDataSource();
}
}
}
void DeluxeContext::simulate(const size_t NumCycles) const noexcept {
ASSERT(std::all_of(
DeluxeUnits.begin(), DeluxeUnits.end(), [&](const AgentHandle &H) {
return System->isDeluxeAgent(H) ||
System->isDeluxeSensor(H) &&
System->getDeluxeSensor(H)->simulationDataSourceIsSet();
}));
for (size_t I = 1; I <= NumCycles; ++I) {
LOG_TRACE("Simulation cycle: " + std::to_string(I));
for (auto U : DeluxeUnits) {
U.sendMessage(Message::create(atoms::Trigger::Value));
}
}
}
} // End namespace deluxe
} // End namespace rosa
diff --git a/lib/deluxe/DeluxeExecutionPolicy.cpp b/lib/deluxe/DeluxeExecutionPolicy.cpp
index bbea1ea..10410ca 100644
--- a/lib/deluxe/DeluxeExecutionPolicy.cpp
+++ b/lib/deluxe/DeluxeExecutionPolicy.cpp
@@ -1,67 +1,73 @@
//===-- deluxe/DeluxeExecutionPolicy.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 deluxe/DeluxeExecutionPolicy.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2019
///
/// \brief Implementation for rosa/deluxe/DeluxeExecutionPolicy.h.
///
//===----------------------------------------------------------------------===//
#include "rosa/deluxe/DeluxeExecutionPolicy.h"
#include "rosa/deluxe/DeluxeSystem.hpp"
#include "executionpolicies/Decimation.h"
#include "executionpolicies/AwaitAll.h"
#include "executionpolicies/AwaitAny.h"
namespace rosa {
namespace deluxe {
std::unique_ptr<DeluxeExecutionPolicy>
DeluxeExecutionPolicy::decimation(const size_t D) {
return std::unique_ptr<DeluxeExecutionPolicy>(new Decimation(D));
}
std::unique_ptr<DeluxeExecutionPolicy>
DeluxeExecutionPolicy::awaitAll(const std::set<size_t> &S) {
return std::unique_ptr<DeluxeExecutionPolicy>(new AwaitAll(S));
}
std::unique_ptr<DeluxeExecutionPolicy>
DeluxeExecutionPolicy::awaitAny(const std::set<size_t> &S) {
return std::unique_ptr<DeluxeExecutionPolicy>(new AwaitAny(S));
}
bool DeluxeExecutionPolicy::isDeluxeAgent(const AgentHandle H, const DeluxeSystem &S) const noexcept {
return S.isDeluxeAgent(H);
}
size_t DeluxeExecutionPolicy::numberOfDeluxeAgentInputs(
const AgentHandle H, const DeluxeSystem &S) const noexcept {
auto A = S.getDeluxeAgent(H);
return A ? A->NumberOfInputs : 0;
}
} // End namespace deluxe
} // End namespace rosa
namespace std {
string to_string(const rosa::deluxe::DeluxeExecutionPolicy &EP) {
return EP.dump();
}
ostream &operator<<(ostream &OS,
const rosa::deluxe::DeluxeExecutionPolicy &EP) {
OS << to_string(EP);
return OS;
}
} // End namespace std
diff --git a/lib/deluxe/DeluxeSensor.cpp b/lib/deluxe/DeluxeSensor.cpp
index 90eeafe..0906948 100644
--- a/lib/deluxe/DeluxeSensor.cpp
+++ b/lib/deluxe/DeluxeSensor.cpp
@@ -1,132 +1,138 @@
//===-- deluxe/DeluxeSensor.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 deluxe/DeluxeSensor.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2019
///
/// \brief Implementation of rosa/deluxe/DeluxeSensor.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/deluxe/DeluxeSensor.hpp"
#include "rosa/deluxe/DeluxeSystem.hpp"
namespace rosa {
namespace deluxe {
bool DeluxeSensor::inv(void) const noexcept {
// Check execution policy.
// \note The \c rosa::System the \c rosa::Unit is created with is a
// \c rosa::DeluxeSystem.
const DeluxeSystem &DS = static_cast<DeluxeSystem &>(Unit::system());
if (!ExecutionPolicy || !ExecutionPolicy->canHandle(Self, DS)) {
return false;
}
// Check the index of next expected element from the *master*.
const token_size_t MITL = lengthOfToken(MasterInputType);
if ((MITL != 0 && MasterInputNextPos >= MITL) ||
(MITL == 0 && MasterInputNextPos != 0)) {
return false;
}
// All checks were successful, the invariant is held.
return true;
}
id_t DeluxeSensor::masterId(void) const noexcept {
ASSERT(Master);
return unwrapAgent(*Master).Id;
}
DeluxeSensor::~DeluxeSensor(void) noexcept {
ASSERT(inv());
LOG_TRACE_STREAM << "Destroying DeluxeSensor " << FullName << "..."
<< std::endl;
// Make sure \p this object is not a registered *slave*.
if (Master) {
ASSERT(unwrapAgent(*Master).Kind == atoms::AgentKind); // Sanity check.
DeluxeAgent &M = static_cast<DeluxeAgent&>(unwrapAgent(*Master));
ASSERT(M.positionOfSlave(self()) != M.NumberOfInputs); // Sanity check.
M.registerSlave(M.positionOfSlave(self()), {});
Master = {};
}
}
const DeluxeExecutionPolicy &DeluxeSensor::executionPolicy(void) const
noexcept {
ASSERT(inv());
return *ExecutionPolicy;
}
bool DeluxeSensor::setExecutionPolicy(
std::unique_ptr<DeluxeExecutionPolicy> &&EP) noexcept {
ASSERT(inv());
LOG_TRACE_STREAM << "DeluxeSensor " << FullName
<< " setting execution policy " << *EP << std::endl;
bool Success = false;
// \note The \c rosa::System the \c rosa::Unit is created with is a
// \c rosa::DeluxeSystem.
const DeluxeSystem &DS = static_cast<DeluxeSystem &>(Unit::system());
if (EP && EP->canHandle(self(), DS)) {
ExecutionPolicy.swap(EP);
Success = true;
} else {
LOG_TRACE_STREAM << "Execution policy " << *EP
<< " cannot handle DeluxeSensor " << FullName << std::endl;
}
ASSERT(inv());
return Success;
}
Optional<AgentHandle> DeluxeSensor::master(void) const noexcept {
ASSERT(inv());
return Master;
}
void DeluxeSensor::registerMaster(const Optional<AgentHandle> _Master) noexcept {
ASSERT(inv() && (!_Master || unwrapAgent(*_Master).Kind == atoms::AgentKind));
Master = _Master;
ASSERT(inv());
}
void DeluxeSensor::clearSimulationDataSource(void) noexcept {
ASSERT(inv());
SFP = nullptr;
ASSERT(inv());
}
bool DeluxeSensor::simulationDataSourceIsSet(void) const noexcept {
ASSERT(inv());
return SFP != nullptr;
}
void DeluxeSensor::handleTrigger(atoms::Trigger) noexcept {
ASSERT(inv() && MasterInputNextPos == 0);
// Process master-input.
MFP();
// Obtain the next sensory value.
// \note Execution policy is respected within the data source function.
// Use \c rosa::deluxe::DeluxeSensor::SFP if set, otherwise
// \c rosa::deluxe::DeluxeSensor::FP.
const H &F = SFP ? SFP : FP;
F();
ASSERT(inv());
}
} // End namespace deluxe
} // End namespace rosa
diff --git a/lib/deluxe/DeluxeSystem.cpp b/lib/deluxe/DeluxeSystem.cpp
index be0cd97..2230cf4 100755
--- a/lib/deluxe/DeluxeSystem.cpp
+++ b/lib/deluxe/DeluxeSystem.cpp
@@ -1,66 +1,72 @@
//===-- deluxe/DeluxeSystem.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 deluxe/DeluxeSystem.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation of rosa/deluxe/DeluxeSystem.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/deluxe/DeluxeSystem.hpp"
#include "DeluxeSystemImpl.hpp"
namespace rosa {
namespace deluxe {
std::unique_ptr<DeluxeSystem>
DeluxeSystem::createSystem(const std::string &Name) noexcept {
return std::unique_ptr<DeluxeSystem>(new DeluxeSystemImpl(Name));
}
Optional<const DeluxeSensor &>
DeluxeSystem::getDeluxeSensor(const AgentHandle &H) const noexcept {
if (isDeluxeSensor(H)) {
return {static_cast<const DeluxeSensor &>(unwrapAgent(H))};
} else {
return {};
}
}
Optional<DeluxeSensor &> DeluxeSystem::getDeluxeSensor(AgentHandle &H) const
noexcept {
if (isDeluxeSensor(H)) {
return {static_cast<DeluxeSensor &>(unwrapAgent(H))};
} else {
return {};
}
}
Optional<const DeluxeAgent &>
DeluxeSystem::getDeluxeAgent(const AgentHandle &H) const noexcept {
if (isDeluxeAgent(H)) {
return {static_cast<const DeluxeAgent &>(unwrapAgent(H))};
} else {
return {};
}
}
Optional<DeluxeAgent &> DeluxeSystem::getDeluxeAgent(AgentHandle &H) const
noexcept {
if (isDeluxeAgent(H)) {
return {static_cast<DeluxeAgent &>(unwrapAgent(H))};
} else {
return {};
}
}
} // End namespace deluxe
} // End namespace rosa
diff --git a/lib/deluxe/DeluxeSystemImpl.cpp b/lib/deluxe/DeluxeSystemImpl.cpp
index 0f24882..d660441 100755
--- a/lib/deluxe/DeluxeSystemImpl.cpp
+++ b/lib/deluxe/DeluxeSystemImpl.cpp
@@ -1,37 +1,43 @@
//===-- deluxe/DeluxeSystemImpl.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 deluxe/DeluxeSystemImpl.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation of deluxe/DeluxeSystemImpl.hpp.
///
//===----------------------------------------------------------------------===//
#include "DeluxeSystemImpl.hpp"
namespace rosa {
namespace deluxe {
DeluxeSystemImpl::DeluxeSystemImpl(const std::string &Name) noexcept
: DeluxeSystem(),
MessagingSystemImpl(Name) {
LOG_TRACE("System '" + Name + "' has a deluxe flavor");
}
bool DeluxeSystemImpl::isDeluxeSensor(const AgentHandle &H) const noexcept {
return unwrapAgent(H).Kind == atoms::SensorKind;
}
bool DeluxeSystemImpl::isDeluxeAgent(const AgentHandle &H) const noexcept {
return unwrapAgent(H).Kind == atoms::AgentKind;
}
} // End namespace deluxe
} // End namespace rosa
diff --git a/lib/deluxe/DeluxeSystemImpl.hpp b/lib/deluxe/DeluxeSystemImpl.hpp
index 8538819..62c0c59 100755
--- a/lib/deluxe/DeluxeSystemImpl.hpp
+++ b/lib/deluxe/DeluxeSystemImpl.hpp
@@ -1,114 +1,120 @@
//===-- deluxe/DeluxeSystemImpl.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 deluxe/DeluxeSystemImpl.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Declaration of a basic implementation of the
/// \c rosa::deluxe::DeluxeSystem interface.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_LIB_DELUXE_DELUXESYSTEMIMPL_HPP
#define ROSA_LIB_DELUXE_DELUXESYSTEMIMPL_HPP
#include "../core/MessagingSystemImpl.hpp"
#include "rosa/deluxe/DeluxeSystem.hpp"
namespace rosa {
namespace deluxe {
/// Implements \c rosa::deluxe::DeluxeSystem by extending
/// \c rosa::MessagingSystemImpl.
class DeluxeSystemImpl : public DeluxeSystem, public MessagingSystemImpl {
/// Alies for the base-class \c rosa::MessagingSystemImpl.
using Base = MessagingSystemImpl;
public:
/// Creates an instance.
///
/// \param Name name of the new instance
DeluxeSystemImpl(const std::string &Name) noexcept;
/// \defgroup DeluxeSystemImplCallForwardings Call forwardings of rosa::deluxe::DeluxeSystemImpl
///
/// \c rosa::deluxe::DeluxeSystemImpl call forwardings
///
/// \note Simply forwarding calls to implementations provided by
/// \c rosa::deluxe::DeluxeSystem::Base for the \c rosa::MessagingSystem
/// interface.
///
/// \todo How could we use the inherited implementations in a simpler way?
///@{
bool operator==(const System &Other) const noexcept override {
return Base::operator==(Other);
}
protected:
id_t nextId(void) noexcept override { return Base::nextId(); }
bool isSystemCleaned(void) const noexcept override {
return Base::isSystemCleaned();
}
void markCleaned(void) noexcept override { Base::markCleaned(); }
void registerUnit(Unit &U) noexcept override { Base::registerUnit(U); }
void destroyUnit(Unit &U) noexcept override { Base::destroyUnit(U); }
bool isUnitRegistered(const Unit &U) const noexcept override {
return Base::isUnitRegistered(U);
}
public:
const std::string &name(void) const noexcept override { return Base::name(); }
size_t numberOfConstructedUnits(void) const noexcept override {
return Base::numberOfConstructedUnits();
}
size_t numberOfLiveUnits(void) const noexcept override {
return Base::numberOfLiveUnits();
}
bool empty(void) const noexcept override { return Base::empty(); }
void send(const AgentHandle &H, message_t &&M) noexcept override {
Base::send(H, std::move(M));
}
///@}
/// 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
bool isDeluxeSensor(const AgentHandle &H) const noexcept override;
/// 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
bool isDeluxeAgent(const AgentHandle &H) const noexcept override;
};
} // End namespace deluxe
} // End namespace rosa
#endif // ROSA_LIB_DELUXE_DELUXESYSTEMIMPL_HPP
diff --git a/lib/deluxe/DeluxeTuple.cpp b/lib/deluxe/DeluxeTuple.cpp
index 41895fb..9b8e903 100644
--- a/lib/deluxe/DeluxeTuple.cpp
+++ b/lib/deluxe/DeluxeTuple.cpp
@@ -1,28 +1,34 @@
//===-- deluxe/DeluxeTuple.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 deluxe/DeluxeTuple.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2019
///
/// \brief Implementation of rosa/deluxe/DeluxeTuple.hpp.
///
///
//===----------------------------------------------------------------------===//
#include "rosa/deluxe/DeluxeTuple.hpp"
namespace rosa {
namespace deluxe {
void DeluxeTuple<>::dump(std::ostream &OS) noexcept {
OS << "{ }";
}
} // End namespace deluxe
} // End namespace rosa
diff --git a/lib/deluxe/executionpolicies/AwaitAll.cpp b/lib/deluxe/executionpolicies/AwaitAll.cpp
index 70740b2..4d5c449 100644
--- a/lib/deluxe/executionpolicies/AwaitAll.cpp
+++ b/lib/deluxe/executionpolicies/AwaitAll.cpp
@@ -1,34 +1,40 @@
//===-- deluxe/executionpolicies/AwaitAll.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 deluxe/executionpolicies/AwaitAll.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2019
///
/// \brief Implementation for deluxe/executionpolicies/AwaitAll.h.
///
//===----------------------------------------------------------------------===//
#include "AwaitAll.h"
#include <algorithm>
namespace rosa {
namespace deluxe {
AwaitAll::AwaitAll(const std::set<size_t> &S)
: AwaitBase(S,
CheckerType(std::all_of<std::set<size_t>::const_iterator,
std::function<bool(const size_t)>>)) {}
std::string AwaitAll::dump(void) const noexcept {
return "Await all of " + dumpS();
}
} // End namespace deluxe
} // End namespace rosa
diff --git a/lib/deluxe/executionpolicies/AwaitAll.h b/lib/deluxe/executionpolicies/AwaitAll.h
index 1374f90..deab70c 100644
--- a/lib/deluxe/executionpolicies/AwaitAll.h
+++ b/lib/deluxe/executionpolicies/AwaitAll.h
@@ -1,48 +1,54 @@
//===-- deluxe/executionpolicies/AwaitAll.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 deluxe/executionpolicies/AwaitAll.h
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2019
///
/// \brief Declaration of the *execution policy* *await all*.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_LIB_DELUXE_EXECUTIONPOLICIES_AWAITALL_H
#define ROSA_LIB_DELUXE_EXECUTIONPOLICIES_AWAITALL_H
#include "AwaitBase.h"
namespace rosa {
namespace deluxe {
/// Implementation of the *execution policy* *await all*.
///
/// \see \c rosa::deluxe::DeluxeExecutionPolicy::awaitAll()
class AwaitAll : public AwaitBase {
public:
/// Constructor.
///
/// The constructor instatiates \c rosa::deluxe::AwaitBase so that execution
/// is allowed when all *slave* positions included in \p S have received new
/// input since the last triggering.
///
/// \param S set of *slave* positoins to check
AwaitAll(const std::set<size_t> &S);
/// Dumps \p this object into textual representation.
///
/// \return textual representation of \p this object
std::string dump(void) const noexcept override;
};
} // End namespace deluxe
} // End namespace rosa
#endif // ROSA_LIB_DELUXE_EXECUTIONPOLICIES_AWAITALL_H
diff --git a/lib/deluxe/executionpolicies/AwaitAny.cpp b/lib/deluxe/executionpolicies/AwaitAny.cpp
index 109c4cd..7395d91 100644
--- a/lib/deluxe/executionpolicies/AwaitAny.cpp
+++ b/lib/deluxe/executionpolicies/AwaitAny.cpp
@@ -1,34 +1,40 @@
//===-- deluxe/executionpolicies/AwaitAny.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 deluxe/executionpolicies/AwaitAny.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2019
///
/// \brief Implementation for deluxe/executionpolicies/AwaitAny.h.
///
//===----------------------------------------------------------------------===//
#include "AwaitAny.h"
#include <algorithm>
namespace rosa {
namespace deluxe {
AwaitAny::AwaitAny(const std::set<size_t> &S)
: AwaitBase(S,
CheckerType(std::any_of<std::set<size_t>::const_iterator,
std::function<bool(const size_t)>>)) {}
std::string AwaitAny::dump(void) const noexcept {
return "Await any of " + dumpS();
}
} // End namespace deluxe
} // End namespace rosa
diff --git a/lib/deluxe/executionpolicies/AwaitAny.h b/lib/deluxe/executionpolicies/AwaitAny.h
index d69df29..2ec8361 100644
--- a/lib/deluxe/executionpolicies/AwaitAny.h
+++ b/lib/deluxe/executionpolicies/AwaitAny.h
@@ -1,48 +1,54 @@
//===-- deluxe/executionpolicies/AwaitAny.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 deluxe/executionpolicies/AwaitAny.h
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2019
///
/// \brief Declaration of the *execution policy* *await any*.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_LIB_DELUXE_EXECUTIONPOLICIES_AWAITANY_H
#define ROSA_LIB_DELUXE_EXECUTIONPOLICIES_AWAITANY_H
#include "AwaitBase.h"
namespace rosa {
namespace deluxe {
/// Implementation of the *execution policy* *await any*.
///
/// \see \c rosa::deluxe::DeluxeExecutionPolicy::awaitAny()
class AwaitAny : public AwaitBase {
public:
/// Constructor.
///
/// The constructor instatiates \c rosa::deluxe::AwaitBase so that execution
/// is allowed when any of the *slave* positions included in \p S has received
/// new input since the last triggering.
///
/// \param S set of *slave* positoins to check
AwaitAny(const std::set<size_t> &S);
/// Dumps \p this object into textual representation.
///
/// \return textual representation of \p this object
std::string dump(void) const noexcept override;
};
} // End namespace deluxe
} // End namespace rosa
#endif // ROSA_LIB_DELUXE_EXECUTIONPOLICIES_AWAITANY_H
diff --git a/lib/deluxe/executionpolicies/AwaitBase.cpp b/lib/deluxe/executionpolicies/AwaitBase.cpp
index d4ec684..4e03789 100644
--- a/lib/deluxe/executionpolicies/AwaitBase.cpp
+++ b/lib/deluxe/executionpolicies/AwaitBase.cpp
@@ -1,60 +1,66 @@
//===-- deluxe/executionpolicies/AwaitBase.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 deluxe/executionpolicies/AwaitBase.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2019
///
/// \brief Implementation for deluxe/executionpolicies/AwaitBase.h.
///
//===----------------------------------------------------------------------===//
#include "AwaitBase.h"
#include "rosa/support/debug.hpp"
#include <algorithm>
#include <sstream>
namespace rosa {
namespace deluxe {
AwaitBase::AwaitBase(const std::set<size_t> &S, CheckerType &&Checker)
: Set(S), Checker(Checker) {}
bool AwaitBase::canHandle(const AgentHandle H, const DeluxeSystem &S) const
noexcept {
return isDeluxeAgent(H, S) &&
canHandleNumberOfInputs(numberOfDeluxeAgentInputs(H, S));
}
bool AwaitBase::shouldProcess(const std::vector<bool> &InputChanged) noexcept {
// Sanity check of usage.
ASSERT(canHandleNumberOfInputs(InputChanged.size()));
return Checker(Set.begin(), Set.end(),
[&InputChanged](const size_t I) { return InputChanged[I]; });
}
bool AwaitBase::canHandleNumberOfInputs(const size_t NumberOfInputs) const
noexcept {
const auto MaxElemIt = std::max_element(Set.begin(), Set.end());
const size_t MaxElem = (MaxElemIt == Set.end()) ? 0 : *MaxElemIt;
return MaxElem <= NumberOfInputs;
}
std::string AwaitBase::dumpS(void) const noexcept {
std::stringstream SS;
SS << "[";
for (const auto &Value : Set) {
SS << " " << Value;
}
SS << " ]";
return SS.str();
}
} // End namespace deluxe
} // End namespace rosa
diff --git a/lib/deluxe/executionpolicies/AwaitBase.h b/lib/deluxe/executionpolicies/AwaitBase.h
index e196701..9483c3d 100644
--- a/lib/deluxe/executionpolicies/AwaitBase.h
+++ b/lib/deluxe/executionpolicies/AwaitBase.h
@@ -1,105 +1,111 @@
//===-- deluxe/executionpolicies/AwaitBase.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 deluxe/executionpolicies/AwaitBase.h
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2019
///
/// \brief Declaration of an *execution policy* that makes decisions depending
/// on input state of *slave* positions.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_LIB_DELUXE_EXECUTIONPOLICIES_AWAITBASE_H
#define ROSA_LIB_DELUXE_EXECUTIONPOLICIES_AWAITBASE_H
#include "rosa/deluxe/DeluxeExecutionPolicy.h"
#include <functional>
namespace rosa {
namespace deluxe {
/// Implementation of an *execution policy* that makes decisions depending on
/// whether new input has been received at some *slave* positions since the
/// laste triggering.
///
/// The class implements the \c rosa::deluxe::DeluxeExecutionPolicy interface
/// but delegates the defintion of the actual decision-making function to
/// subclasses.
///
/// \see rosa::deluxe::AwaitAll
/// \see rosa::deluxe::AwaitAny
class AwaitBase : public DeluxeExecutionPolicy {
protected:
/// Set of *slave* positions to check.
const std::set<size_t> Set;
/// Type of decision-making function used in \c
/// rosa::deluxe::AwaitBase::shouldProcess().
using CheckerType = std::function<bool(std::set<size_t>::const_iterator,
std::set<size_t>::const_iterator,
std::function<bool(const size_t)>)>;
// Decision-making function for \c rosa::deluxe::AwaitBase::shouldProcess().
const CheckerType Checker;
/// Protected constructor, only subclasses can instatiate the class.
///
/// \param S set of *slave* positions to await input from
/// \param Checker function that decides about execution
AwaitBase(const std::set<size_t> &S, CheckerType &&Checker);
public:
/// Tells if \p this object can handle the deluxe *unit* referred by \p H.
///
/// Any *execution policy* based on this class can handle *agents* with at
/// least as many *slave* positions as the largest one defined in \c
/// rosa::deluxe::AwaitBase::Set.
///
/// \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
bool canHandle(const AgentHandle H, const DeluxeSystem &S) const
noexcept override;
/// Tells if processing function should be executed on the current triggering.
///
/// Waiting for input allows execution when \c
/// rosa::deluxe::AwaitBase::Checker evaluates to \c true with respect to \c
/// rosa::deluxe::AwaitBase::Set and \p InputChanged.
///
/// \param InputChanged flags indicating whether new input has been received
/// at *slave* positions
///
/// \return if to execute processing function
bool shouldProcess(const std::vector<bool> &InputChanged) noexcept override;
private:
/// Tells if \p this object can handle a *unit* with \p NumberOfInputs *slave*
/// positions.
///
/// \param NumberOfInputs the number of *slave* positions to consider
///
/// \return if \p this object can handle a *unit* with \p NumberOfInputs
/// *slave* positions
bool canHandleNumberOfInputs(const size_t NumberOfInputs) const noexcept;
protected:
/// Dumps the set of *slave* positions that \p this object checks.
///
/// \return textual representation of \c rosa::deluxe::AwaitBase::Set of \p
/// this object
std::string dumpS(void) const noexcept;
};
} // End namespace deluxe
} // End namespace rosa
#endif // ROSA_LIB_DELUXE_EXECUTIONPOLICIES_AWAITBASE_H
diff --git a/lib/deluxe/executionpolicies/Decimation.cpp b/lib/deluxe/executionpolicies/Decimation.cpp
index 2f324f9..b17946c 100644
--- a/lib/deluxe/executionpolicies/Decimation.cpp
+++ b/lib/deluxe/executionpolicies/Decimation.cpp
@@ -1,41 +1,47 @@
//===-- deluxe/executionpolicies/Decimation.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 deluxe/executionpolicies/Decimation.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2019
///
/// \brief Implementation for deluxe/executionpolicies/Decimation.h.
///
//===----------------------------------------------------------------------===//
#include "Decimation.h"
#include <algorithm>
namespace rosa {
namespace deluxe {
Decimation::Decimation(const size_t D)
: Rate(std::max<size_t>(D, 1)), Cycle(0) {}
bool Decimation::canHandle(const AgentHandle, const DeluxeSystem &) const
noexcept {
return true;
}
bool Decimation::shouldProcess(const std::vector<bool> &) noexcept {
return (Cycle++ % Rate) == 0;
}
std::string Decimation::dump(void) const noexcept {
return "Decimation with rate " + std::to_string(Rate);
}
} // End namespace deluxe
} // End namespace rosa
diff --git a/lib/deluxe/executionpolicies/Decimation.h b/lib/deluxe/executionpolicies/Decimation.h
index b4d826c..14bb954 100644
--- a/lib/deluxe/executionpolicies/Decimation.h
+++ b/lib/deluxe/executionpolicies/Decimation.h
@@ -1,73 +1,79 @@
//===-- deluxe/executionpolicies/Decimation.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 deluxe/executionpolicies/Decimation.h
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2019
///
/// \brief Declaration of the *execution policy* *decimation*.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_LIB_DELUXE_EXECUTIONPOLICIES_DECIMATION_H
#define ROSA_LIB_DELUXE_EXECUTIONPOLICIES_DECIMATION_H
#include "rosa/deluxe/DeluxeExecutionPolicy.h"
namespace rosa {
namespace deluxe {
/// Implementation of the *execution policy* *decimation*.
///
/// \see \c rosa::deluxe::DeluxeExecutionPolicy::decimation()
class Decimation : public DeluxeExecutionPolicy {
/// The rate of *decimation*.
const size_t Rate;
/// Counter of triggerings.
size_t Cycle;
public:
/// Constructor.
///
/// \param D the rate of *decimation*
Decimation(const size_t D);
/// Tells if \p this object can handle the deluxe *unit* referred by \p H.
///
/// *Decimation* can handle any *units*.
///
/// \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
bool canHandle(const AgentHandle H, const DeluxeSystem &S) const
noexcept override;
/// Tells if processing function should be executed on the current triggering.
///
/// *Decimation* allows execution on each \c rosa::deluxe::Decimation::Rate
/// <sup>th</sup> triggering (i.e., calling of the function), which is counted
/// by \p this object in \c rosa::deluxe::Decimation::Cycle.
///
/// \param InputChanged *ignored*
///
/// \return if to execute processing function
bool shouldProcess(const std::vector<bool> &InputChanged) noexcept override;
/// Dumps \p this object into textual representation.
///
/// \return textual representation of \p this object
std::string dump(void) const noexcept override;
};
} // End namespace deluxe
} // End namespace rosa
#endif // ROSA_LIB_DELUXE_EXECUTIONPOLICIES_DECIMATION_H
diff --git a/lib/deluxe/namespace.cpp b/lib/deluxe/namespace.cpp
index d565236..3d8e057 100755
--- a/lib/deluxe/namespace.cpp
+++ b/lib/deluxe/namespace.cpp
@@ -1,20 +1,26 @@
//===-- deluxe/namespace.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 deluxe/namespace.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation for rosa/deluxe/namespace.h.
///
/// \note Empty implementation, source file here to have a compile database
/// entry for rosa/deluxe/namespace.h.
///
//===----------------------------------------------------------------------===//
#include "rosa/deluxe/namespace.h"
diff --git a/lib/support/CMakeLists.txt b/lib/support/CMakeLists.txt
index 6e191ef..318486d 100644
--- a/lib/support/CMakeLists.txt
+++ b/lib/support/CMakeLists.txt
@@ -1,38 +1,46 @@
set(LIB_INCLUDE_DIR ${ROSA_MAIN_INCLUDE_DIR}/rosa/support)
add_library(ROSASupport
${LIB_INCLUDE_DIR}/debug.hpp
debug.cpp
${LIB_INCLUDE_DIR}/terminal_colors.h
terminal_colors.cpp
${LIB_INCLUDE_DIR}/log.h
log.cpp
${LIB_INCLUDE_DIR}/math.hpp
math.cpp
${LIB_INCLUDE_DIR}/type_helper.hpp
type_helper.cpp
${LIB_INCLUDE_DIR}/types.hpp
types.cpp
${LIB_INCLUDE_DIR}/atom.hpp
atom.cpp
${LIB_INCLUDE_DIR}/type_pair.hpp
type_pair.cpp
${LIB_INCLUDE_DIR}/type_list.hpp
type_list.cpp
${LIB_INCLUDE_DIR}/squashed_int.hpp
squashed_int.cpp
${LIB_INCLUDE_DIR}/type_numbers.hpp
type_numbers.cpp
${LIB_INCLUDE_DIR}/type_token.hpp
type_token.cpp
${LIB_INCLUDE_DIR}/tokenized_storages.hpp
tokenized_storages.cpp
${LIB_INCLUDE_DIR}/sequence.hpp
sequence.cpp
${LIB_INCLUDE_DIR}/csv/namespace.h
csv/namespace.cpp
${LIB_INCLUDE_DIR}/csv/CSVReader.hpp
csv/CSVReader.cpp
${LIB_INCLUDE_DIR}/csv/CSVWriter.hpp
csv/CSVWriter.cpp
+ ${LIB_INCLUDE_DIR}/iterator/namespace.h
+ iterator/namespace.cpp
+ ${LIB_INCLUDE_DIR}/iterator/split_tuple_iterator.hpp
+ iterator/split_tuple_iterator.cpp
+ ${LIB_INCLUDE_DIR}/writer/namespace.h
+ writer/namespace.cpp
+ ${LIB_INCLUDE_DIR}/writer/split_tuple_writer.hpp
+ writer/split_tuple_writer.cpp
)
diff --git a/lib/support/atom.cpp b/lib/support/atom.cpp
index 9ef7c43..5670cfb 100644
--- a/lib/support/atom.cpp
+++ b/lib/support/atom.cpp
@@ -1,59 +1,64 @@
//===-- support/atom.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 support/atom.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2019
///
/// \brief Implementation of non-static part of atom facilities of
/// rosa/support/atom.hpp.
///
/// \note This implementation is based on the `atom` implementation of CAF.
-/// \todo Check license.
///
//===----------------------------------------------------------------------===//
#include "rosa/support/atom.hpp"
#include <cstring>
namespace rosa {
AtomValue atom_from_string(const std::string &S) {
if (S.size() > MaxAtomLength) {
return atom("");
}
char AtomBuf[MaxAtomLength + 1];
std::memcpy(AtomBuf, S.c_str(), S.size());
AtomBuf[S.size()] = '\0';
return atom(AtomBuf);
}
} // End namespace rosa
namespace std {
string to_string(const rosa::AtomValue &What) {
auto X = static_cast<rosa::atom_t>(What);
string S;
S.reserve(rosa::MaxAtomLength + 1);
// Don't read characters before we found the leading 0xF.
// First four bits set?
bool ReadChars = ((X & 0xF000000000000000) >> 60) == 0xF;
uint64_t Mask = 0x0FC0000000000000;
for (int BitShift = 54; BitShift >= 0; BitShift -= 6, Mask >>= 6) {
if (ReadChars) {
S += rosa::AtomDecodingTable[(X & Mask) >> BitShift];
} else if (((X & Mask) >> BitShift) == 0xF) {
ReadChars = true;
}
}
return S;
}
} // End namespace std
diff --git a/lib/support/csv/CSVReader.cpp b/lib/support/csv/CSVReader.cpp
index bf055fb..6906737 100755
--- a/lib/support/csv/CSVReader.cpp
+++ b/lib/support/csv/CSVReader.cpp
@@ -1,20 +1,26 @@
//===-- support/csv/CSVReader.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 support/csv/CSVReader.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation for rosa/support/csv/CSVReader.hpp.
///
/// \note Empty implementation, source file here to have a compile database
/// entry for rosa/support/csv/CSVReader.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/support/csv/CSVReader.hpp"
diff --git a/lib/support/csv/CSVWriter.cpp b/lib/support/csv/CSVWriter.cpp
index 4310703..fe7da5e 100755
--- a/lib/support/csv/CSVWriter.cpp
+++ b/lib/support/csv/CSVWriter.cpp
@@ -1,20 +1,26 @@
//===-- support/csv/CSVWriter.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 support/csv/CSVWriter.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation for rosa/support/csv/CSVWriter.hpp.
///
/// \note Empty implementation, source file here to have a compile database
/// entry for rosa/support/csv/CSVWriter.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/support/csv/CSVWriter.hpp"
diff --git a/lib/support/csv/namespace.cpp b/lib/support/csv/namespace.cpp
index 744e6c1..f31d061 100755
--- a/lib/support/csv/namespace.cpp
+++ b/lib/support/csv/namespace.cpp
@@ -1,20 +1,26 @@
//===-- support/csv/namespace.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 support/csv/namespace.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Placeholder for rosa/support/csv/namespace.h.
///
/// \note Empty implementation, source file here to have a compile database
/// entry for rosa/support/csv/namespace.h.
///
//===----------------------------------------------------------------------===//
#include "rosa/support/csv/namespace.h"
diff --git a/lib/support/debug.cpp b/lib/support/debug.cpp
index 0bce8c1..2730989 100644
--- a/lib/support/debug.cpp
+++ b/lib/support/debug.cpp
@@ -1,28 +1,34 @@
//===-- support/debug.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 support/debug.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation for rosa/support/debug.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/support/debug.hpp"
#include <iostream>
namespace rosa {
std::ostream &dbgs(void) {
// Always return the standard error stream.
return std::cerr;
}
} // End namespace rosa
diff --git a/lib/agent/namespace.cpp b/lib/support/iterator/namespace.cpp
old mode 100755
new mode 100644
similarity index 50%
copy from lib/agent/namespace.cpp
copy to lib/support/iterator/namespace.cpp
index cfac9b9..57fe49f
--- a/lib/agent/namespace.cpp
+++ b/lib/support/iterator/namespace.cpp
@@ -1,20 +1,20 @@
-//===-- agent/namespace.cpp -------------------------------------*- C++ -*-===//
+//===-- support/iterator/namespace.cpp --------------------------*- C++ -*-===/
//
// The RoSA Framework
//
-//===----------------------------------------------------------------------===//
+//===----------------------------------------------------------------------===/
///
-/// \file agent/namespace.cpp
+/// \file support/iterator/namespace.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
-/// \date 2017
+/// \date 2019
///
-/// \brief Implementation for rosa/agent/namespace.h.
+/// \brief Placeholder for rosa/support/iterator/namespace.h.
///
/// \note Empty implementation, source file here to have a compile database
-/// entry for rosa/agent/namespace.h.
+/// entry for rosa/support/iterator/namespace.h.
///
-//===----------------------------------------------------------------------===//
+//===----------------------------------------------------------------------===/
-#include "rosa/agent/namespace.h"
+#include "rosa/support/iterator/namespace.h"
diff --git a/lib/support/iterator/split_tuple_iterator.cpp b/lib/support/iterator/split_tuple_iterator.cpp
new file mode 100644
index 0000000..72f4f1f
--- /dev/null
+++ b/lib/support/iterator/split_tuple_iterator.cpp
@@ -0,0 +1,20 @@
+//===-- support/iterator/split_tuple_iterator.cpp ---------------*- C++ -*-===/
+//
+// The RoSA Framework
+//
+//===----------------------------------------------------------------------===/
+///
+/// \file support/iterator/split_tuple_iterator.cpp
+///
+/// \author David Juhasz (david.juhasz@tuwien.ac.at)
+///
+/// \date 2019
+///
+/// \brief Implementation for rosa/support/iterator/split_tuple_iterator.hpp.
+///
+/// \note Empty implementation, source file here to have a compile database
+/// entry for rosa/support/iterator/split_tuple_iterator.hpp.
+///
+//===----------------------------------------------------------------------===/
+
+#include "rosa/support/iterator/split_tuple_iterator.hpp"
diff --git a/lib/support/log.cpp b/lib/support/log.cpp
index 248128b..40cd6e0 100644
--- a/lib/support/log.cpp
+++ b/lib/support/log.cpp
@@ -1,64 +1,70 @@
//===-- support/log.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 support/log.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation for rosa/support/log.h.
///
//===----------------------------------------------------------------------===//
#include "rosa/support/log.h"
#include "rosa/support/debug.hpp" // NOLINT
#include "rosa/support/terminal_colors.h"
#include <array>
namespace rosa {
/// Textual representation of \c rosa::LogLevel values.
///
/// \note Do not index with \c rosa::LogLevel::NumLogLevels!
constexpr std::array<const char *, static_cast<size_t>(LogLevel::NumLogLevels)>
LogLevelStrings{{"ERROR", "WARNING", "INFO", "DEBUG", "TRACE"}};
/// Terminal colors associated to \c rosa::LogLevel values.
///
/// \note Do not index with \c rosa::LogLevel::NumLogLevels!
constexpr std::array<terminal::Color,
static_cast<size_t>(LogLevel::NumLogLevels)>
LogLevelColors{{
terminal::Color::Red, ///< color corresponding to
/// `rosa::LogLevel::Error`
terminal::Color::Yellow, ///< color corresponding to
/// `rosa::LogLevel::Warning`
terminal::Color::Green, ///< color corresponding to
/// `rosa::LogLevel::Info`
terminal::Color::Blue, ///< color corresponding to
/// `rosa::LogLevel::Debug`
terminal::Color::Default, ///< color corresponding to
/// `rosa::LogLevel::Trace`
}};
std::string logLevelToString(const LogLevel logLevel) {
ASSERT(logLevel != LogLevel::NumLogLevels);
return LogLevelStrings[static_cast<size_t>(logLevel)];
}
std::ostream &operator<<(std::ostream &OS, const LogLevel logLevel) {
ASSERT(logLevel != LogLevel::NumLogLevels);
OS << LogLevelColors[static_cast<size_t>(logLevel)] << "["
<< logLevelToString(logLevel) << "]" << terminal::Color::Default;
return OS;
}
std::ostream LogSink(nullptr);
} // End namespace rosa
diff --git a/lib/support/math.cpp b/lib/support/math.cpp
index 76d32a2..ee3fe6d 100644
--- a/lib/support/math.cpp
+++ b/lib/support/math.cpp
@@ -1,20 +1,26 @@
//===-- support/math.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 support/math.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation for rosa/support/math.hpp.
///
/// \note Empty implementation, source file here to have a compile database
/// entry for rosa/support/math.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/support/math.hpp"
diff --git a/lib/support/sequence.cpp b/lib/support/sequence.cpp
index 1c738f5..4d763c6 100755
--- a/lib/support/sequence.cpp
+++ b/lib/support/sequence.cpp
@@ -1,20 +1,26 @@
//===-- support/sequence.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 support/sequence.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation for rosa/support/sequence.hpp.
///
/// \note Empty implementation, source file here to have a compile database
/// entry for rosa/support/sequence.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/support/sequence.hpp"
diff --git a/lib/support/squashed_int.cpp b/lib/support/squashed_int.cpp
index 87e3a36..5405127 100644
--- a/lib/support/squashed_int.cpp
+++ b/lib/support/squashed_int.cpp
@@ -1,20 +1,26 @@
//===-- support/squashed_int.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 support/squashed_int.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation for rosa/support/squashed_int.hpp.
///
/// \note Empty implementation, source file here to have a compile database
/// entry for rosa/support/squashed_int.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/support/squashed_int.hpp"
diff --git a/lib/support/terminal_colors.cpp b/lib/support/terminal_colors.cpp
index fb34238..26c36cf 100644
--- a/lib/support/terminal_colors.cpp
+++ b/lib/support/terminal_colors.cpp
@@ -1,83 +1,89 @@
//===-- support/terminal_colors.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 support/terminal_colors.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation for rosa/support/terminal_colors.h.
///
//===----------------------------------------------------------------------===//
#include "rosa/support/terminal_colors.h"
#include "rosa/config/config.h"
#include "rosa/support/debug.hpp"
#include <array>
#ifdef ROSA_POSIX
#include <unistd.h>
#endif // defined(ROSA_POSIX)
namespace rosa {
namespace terminal {
/// ANSI color codes for POSIX terminals.
///
/// \note Use \c std::array for runtime efficiency, though static casting is
/// necessary for indexing.
///
/// \note Do not index with \c rosa::Color::NumColors!
constexpr std::array<const char *, static_cast<size_t>(Color::NumColors)>
ANSIColorCodes{{
"0", ///< Default - Attribute reset
"30", ///< Black
"31", ///< Red
"32", ///< Green
"33", ///< Yelow
"34", ///< Blue
"35", ///< Magenta
"36", ///< Cyan
"37", ///< Lightgrey
"90", ///< Darkgrey
"91", ///< Lightred
"92", ///< Lightgreen
"93", ///< Lightyellow
"94", ///< Lightblue
"95", ///< Lightmagenta
"96", ///< Lightcyan
"97" ///< White
}};
std::ostream &operator<<(std::ostream &os, const Color color) {
ASSERT(color != Color::NumColors);
// Handle color only when printing to terminal and it supports colors.
#ifdef ROSA_POSIX
// Identify terminal by checking if the stream is buffered as one of the
// standard outputs, and the corresponding C standard output stream is
// pointing to a terminal.
//
// \note This implementation may be tricked so that it does not print color
// to terminal, but should never print color codes to a non-terminal output.
if ((os.rdbuf() == std::cout.rdbuf() && isatty(fileno(stdin))) ||
((os.rdbuf() == std::cerr.rdbuf() || os.rdbuf() == std::clog.rdbuf()) &&
isatty(fileno(stderr)))) {
os << "\033[" << ANSIColorCodes[static_cast<size_t>(color)] << "m";
}
#elif defined(ROSA_WINDOWS)
// \todo Windows terminals support ANSI color codes from Windows 10
// Threshold 2, custom stuff is required for earlier versions.
(void)ANSIColorCodes; // To shut up compiler warning.
#endif // defined(ROSA_POSIX) || defined(ROSA_WINDOWS)
return os;
}
} // End namespace terminal
} // End namespace rosa
diff --git a/lib/support/tokenized_storages.cpp b/lib/support/tokenized_storages.cpp
index 59d8eb6..69d1ea8 100755
--- a/lib/support/tokenized_storages.cpp
+++ b/lib/support/tokenized_storages.cpp
@@ -1,25 +1,31 @@
//===-- support/tokenized_storages.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 support/tokenized_storages.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2019
///
/// \brief Implementation of rosa/support/tokenized_storages.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/support/tokenized_storages.hpp"
namespace rosa {
// Implementation of the static member field \c
// rosa::TokenizedStorage<>::Offsets.
const std::vector<size_t> TokenizedStorage<>::Offsets = std::vector<size_t>(0);
} // End namespace rosa
\ No newline at end of file
diff --git a/lib/support/type_helper.cpp b/lib/support/type_helper.cpp
index 573a9ff..5a4bb09 100644
--- a/lib/support/type_helper.cpp
+++ b/lib/support/type_helper.cpp
@@ -1,20 +1,26 @@
//===-- support/type_helper.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 support/type_helper.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation for rosa/support/type_helper.hpp.
///
/// \note Empty implementation, source file here to have a compile database
/// entry for rosa/support/type_helper.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/support/type_helper.hpp"
diff --git a/lib/support/type_list.cpp b/lib/support/type_list.cpp
index dff5d5a..b82fcde 100644
--- a/lib/support/type_list.cpp
+++ b/lib/support/type_list.cpp
@@ -1,20 +1,26 @@
//===-- support/type_list.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 support/type_list.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation for rosa/support/type_list.hpp.
///
/// \note Empty implementation, source file here to have a compile database
/// entry for rosa/support/type_list.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/support/type_list.hpp"
diff --git a/lib/support/type_numbers.cpp b/lib/support/type_numbers.cpp
index cb979fa..ecdb1ec 100644
--- a/lib/support/type_numbers.cpp
+++ b/lib/support/type_numbers.cpp
@@ -1,20 +1,26 @@
//===-- support/type_numbers.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 support/type_numbers.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation for rosa/support/type_numbers.hpp.
///
/// \note Empty implementation, source file here to have a compile database
/// entry for rosa/support/type_numbers.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/support/type_numbers.hpp"
diff --git a/lib/support/type_pair.cpp b/lib/support/type_pair.cpp
index feced41..bb7178a 100644
--- a/lib/support/type_pair.cpp
+++ b/lib/support/type_pair.cpp
@@ -1,20 +1,26 @@
//===-- support/type_pair.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 support/type_pair.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation for rosa/support/type_pair.hpp.
///
/// \note Empty implementation, source file here to have a compile database
/// entry for rosa/support/type_pair.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/support/type_pair.hpp"
diff --git a/lib/support/type_token.cpp b/lib/support/type_token.cpp
index 508b471..5a729bb 100644
--- a/lib/support/type_token.cpp
+++ b/lib/support/type_token.cpp
@@ -1,131 +1,137 @@
//===-- support/type_token.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 support/type_token.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2019
///
/// \brief Implementation for rosa/support/type_token.hpp.
///
/// \todo Automatically generate proper cases for the switches with a big number
/// of handcrafted similar cases in them.
///
//===----------------------------------------------------------------------===//
#include "rosa/support/type_token.hpp"
namespace rosa {
using namespace token;
bool validToken(const Token T) {
Token TT = T;
bool Valid = true;
while (Valid && !emptyToken(TT)) {
Valid &= validTypeNumber(headOfToken(TT));
dropHeadOfToken(TT);
}
return Valid;
}
bool emptyToken(const Token T) { return static_cast<token_t>(T) == 0; }
token_size_t lengthOfToken(const Token T) {
Token TT = T;
token_size_t N = 0;
while (!emptyToken(TT)) {
++N;
dropHeadOfToken(TT);
}
return N;
}
size_t sizeOfValuesOfToken(const Token T) {
ASSERT(validToken(T));
Token TT = T;
size_t S = 0;
while (!emptyToken(TT)) {
S += sizeOfHeadOfToken(TT);
dropHeadOfToken(TT);
}
return S;
}
#define SIZECASE(N) \
{ \
case N: \
return TypeForNumber<static_cast<TypeNumber>(N)>::Size; \
}
size_t sizeOfHeadOfToken(const Token T) {
ASSERT(!emptyToken(T) && validToken(T));
switch (static_cast<type_nr_t>(headOfToken(T))) {
default: {
// Should never come here when \p T is valid and the case-list below covers
// \c rosa::BuiltinTypes.
ROSA_CRITICAL("unknown type number");
}
SIZECASE(1);
SIZECASE(2);
SIZECASE(3);
SIZECASE(4);
SIZECASE(5);
SIZECASE(6);
SIZECASE(7);
SIZECASE(8);
SIZECASE(9);
SIZECASE(10);
SIZECASE(11);
SIZECASE(12);
SIZECASE(13);
SIZECASE(14);
SIZECASE(15);
}
}
#define NAMECASE(N) \
{ \
case N: \
return TypeForNumber<static_cast<TypeNumber>(N)>::Name; \
}
const char *nameOfHeadOfToken(const Token T) {
ASSERT(!emptyToken(T) && validToken(T));
switch (static_cast<type_nr_t>(headOfToken(T))) {
default: {
// Should never come here when \p T is valid and the case-list below covers
// \c rosa::BuiltinTypes.
ROSA_CRITICAL("unknown type number");
}
NAMECASE(1);
NAMECASE(2);
NAMECASE(3);
NAMECASE(4);
NAMECASE(5);
NAMECASE(6);
NAMECASE(7);
NAMECASE(8);
NAMECASE(9);
NAMECASE(10);
NAMECASE(11);
NAMECASE(12);
NAMECASE(13);
NAMECASE(14);
NAMECASE(15);
}
}
void dropHeadOfToken(Token &T) {
T = static_cast<Token>(static_cast<token_t>(T) >> RepresentationBits);
}
void dropNOfToken(Token &T, const size_t N) {
T = static_cast<Token>(static_cast<token_t>(T) >> (N * RepresentationBits));
}
} // End namespace rosa
diff --git a/lib/support/types.cpp b/lib/support/types.cpp
index 3c321eb..7b9fb78 100644
--- a/lib/support/types.cpp
+++ b/lib/support/types.cpp
@@ -1,20 +1,26 @@
//===-- support/types.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 support/types.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Implementation for rosa/support/types.hpp.
///
/// \note Empty implementation, source file here to have a compile database
/// entry for rosa/support/types.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/support/types.hpp"
diff --git a/lib/agent/Functionality.cpp b/lib/support/writer/namespace.cpp
similarity index 59%
copy from lib/agent/Functionality.cpp
copy to lib/support/writer/namespace.cpp
index f6b60c4..b659e2d 100644
--- a/lib/agent/Functionality.cpp
+++ b/lib/support/writer/namespace.cpp
@@ -1,20 +1,20 @@
-//===-- agent/Functionality.cpp ---------------------------------*- C++ -*-===//
+//===-- support/writer/namespace.cpp -----------------------------*- C++ -*-===/
//
// The RoSA Framework
//
-//===----------------------------------------------------------------------===//
+//===----------------------------------------------------------------------===/
///
-/// \file agent/Functionality.cpp
+/// \file support/writer/namespace.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
-/// \date 2017
+/// \date 2019
///
-/// \brief Implementation for rosa/agent/Functionality.h.
+/// \brief Placeholder for rosa/support/iterator/namespace.h.
///
/// \note Empty implementation, source file here to have a compile database
-/// entry for rosa/agent/Functionality.h.
+/// entry for rosa/support/writer/namespace.h.
///
-//===----------------------------------------------------------------------===//
+//===----------------------------------------------------------------------===/
-#include "rosa/agent/Functionality.h"
+#include "rosa/support/writer/namespace.h"
diff --git a/lib/support/writer/split_tuple_writer.cpp b/lib/support/writer/split_tuple_writer.cpp
new file mode 100644
index 0000000..651e012
--- /dev/null
+++ b/lib/support/writer/split_tuple_writer.cpp
@@ -0,0 +1,20 @@
+//===-- support/writer/split_tuple_writer.cpp --------------------*- C++ -*-===/
+//
+// The RoSA Framework
+//
+//===----------------------------------------------------------------------===/
+///
+/// \file support/writer/split_tuple_writer.cpp
+///
+/// \author David Juhasz (david.juhasz@tuwien.ac.at)
+///
+/// \date 2019
+///
+/// \brief Implementation for rosa/support/writer/split_tuple_writer.hpp.
+///
+/// \note Empty implementation, source file here to have a compile database
+/// entry for rosa/support/writer/split_tuple_writer.hpp.
+///
+//===----------------------------------------------------------------------===/
+
+#include "rosa/support/writer/split_tuple_writer.hpp"

File Metadata

Mime Type
text/x-diff
Expires
Sat, Mar 15, 3:48 AM (17 h, 51 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
129052
Default Alt Text
(700 KB)

Event Timeline