diff --git a/.gitignore b/.gitignore index 600d2d3..cd73404 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -.vscode \ No newline at end of file +.vscode +build* +CMakeLists.txt.user diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..ce72e40 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "modules/cxxopts"] + path = modules/cxxopts + url = https://github.com/jarro2783/cxxopts.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 03bbc68..f8d7bdb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,188 +1,197 @@ cmake_minimum_required(VERSION 3.3.0) # Start project, set version here. project("RoSA") # NOTE: Adjust the variables version and release in docs/conf.py according to # version changes here. set(ROSA_VERSION_MAJOR 0) set(ROSA_VERSION_MINOR 1) set(ROSA_VERSION_PATCH 0) # Add path for custom modules set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules" ) # Package information if( NOT PACKAGE_VERSION ) set(PACKAGE_VERSION "${ROSA_VERSION_MAJOR}.${ROSA_VERSION_MINOR}.${ROSA_VERSION_PATCH}") endif() set(PACKAGE_NAME "Research on Self-Awareness Framework") set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}") set(PACKAGE_BUGREPORT "david.juhasz@tuwien.ac.at") #TODO: cpack? # Sanity check we are to make out-of-tree build if( CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR ) message(FATAL_ERROR "In-source builds are not allowed.") endif() string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE) # Set various paths set(ROSA_MAIN_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(ROSA_MAIN_INCLUDE_DIR ${ROSA_MAIN_SRC_DIR}/include) set(ROSA_MAIN_BIN_DIR ${ROSA_MAIN_SRC_DIR}/bin) set(ROSA_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) set(ROSA_RUNTIME_OUTPUT_INTDIR ${ROSA_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bin) set(ROSA_LIBRARY_OUTPUT_INTDIR ${ROSA_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib) set(ROSA_EXEC_BINARY_DIR ${ROSA_BINARY_DIR}/bin) set(ROSA_LIBRARY_DIR ${ROSA_BINARY_DIR}/lib) set(ROSA_INCLUDE_DIR ${ROSA_BINARY_DIR}/include) +set(ROSA_MAIN_MODULE_DIR ${ROSA_MAIN_SRC_DIR}/modules) # Add some generic helpers. include(AddCMakeTools) # Set build options option(ROSA_INCLUDE_TOOLS "Generate build targets for RoSA tools." ON) option(ROSA_INCLUDE_EXAMPLES "Generate build targets for RoSA examples." ON) option(ROSA_ENABLE_PEDANTIC "Compile with pedantic enabled." ON) # Assertions are always enabled for Debug builds, this option is respected only # for non-Debug builds. option(ROSA_ENABLE_ASSERTIONS "Enable assertions for non-Debug builds." OFF) option(ROSA_ENABLE_CLANG_TIDY "Run clang-tidy checks when building RoSA." OFF) option(ROSA_INCLUDE_CLANG_FORMAT "Generate build target for formatting RoSA sources with clang-format." OFF) option(ROSA_INCLUDE_DOCS "Generate build targets for RoSA documentation." ON) option(ROSA_BUILD_DOCS "Build RoSA documentation." OFF) option(ROSA_ENABLE_DOXYGEN "Use doxygen to generate RoSA API documentation." OFF) option(ROSA_ENABLE_SPHINX "Use Sphinx to generate RoSA documentation." OFF) set(ROSA_LOG_LEVEL "" CACHE STRING "Level of logging to be used.") set(ROSA_INCLUDE_APPS "" CACHE STRING "Generate build targets for the defined RoSA applications.") # All options referred to from HandleROSAOptions have to be specified # BEFORE this include, otherwise options will not be correctly set on # first cmake run include(config-ix) include(HandleROSAOptions) # Configure the ROSA configuration header file. if( NOT ROSA_LOG_LEVEL STREQUAL "") if( ${ROSA_LOG_LEVEL} EQUAL 5 ) set(ROSA_LOG_LEVEL_INT -1) else() set(ROSA_LOG_LEVEL_INT ${ROSA_LOG_LEVEL}) endif() else() set(ROSA_LOG_LEVEL_INT -1) endif() configure_file( ${ROSA_MAIN_INCLUDE_DIR}/rosa/config/rosa_config.h.cmake ${ROSA_INCLUDE_DIR}/rosa/config/rosa_config.h ) +# Handling modules +configure_file( + ${ROSA_MAIN_MODULE_DIR}/cxxopts/include/cxxopts.hpp + ${ROSA_INCLUDE_DIR}/cxxopts/cxxopts.hpp + COPYONLY +) + # They are not referenced. See set_output_directory(). set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${ROSA_EXEC_BINARY_DIR}) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${ROSA_LIBRARY_DIR}) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${ROSA_LIBRARY_DIR}) # Set include directories set(CMAKE_INCLUDE_CURRENT_DIR ON) include_directories(${ROSA_INCLUDE_DIR} ${ROSA_MAIN_INCLUDE_DIR}) # Set up YCM set(CMAKE_EXPORT_COMPILE_COMMANDS 1) message("Set compilation_database_folder in .ymc_extra_conf.py in the source directory if you want to use YMC.") # Add parts of the project include(AddROSATools) add_subdirectory(lib) add_subdirectory(apps) +add_subdirectory(modules) if( ROSA_INCLUDE_TOOLS ) add_subdirectory(tools) endif() if( ROSA_INCLUDE_EXAMPLES ) add_subdirectory(examples) endif() if( ROSA_INCLUDE_DOCS ) add_subdirectory(docs) endif() #TODO: install? # Print summary set(ROSA_ENABLE_ASSERTIONS_STR "OFF") if( ROSA_ENABLE_ASSERTIONS_INT EQUAL 1 ) set(ROSA_ENABLE_ASSERTIONS_STR "ON") endif() set(LOG_LEVEL_STR "disabled") if( NOT ROSA_LOG_LEVEL STREQUAL "" ) if( ${ROSA_LOG_LEVEL} EQUAL 0 ) set(LOG_LEVEL_STR "ERROR") elseif( ${ROSA_LOG_LEVEL} EQUAL 1 ) set(LOG_LEVEL_STR "WARNING") elseif( ${ROSA_LOG_LEVEL} EQUAL 2 ) set(LOG_LEVEL_STR "INFO") elseif( ${ROSA_LOG_LEVEL} EQUAL 3 ) set(LOG_LEVEL_STR "DEBUG") elseif( ${ROSA_LOG_LEVEL} EQUAL 4 ) set(LOG_LEVEL_STR "TRACE") elseif( ${ROSA_LOG_LEVEL} EQUAL 5 ) set(LOG_LEVEL_STR "disabled") else() set(LOG_LEVEL_STR "invalid") endif() endif() message(STATUS "\n====================| Build Summary |====================" "\n" "\nRoSA version: ${PACKAGE_VERSION}" "\n" "\nBuild type: ${CMAKE_BUILD_TYPE}" "\nAssertions: ${ROSA_ENABLE_ASSERTIONS_STR}" "\nClang-tidy: ${ROSA_ENABLE_CLANG_TIDY}" "\nClang-format: ${ROSA_INCLUDE_CLANG_FORMAT}" "\nLog level: ${LOG_LEVEL_STR}" "\n" "\nBuild apps: ${ROSA_INCLUDE_APPS}" "\nBuild tools: ${ROSA_INCLUDE_TOOLS}" "\nBuild examples: ${ROSA_INCLUDE_EXAMPLES}" "\nInclude docs: ${ROSA_INCLUDE_DOCS}" "\nBuild docs: ${ROSA_BUILD_DOCS}" "\n -enable doxygen: ${ROSA_ENABLE_DOXYGEN}" "\n -enable Sphinx: ${ROSA_ENABLE_SPHINX}" "\n" "\nCC: ${CMAKE_C_COMPILER}" "\nCFLAGS: ${CMAKE_C_FLAGS}" "\nCXX: ${CMAKE_CXX_COMPILER}" "\nCXXFLAGS: ${CMAKE_CXX_FLAGS}" "\nLIBRARIES: ${LD_FLAGS}" "\n" "\nSource directory: ${ROSA_MAIN_SRC_DIR}" "\nBuild directory: ${ROSA_BINARY_DIR}" "\nExecutable path: ${ROSA_EXEC_BINARY_DIR}" "\nLibrary path: ${ROSA_LIBRARY_DIR}" "\nGenerator: ${CMAKE_GENERATOR}" "\n" "\n===========================================================\n") 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/CMakeLists.txt b/apps/CMakeLists.txt index 813fd09..d00c89d 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -1,2 +1,7 @@ +# Allow exceptions by removing restricting flag. +if ( ROSA_COMPILER_IS_GCC_COMPATIBLE ) + remove("-fno-exceptions" CMAKE_CXX_FLAGS) +endif() + # Add the different subdirectories ADDALLSUBDIRS() diff --git a/apps/sa-ews1/sa-ews1.cpp b/apps/sa-ews1/sa-ews1.cpp index 67a1ac6..e137d8e 100644 --- a/apps/sa-ews1/sa-ews1.cpp +++ b/apps/sa-ews1/sa-ews1.cpp @@ -1,316 +1,324 @@ //===-- apps/sa-ews1/sa-ews1.cpp --------------------------------*- C++ -*-===// // // The RoSA Framework -- Application SA-EWS1 // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file apps/sa-ews1/sa-ews1.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief The application SA-EWS1 implements the case study from the paper: /// M. Götzinger, N. Taherinejad, A. M. Rahmani, P. Liljeberg, A. Jantsch, and /// H. Tenhunen: Enhancing the Early Warning Score System Using Data Confidence /// DOI: 10.1007/978-3-319-58877-3_12 //===----------------------------------------------------------------------===// #include "rosa/agent/Abstraction.hpp" #include "rosa/agent/Confidence.hpp" #include "rosa/config/version.h" #include "rosa/deluxe/DeluxeContext.hpp" #include "rosa/support/csv/CSVReader.hpp" #include "rosa/support/csv/CSVWriter.hpp" #include using namespace rosa; using namespace rosa::agent; using namespace rosa::deluxe; using namespace rosa::terminal; const std::string AppName = "SA-EWS1"; /// Paths for the CSV files for simulation. /// ///@{ const std::string HRCSVPath = "HR.csv"; const std::string BRCSVPath = "BR.csv"; const std::string SpO2CSVPath = "SpO2.csv"; const std::string BPSysCSVPath = "BPSys.csv"; const std::string BodyTempCSVPath = "BodyTemp.csv"; const std::string ScoreCSVPath = "Score.csv"; ///@} /// How many cycles of simulation to perform. const size_t NumberOfSimulationCycles = 16; /// Warning levels for abstraction. enum WarningScore { No = 0, Low = 1, High = 2, Emergency = 3 }; /// Helper function creating a deluxe agent for pre-processing sensory values. /// /// Received values are first validated for confidence. Values which the /// validator does not mark confident are ignored. Confident values are /// abstracted into a \c WarningScore value, which is the result of the /// processing function. /// /// \note The result, \c WarningScore, is returned as \c uint32_t because /// enumeration types are not integrated into built-in types. Hence, a master /// to these agents receives its input as \c uint32_t values, and may cast them /// to \c WarningScore explicitly. /// /// \tparam T type of values to receive from the sensor /// /// \param C the deluxe context to create the agent in /// \param Name name of the new agent /// \param CC confidence validator to use /// \param A abstraction to use /// /// \return handle for the new agent template AgentHandle createLowLevelAgent(std::unique_ptr &C, const std::string &Name, const Confidence &CC, const Abstraction &A) { - using handler = DeluxeAgent::D; + using handler = std::function(std::pair)>; using result = Optional; return C->createAgent( Name, handler([&, Name](std::pair I) -> result { LOG_INFO_STREAM << "\n******\n" << Name << " " << (I.second ? "" : "") << " value: " << I.first << "\n******\n"; return (I.second && CC(I.first)) ? result(A(I.first)) : result(); })); } int main(void) { LOG_INFO_STREAM << '\n' << library_string() << " -- " << Color::Red << AppName << "app" << Color::Default << '\n' << Color::Yellow << "CSV files are read from and written to the current working directory." << Color::Default << '\n'; std::unique_ptr C = DeluxeContext::create(AppName); // // Create deluxe sensors. // LOG_INFO("Creating sensors."); // All sensors are created without defining a normal generator function, but // with the default value of the second argument. That, however, requires the // data type to be explicitly defined. This is good for simulation only. AgentHandle HRSensor = C->createSensor("HR Sensor"); AgentHandle BRSensor = C->createSensor("BR Sensor"); AgentHandle SpO2Sensor = C->createSensor("SpO2 Sensor"); AgentHandle BPSysSensor = C->createSensor("BPSys Sensor"); AgentHandle BodyTempSensor = C->createSensor("BodyTemp Sensor"); // // Create functionalities. // LOG_INFO("Creating Functionalities for Agents."); // // Define confidence validators. // // Lower bounds are inclusive and upper bounds are exclusive. Confidence HRConfidence(0, 501); Confidence BRConfidence(0, 301); Confidence SpO2Confidence(0, 101); Confidence BPSysConfidence(0,501); Confidence BodyTempConfidence(-60, nextRepresentableFloatingPoint(50.0f)); // // Define abstractions. // RangeAbstraction HRAbstraction( {{{0, 40}, Emergency}, {{40, 51}, High}, {{51, 60}, Low}, {{60, 100}, No}, {{100, 110}, Low}, {{110, 129}, High}, {{129, 200}, Emergency}}, Emergency); RangeAbstraction BRAbstraction({{{0, 9}, High}, {{9, 14}, No}, {{14, 20}, Low}, {{20, 29}, High}, {{29, 50}, Emergency}}, Emergency); RangeAbstraction SpO2Abstraction({{{1, 85}, Emergency}, {{85, 90}, High}, {{90, 95}, Low}, {{95, 100}, No}}, Emergency); RangeAbstraction BPSysAbstraction( {{{0, 70}, Emergency}, {{70, 81}, High}, {{81, 101}, Low}, {{101, 149}, No}, {{149, 169}, Low}, {{169, 179}, High}, {{179, 200}, Emergency}}, Emergency); RangeAbstraction BodyTempAbstraction( {{{0.f, 28.f}, Emergency}, {{28.f, 32.f}, High}, {{32.f, 35.f}, Low}, {{35.f, 38.f}, No}, {{38.f, 39.5f}, High}, {{39.5f, 100.f}, Emergency}}, Emergency); // // Create low-level deluxe agents with \c createLowLevelAgent. // LOG_INFO("Creating low-level agents."); AgentHandle HRAgent = createLowLevelAgent(C, "HR Agent", HRConfidence, HRAbstraction); AgentHandle BRAgent = createLowLevelAgent(C, "BR Agent", BRConfidence, BRAbstraction); AgentHandle SpO2Agent = createLowLevelAgent(C, "SpO2 Agent", SpO2Confidence, SpO2Abstraction); AgentHandle BPSysAgent = createLowLevelAgent(C, "BPSys Agent", BPSysConfidence, BPSysAbstraction); AgentHandle BodyTempAgent = createLowLevelAgent( C, "BodyTemp Agent", BodyTempConfidence, BodyTempAbstraction); // // Connect sensors to low-level agents. // LOG_INFO("Connect sensors to their corresponding low-level agents."); C->connectSensor(HRAgent, 0, HRSensor, "HR Sensor Channel"); C->connectSensor(BRAgent, 0, BRSensor, "BR Sensor Channel"); C->connectSensor(SpO2Agent, 0, SpO2Sensor, "SpO2 Sensor Channel"); C->connectSensor(BPSysAgent, 0, BPSysSensor, "BPSys Sensor Channel"); C->connectSensor(BodyTempAgent, 0, BodyTempSensor, "BodyTemp Sensor Channel"); // // Create a high-level deluxe agent. // LOG_INFO("Create high-level agent."); // The new agent logs its input values and results in the the sum of them. AgentHandle BodyAgent = C->createAgent( "Body Agent", - DeluxeAgent::D( + std::function( + std::pair, std::pair, + std::pair, std::pair, + std::pair)>( [](std::pair HR, std::pair BR, std::pair SpO2, std::pair BPSys, std::pair BodyTemp) -> Optional { LOG_INFO_STREAM << "\n*******\nBody Agent trigged with values:\n" << (HR.second ? "" : "") << " HR warning score: " << HR.first << "\n" << (BR.second ? "" : "") << " BR warning score: " << BR.first << "\n" << (SpO2.second ? "" : "") << " SpO2 warning score: " << SpO2.first << "\n" << (BPSys.second ? "" : "") << " BPSys warning score: " << BPSys.first << "\n" << (BodyTemp.second ? "" : "") << " BodyTemp warning score: " << BodyTemp.first << "\n******\n"; return {HR.first + BR.first + SpO2.first + BPSys.first + BodyTemp.first}; })); // // Connect low-level agents to the high-level agent. // LOG_INFO("Connect low-level agents to the high-level agent."); C->connectAgents(BodyAgent, 0, HRAgent, "HR Agent Channel"); C->connectAgents(BodyAgent, 1, BRAgent, "BR Agent Channel"); C->connectAgents(BodyAgent, 2, SpO2Agent, "SpO2 Agent Channel"); C->connectAgents(BodyAgent, 3, BPSysAgent, "BPSys Agent Channel"); C->connectAgents(BodyAgent, 4, BodyTempAgent, "BodyTemp Agent Channel"); // // For simulation output, create a logger agent writing the output of the // high-level agent into a CSV file. // LOG_INFO("Create a logger agent."); // Create CSV writer. std::ofstream ScoreCSV(ScoreCSVPath); csv::CSVWriter ScoreWriter(ScoreCSV); // The agent writes each new input value into a CSV file and produces nothing. AgentHandle LoggerAgent = C->createAgent( "Logger Agent", - DeluxeAgent::D( + std::function(std::pair)>( [&ScoreWriter](std::pair Score) -> Optional { if (Score.second) { // The state of \p ScoreWriter is not checked, expecting good. ScoreWriter << Score.first; } return {}; })); // // Connect the high-level agent to the logger agent. // LOG_INFO("Connect the high-level agent to the logger agent."); C->connectAgents(LoggerAgent, 0, BodyAgent, "Body Agent Channel"); // // Do simulation. // LOG_INFO("Setting up and performing simulation."); // // Initialize deluxe context for simulation. // C->initializeSimulation(); // // Open CSV files and register them for their corresponding sensors. // // Type aliases for iterators. - using CSVInt = csv::CSVFlatIterator; - using CSVFloat = csv::CSVFlatIterator; + using CSVInt = csv::CSVIterator; + using CSVFloat = csv::CSVIterator; std::ifstream HRCSV(HRCSVPath); C->registerSensorValues(HRSensor, CSVInt(HRCSV), CSVInt()); std::ifstream BRCSV(BRCSVPath); C->registerSensorValues(BRSensor, CSVInt(BRCSV), CSVInt()); std::ifstream SpO2CSV(SpO2CSVPath); C->registerSensorValues(SpO2Sensor, CSVInt(SpO2CSV), CSVInt()); std::ifstream BPSysCSV(BPSysCSVPath); C->registerSensorValues(BPSysSensor, CSVInt(BPSysCSV), CSVInt()); std::ifstream BodyTempCSV(BodyTempCSVPath); C->registerSensorValues(BodyTempSensor, CSVFloat(BodyTempCSV), CSVFloat()); // // Simulate. // C->simulate(NumberOfSimulationCycles); return 0; } diff --git a/cmake/modules/HandleROSAOptions.cmake b/cmake/modules/HandleROSAOptions.cmake index 6c89812..e569892 100644 --- a/cmake/modules/HandleROSAOptions.cmake +++ b/cmake/modules/HandleROSAOptions.cmake @@ -1,214 +1,221 @@ # This CMake module is responsible for interpreting the user defined ROSA_ # options and executing the appropriate CMake commands to realize the users' # selections. include(CheckCCompilerFlag) include(CheckCXXCompilerFlag) if ( "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" ) set(ROSA_COMPILER_IS_CLANG ON) # Enforce minimum version. if ( CMAKE_CXX_COMPILER_VERSION VERSION_LESS "3.9.0" ) message(FATAL_ERROR "Insufficient LLVM version") endif() else() set(ROSA_COMPILER_IS_CLANG OFF) endif() if( CMAKE_COMPILER_IS_GNUCXX ) set(ROSA_COMPILER_IS_GCC_COMPATIBLE ON) elseif( MSVC ) set(ROSA_COMPILER_IS_GCC_COMPATIBLE OFF) elseif( ROSA_COMPILER_IS_CLANG ) set(ROSA_COMPILER_IS_GCC_COMPATIBLE ON) else() message(FATAL_ERROR "Unexpected C++ compiler!") endif() function(append value) foreach(variable ${ARGN}) set(${variable} "${${variable}} ${value}" PARENT_SCOPE) endforeach(variable) endfunction() function(append_if condition value) if (${condition}) foreach(variable ${ARGN}) set(${variable} "${${variable}} ${value}" PARENT_SCOPE) endforeach(variable) endif() endfunction() +function(remove value) + foreach(variable ${ARGN}) + string(REPLACE "${value}" "" NEWVAR "${${variable}}") + set(${variable} "${NEWVAR}" PARENT_SCOPE) + endforeach(variable) +endfunction() + # NDEBUG on non-Debug builds append("-DNDEBUG" CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_C_FLAGS_MINSIZEREL) # Assertions are always enabled on Debug builds, this is interesting for # non-Debug builds. if( NOT "${uppercase_CMAKE_BUILD_TYPE}" STREQUAL "DEBUG" AND ${ROSA_ENABLE_ASSERTIONS} ) # MSVC doesn't like _DEBUG on release builds. See PR 4379. if( NOT MSVC ) add_definitions( -D_DEBUG ) set(ENABLE_ASSERTIONS "1") else() set(ENABLE_ASSERTIONS "0") endif() # On non-Debug builds we NDEBUG, but in this case we need to undefine it: add_definitions(-UNDEBUG) # Also remove /D NDEBUG to avoid MSVC warnings about conflicting defines. foreach (flags_var_to_scrub CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_C_FLAGS_MINSIZEREL) string (REGEX REPLACE "(^| )[/-]D *NDEBUG($| )" " " "${flags_var_to_scrub}" "${${flags_var_to_scrub}}") endforeach() endif() set(ROSA_ENABLE_ASSERTIONS_INT -1) if( "${uppercase_CMAKE_BUILD_TYPE}" STREQUAL "DEBUG" OR ${ROSA_ENABLE_ASSERTIONS} ) set(ROSA_ENABLE_ASSERTIONS_INT 1) endif() if( ROSA_COMPILER_IS_GCC_COMPATIBLE ) append("-Wall -Wextra -Wdocumentation -Werror" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) append_if(ROSA_ENABLE_PEDANTIC "-pedantic -Wno-long-long" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) append("-fno-rtti -fno-exceptions" CMAKE_CXX_FLAGS) append("-Wno-c++1z-compat" CMAKE_CXX_FLAGS) check_cxx_compiler_flag("-std=c++17" CXX_SUPPORTS_CXX17) if ( CXX_SUPPORTS_CXX17 ) append("-std=c++17" CMAKE_CXX_FLAGS) else() message(FATAL_ERROR "RoSA requires C++17 support but the '-std=c++17' flag isn't supported.") endif() check_c_compiler_flag("-std=c11" C_SUPPORTS_C11) if ( C_SUPPORTS_C11 ) append("-std=c11" CMAKE_C_FLAGS) else() message(FATAL_ERROR "RoSA requires C11 support but the '-std=c11' flag isn't supported.") endif() elseif ( MSVC ) if (ROSA_COMPILER_IS_CLANG) # Remove/adjsut incorrect flags that might be generated automatically. foreach (flags_var_to_scrub CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_C_FLAGS_MINSIZEREL) string (REGEX REPLACE "(^| )-Wall($| )" " " "${flags_var_to_scrub}" "${${flags_var_to_scrub}}") string (REGEX REPLACE "(^| )-frtti($| )" " " "${flags_var_to_scrub}" "${${flags_var_to_scrub}}") string (REGEX REPLACE "(^| )-fexceptions($| )" " " "${flags_var_to_scrub}" "${${flags_var_to_scrub}}") string (REGEX REPLACE "(^| )-fno-inline($| )" " -Ob0 " "${flags_var_to_scrub}" "${${flags_var_to_scrub}}") string (REGEX REPLACE "(^| )-gline-tables-only($| )" " " "${flags_var_to_scrub}" "${${flags_var_to_scrub}}") message("${flags_var_to_scrub}: ${${flags_var_to_scrub}}") endforeach() endif() # Flags matching GCC-compatible "-Wall -Wextra -Werror". # NOTE: Do not use -Wall for MSVC because it generates silly warning (e.g., # warnings about things in external libraries), use -W4 instead. append("/W4 /WX" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) if ( ROSA_COMPILER_IS_CLANG ) # GCC-compatible flags without corresponding MSVC options. append("-Wdocumentation" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) # The flag "-pedantic" is not in the MSVC flavour. append_if(ROSA_ENABLE_PEDANTIC "-Wno-long-long" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) endif() # Flags matching GCC-compatible "-fno-rtti". append("/GR-" CMAKE_CXX_FLAGS) # NOTE: We cannot disable unwind semantics (i.e., -fno-exceptions) for Windows # libraries... append("/EHsc" CMAKE_CXX_FLAGS) if ( ROSA_COMPILER_IS_CLANG ) append("-Wno-c++1z-compat" CMAKE_CXX_FLAGS) # MSVC puts there some garbage which needs to be ignored... append("-Qunused-arguments" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) endif() if ( ROSA_COMPILER_IS_CLANG ) check_cxx_compiler_flag("-Xclang -std=c++17" CXX_SUPPORTS_CXX17) if ( CXX_SUPPORTS_CXX17 ) append("-Xclang -std=c++17" CMAKE_CXX_FLAGS) else() message(FATAL_ERROR "RoSA requires C++17 support but the '-std=c++17' flag isn't supported.") endif() check_c_compiler_flag("-Xclang -std=c11" C_SUPPORTS_C11) if ( C_SUPPORTS_C11 ) append("-Xclang -std=c11" CMAKE_C_FLAGS) else() message(FATAL_ERROR "RoSA requires C11 support but the '-std=c11' flag isn't supported.") endif() else () check_cxx_compiler_flag("/std:c++17 /permissive-" CXX_SUPPORTS_CXX17) if ( CXX_SUPPORTS_CXX17 ) append("/std:c++17 /permissive-" CMAKE_CXX_FLAGS) else() message(FATAL_ERROR "RoSA requires C++17 support but the '/std:c++17 /permissive-' flags aren't supported.") endif() message(WARNING "RoSA is supposed to use C11 code in C files, which MSVC might not fully support.") # Make MSVC ignore warning C4514 about unreferenced inline functions. append("/nowarn:4514") # Make MSVC ignore warning LNK4221 in libraries. The warning is caused by # empty object files that correspond to the empty .cpp files, which we have # for generating compile database entries for all header files. append("/IGNORE:4221" CMAKE_STATIC_LINKER_FLAGS) endif() endif() if( ROSA_ENABLE_CLANG_TIDY ) # Set clang-tidy arguments, use the same for both C and CXX. set(CMAKE_C_CLANG_TIDY "${CLANGTIDY_EXECUTABLE};-header-filter=.*") set(CMAKE_CXX_CLANG_TIDY ${CMAKE_C_CLANG_TIDY}) # Apply suggested fixes if requested. append_if(ROSA_CLANG_TIDY_FIX ",-fix" CMAKE_C_CLANG_TIDY CMAKE_CXX_CLANG_TIDY) endif( ROSA_ENABLE_CLANG_TIDY ) if( ROSA_INCLUDE_APPS STREQUAL "") message(STATUS "Include all apps by default.") SUBDIRLIST(ROSA_INCLUDE_APPS ${ROSA_MAIN_SRC_DIR}/apps) else() message(STATUS "Include apps: ${ROSA_INCLUDE_APPS}.") endif() diff --git a/docs/Dev.rst b/docs/Dev.rst index 78ed678..f4bdda9 100755 --- a/docs/Dev.rst +++ b/docs/Dev.rst @@ -1,373 +1,387 @@ ============================= Developing the RoSA Framework ============================= .. contents:: :local: This document provides information that might be useful for contributing to RoSA. Please also consult :doc:`Build`. .. _Dev_Source_Directory: The Source Directory ==================== The source directory consists of the following subdirectories: `cmake` Contains files used for configuring the `CMake Project`_. `docs` Contains `Documentation`_-related files. `examples` Contains `Examples`_ on using the public API. `include/rosa` Contains the RoSA public API -- that is the interface of RoSA `Libraries`_. The directory `include` is to be used as include directory and RoSA header files are to be included in C++ sources as `"rosa/"`. `lib` Contains the implementation of the RoSA public API -- that is the implementation of RoSA `Libraries`_. `apps` Contains `Apps`_ based on RoSA features. `tools` Contains `Tools`_ based on RoSA features. +`modules` + Contains third-party `Modules`_ that are not part of RoSA but used by `Apps`_ + Software Sources ================ The section describes the `Logical Structure`_ of the software sources and what `Coding Standards`_ are supposed to be followed for the implementation. Logical Structure ----------------- Various features provided by RoSA are sorted into different `Libraries`_. `Examples`_ , `Apps`_, and `Tools`_ using those `Libraries`_ are separated from the implementation of the RoSA features into different directories. +`Examples`_ , `Apps`_, and `Tools`_ may also use third-party libraries, +called `Modules`_. Libraries ~~~~~~~~~ The framework consists of separate libraries providing different features. The public interfaces for RoSA libraries are defined in `include/rosa`, while corresponding implementation is in `lib`. Each library has its own subdirectory in the mentioned directories. RoSA provides the following libraries: `config` Provides information on the configuration used to build the framework, e.g., version number, log level, assertions, and debugging. `support` Provides general features -- template metaprograms dealing with types, for instance -- for implementing other libraries. `core` Provides the basic RoSA features, like systems managing agents passing messages. `agent` Provides features to be used for implementing agents. `deluxe` Provides a somewhat more modular interface for defining systems with RoSA. .. _Library_Dependencies: Dependencies '''''''''''' The following table summarizes dependencies among libraries. A marking in a row denotes that the library in the beginning of the row depends on the library in the head of the given column. +---------+--------+---------+------+--------+-------+ | | config | support | core | deluxe | agent | +=========+========+=========+======+========+=======+ | config | | | | | | +---------+--------+---------+------+--------+-------+ | support | | | | | | +---------+--------+---------+------+--------+-------+ | core | | × | | | | +---------+--------+---------+------+--------+-------+ | deluxe | | | × | | | +---------+--------+---------+------+--------+-------+ | agent | | | | | | +---------+--------+---------+------+--------+-------+ Examples ~~~~~~~~ Some simple samples are provided in `examples` to demonstrate how to to use different parts of the RoSA API. Apps ~~~~ Apps, actual applications based on the RoSA libraries, are implemented in `apps`. Tools ~~~~~ Tools, programs based on the RoSA libraries and providing standalone functionalities, are implemented in `tools`. +Modules +~~~~~~~ + +This directory contains third-party modules that can be used inside RoSA. These +include: + + * cxxopts: https://github.com/jarro2783/cxxopts + Example usage can be seen in modules/cxxopts/src/example.cpp + .. _Coding_Standards: Coding Standards ---------------- RoSA is implemented in standard *C++17* code. All the software sources are to be written in accordance to the `LLVM Coding Standards`_. .. _nortti-noexceptions: Feature Restrictions ~~~~~~~~~~~~~~~~~~~~ Pay attention `not to use RTTI and Exceptions`_. Those features are disabled in the CMake project. Documentation Comments ~~~~~~~~~~~~~~~~~~~~~~ It is important to remember to document source code using `doxygen comments`_ as `API Documentation`_ is generated directly from sources. Note that the syntax of documentation comments is checked during compilation -- at least when using a GCC-compatible compiler. Further, check :ref:`Doxygen warnings ` as issues not being detected by the compiler may be found when actually generating the documentation. Whenever you work on a source file, make sure your name is in the author-list defined in the header comment of the file. Each author should be defined with a separate `\\author` command so that recent authors come first. Authors not participating in further development of a file anymore may be marked with the period of their contribution. If declarations belonging to a namespace are spread to more than one source files, document the namespace in a separate `namespace.h` in the directory belonging to the library. Otherwise, document the namespace in the only file in which entities of the namespace are declared. Header Files ~~~~~~~~~~~~ Follow the recommendations on public and private header files and the usage of `#include` from the `LLVM Coding Standards`_. Use `.h` and `.hpp` extensions to indicate the content of the header file: * header files containing any *definition* -- template or inline definition -- or including another header file with `.hpp` extension have `.hpp` extension; * header files containing only *declarations* and including only header files with `.h` extension have `.h` extension. It may happen that a header file does not need any corresponding implementation in a `.cpp` file. Nevertheless, do create a corresponding `.cpp` file which only includes the header file in this case. That makes sure that the header file is compiled and hence checked for errors, and also a corresponding entry in the compilation database is generated. Checking and Enforcing the Coding Standards ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The tools `clang-tidy `_ and `clang-format `_ can be used to check and enforce the coding standards. The two tools are integrated into the CMake project, refer to CMake variables :ref:`ROSA_ENABLE_CLANG_TIDY ` and :ref:`ROSA_INCLUDE_CLANG_FORMAT `. Note that there may be situations when `clang-tidy` checks result in false positives -- for example, for some cases of the order of `#include` directives. One can order `clang-tidy` to suppress warnings for a line of code by marking that line with:: // NOLINT It may be preferred to diverge from the standard formatting -- for example for the sake of readability of static definition of arrays following some structure. One can disable `clang-format` for some lines of code by designating a range with two special comments as:: // clang-format off ... clang-format is disabled here ... // clang-format on Documentation ============= The RoSA Framework is delivered with two kinds of documentation: `General Documentation`_ and `API Documentation`_, generation of both of which is integrated into the CMake project. References between the two documentations are relative addresses corresponding to the directory structure of the :ref:`generated documentation `. General Documentation --------------------- General documentation is written as `reStructuredText `_ compiled with `Sphinx `_. For build integration, refer to the CMake variable :ref:`ROSA_ENABLE_SPHINX `. Documentation files are located in `docs` with extension `.rst`. The main page of the documentation is `docs/index.rst`. Configuration for building the documentation is `docs/conf.py`. The directory `docs/CommandGuide` contains documentation for each separate tool. Those pages are included in the HTML documentation via `docs/CommandGuide/index.rst`. Moreover, man pages can be generated from those tool documentation pages. API Documentation ----------------- API documentation is directly generated from sources with `Doxygen `_. For build integration, refer to the CMake variable :ref:`ROSA_ENABLE_DOXYGEN `. The main page used for the API documentation is `docs/doxygen-mainpage.dox`. Configuration for generating the API documentation is `docs/doxygen.cfg.in`. .. _CMake Project: Managing the CMake Project ========================== This section briefly summarizes when and how to modify CMake files during the development process. No general discussion on CMake features is provided here. When modifying `Documentation`_, no need to update the CMake files. Software -------- One needs to modify the CMake files only if source files are to be added or removed from the project. Here follows some typical scenarios. Source Files ~~~~~~~~~~~~ Each library and executable target has its own directory and its own definition as a file called `CMakeLists.txt` in that directory. When adding or removing a source file -- both headers and `.cpp` files -- to a library or executable, locate the corresponding `CMakeLists.txt` file. The file is typically in the same directory where the file to be added or removed is located. Except for header files of the public API, for which the corresponding CMake target is defined in a `lib` subdirectory corresponding to the library the header files belongs to. Update the source list in the argument of the `add_library` or `add_executable` command in the `CMakeLists.txt`, for libraries and executables, respectively. A library and executable may use features provided by another library. Such a dependency is to be defined in the `CMakeLists.txt` file of the dependent target by using the `ROSA_add_library_dependencies` command. CMake Libraries ~~~~~~~~~~~~~~~ When adding or removing a library, add or remove the corresponding directories from `include` and `lib`. If you have already had generated a build project with CMake, `touch` [#]_ `lib/CMakeLists.txt` to make CMake rescan subdirectories on next build. When defining a new library, the new subdirectory under `lib` needs to contain a `CMakeLists.txt`, which needs to contain at least an `add_library` command defining the name of the library and the source files belonging to it. CMake Executables ~~~~~~~~~~~~~~~~~ When adding or removing an executable, add or remove the corresponding directory from `apps`, `examples`, or `tools`. If you have already had generated a build project with CMake, `touch` `CMakeLists.txt` in the containing directory (like in the case of libraries) to make CMake rescan subdirectories on next build. When defining a new executable, the new subdirectory needs to contain a `CMakeLists.txt`, which needs to contain at least an `add_executable` command defining the name of the executable and the source files belonging to it. .. _Dev Managing Sources: Managing Sources ================ Consider the followings before committing changes to the repository: * your code complies with the `Coding Standards`_ as much as possible; * your code is well documented; * your code is not bloated with unusued code and/or comments; * your changes do not break building and executing the framework: * test all of the supported platforms if possible, * look into the generated documentation if you have edited `General Documentation`_; * you do not pollute the repository with unused and generated files. When committing changes to the repository, provide a concise log message with your commit. Miscellaneous Concerns ====================== Using YCM --------- If you happen to use `YCM `_, just make a copy of the provided `ycm_extra_conf.py.template` file as `.ycm_extra_conf.py` in the RoSA source directory, and set the following two variables in it: `compilation_database_folder` the absolute path of your build directory `extra_system_include_dirs` any system include directory which might not be searched by `libclang` [#]_. You probably want compile with Clang if you use YCM, so run CMake with environment variables `CC=clang` and `CXX=clang++` set. Also note that header files in the `include` directory are compiled for YCM with the compiler flags of a corresponding source file in the `lib` directory, if any. Header files in other locations are supposed to have a corresponding source file in the same directory. Notes ~~~~~ * If the project's include directory (`include/rosa`) would ever be changed, then the YCM configuration file needs to be adjusted accordingly. .. rubric:: Footnotes .. [#] Set the last modified time of the file to the current time. .. [#] See: https://github.com/Valloric/YouCompleteMe/issues/303; use the following command to figure out the used system directories: echo | clang -std=c++11 -v -E -x c++ - .. _`LLVM Coding Standards`: http://llvm.org/docs/CodingStandards.html .. _`not to use RTTI and Exceptions`: http://llvm.org/docs/CodingStandards.html#do-not-use-rtti-or-exceptions .. _`doxygen comments`: http://llvm.org/docs/CodingStandards.html#doxygen-use-in-documentation-comments diff --git a/docs/Issues.rst b/docs/Issues.rst index b3f6fd0..c873116 100755 --- a/docs/Issues.rst +++ b/docs/Issues.rst @@ -1,53 +1,52 @@ ================================================================== Known Issues with the Current Implementation of the RoSA Framework ================================================================== .. contents:: :local: TODO ==== * Project logo - `docs/_themes/rosa-theme/static/logo.png` -* License? * Packaging with `CPack `_. * What about design documentation on the basics of RoSA? * What about testing the framework? Known Issues ============ The issues that are listed below are described assuming :ref:`minimal build dependencies ` are met. * C++ * The noexcept-specification is part of the function type since C++17 but `std::function` is not defined for noexcept template arguments. So we cannot enforce the expected noexcept semantics for functions stored in `std::function`. See affected functions `rosa::Invoker::wrap()`, `rosa::deluxe::DeluxeAgent::DeluxeAgent()`, `rosa::deluxe::DeluxeSensor::DeluxeSensor()`, and `rosa::deluxe::DeluxeSensor::registerSimulationDataSource()`. * MSVC * While :ref:`RoSA does not use exceptions `, we cannot disable unwind semantics with MSVC because some included header files define functions that use exceptions. * Doxygen * "Potential recursive class relation" is detected for `rosa::GenSeq`, which is true if one ignores the template specialization for the terminal case. It would be nice not to have this pointless warning. * clang-tidy * Clang-tidy reports warnings about `noexcept` marking for the move constructor and move assignment operator of `rosa::Optional` in some situations when the template with the non-specialized argument list is used -- for example, in the file `example/deluxe-interface/deluxe-interface.cpp`. However, the condition for the `noexcept` marking should be met and the warning is pointless. diff --git a/docs/index.rst b/docs/index.rst index 6a9b649..e42d88a 100755 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,71 +1,83 @@ ================== The RoSA Framework ================== .. contents:: :local: Overview ======== The RoSA (Research on Self-Awareness) Framework is intended to be used as a tool for experimenting and evaluating new ways of implementing self-aware applications. This documentation was generated for RoSA version |release| on |today|. Documentation ============= .. toctree:: :hidden: Changelog Use Build Dev Issues Release Plan CommandGuide/index :doc:`Use` Describes how to create your own software based on RoSA. :doc:`Build` Describes how to build RoSA. :doc:`Dev` Provides basic information for developers on how to contribute to RoSA. :doc:`Issues` Known issues and things to do with the current implementation of RoSA. :doc:`Release` Describes for maintainers how to make a new release of RoSA. :doc:`Plan` Discusses future work and possible ways to develop RoSA. :doc:`CommandGuide/index` Documentation for RoSA applications and tools. Changes ======= Refer to :doc:`Changelog`. .. _API_Documentation: API Documentation ================= For details of the provided interface, refer to our `API documentation`_. .. _API documentation: ../doxygen/html/index.html +License +======= + +RoSA is distributed under the terms and conditions of the +`Boost Software License 1.0 `_: + +.. literalinclude:: ../LICENSE + :language: none + +.. note:: + 3rd party externals in the directory `modules` are not part of the RoSA source base and are licensed separately. + Indices and Tables ================== * :ref:`genindex` * :ref:`search` diff --git a/examples/CSVFiles/CMakeLists.txt b/examples/CSVFiles/CMakeLists.txt new file mode 100644 index 0000000..cfc92e7 --- /dev/null +++ b/examples/CSVFiles/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(csvfiles main.cpp) +ROSA_add_library_dependencies(csvfiles ROSAConfig) +ROSA_add_library_dependencies(csvfiles ROSADeluxe) diff --git a/examples/CSVFiles/main.cpp b/examples/CSVFiles/main.cpp new file mode 100644 index 0000000..5fc6075 --- /dev/null +++ b/examples/CSVFiles/main.cpp @@ -0,0 +1,360 @@ +//===-- examples/CSVFiles/main.cpp ------------------*- C++ -*-===// +// +// The RoSA Framework +// +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// +//===----------------------------------------------------------------------===// +/// +/// \file examples/basic-system/basic-system.cpp +/// +/// \author Edwin Willegger (edwin.willegger@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief A simple example on the basic \c rosa::csv, \c rosa::iterator and +/// \c rosa::writer classes. Focus is on the tuple impementations. +/// +//===----------------------------------------------------------------------===// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//includes for an complete example to read and write +//with sensors and agents. +#include "rosa/deluxe/DeluxeContext.hpp" + +#include "rosa/config/version.h" + +//includes to test the basic functionality +//to read and write tuples. +#include "rosa/support/csv/CSVReader.hpp" +#include "rosa/support/csv/CSVWriter.hpp" +#include "rosa/support/iterator/split_tuple_iterator.hpp" +#include "rosa/support/writer/split_tuple_writer.hpp" + +/// the name of the example +const std::string ExampleName = "csvfiles"; + +/// How many cycles of simulation to perform. +const size_t NumberOfSimulationCycles = 10; + +/// Paths for the CSV files for simulation. +/// input csv files +const std::string csvPath = "../examples/CSVFiles/"; +const std::string csvFileWithHeader = csvPath + "HR-New.csv"; +const std::string csvFileNoHeader = csvPath + "HR.csv"; +const std::string csvFileHeaderSemi = csvPath + "HR-New-Semicolon.csv"; +/// output csv files +const std::string csvFileWriteHea = csvPath + "csvwriter_noheader.csv"; +const std::string csvFileWriteNoHeaSplit = csvPath + "csvSplitwriter_noheader.csv"; + +using namespace rosa; + +/// +/// This function tests the basic CSVIterator capablities, and shows you +/// how you could work with this class. +/// +void testtupleCSVReader(void){ + + //different streams to get the csv data out of the files + //file contains header and valid data entries, delimter = ',' + std::ifstream file_header_data(csvFileWithHeader); + //file contains header and valid data entries, delimter = ',' + std::ifstream file_header_data_2(csvFileWithHeader); + //file contains header and valid data entries, delimter = ',' + std::ifstream file_header_data_3(csvFileWithHeader); + //file contains header and valid data entries, delimter = ',' + std::ifstream file_header_data_4(csvFileWithHeader); + //file contains header and valid data entries, delimter = ',' + std::ifstream file_header_data_5(csvFileWithHeader); + //file contains header and valid data entries, delimter = ',' + std::ifstream file_header_data_6(csvFileWithHeader); + //file contains no header an valid data entries, delimter = ',' + std::ifstream file2(csvFileNoHeader); + //file contains header and valid data entries, delimter = ';' + std::ifstream file3(csvFileHeaderSemi); + + csv::CSVIterator it(file_header_data); + + it.setDelimeter(','); + + (void)++it; + (void)++it; + //if you iterate over the end of file, the last values + //of the file will remain in the data structure but no + //error occurs. + (void)++it; + (void)++it; + + //------------------------------------------------------------------- + // a possiblity to get the data out of the iterator + std::tuple value = *it; + + // + // Show the value of one iterator + // + LOG_INFO( "Values are: "); + LOG_INFO(std::get<0>(value) ); + LOG_INFO(std::get<1>(value) ); + + + //-------------------------------------------------------------------- + //testing differnet parameters to the constructor + + //uncomment to see that it is not possible to iterate over an vector in the tuple. + //rosa::csv::CSVIterator> it2(file, 1); + + //try to skip a valid number of lines after the header + csv::CSVIterator it2_0(file_header_data_2, 1); + //try to skip a valid number of lines after the header, but you assume that the file has no header + //uncomment this line to crash the programm + //csv::CSVIterator it2_1(file_header_data_3, 0, csv::HeaderInformation::HasNoHeader); + + //try to skip a valid number of lines after the header, but you assume that the file has no header + //uncomment this line to crash the program + //csv::CSVIterator it2_2(file_header_data_4, 1, csv::HeaderInformation::HasNoHeader); + + //try to skip a valid number of lines of a file without header + csv::CSVIterator it2_3(file2, 1, csv::HeaderInformation::HasNoHeader); + + //try to skip a valid number of lines after the header, but with different delimeter + csv::CSVIterator it2_4(file3, 2, csv::HeaderInformation::HasHeader, ';'); + + // if you skip more lines than valid, you generate an infinte loop + //csv::CSVIterator it3(file_header_data_5, 500); + + //if you don't need data from all columns just select the number of columns you + //need. You get the data back from the first column (index 0) to the fourth column + //all values from the fifth column are ignored. + csv::CSVIterator it4(file_header_data_6); +} + +/// +/// This function tests the basic CSVTupleWriter capablities, and shows you +/// how you could work with this class. +/// +void testtupleCSVWriter(void){ + // + // Create output writer with an file + // + std::ofstream file_header_out(csvFileWriteHea); + csv::CSVTupleWriter wri(file_header_out); + + // + // Create test tuples + // + std::tuple values(5, 8.3f, "hallo"); + std::tuple values2(3, 8.3f, "end"); + + // + // Create test header lines for the test tuples + // + std::array header{ + {"zero column", "first column", "second column"}}; + + std::array headerWrong{ + {"zero column", "first column", "second column", "third column"}}; + + std::array headerWrongShort{ + {"zero column", "first column"}}; + + //if you uncomment this line than it would be possible for you to write the header into the stream + //in the next line. + //wri.write(values); + wri.writeHeader(header); + wri.write(values); + wri.write(values); + // it is not possible to write an additional header into the stream. + wri.writeHeader(header); + wri.write(values); + wri << values; + wri << values2; + + //uncomment this line to see, that you can't write a header with the too many elements. + //wri.writeHeader(headerWrong); + //uncomment this line to see, that you can't write a header with the too few elements. + //wri.writeHeader(headerWrongShort); + +} + +/// +/// This function tests the basic splitTupleIterator capablities, and shows you +/// how you could work with this class, this class is used if you want to split +/// a CSVIterator in separate parts. +/// +void testsplitTupleIterator(void) +{ + // + // Create deluxe context + // + std::unique_ptr C = + deluxe::DeluxeContext::create(ExampleName); + + // + // Create deluxe sensors. + // + LOG_INFO("Creating sensors."); + + + // All sensors are created without defining a normal generator function, but + // with the default value of the second argument. That, however, requires the + // data type to be explicitly defined. This is good for simulation only. + // Three different sensors were created, this is just a random number taken. + AgentHandle Elem0Sensor = C->createSensor("Element1 Sensor"); + AgentHandle Elem1Sensor = C->createSensor("Element2 Sensor"); + AgentHandle Elem2Sensor = C->createSensor("Element3 Sensor"); + + + // + // Initialize deluxe context for simulation. + // + C->initializeSimulation(); + + // Type aliases for iterators + using Iterator = rosa::csv::CSVIterator; + using IteratorValue = std::tuple; + + static_assert (std::is_same::value, "Iterator must provide tuples" ); + + // + // Open CSV file and register the columns to the corresponding sensors. + // + std::ifstream TestCSV(csvFileWithHeader); + + // + // Test data looks like: + // Element1, Element2, Element3, Element4, Element5 -- is the header line + // 3, 5, 8, 9.5, 17 -- first line of values + // 100, -8, 30, 18.8, 29 -- other line of values were also in the file + // 5, 20, -100, -200.1, -30 -- if you have less number of values than simulation rounds all values + // -- beyond your last value will be zero. + + //get element iterator ranges + auto [Elem0Range, Elem1Range, Elem2Range] = iterator::splitTupleIterator(Iterator(TestCSV), Iterator()); + + //dissect a range into begin and end iterators by structred bindings + auto[Elem0Begin, Elem0End] = Elem0Range; + + //deissect a range with functions + auto Elem1Begin = iterator::begin(Elem1Range); + auto Elem1End = iterator::end(Elem1Range); + + C->registerSensorValues(Elem0Sensor, std::move(Elem0Begin), Elem0End); + C->registerSensorValues(Elem1Sensor, std::move(Elem1Begin), Elem1End); + C->registerSensorValues(Elem2Sensor, std::move(iterator::begin(Elem2Range)), + iterator::end(Elem2Range)); + + // + // Simulate. + // + C->simulate(NumberOfSimulationCycles); + +} + + +/// +/// This function tests the basic splitTupleWriter capablities, and shows you +/// how you could work with this class, this class is used if you want to split +/// a CSVWriter in separate parts. +/// +void testsplitTupleWriter(void){ + // + // Create output writer with an file + // + std::ofstream file_header_out(csvFileWriteNoHeaSplit); + csv::CSVTupleWriter wri(file_header_out); + + // if you omit, the type definition in the template, than auto generated types were used, + // and they may not fit to the used CSVTupleWriter. + wri << std::make_tuple(1000, 50.6f, "tuple_created"); + auto [T0Writer, T1Writer, T2Writer] = writer::splitTupleWriter(std::move(wri), + writer::IncompleteTuplePolicy::Ignore); + //writing elements in sequential order into the writer classes, but you can write the values into the writers in + //a random order. + T0Writer << (500); + T1Writer << (3.0); + T2Writer << "splitted writter"; + + T2Writer << "splitting is cool"; + T0Writer << (-30); + T1Writer << (-0.004f); + + // you can also write more often values into a writer and later into the other writers + // all data will be processed correctly into the right order. + T0Writer << (1); + T0Writer << (2); + T1Writer << (-0.4f); + T0Writer << (3); + T2Writer << "again"; + T0Writer << (4); + T1Writer << (-0.1f); + T1Writer << (-0.2f); + T2Writer << "and"; + T1Writer << (-0.3f); + T2Writer << "splitting"; + T2Writer << "once again"; + + // again writing data of one tuple entry to the different writers in a random fashion. + T1Writer << (-0.004f); + T2Writer << "splitting is cool"; + T0Writer << (-30); + +} + +int main(void){ + LOG_INFO_STREAM << library_string() << " -- " << terminal::Color::Red + << ExampleName << " example" << terminal::Color::Default << '\n'; + + // + // Testing CSVWriter. + // + LOG_INFO("Testing CSVWriter CSVTupleItrator implementation: "); + + testtupleCSVWriter(); + + // + // Testing CSVReader. + // + LOG_INFO("Testing CSVReader CSVTupleIterator implementation: "); + + testtupleCSVReader(); + + // + // Testing SplitTupleIterator. + // + LOG_INFO("Testing SplitTupleIterator: "); + + testsplitTupleIterator(); + + // + // Testing SplitTupleWriter. + // + LOG_INFO("Testing SplitTupleWriter: "); + testsplitTupleWriter(); + + + // + // info that user knows programm has finished. + // + LOG_INFO( "All tests finished."); + + + return 0; +} + diff --git a/examples/agent-functionalities/agent-functionalities.cpp b/examples/agent-functionalities/agent-functionalities.cpp index c1d77a1..d6e5ede 100644 --- a/examples/agent-functionalities/agent-functionalities.cpp +++ b/examples/agent-functionalities/agent-functionalities.cpp @@ -1,183 +1,194 @@ //===-- examples/agent-functionalities/agent-functionalities.cpp *-- C++-*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file examples/agent-functionalities/agent-functionalities.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief A simple example on defining \c rosa::Agent instances using /// \c rosa::agent::Functionality object as components. /// //===----------------------------------------------------------------------===// +// Make sure M_PI is available, needed for _WIN32 +#define _USE_MATH_DEFINES +#include + #include "rosa/agent/Abstraction.hpp" +#include "rosa/agent/Confidence.hpp" #include "rosa/agent/FunctionAbstractions.hpp" #include "rosa/agent/RangeConfidence.hpp" -#include "rosa/agent/Confidence.hpp" #include "rosa/config/version.h" #include "rosa/core/Agent.hpp" #include "rosa/core/MessagingSystem.hpp" #include "rosa/support/log.h" #include "rosa/support/terminal_colors.h" #include using namespace rosa; using namespace rosa::agent; using namespace rosa::terminal; +// We use pi as float rather than double, which M_PI is. +constexpr float Pi = (float) M_PI; + /// A dummy wrapper for testing \c rosa::MessagingSystem. /// /// \note Since we test \c rosa::MessagingSystem directly here, we need to get /// access to its protected members. That we do by imitating to be a decent /// subclass of \c rosa::MessagingSystem, while calling protected member /// functions on an object of a type from which we actually don't inherit. struct SystemTester : protected MessagingSystem { template static AgentHandle createMyAgent(MessagingSystem *S, const std::string &Name, Funs &&... Fs) { return ((SystemTester *)S)->createAgent(Name, std::move(Fs)...); } static void destroyMyAgent(MessagingSystem *S, const AgentHandle &H) { ((SystemTester *)S)->destroyUnit(unwrapAgent(H)); } }; /// A special \c rosa::Agent with its own state. class MyAgent : public Agent { public: using Tick = AtomConstant; private: enum class Categories { Bad, Normal, Good }; static const std::map CategoryNames; - History H; + StaticLengthHistory H; Confidence C; RangeAbstraction A; PartialFunction L; RangeConfidence RCL; RangeConfidence RCS; public: void handler(Tick, uint8_t V) noexcept { // Record \p V to the \c rosa::agent::History, then print state info. H << V; ASSERT(H.entry() == V); // Sanity check. LOG_INFO_STREAM << "\nNext value: " << PRINTABLE(V) << ", confidence: " << C(H) << ", category: " << CategoryNames.at(A(H.entry())) << ", partial: " << int(L(H.entry())) << ", range-confidence-linear: "; - std::map res_lin = RCL(H.entry()); - for (auto i : res_lin){ - LOG_INFO_STREAM << " " << CategoryNames.at(i.first) - << " " << i.second << "," ; + std::map ResLin = RCL(H.entry()); + for (auto Con : ResLin) { + LOG_INFO_STREAM << " " << CategoryNames.at(Con.first) << " " << Con.second + << ","; } LOG_INFO_STREAM << " range-confidence-sine: "; - std::map res_sine = RCS(H.entry()); - for (auto i : res_sine){ - LOG_INFO_STREAM << " " << CategoryNames.at(i.first) - << " " << i.second << "," ; + std::map ResSine = RCS(H.entry()); + for (auto Con : ResSine) { + LOG_INFO_STREAM << " " << CategoryNames.at(Con.first) << " " << Con.second + << ","; } LOG_INFO_STREAM << '\n'; - } MyAgent(const AtomValue Kind, const rosa::id_t Id, const std::string &Name, MessagingSystem &S) : Agent(Kind, Id, Name, S, THISMEMBER(handler)), H(), C(5, 20, 1), A({{{(uint8_t)10, (uint8_t)14}, Categories::Normal}, {{(uint8_t)15, (uint8_t)17}, Categories::Good}, {{(uint8_t)18, (uint8_t)19}, Categories::Normal}}, Categories::Bad), L({{{0, 2}, std::make_shared>(0, 1)}, {{2, 4}, std::make_shared>(2, 0)}, {{4, 6}, std::make_shared>(6, -1)}}, 0), RCL({ {Categories::Bad, PartialFunction({ - {{0, 3}, std::make_shared> - (0, 1.0/3)}, - {{3, 6}, std::make_shared> - (1, 0)}, - {{6, 9}, std::make_shared> - (3.0, -1.0/3)}, + {{0.f, 3.f}, std::make_shared> + (0.f, 1.f/3)}, + {{3.f, 6.f}, std::make_shared> + (1.f, 0.f)}, + {{6.f, 9.f}, std::make_shared> + (3.f, -1.f/3)}, },0)}, {Categories::Normal, PartialFunction({ - {{6, 9}, std::make_shared> - (-2, 1.0/3)}, - {{9, 12}, std::make_shared> - (1, 0)}, - {{12, 15}, std::make_shared> - (5, -1.0/3)}, + {{6.f, 9.f}, std::make_shared> + (-2.f, 1.f/3)}, + {{9.f, 12.f}, std::make_shared> + (1.f, 0.f)}, + {{12.f, 15.f}, std::make_shared> + (5.f, -1.f/3)}, },0)}, {Categories::Good, PartialFunction({ - {{12, 15}, std::make_shared> - (-4, 1.0/3)}, - {{15, 18}, std::make_shared> - (1, 0)}, - {{18, 21}, std::make_shared> - (7, -1.0/3)}, + {{12.f, 15.f}, std::make_shared> + (-4.f, 1.f/3)}, + {{15.f, 18.f}, std::make_shared> + (1.f, 0.f)}, + {{18.f, 21.f}, std::make_shared> + (7.f, -1.f/3)}, },0)} }), RCS({ {Categories::Bad, PartialFunction({ - {{0, 3}, std::make_shared> - (M_PI/3, 0.5, -M_PI/2, 0.5)}, - {{3, 6}, std::make_shared>(1, 0)}, - {{6, 9}, std::make_shared> - (M_PI/3, 0.5, -M_PI/2 + 3, 0.5)}, + {{0.f, 3.f}, std::make_shared> + (Pi/3, 0.5f, -Pi/2, 0.5f)}, + {{3.f, 6.f}, std::make_shared>(1.f, 0.f)}, + {{6.f, 9.f}, std::make_shared> + (Pi/3, 0.5f, -Pi/2 + 3, 0.5f)}, },0)}, {Categories::Normal, PartialFunction({ - {{6, 9}, std::make_shared> - (M_PI/3, 0.5, -M_PI/2, 0.5)}, - {{9, 12}, std::make_shared>(1, 0)}, - {{12, 15}, std::make_shared> - (M_PI/3, 0.5, -M_PI/2 + 3, 0.5)}, + {{6.f, 9.f}, std::make_shared> + (Pi/3, 0.5f, -Pi/2, 0.5f)}, + {{9.f, 12.f}, std::make_shared>(1.f, 0.f)}, + {{12.f, 15.f}, std::make_shared> + (Pi/3, 0.5f, -Pi/2 + 3, 0.5f)}, },0)}, {Categories::Good, PartialFunction({ - {{12, 15}, std::make_shared> - (M_PI/3, 0.5, -M_PI/2, 0.5)}, - {{15, 18}, std::make_shared>(1, 0)}, - {{18, 21}, std::make_shared> - (M_PI/3, 0.5, -M_PI/2 + 3, 0.5)}, + {{12.f, 15.f}, std::make_shared> + (Pi/3, 0.5f, -Pi/2, 0.5f)}, + {{15.f, 18.f}, std::make_shared>(1.f, 0.f)}, + {{18.f, 21.f}, std::make_shared> + (Pi/3, 0.5f, -Pi/2 + 3, 0.5f)}, },0)} }, true){} }; const std::map MyAgent::CategoryNames{ {Categories::Bad, "Bad"}, {Categories::Normal, "Normal"}, {Categories::Good, "Good"}}; int main(void) { - LOG_INFO_STREAM << library_string() << " -- " << Color::Red - << "agent-functionalities example" << Color::Default - << '\n'; + LOG_INFO_STREAM << library_string() << " -- " << Color::Red + << "agent-functionalities example" << Color::Default << '\n'; std::unique_ptr S = MessagingSystem::createSystem("Sys"); MessagingSystem *SP = S.get(); AgentHandle A = SystemTester::createMyAgent(SP, "MyAgent"); - std::vector Vs{0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 13, - 15, 14, 15, 16, 19, 20, 21}; + std::vector Vs{0, 1, 2, 3, 4, 5, 6, 7, 9, 10, + 11, 13, 15, 14, 15, 16, 19, 20, 21}; for (auto I = Vs.begin(); I != Vs.end(); ++I) { A.send(MyAgent::Tick::Value, *I); } SystemTester::destroyMyAgent(SP, A); return 0; } diff --git a/examples/basic-system/basic-system.cpp b/examples/basic-system/basic-system.cpp index a8d013a..39a8229 100644 --- a/examples/basic-system/basic-system.cpp +++ b/examples/basic-system/basic-system.cpp @@ -1,70 +1,76 @@ //===-- examples/basic-system/basic-system.cpp ------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file examples/basic-system/basic-system.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief A simple example on the basic \c rosa::System and \c rosa::Unit /// classes. /// //===----------------------------------------------------------------------===// #include "rosa/config/version.h" #include "rosa/core/System.hpp" #include "rosa/core/Unit.h" #include "rosa/support/log.h" #include "rosa/support/terminal_colors.h" using namespace rosa; using namespace rosa::terminal; /// A dummy wrapper for testing \c rosa::System. /// /// \note Since we test \c rosa::System directly here, we need to get access to /// its protected members. That we do by imitating to be a decent subclass of /// \c rosa::System, while calling protected member functions on an object of a /// type from which we actually don't inherit. struct SystemTester : protected System { static constexpr AtomValue UnitKind = atom("unit"); static Unit &createMyUnit(System *S, const std::string &Name = std::string()) { return ((SystemTester *)S) ->createUnit([&Name](const rosa::id_t Id, System &S) noexcept { const std::string N( Name.empty() ? "Unit_" + std::to_string(S.numberOfConstructedUnits()) : Name); return new Unit(UnitKind, Id, N, S); }); } static void destroyMyUnit(System *S, Unit &U) { ((SystemTester *)S)->destroyUnit(U); } }; int main(void) { LOG_INFO_STREAM << library_string() << " -- " << Color::Red << "basic-system example" << Color::Default << '\n'; std::unique_ptr S = System::createSystem("Sys"); System *SP = S.get(); Unit &Unit1 = SystemTester::createMyUnit(SP), &Unit2 = SystemTester::createMyUnit(SP, "Second"), &Unit3 = SystemTester::createMyUnit(SP); SystemTester::destroyMyUnit(SP, Unit1); SystemTester::destroyMyUnit(SP, Unit3); LOG_INFO_STREAM << "Dumping Unit2\n" << Unit2 << '\n'; SystemTester::destroyMyUnit(SP, Unit2); return 0; } diff --git a/examples/deluxe-interface/deluxe-interface.cpp b/examples/deluxe-interface/deluxe-interface.cpp old mode 100755 new mode 100644 index d50396e..3795e86 --- a/examples/deluxe-interface/deluxe-interface.cpp +++ b/examples/deluxe-interface/deluxe-interface.cpp @@ -1,177 +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 +/// \date 2017-2019 /// /// \brief A simple example on the \c rosa::deluxe::DeluxeContext and related /// classes. //===----------------------------------------------------------------------===// #include "rosa/config/version.h" #include "rosa/deluxe/DeluxeContext.hpp" #include #include #include using namespace rosa; using namespace rosa::deluxe; using namespace rosa::terminal; /// How many cycles of simulation to perform. const size_t NumberOfSimulationCycles = 16; -/// Helper function creating a deluxe agent for logging and forwarding values. -/// -/// Received values are dumped to \c LOG_INFO_STREAM and then returned as -/// result. -/// -/// \tparam T type of values to handle -/// -/// \param C the deluxe context to create the agent in -/// \param Name name of the new agent -/// -/// \return handle for the new agent -template -AgentHandle createLowLevelAgent(std::unique_ptr &C, - const std::string &Name) { - using handler = DeluxeAgent::D; - using result = Optional; - return C->createAgent( - Name, handler([&, Name](std::pair I) -> result { - LOG_INFO_STREAM << "\n******\n" - << Name << " " << (I.second ? "" : "") - << " value: " << I.first << "\n******\n"; - return {I.first}; - })); -} - int main(void) { LOG_INFO_STREAM << '\n' << library_string() << " -- " << Color::Red << "deluxe-interface example" << Color::Default << '\n'; std::unique_ptr C = DeluxeContext::create("Deluxe"); // // Create deluxe sensors. // LOG_INFO("Creating sensors."); // All sensors are created without defining a normal generator function, but - // with the default value of the second argument. That, however, requires the + // with the default value of the last argument. That, however, requires the // data type to be explicitly defined. This is good for simulation only. + + // The first and second sensors do not receive master-input. + AgentHandle BoolSensor = C->createSensor("BoolSensor"); AgentHandle IntSensor = C->createSensor("IntSensor"); - AgentHandle FloatSensor = C->createSensor("FloatSensor"); + + // This sensor receives master-input and dumps it to \c LOG_INFO_STREAM. + const std::string FloatSensorName = "FloatSensor"; + AgentHandle FloatSensor = C->createSensor( + FloatSensorName, [&FloatSensorName](std::pair I) { + LOG_INFO_STREAM << "\n******\n" + << FloatSensorName + << " master-input " << (I.second ? "" : "") + << " value: " << I.first << "\n******\n"; + }); + + // This sensor do not receive master-input but produces tuples. + using TupleType = DeluxeTuple; + AgentHandle TupleSensor = C->createSensor("TupleSensor"); + + // + // Check and set execution policy for sensors. + // + LOG_INFO("Execution policies for sensors."); + + LOG_INFO(std::to_string(*C->getExecutionPolicy(IntSensor))); + C->setExecutionPolicy(IntSensor, DeluxeExecutionPolicy::decimation(2)); + C->setExecutionPolicy(FloatSensor, DeluxeExecutionPolicy::decimation(2)); + LOG_INFO(std::to_string(*C->getExecutionPolicy(IntSensor))); // // Create low-level deluxe agents with \c createLowLevelAgent. // LOG_INFO("Creating low-level agents."); - AgentHandle IntAgent = createLowLevelAgent(C, "IntAgent"); - AgentHandle FloatAgent = createLowLevelAgent(C, "FloatAgent"); + // All agents below dump their received values to \c LOG_INFO_STREAM on each + // triggering. + + // This agent does not receive master-input and does not produce + // master-output. It results in the value it received. + const std::string BoolAgentName = "BoolAgent"; + using BoolResult = Optional; + using BoolHandler = std::function)>; + AgentHandle BoolAgent = C->createAgent( + BoolAgentName, + BoolHandler([&BoolAgentName](std::pair I) -> BoolResult { + LOG_INFO_STREAM << "\n******\n" + << BoolAgentName << " " + << (I.second ? "" : "") + << " value: " << I.first << "\n******\n"; + return {I.first}; + })); + + // This agent receives master-input but does not produce master-output. The + // agent maintains a state in \c IntAgentOffset. The master-input handler + // updates \c IntAgentOffset according to each received (new) value from its + // master. The slave-input handler results in the sum of the received value + // and the actual value of \c IntAgentOffset. + const std::string IntAgentName = "IntAgent"; + using IntMasterHandler = std::function)>; + using IntResult = Optional; + using IntHandler = std::function)>; + uint32_t IntAgentOffset = 0; + AgentHandle IntAgent = C->createAgent( + IntAgentName, + // Master-input handler. + IntMasterHandler([&IntAgentName, + &IntAgentOffset](std::pair I) { + LOG_INFO_STREAM << "\n******\n" + << IntAgentName + << " master-input " << (I.second ? "" : "") + << " value: " << I.first << "\n******\n"; + if (I.second) { + IntAgentOffset = I.first; + } + }), + // Slave-input handler. + IntHandler([&IntAgentName, + &IntAgentOffset](std::pair I) -> IntResult { + LOG_INFO_STREAM << "\n******\n" + << IntAgentName << " " << (I.second ? "" : "") + << " value: " << I.first << "\n******\n"; + return {I.first + IntAgentOffset}; + })); + + // This agent receives master-input and produces master-output. The + // master-input handler propagaates each received (new) value to its slave as + // master-output. The slave-input handler results in the value it received and + // produces no actual master-output. + const std::string FloatAgentName = "FloatAgent"; + using FloatMasterResult = std::tuple>; + using FloatMasterHandler = + std::function)>; + using FloatResult = std::tuple, Optional>; + using FloatHandler = std::function)>; + AgentHandle FloatAgent = C->createAgent( + FloatAgentName, + // Master-input handler. + FloatMasterHandler([&FloatAgentName]( + std::pair I) -> FloatMasterResult { + LOG_INFO_STREAM << "\n******\n" + << FloatAgentName + << " master-input " << (I.second ? "" : "") + << " value: " << I.first << "\n******\n"; + const auto Output = + I.second ? Optional(I.first) : Optional(); + return {Output}; + }), + // Slave-input handler. + FloatHandler([&FloatAgentName](std::pair I) -> FloatResult { + LOG_INFO_STREAM << "\n******\n" + << FloatAgentName << " " + << (I.second ? "" : "") + << " value: " << I.first << "\n******\n"; + return {{I.first}, {}}; + })); + + // This agent does not receive master-input and does not produce + // master-output. It results in the sum of the values it receives in a tuple. + const std::string TupleAgentName = "TupleAgent"; + using TupleSumResult = Optional>; + using TupleHandler = + std::function)>; + AgentHandle TupleAgent = C->createAgent( + TupleAgentName, + TupleHandler( + [&TupleAgentName](std::pair I) -> TupleSumResult { + LOG_INFO_STREAM << "\n******\n" + << TupleAgentName << " " + << (I.second ? "" : "") + << " value: " << I.first << "\n******\n"; + return {std::get<0>(I.first) + std::get<1>(I.first)}; + })); + + // + // Set execution policies for low-level agents. + // + LOG_INFO("Setting Execution policies for low-level agents."); + + C->setExecutionPolicy(IntAgent, DeluxeExecutionPolicy::awaitAll({0})); + C->setExecutionPolicy(FloatAgent, DeluxeExecutionPolicy::awaitAll({0})); // // Connect sensors to low-level agents. // LOG_INFO("Connect sensors to their corresponding low-level agents."); + C->connectSensor(BoolAgent, 0, BoolSensor, "Bool Sensor Channel"); C->connectSensor(IntAgent, 0, IntSensor, "Int Sensor Channel"); C->connectSensor(FloatAgent, 0, FloatSensor, "Float Sensor Channel"); + C->connectSensor(TupleAgent, 0, TupleSensor, "Tuple Sensor Channel"); // // Create a high-level deluxe agent. // LOG_INFO("Create high-level agent."); - // The new agent logs its input values and results in the the sum of them. + using SingleDoubleOutputType = Optional>; + using SingleUInt32OutputType = Optional>; + using NoOutputType = Optional; + + // This agent does not receive master-input but produces master-output for its + // slaves at positions `1` and `2` but not for that at position `0`. The agent + // maintains a state in \c SumAgentState. The handler increments \c + // SumAgentState upon each received (new) `true` value from its slave at + // position `0`. Whenever \c SumAgentState has been updated, it is sent to the + // slaves at positions `1` and `2`. The handler results in the sum of the + // values received from slaves at positions `1`, `2`, and `3`. + using SumResult = + std::tuple; + using SumHandler = std::function, bool>, std::pair, bool>, + std::pair, bool>, + std::pair, bool>)>; + uint32_t SumAgentState = 0; AgentHandle SumAgent = C->createAgent( - "Sum Agent", DeluxeAgent::D( - [](std::pair I1, - std::pair I2) -> Optional { - LOG_INFO_STREAM - << "\n*******\nSum Agent triggered with values:\n" - << (I1.second ? "" : "") - << " int value: " << I1.first << "\n" - << (I2.second ? "" : "") - << " float value: " << I2.first << "\n******\n"; - return {I1.first + I2.first}; - })); + "Sum Agent", + SumHandler([&SumAgentState]( + std::pair, bool> I0, + std::pair, bool> I1, + std::pair, bool> I2, + std::pair, bool> I3) -> SumResult { + const auto V0 = std::get<0>(I0.first); + const auto V1 = std::get<0>(I1.first); + const auto V2 = std::get<0>(I2.first); + const auto V3 = std::get<0>(I3.first); + LOG_INFO_STREAM << "\n*******\nSum Agent triggered with values:\n" + << (I0.second ? "" : "") + << " bool value: " << V0 << "\n" + << (I1.second ? "" : "") + << " int value: " << V1 << "\n" + << (I2.second ? "" : "") + << " float value: " << V2 << "\n" + << (I3.second ? "" : "") + << " double value: " << V3 << "\n******\n"; + if (I0.second && V0) { + ++SumAgentState; + } + const SingleUInt32OutputType MasterOutput = + I0.second && V0 + ? SingleUInt32OutputType(DeluxeTuple(SumAgentState)) + : SingleUInt32OutputType(); + const DeluxeTuple Output = {V1 + V2 + V3}; + return {{Output}, {}, {MasterOutput}, {MasterOutput}, {}}; + })); // // Connect low-level agents to the high-level agent. // LOG_INFO("Connect low-level agents to the high-level agent."); - C->connectAgents(SumAgent, 0, IntAgent, "Int Agent Channel"); - C->connectAgents(SumAgent, 1, FloatAgent, "Float Agent Channel"); + 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 logs each new input value and produces nothing. + // 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", - DeluxeAgent::D( + std::function(std::pair)>( [](std::pair Sum) -> Optional { if (Sum.second) { LOG_INFO_STREAM << "Result: " << Sum.first << "\n"; } return {}; })); // // Connect the high-level agent to the logger agent. // LOG_INFO("Connect the high-level agent to the logger agent."); C->connectAgents(LoggerAgent, 0, SumAgent, "Sum Agent Channel"); // // Do simulation. // LOG_INFO("Setting up and performing simulation."); // // Initialize deluxe context for simulation. // C->initializeSimulation(); // // Create some vectors and register them for their corresponding sensors. // + std::vector BoolValues(NumberOfSimulationCycles); + std::generate(BoolValues.begin(), BoolValues.end(), + [i = 0](void) mutable -> bool { return (++i % 4) == 0; }); + C->registerSensorValues(BoolSensor, BoolValues.begin(), BoolValues.end()); + std::vector IntValues(NumberOfSimulationCycles); std::generate(IntValues.begin(), IntValues.end(), [i = 0](void) mutable { return ++i; }); C->registerSensorValues(IntSensor, IntValues.begin(), IntValues.end()); std::vector FloatValues(NumberOfSimulationCycles); std::generate(FloatValues.begin(), FloatValues.end(), [f = 0.5f](void) mutable { f += 0.3f; return std::floor(f) + 0.5f; }); C->registerSensorValues(FloatSensor, FloatValues.begin(), FloatValues.end()); + std::vector TupleValues(NumberOfSimulationCycles); + std::generate(TupleValues.begin(), TupleValues.end(), + [f1 = 0.f, f2 = 3.14f](void) mutable -> TupleType { + f1 += f2; + f2 -= f1; + return {f1, f2}; + }); + C->registerSensorValues(TupleSensor, TupleValues.begin(), TupleValues.end()); + // // Simulate. // C->simulate(NumberOfSimulationCycles); return 0; } diff --git a/examples/messaging-system/messaging-system.cpp b/examples/messaging-system/messaging-system.cpp index e7817cc..868cf25 100644 --- a/examples/messaging-system/messaging-system.cpp +++ b/examples/messaging-system/messaging-system.cpp @@ -1,136 +1,142 @@ //===-- examples/messaging-system/messaging-system.cpp ----------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file examples/messaging-system/messaging-system.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief A simple example on the \c rosa::MessagingSystem and \c rosa::Agent /// classes. //===----------------------------------------------------------------------===// #include "rosa/config/version.h" #include "rosa/core/Agent.hpp" #include "rosa/core/MessagingSystem.hpp" #include "rosa/support/log.h" #include "rosa/support/terminal_colors.h" using namespace rosa; using namespace rosa::terminal; /// A dummy wrapper for testing \c rosa::MessagingSystem. /// /// \note Since we test \c rosa::MessagingSystem directly here, we need to get /// access to its protected members. That we do by imitating to be a decent /// subclass of \c rosa::MessagingSystem, while calling protected member /// functions on an object of a type from which we actually don't inherit. struct SystemTester : protected MessagingSystem { template static AgentHandle createMyAgent(MessagingSystem *S, const std::string &Name, Funs &&... Fs) { return ((SystemTester *)S)->createAgent(Name, std::move(Fs)...); } static void destroyMyAgent(MessagingSystem *S, const AgentHandle &H) { ((SystemTester *)S)->destroyUnit(unwrapAgent(H)); } }; /// A special \c rosa::Agent subclass with its own state. class MyAgent : public Agent { public: using Tick = AtomConstant; using Report = AtomConstant; private: size_t Counter; public: void handler(Tick) noexcept { LOG_INFO_STREAM << "MyAgent Tick count: " << ++Counter << '\n'; } MyAgent(const AtomValue Kind, const rosa::id_t Id, const std::string &Name, MessagingSystem &S) : Agent(Kind, Id, Name, S, Invoker::F([this](Report) noexcept { LOG_INFO_STREAM << "MyAgent count: " << Counter << '\n'; }), THISMEMBER(handler)), Counter(0) {} }; int main(void) { LOG_INFO_STREAM << library_string() << " -- " << Color::Red << "messaging-system example" << Color::Default << '\n'; std::unique_ptr S = MessagingSystem::createSystem("Sys"); MessagingSystem *SP = S.get(); LOG_INFO("\n\n** Stateless Agents\n"); AgentHandle Agent1 = SystemTester::createMyAgent( SP, "Agent1", Invoker::F([](const std::string &M) noexcept { LOG_INFO("Agent1: " + M); })); using Print = AtomConstant; using Forward = AtomConstant; AgentHandle Agent2 = SystemTester::createMyAgent( SP, "Agent2", Invoker::F([](Print, uint8_t N) noexcept { LOG_INFO("Agent2: " + std::to_string(N)); }), Invoker::F([&Agent1](Forward, uint8_t N) noexcept { if (Agent1) { Agent1.send(std::to_string(N)); } else { LOG_INFO("Agent2 cannot forward: Agent1 is not valid"); } })); LOG_INFO_STREAM << "\nAgent1 is valid: " << bool(Agent1) << "\nAgent2 is valid: " << bool(Agent2) << '\n'; LOG_INFO("Sending a print-message to Agent2..."); SP->send(Agent2, Print::Value, 42); LOG_INFO("Sending a forward-message to Agent2..."); SP->send(Agent2, Forward::Value, 42); LOG_INFO("Sending an unexpected message to Agent2..."); SP->send(Agent2, unit); SystemTester::destroyMyAgent(SP, Agent1); LOG_INFO_STREAM << "\nAgent1 is valid: " << bool(Agent1) << "\nAgent2 is valid: " << bool(Agent2) << '\n'; LOG_INFO("Sending a forward-message to Agent2..."); SP->send(Agent2, Forward::Value, 42); SystemTester::destroyMyAgent(SP, Agent2); LOG_INFO("\n\n** Stateful Agents\n"); AgentHandle Agent3 = SystemTester::createMyAgent(SP, "Agent3"); for (size_t I = 0; I < 2; ++I) { LOG_INFO("Sending report-message to Agent3..."); Agent3.send(MyAgent::Report::Value); LOG_INFO("Sending tick-message to Agent3..."); Agent3.send(MyAgent::Tick::Value); } SystemTester::destroyMyAgent(SP, Agent3); LOG_INFO("\n"); return 0; } diff --git a/examples/messaging/messaging.cpp b/examples/messaging/messaging.cpp index 7d1269b..ed453d6 100644 --- a/examples/messaging/messaging.cpp +++ b/examples/messaging/messaging.cpp @@ -1,126 +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 +/// \date 2017-2019 /// /// \brief An example showcasing features related to the \c rosa::Message class. /// //===----------------------------------------------------------------------===// #include "rosa/config/version.h" #include "rosa/core/MessageHandler.hpp" #include "rosa/support/log.h" #include "rosa/support/terminal_colors.h" using namespace rosa; using namespace rosa::terminal; int main(void) { LOG_INFO_STREAM << library_string() << " -- " << Color::Red << "messaging" << Color::Default << '\n'; auto &Log = LOG_INFO_STREAM << '\n'; // Message interface. auto PMsg = Message::create(1, 2); auto &Msg = *PMsg; Log << "Checking on a 'Message with TypeList<>':" << "\n Size: " << Msg.Size << "\n Pos 0 is uint8_t: " << Msg.isTypeAt(0) << "\n Pos 1 is uint16_t: " << Msg.isTypeAt(1) << "\n Pos 2 is uint32_t: " << Msg.isTypeAt(1) << "\n Value at pos 0: " << PRINTABLE(Msg.valueAt(0)) << "\n Value at pos 1: " << Msg.valueAt(1) << "\n\n"; // MessageMatcher. using MyMatcher = MsgMatcher; Log << "Matching against 'TypeList':" << "\n matching: " << MyMatcher::doesStronglyMatch(Msg); auto Vs = MyMatcher::extractedValues(Msg); Log << "\n value: '(" << PRINTABLE(std::get<0>(Vs)) << ", " << std::get<1>(Vs) << ")'\n\n"; using MyWrongMatcher = MsgMatcher; Log << "Matching against 'TypeList':" << "\n matching: " << MyWrongMatcher::doesStronglyMatch(Msg) << "\n\n"; using MyAtom = AtomConstant; const MyAtom &A = MyAtom::Value; using MyNAtom = AtomConstant; auto PAMsg = Message::create(A); auto &AMsg = *PAMsg; Log << "Checking on a 'Message with TypeList>':" << "\n Size: " << AMsg.Size << "\n Pos 0 is 'AtomValue': " << AMsg.isTypeAt(0) << "\n Pos 0 is 'AtomConstant': " << AMsg.isTypeAt(0) << "\n\n"; using MyAtomMatcher = MsgMatcher; Log << "Matching against 'TypeList>':" << "\n matching: " << MyAtomMatcher::doesStronglyMatch(AMsg) << "\n value: '(" - << to_string(std::get<0>(MyAtomMatcher::extractedValues(AMsg))) << ")'" + << std::to_string(std::get<0>(MyAtomMatcher::extractedValues(AMsg))) + << ")'" << "\n\n"; using MyWrongAtomMatcher = MsgMatcher; Log << "Matching against 'TypeList>':" << "\n matching: " << MyWrongAtomMatcher::doesStronglyMatch(AMsg) << "\n\n"; // Invoker. auto IP = Invoker::wrap(Invoker::F([&Log](MyAtom) noexcept->void { Log << "** Handling 'Message with " "TypeList>'.\n"; })); auto &I = *IP; // Get a reference from the pointer. Log << "Invoking a function of signature 'void(AtomConstant) " "noexcept':" << "\n with 'Message with TypeList'" << "\n does Message match Invoker: " << I.match(Msg) << "\n invoking..."; I(Msg); Log << "\n with 'Message with TypeList>'..." << "\n does Message match Invoker: " << I.match(AMsg) << "\n invoking..."; I(AMsg); Log << "\n\n"; // MessageHandler. MessageHandler Handler{ Invoker::F([&Log](uint8_t, uint16_t) { Log << "** Handling 'Message with TypeList'\n"; }), Invoker::F([&Log](MyAtom) { Log << "** Handling 'Message with " "TypeList>'\n"; })}; auto PANMsg = Message::create(MyNAtom::Value); auto &ANMsg = *PANMsg; Log << "Handling Messages with 'MessageHandler " "{ Invoker::F, " "Invoker::F> }':" << "\n 'Message with TypeList'" << "\n can handle: " << Handler.canHandle(Msg) << "\n handling..."; Handler(Msg); Log << "\n 'Message with TypeList>'" << "\n can handle: " << Handler.canHandle(AMsg) << "\n handling..."; Handler(AMsg); Log << "\n 'Message with TypeList>'" << "\n can handle: " << Handler.canHandle(ANMsg) << "\n handling..."; Handler(ANMsg); Log << "\n\n"; Log << "Terminating, destroying automatic variables.\n"; return 0; } diff --git a/examples/type-facilities/type-facilities.cpp b/examples/type-facilities/type-facilities.cpp index d714a73..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 +/// \date 2017-2019 /// /// \brief An example showcasing various type-related support facilities. /// //===----------------------------------------------------------------------===// #include "rosa/config/version.h" #include "rosa/support/log.h" #include "rosa/support/terminal_colors.h" #include "rosa/support/type_token.hpp" using namespace rosa; using namespace rosa::terminal; int main(void) { LOG_INFO_STREAM << library_string() << " -- " << Color::Red << "type facilities" << Color::Default << '\n'; auto &Log = LOG_TRACE_STREAM; Log << "\nNumberOfBuiltinTypes: " << NumberOfBuiltinTypes << "\nTokenBits: " << token::TokenBits << "\nRepresentationBits: " << token::RepresentationBits << "\nMaxTokenizableListSize: " << token::MaxTokenizableListSize << "\n\n"; Log << "Type number information on 'uint8_t':"; constexpr TypeNumber TN = TypeNumberOf::Value; Log << "\n type number: " << PRINTABLE_TN(TN) << "\n size: " << TypeForNumber::Size << "\n name: " << TypeForNumber::Name << "\n\n"; Log << "Type number information on 'std::string':"; constexpr TypeNumber TNS = TypeNumberOf::Value; Log << "\n type number: " << PRINTABLE_TN(TNS) << "\n size: " << TypeForNumber::Size << "\n name: " << TypeForNumber::Name << "\n\n"; Log << "Type number information of AtomConstants:"; using Atom1 = AtomConstant; using Atom2 = AtomConstant; Log << "\n std::is_same::value: " << std::is_same::value << "\n TypeNumberOf::Value: " << PRINTABLE_TN(TypeNumberOf::Value) << "\n TypeNumberOf::Value: " << PRINTABLE_TN(TypeNumberOf::Value) << "\n name: " << TypeForNumber::Value>::Name << "\n\n"; Log << "Type token information on 'TypeList':"; // \c rosa::Token is generated statically. constexpr Token T = TypeToken::Value; STATIC_ASSERT( (T == TypeListToken>::Value), "alias template definition is wrong"); Token T_ = T; // We need a non-const value for dropping head later. // Iterate over encoded entries in \c T_. while (!emptyToken(T_)) { Log << "\n token: " << PRINTABLE_TOKEN(T_) << "\n valid: " << validToken(T_) << "\n empty: " << emptyToken(T_) - << "\n length: " << lengthOfToken(T_) + << "\n length: " << static_cast(lengthOfToken(T_)) << "\n full size: " << sizeOfValuesOfToken(T_) << "\n head type number: " << PRINTABLE_TN(headOfToken(T_)) << "\n size of head: " << sizeOfHeadOfToken(T_) << "\n name of head: " << nameOfHeadOfToken(T_) << "\n is head uint8_t: " << isHeadOfTokenTheSameType(T_) << "\n is head uint16_t: " << isHeadOfTokenTheSameType(T_) << "\n is head std::string: " << isHeadOfTokenTheSameType(T_) << "\nDropping head..."; dropHeadOfToken(T_); } // Here when Token became empty. Log << "\n token: " << PRINTABLE_TOKEN(T_) << "\n empty: " << emptyToken(T_) << '\n'; return 0; } diff --git a/include/rosa/agent/Abstraction.hpp b/include/rosa/agent/Abstraction.hpp index 032c970..6afa7fd 100644 --- a/include/rosa/agent/Abstraction.hpp +++ b/include/rosa/agent/Abstraction.hpp @@ -1,225 +1,244 @@ //===-- rosa/agent/Abstraction.hpp ------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/agent/Abstraction.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Definition of *abstraction* *functionality*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_ABSTRACTION_HPP #define ROSA_AGENT_ABSTRACTION_HPP #include "rosa/agent/Functionality.h" #include "rosa/support/debug.hpp" #include #include namespace rosa { namespace agent { /// Abstracts values from a type to another one. /// /// \tparam T type to abstract from /// \tparam A type to abstract to template class Abstraction : public Functionality { protected: /// Value to abstract to by default. const A Default; public: /// Creates an instance. /// /// \param Default value to abstract to by default Abstraction(const A Default) noexcept : Default(Default) {} /// Destroys \p this object. ~Abstraction(void) = default; /// Checks wether the Abstraction evaluates to default at the given position - bool isDefaultAt(const T &V) const{ + /// + /// \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; + return true; } /// Abstracts a value from type \p T to type \p A. /// /// \note The default implementation always returns /// \c rosa::agent::Abstraction::Default, hence the actual argument is /// ignored. /// /// \return the abstracted value virtual A operator()(const T &) const noexcept { return Default; } }; /// Implements \c rosa::agent::Abstraction as a \c std::map from a type to /// another one. /// /// \note This implementation is supposed to be used to abstract between /// enumeration types, which is statically enforced. /// /// \tparam T type to abstract from /// \tparam A type to abstract to template class MapAbstraction : public Abstraction, private std::map { // Make sure the actual type arguments are enumerations. STATIC_ASSERT((std::is_enum::value && std::is_enum::value), "mapping not enumerations"); // Bringing into scope inherited members. using Abstraction::Default; using std::map::end; using std::map::find; public: /// Creates an instance by initializing the underlying \c std::map. /// /// \param Map the mapping to do abstraction according to /// \param Default value to abstract to by default MapAbstraction(const std::map &Map, const A Default) noexcept : Abstraction(Default), std::map(Map) {} /// Destroys \p this object. ~MapAbstraction(void) = default; /// Checks wether the Abstraction evaluates to default at the given position - bool isDefaultAt(const T &V) const { + /// + /// \param V the value at which to check if the function falls back to it's + /// default value. + /// \return true if the Abstraction falls back to the default value + bool isDefaultAt(const T &V) const noexcept override { const auto I = find(V); return I == end() ? true : false; } /// Abstracts a value from type \p T to type \p A based on the set mapping. /// /// Results in the value associated by the set mapping to the argument, or /// \c rosa::agent::MapAbstraction::Default if the actual argument is not /// associated with anything by the set mapping. /// /// \param V value to abstract /// /// \return the abstracted value based on the set mapping A operator()(const T &V) const noexcept override { const auto I = find(V); return I == end() ? Default : *I; } }; /// Implements \c rosa::agent::Abstraction as a \c std::map from ranges of a /// type to values of another type. /// /// \note This implementation is supposed to be used to abstract ranges of /// arithmetic types into enumerations, which is statically enforced. /// /// \invariant The keys in the underlying \c std::map define valid ranges /// such that `first <= second` and there are no overlapping ranges defined by /// the keys. /// /// \tparam T type to abstract from /// \tparam A type to abstract to template class RangeAbstraction : public Abstraction, private std::map, A> { // Make sure the actual type arguments are matching our expectations. STATIC_ASSERT((std::is_arithmetic::value), "abstracting not arithmetic"); /// \todo check if this compiles with the definition of abstractions as /// self-aware properties //STATIC_ASSERT((std::is_enum::value), "abstracting not to enumeration"); // Bringing into scope inherited members. using Abstraction::Default; using std::map, A>::begin; using std::map, A>::end; using std::map, A>::find; public: /// Creates an instance by Initializing the unserlying \c std::map. /// /// \param Map the mapping to do abstraction according to /// \param Default value to abstract to by default /// /// \pre Each key defines a valid range such that `first <= second` and /// there are no overlapping ranges defined by the keys. RangeAbstraction(const std::map, A> &Map, const A &Default) : Abstraction(Default), std::map, A>(Map) { // Sanity check. ASSERT(std::all_of( begin(), end(), [this](const std::pair, A> &P) { return P.first.first <= P.first.second && std::all_of(++find(P.first), end(), [&P](const std::pair, A> &R) { // \note Values in \c Map are sorted. return P.first.first < P.first.second && P.first.second <= R.first.first || P.first.first == P.first.second && P.first.second < R.first.first; }); })); } /// Destroys \p this object. ~RangeAbstraction(void) = default; /// Checks wether the Abstraction evaluates to default at the given position - bool isDefaultAt(const T &V) const{ + /// + /// \param V the value at which to check if the function falls back to it's + /// default value. + /// \return true if the Abstraction falls back to the default value + bool isDefaultAt(const T &V) const noexcept override { auto I = begin(); bool Found = false; // Indicates if \c I refers to a matching range. bool Failed = false; // Indicates if it is pointless to continue searching. while (!Found && !Failed && I != end()) { if (V < I->first.first) { // No match so far and \p V is below the next range, never will match. // \note Keys are sorted in the map. return true; } else if (I->first.first <= V && V < I->first.second) { // Matching range found. return false; } else { // Cannot conclude in this step, move to the next range. ++I; } } return true; } /// Abstracts a value from type \p T to type \p A based on the set mapping. /// /// Results in the value associated by the set mapping to the argument, or /// \c rosa::agent::RangeAbstraction::Default if the actual argument is not /// included in any of the ranges in the set mapping. /// /// \param V value to abstract /// /// \return the abstracted value based on the set mapping A operator()(const T &V) const noexcept override { auto I = begin(); bool Found = false; // Indicates if \c I refers to a matching range. bool Failed = false; // Indicates if it is pointless to continue searching. while (!Found && !Failed && I != end()) { if (V < I->first.first) { // No match so far and \p V is below the next range, never will match. // \note Keys are sorted in the map. Failed = true; } else if (I->first.first <= V && V < I->first.second) { // Matching range found. Found = true; } else { // Cannot conclude in this step, move to the next range. ++I; } } ASSERT(!Found || I != end()); return Found ? I->second : Default; } }; } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_ABSTRACTION_HPP diff --git a/include/rosa/agent/Confidence.hpp b/include/rosa/agent/Confidence.hpp index 3d07606..3ec78ae 100644 --- a/include/rosa/agent/Confidence.hpp +++ b/include/rosa/agent/Confidence.hpp @@ -1,204 +1,208 @@ //===-- rosa/agent/Confidence.hpp -------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/agent/Confidence.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Definition of *confidence* *functionality*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_CONFIDENCE_HPP #define ROSA_AGENT_CONFIDENCE_HPP #include "rosa/agent/History.hpp" #include "rosa/support/debug.hpp" #include namespace rosa { namespace agent { /// Confidence validator. /// /// Checks the plausibility of given values by validating if a valid region /// contains them. It also capable of checking consistency by validating the /// rate of change recorded by a \c rosa::agent::History object against a /// maximal absolute valid rate of change. /// /// \tparam T type of values to validate /// /// \note The template is defined only for arithmetic types. /// /// \note The lower bound is inclusive and the upper bound is exclusive. /// /// \invariant The bounds are defined in a meaningful way:\code /// LowerBound <= UpperBound /// \endcode template class Confidence : public Functionality { // Make sure the actual type argument is an arithmetic type. STATIC_ASSERT(std::is_arithmetic::value, "not arithmetic Confidence"); public: /// Unsigned type corresponding to \p T. using UT = unsigned_t; /// The minimal value of type \p T. /// \note Not exist \c V of type \p T such that `V < Min`. static constexpr T Min = std::is_integral::value ? std::numeric_limits::min() : std::numeric_limits::lowest(); /// The maximal value of type \p T. /// \note Not exist \c V of type \p T such that `V > Max`. static constexpr T Max = (std::is_integral::value || !std::numeric_limits::has_infinity) ? std::numeric_limits::max() : std::numeric_limits::infinity(); /// The maximal value of type \c UT. /// \note Not exist \c V of type \c UT such that `V > UnsignedMax`. static constexpr UT UnsignedMax = (std::is_integral::value || !std::numeric_limits::has_infinity) ? std::numeric_limits::max() : std::numeric_limits::infinity(); private: /// The inclusive lower bound for plausibility check. T LowerBound; /// The exclusive upper bound for plausibility check. T UpperBound; /// The maximal absolute rate of change for consistency check. UT ChangeRate; public: /// Creates an instance by setting the validator variables. /// /// \param LowerBound the lower bound for plausability check /// \param UpperBound the upper bound for plausability check /// \param ChangeRate maximal absolute rate of change for consistency check /// /// \pre The bounds are defined in a meaningful way:\code /// LowerBound <= UpperBound /// \endcode Confidence(const T LowerBound = Min, const T UpperBound = Max, const UT ChangeRate = UnsignedMax) noexcept - : LowerBound(LowerBound), - UpperBound(UpperBound), - ChangeRate(ChangeRate) { + : LowerBound(LowerBound), UpperBound(UpperBound), ChangeRate(ChangeRate) { // Make sure Confidence is created in a valid state. if (LowerBound > UpperBound) { ROSA_CRITICAL("Confidence with LowerBound higher than UpperBound"); } } /// Destroys \p this object. ~Confidence(void) = default; /// Gives a snapshot of the current state of the validator variables. /// /// \param [out] LowerBound to copy \c rosa::agent::Confidence::LowerBound /// into /// \param [out] UpperBound to copy \c rosa::agent::Confidence::UpperBound /// into /// \param [out] ChangeRate to copy \c rosa::agent::Confidence::ChangeRate /// into void getParameters(T &LowerBound, T &UpperBound, UT &ChangeRate) const noexcept { // Copy members to the given references. LowerBound = this->LowerBound; UpperBound = this->UpperBound; ChangeRate = this->ChangeRate; } /// Sets the lower bound for plausability check. /// /// Beyond setting the lower bound, the function also adjusts the upper bound /// to the given lower bound if the new lower bound would be higher than the /// upper bound. /// /// \param LowerBound the new lower bound to set void setLowerBound(const T LowerBound) noexcept { // Adjust UpperBound if necessary, then set LowerBound. if (UpperBound < LowerBound) { UpperBound = LowerBound; } this->LowerBound = LowerBound; } /// Sets the upper bound for plausability check. /// /// Beyond setting the upper bound, the function also adjusts the lower bound /// to the given upper bound if the new upper bound would be lower than the /// lower bound. /// /// \param UpperBound the new upper bound to set void setUpperBound(const T UpperBound) noexcept { // Adjust LowerBound if necessary, then set UpperBound. if (UpperBound < LowerBound) { LowerBound = UpperBound; } this->UpperBound = UpperBound; } /// Sets the maximal rate of change for consistency check. /// /// \param ChangeRate the new rate of change to set void setChangeRate(const UT ChangeRate) noexcept { // Set ChangeRate. this->ChangeRate = ChangeRate; } /// Tells the binary confidence on the plausibility of a value. /// /// \param V value to check /// /// \return whether \c V is within the range defined by /// \c rosa::agent::Confidence::LowerBound and /// \c rosa::agent::Confidence::UpperBound. bool operator()(const T V) const noexcept { // Return if \c V is plausible. return LowerBound <= V && V < UpperBound; } /// Tells the binary confidence on the plausibility and consistency of the /// last value recorded by a \c rosa::agent::History instance. /// /// Consistency of the last value is checked by validating the difference with /// its preceding entry. /// /// \note The \c rosa::agent::History instance needs to store values of type /// \p T. /// /// \note An empty \c rosa::agent::History instance results in full /// confidence. /// /// \tparam N number of values \p H is able to store /// \tparam P retention policy followed by \p H when capacity is reached /// /// \param H *history* whose last entry to check template - bool operator()(const History &H) const noexcept { + bool operator()(const StaticLengthHistory &H) const noexcept { if (H.empty()) { // No entry to validate. return true; } else { // Validate the last entry and the one step average absolute difference. return (*this)(H.entry()) && H.averageAbsDiff(1) <= ChangeRate; } } }; } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_CONFIDENCE_HPP diff --git a/include/rosa/agent/FunctionAbstractions.hpp b/include/rosa/agent/FunctionAbstractions.hpp index 606125a..2fa6912 100644 --- a/include/rosa/agent/FunctionAbstractions.hpp +++ b/include/rosa/agent/FunctionAbstractions.hpp @@ -1,194 +1,364 @@ //===-- rosa/agent/FunctionAbstractions.hpp ---------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/agent/FunctionAbstractions.hpp /// /// \author Benedikt Tutzer (benedikt.tutzer@tuwien.ac.at) /// /// \date 2019 /// /// \brief Definition of *FunctionAbstractions* *functionality*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_FUNCTIONABSTRACTIONS_HPP #define ROSA_AGENT_FUNCTIONABSTRACTIONS_HPP -#include "rosa/agent/Functionality.h" #include "rosa/agent/Abstraction.hpp" +#include "rosa/agent/Functionality.h" #include "rosa/support/debug.hpp" #include -#include #include #include +#include namespace rosa { namespace agent { -/// Evaluates a linear function at a given value. +/// 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 T type of the functions domain -/// \tparam A type of the functions range -template class LinearFunction : - public Abstraction{ +/// \tparam D type of the functions domain +/// \tparam R type of the functions range +template +class LinearFunction : public Abstraction { // Make sure the actual type arguments are matching our expectations. - STATIC_ASSERT((std::is_arithmetic::value), - "LinearFunction not arithmetic T"); - STATIC_ASSERT((std::is_arithmetic::value), - "LinearFunction not to arithmetic"); + STATIC_ASSERT((std::is_arithmetic::value), + "LinearFunction not arithmetic T"); + STATIC_ASSERT((std::is_arithmetic::value), + "LinearFunction not to arithmetic"); + protected: - const T Intercept; - const T Coefficient; + /// 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 - /// domain - LinearFunction(T Intercept, T Coefficient) noexcept - : Abstraction(Intercept), - Intercept(Intercept), + LinearFunction(D Intercept, D Coefficient) noexcept + : Abstraction(Intercept), Intercept(Intercept), Coefficient(Coefficient) {} + /// Creates an instance given the two points on a linear function. + /// + /// \param x1 The x-value of the first point + /// \param y1 The x-value of the first point + /// \param x2 The y-value of the second point + /// \param y2 The y-value of the second point + LinearFunction(D x1, R y1, D x2, R y2) noexcept + : Abstraction(y1 - x1 * (y1 - y2) / (x1 - x2), + (y1 - y2) / (x1 - x2)) {} + + /// Creates an instance given the two points on a linear function. + /// + /// \param p1 The coordinates of the first point + /// \param p2 The coordinates of the second point + LinearFunction(std::pair p1, std::pair p2) noexcept + : LinearFunction(p1.first, p1.second, p2.first, p2.second) {} + /// Destroys \p this object. ~LinearFunction(void) = default; /// Checks wether the Abstraction evaluates to default at the given position /// As LinearFunctions can be evaluated everythwere, this is always false - bool isDefaultAt(const T &V) const{ + /// + /// \param V the value at which to check if the function falls back to it's + /// default value. + /// + /// \return false + bool isDefaultAt(const D &V) const noexcept override { (void)V; return false; } + /// Getter for member variable Intercept + /// + /// \return Intercept + D getIntercept() const { return Intercept; } + + /// Setter for member variable Intercept + /// + /// \param Intercept the new Intercept + void setIntercept(const D &Intercept) { this->Intercept = Intercept; } + + /// Getter for member variable Coefficient + /// + /// \return Coefficient + D getCoefficient() const { return Coefficient; } + + /// Setter for member variable Coefficient + /// + /// \param Coefficient the new Intercept + void setCoefficient(const D &Coefficient) { this->Coefficient = Coefficient; } + + /// Set Intercept and Coefficient from two points on the linear function + /// + /// \param x1 The x-value of the first point + /// \param y1 The x-value of the first point + /// \param x2 The y-value of the second point + /// \param y2 The y-value of the second point + void setFromPoints(D x1, R y1, D x2, R y2) { + Coefficient = (y1 - y2) / (x1 - x2); + Intercept = y1 - Coefficient * x1; + } + + /// Set Intercept and Coefficient from two points on the linear function + /// + /// \param p1 The coordinates of the first point + /// \param p2 The coordinates of the second point + inline void setFromPoints(std::pair p1, std::pair p2) { + setFromPoints(p1.first, p1.second, p2.first, p2.second); + } + /// Evaluates the linear function /// /// \param X the value at which to evaluate the function - /// \return the result - virtual A operator()(const T &X) const noexcept override { - return Intercept + X*Coefficient; + /// + /// \return Coefficient*X + Intercept + virtual R operator()(const D &X) const noexcept override { + return Intercept + X * Coefficient; } }; -/// Evaluates a sine function at a given value. +/// Implements \c rosa::agent::Abstraction as a sine function, +/// y = Amplitude * sin(Frequency * X + Phase) + Average. /// -/// \tparam T type of the functions domain -/// \tparam A type of the functions range -template class SineFunction : - public Abstraction{ +/// \note This implementation is supposed to be used to represent a sine +/// function from an arithmetic domain to an arithmetic range. This is enforced +/// statically. +/// +/// \tparam D type of the functions domain +/// \tparam R type of the functions range +template +class SineFunction : public Abstraction { // Make sure the actual type arguments are matching our expectations. - STATIC_ASSERT((std::is_arithmetic::value), - "SineFunction not arithmetic T"); - STATIC_ASSERT((std::is_arithmetic::value), - "SineFunction not to arithmetic"); + STATIC_ASSERT((std::is_arithmetic::value), + "SineFunction not arithmetic T"); + STATIC_ASSERT((std::is_arithmetic::value), + "SineFunction not to arithmetic"); + protected: - const T Frequency; - const T Amplitude; - const T Phase; - const T Average; + /// 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 - /// domain - SineFunction(T Frequency, T Amplitude, T Phase, T Average) noexcept - : Abstraction(Average), - Frequency(Frequency), - Amplitude(Amplitude), - Phase(Phase), - Average(Average) {} + SineFunction(D Frequency, D Amplitude, D Phase, D Average) noexcept + : Abstraction(Average), Frequency(Frequency), Amplitude(Amplitude), + Phase(Phase), Average(Average) {} /// Destroys \p this object. ~SineFunction(void) = default; /// Checks wether the Abstraction evaluates to default at the given position /// As SineFunctions can be evaluated everythwere, this is always false - bool isDefaultAt(const T &V) const{ + /// + /// \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 linear function + /// Evaluates the sine function /// /// \param X the value at which to evaluate the function - /// \return the result - virtual A operator()(const T &X) const noexcept override { - return Amplitude*sin(Frequency * X + Phase) + Average; + /// \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; } }; -/// Implements \c rosa::agent::RangeAbstraction as an abstraction from -/// \c std::map from ranges of a type to abstractions of that type to another -/// type. The resulting abstractions are evaluated for the given values. +enum StepDirection { StepUp, StepDown }; + +/// Implements \c rosa::agent::PartialFunction as a step function from 0 to 1 +/// with a ramp in between /// -/// \note This implementation is supposed to be used to abstract ranges of -/// arithmetic types into abstractions from that type to another arithmetic -/// type, which is statically enforced. +/// \tparam D type of the functions domain +/// \tparam R type of the functions range +template +class StepFunction : public Abstraction { + // Make sure the actual type arguments are matching our expectations. + STATIC_ASSERT((std::is_arithmetic::value), "abstracting not arithmetic"); + STATIC_ASSERT((std::is_arithmetic::value), + "abstracting not to arithmetic"); + +private: + D Coefficient; + D RightLimit; + StepDirection Direction; + +public: + /// Creates an instance by Initializing the underlying \c Abstraction. + /// + /// \param Coefficient Coefficient of the ramp + /// \param Direction wether to step up or down + /// + /// \pre Coefficient > 0 + StepFunction(D Coefficient, StepDirection Direction = StepUp) + : Abstraction(0), Coefficient(Coefficient), + RightLimit(1.0f / Coefficient), Direction(Direction) { + ASSERT(Coefficient > 0); + } + + /// Destroys \p this object. + ~StepFunction(void) = default; + + /// Setter for Coefficient + /// + /// \param Coefficient the new Coefficient + void setCoefficient(const D &Coefficient) { + ASSERT(Coefficient > 0); + this->Coefficient = Coefficient; + this->RightLimit = 1 / Coefficient; + } + + /// Setter for RightLimit + /// + /// \param RightLimit the new RightLimit + void setRightLimit(const D &RightLimit) { + ASSERT(RightLimit > 0); + this->RightLimit = RightLimit; + this->Coefficient = 1 / RightLimit; + } + + /// Checks wether the Abstraction evaluates to default at the given position + /// + /// \param V the value at which to check if the function falls back to it's + /// default value. + /// + /// \return false if the is negative, true otherwise + bool isDefaultAt(const D &V) const noexcept override { return V > 0; } + + /// Executes the Abstraction + /// + /// \param V value to abstract + /// + /// \return the abstracted value + R operator()(const D &V) const noexcept override { + R ret = 0; + if (V <= 0) + ret = 0; + else if (V >= RightLimit) + ret = 1; + else + ret = V * Coefficient; + return Direction == StepDirection::StepUp ? ret : 1 - ret; + } +}; + +/// Implements \c rosa::agent::Abstraction as a partial function from a domain +/// to a range. /// -/// \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. +/// \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. /// -/// \tparam T type to abstract from -/// \tparam A type to abstract to -template -class PartialFunction : public Abstraction { +/// A partial function is defined as a list of abstractions, where each +/// abstraction is associated a range in which it is defined. These ranges must +/// be mutually exclusive. +/// +/// \tparam D type of the functions domain +/// \tparam R type of the functions range +template +class PartialFunction : public Abstraction { // Make sure the actual type arguments are matching our expectations. - STATIC_ASSERT((std::is_arithmetic::value), "abstracting not arithmetic"); - STATIC_ASSERT((std::is_arithmetic::value), - "abstracting not to arithmetic"); + STATIC_ASSERT((std::is_arithmetic::value), "abstracting not arithmetic"); + STATIC_ASSERT((std::is_arithmetic::value), + "abstracting not to arithmetic"); private: - RangeAbstraction>> RA; + /// A \c rosa::agent::RangeAbstraction RA is used to represent the association + /// from ranges to Abstractions. + /// This returns the Abstraction that is defined for any given value, or + /// a default Abstraction if no Abstraction is defined for that value. + RangeAbstraction>> RA; public: - /// Creates an instance by Initializing the underlying \c RangeAbstraction. + /// Creates an instance by Initializing the underlying \c Abstraction. /// /// \param Map the mapping to do abstraction according to /// \param Default abstraction to abstract to by default /// /// \pre Each key defines a valid range such that `first <= second` and /// there are no overlapping ranges defined by the keys. - PartialFunction(const std::map, - std::shared_ptr>> &Map, - const A Default) - : Abstraction(Default), - RA(Map, std::shared_ptr> - (new Abstraction(Default))) { + PartialFunction( + const std::map, std::shared_ptr>> &Map, + const R Default) + : Abstraction(Default), + RA(Map, + std::shared_ptr>(new Abstraction(Default))) { } /// Destroys \p this object. ~PartialFunction(void) = default; /// Checks wether the Abstraction evaluates to default at the given position - bool isDefaultAt(const T &V) const{ - return RA.isDefaultAt(V); + /// + /// \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); } - /// Evaluates an Abstraction 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. + /// 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 - A operator()(const T &V) const noexcept override { + R operator()(const D &V) const noexcept override { return RA(V)->operator()(V); } }; } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_FUNCTIONABSTRACTIONS_HPP diff --git a/include/rosa/agent/Functionality.h b/include/rosa/agent/Functionality.h index 5825529..051dae9 100644 --- a/include/rosa/agent/Functionality.h +++ b/include/rosa/agent/Functionality.h @@ -1,34 +1,40 @@ //===-- rosa/agent/Functionality.h ------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/agent/Functionality.h /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Declaration of `rosa::Functionality` base-class. /// //===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_FUNCTIONALITY_H #define ROSA_AGENT_FUNCTIONALITY_H namespace rosa { namespace agent { /// Base class for actual *functionalities* that implement various concepts of /// self-awareness and are supposed to be used in implementing *agents*. class Functionality { public: Functionality(void) noexcept = default; virtual ~Functionality(void) = default; }; } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_FUNCTIONALITY_H diff --git a/include/rosa/agent/History.hpp b/include/rosa/agent/History.hpp index 1097ad0..f164fae 100644 --- a/include/rosa/agent/History.hpp +++ b/include/rosa/agent/History.hpp @@ -1,292 +1,534 @@ //===-- rosa/agent/History.hpp ----------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/agent/History.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Definition of *history* *functionality*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_HISTORY_HPP #define ROSA_AGENT_HISTORY_HPP #include "rosa/agent/Functionality.h" #include "rosa/config/config.h" #include "rosa/support/debug.hpp" #include "rosa/support/type_helper.hpp" #include +#include namespace rosa { namespace agent { /// Retention policies defining what a \c rosa::agent::History instance should /// do when the number of recorded entries reached its capacity. enum class HistoryPolicy { SRWF, ///< Stop Recording When Full -- no new entry is recorded when full - FIFO ///< First In First Out -- overwrite the earliest entry with a new one + FIFO, ///< First In First Out -- overwrite the earliest entry with a new one + LIFO ///< Last In First Out -- overwrite the latest entry with a new one }; -/// Implements *history* by recording and storing values. -/// -/// \note Not thread-safe implementation, which should not be a problem as any -/// instance of \c rosa::agent::Functionality is an internal component of a -/// \c rosa::Agent, which is the basic unit of concurrency. -/// -/// \tparam T type of values to store -/// \tparam N number of values to store at most -/// \tparam P retention policy to follow when capacity is reached -/// -/// \invariant The size of the underlying \c std::array is `N + 1`:\code -/// max_size() == N + 1 && N == max_size() - 1 -/// \endcode -template -class History : public Functionality, private std::array { - - // Bring into scope inherited functions that are used. - using std::array::max_size; - using std::array::operator[]; - - /// The index of the first data element in the circular buffer. - size_t Data; - - /// The index of the first empty slot in the circular buffer. - size_t Space; +template class History : public Functionality { public: - /// Creates an instances by initializing the indices for the circular buffer. - History(void) noexcept : Data(0), Space(0) {} + History(void) noexcept {} /// Destroys \p this object. - ~History(void) = default; + virtual ~History(void) = default; /// Tells the retention policy applied to \p this object. /// /// \return \c rosa::agent::History::P - static constexpr HistoryPolicy policyOfHistory(void) noexcept { return P; } + static constexpr HistoryPolicy policy(void) noexcept { return P; } /// Tells how many entries may be recorded by \c this object. /// /// \note The number of entries that are actually recorded may be smaller. /// - /// \return \c rosa::agent::History::N - static constexpr size_t lengthOfHistory(void) noexcept { return N; } + /// \return The max number of entries that may be recorded + virtual size_t maxLength(void) const noexcept = 0; /// Tells how many entries are currently recorded by \p this object. /// /// \return number of entries currently recorded by \p this object. /// /// \post The returned value cannot be larger than the capacity of \p this /// object:\code /// 0 <= numberOfEntries() && numberOfEntries <= lengthOfHistory() /// \endcode - size_t numberOfEntries(void) const noexcept { - return Data <= Space ? Space - Data : max_size() - Data + Space; - } + virtual size_t numberOfEntries(void) const noexcept = 0; /// Tells if \p this object has not recorded anything yet. /// /// \return if \p this object has no entries recorded bool empty(void) const noexcept { return numberOfEntries() == 0; } + /// Tells if the history reached it's maximum length + /// + /// \return if the history reached it's maximum length. + bool full(void) const noexcept { return numberOfEntries() == maxLength(); } + /// Gives a constant lvalue reference to an entry stored in \p this object. /// /// \note The recorded entries are indexed starting from the latest one. /// /// \param I the index at which the stored entry to take from /// /// \pre \p I is a valid index:\code - /// 0 <= I && I <= numberOfEntries() + /// 0 <= I && I < numberOfEntries() /// \endcode - const T &entry(const size_t I = 0) const noexcept { - ASSERT(0 <= I && I < numberOfEntries()); // Boundary check. - // Position counted back from the last recorded entry. - typename std::make_signed::type Pos = Space - (1 + I); - // Actual index wrapped around to the end of the buffer if negative. - return (*this)[Pos >= 0 ? Pos : max_size() + Pos]; - } + virtual const T &entry(const size_t I = 0) const noexcept = 0; + + /// Removes all entries recorded in \p this object. + virtual void clear() noexcept = 0; private: - /// Tells if the circular buffer is full. + /// Pushes a new entry into the history. /// - /// \return if the circular buffer is full. - bool full(void) const noexcept { return numberOfEntries() == N; } - - /// Pushes a new entry into the circular buffer. + /// \note The earliest entry gets overwritten if the history is full. /// - /// \note The earliest entry gets overwritten if the buffer is full. + /// \param V value to push into the history + virtual void pushBack(const T &V) noexcept = 0; + + /// Replaces the most recent entry in the history. /// - /// \param V value to push into the buffer - void pushBack(const T &V) noexcept { - // Store value to the first empty slot and step Space index. - (*this)[Space] = V; - Space = (Space + 1) % max_size(); - if (Data == Space) { - // Buffer was full, step Data index. - Data = (Data + 1) % max_size(); - } - } + /// \param V value to replace the most current value with + virtual void replaceFront(const T &V) noexcept = 0; public: /// Adds a new entry to \p this object and tells if the operation was /// successful. /// /// \note Success of the operation depends on the actual policy. /// /// \param V value to store /// /// \return if \p V was successfully stored bool addEntry(const T &V) noexcept { switch (P) { default: ROSA_CRITICAL("unkown HistoryPolicy"); + case HistoryPolicy::LIFO: + if (full()) { + replaceFront(V); + return true; + } case HistoryPolicy::SRWF: if (full()) { return false; } // \note Fall through to FIFO which unconditionally pushes the new entry. case HistoryPolicy::FIFO: // FIFO and SRWF not full. pushBack(V); return true; } } /// Tells the trend set by the entries recorded by \p this object. /// /// The number of steps to go back when calculating the trend is defined as /// argument to the function. /// /// \note The number of steps that can be made is limited by the number of /// entries recorded by \p this object. /// /// \note The function is made a template only to be able to use /// \c std::enable_if. /// /// \tparam X always use the default! /// /// \param D number of steps to go back in *history* /// /// \return trend set by analyzed entries /// /// \pre Statically, \p this object stores signed arithmetic values:\code /// std::is_arithmetic::value && std::is_signed::value /// \endcode Dynamically, \p D is a valid number of steps to take:\code - /// 0 <= D && D < N + /// 0 <= D && D < lengthOfHistory() /// \endcode template typename std::enable_if< std::is_arithmetic::value && std::is_signed::value, X>::type - trend(const size_t D = N - 1) const noexcept { + trend(const size_t D) const noexcept { STATIC_ASSERT((std::is_same::value), "not default template arg"); - ASSERT(0 <= D && D < N); // Boundary check. + ASSERT(0 <= D && D < maxLength()); // Boundary check. if (numberOfEntries() < 2 || D < 1) { // No entries for computing trend. return {}; // Zero element of \p T } else { // Here at least two entries. // \c S is the number of steps that can be done. const size_t S = std::min(numberOfEntries() - 1, D); size_t I = S; // Compute trend with linear regression. size_t SumIndices = 0; T SumEntries = {}; T SumSquareEntries = {}; T SumProduct = {}; while (I > 0) { // \note Indexing for the regression starts in the past. const size_t Index = S - I; const T Entry = entry(--I); SumIndices += Index; SumEntries += Entry; SumSquareEntries += Entry * Entry; SumProduct += Entry * Index; } return (SumProduct * S - SumEntries * SumIndices) / (SumSquareEntries * S - SumEntries * SumEntries); } } /// Tells the average absolute difference between consecutive entries recorded /// by \p this object /// The number of steps to go back when calculating the average is defined as /// argument to the function. /// /// \note The number of steps that can be made is limited by the number of /// entries recorded by \p this object. /// /// \note The function is made a template only to be able to use /// \c std::enable_if. /// /// \tparam X always use the default! /// /// \param D number of steps to go back in *history* /// /// \pre Statically, \p this object stores arithmetic values:\code /// std::is_arithmetic::value /// \endcode Dynamically, \p D is a valid number of steps to take:\code - /// 0 <= D && D < N + /// 0 <= D && D < lengthOfHistory() /// \endcode template typename std::enable_if::value, size_t>::type - averageAbsDiff(const size_t D = N - 1) const noexcept { + averageAbsDiff(const size_t D) const noexcept { STATIC_ASSERT((std::is_same::value), "not default template arg"); - ASSERT(0 <= D && D < N); // Boundary check. + ASSERT(0 <= D && D < maxLength()); // Boundary check. if (numberOfEntries() < 2 || D < 1) { // No difference to average. return {}; // Zero element of \p T } else { // Here at least two entries. // \c S is the number of steps that can be done. const size_t S = std::min(numberOfEntries() - 1, D); // Sum up differences as non-negative values only, hence using an // unsigned variable for that. size_t Diffs = {}; // Init to zero. // Count down entry indices and sum up all the absolute differences. size_t I = S; T Last = entry(I); while (I > 0) { T Next = entry(--I); Diffs += Last < Next ? Next - Last : Last - Next; Last = Next; } // Return the average of the summed differences. return Diffs / S; } } + + /// Tells the average of all entries recorded by \p this object + /// + /// \tparam R type of the result + template R average() const noexcept { + R Average = 0; + for (size_t I = 0; I < numberOfEntries(); I++) { + Average += entry(I); + } + Average /= numberOfEntries(); + return Average; + } +}; + +/// Implements *history* by recording and storing values. +/// The length of the underlying std::array is static and must be set at +/// compile-time +/// +/// \note Not thread-safe implementation, which should not be a problem as any +/// instance of \c rosa::agent::Functionality is an internal component of a +/// \c rosa::Agent, which is the basic unit of concurrency. +/// +/// \tparam T type of values to store +/// \tparam N number of values to store at most +/// \tparam P retention policy to follow when capacity is reached +/// +/// \invariant The size of the underlying \c std::array is `N + 1`:\code +/// max_size() == N + 1 && N == max_size() - 1 +/// \endcode +template +class StaticLengthHistory : public History, private std::array { + + // Bring into scope inherited functions that are used. + using std::array::max_size; + using std::array::operator[]; + + /// The index of the first data element in the circular buffer. + size_t Data; + + /// The index of the first empty slot in the circular buffer. + size_t Space; + +public: + using History::policy; + using History::empty; + using History::full; + using History::addEntry; + using History::trend; + using History::averageAbsDiff; + + /// Creates an instances by initializing the indices for the circular buffer. + StaticLengthHistory(void) noexcept : Data(0), Space(0) {} + + /// Destroys \p this object. + ~StaticLengthHistory(void) override = default; + + /// Tells how many entries may be recorded by \c this object. + /// + /// \note The number of entries that are actually recorded may be smaller. + /// + /// \return \c rosa::agent::History::N + size_t maxLength(void) const noexcept override { return N; } + + /// Tells how many entries are currently recorded by \p this object. + /// + /// \return number of entries currently recorded by \p this object. + /// + /// \post The returned value cannot be larger than the capacity of \p this + /// object:\code + /// 0 <= numberOfEntries() && numberOfEntries <= lengthOfHistory() + /// \endcode + size_t numberOfEntries(void) const noexcept override { + return Data <= Space ? Space - Data : max_size() - Data + Space; + } + + /// Gives a constant lvalue reference to an entry stored in \p this object. + /// + /// \note The recorded entries are indexed starting from the latest one. + /// + /// \param I the index at which the stored entry to take from + /// + /// \pre \p I is a valid index:\code + /// 0 <= I && I < numberOfEntries() + /// \endcode + const T &entry(const size_t I = 0) const noexcept override { + ASSERT(0 <= I && I < numberOfEntries()); // Boundary check. + // Position counted back from the last recorded entry. + typename std::make_signed::type Pos = Space - (1 + I); + // Actual index wrapped around to the end of the buffer if negative. + return (*this)[Pos >= 0 ? Pos : max_size() + Pos]; + } + + /// Removes all entries recorded in \p this object. + void clear() noexcept override { + Data = 0; + Space = 0; + } + +private: + /// Pushes a new entry into the circular buffer. + /// + /// \note The earliest entry gets overwritten if the buffer is full. + /// + /// \param V value to push into the buffer + void pushBack(const T &V) noexcept override { + // Store value to the first empty slot and step Space index. + (*this)[Space] = V; + Space = (Space + 1) % max_size(); + if (Data == Space) { + // Buffer was full, step Data index. + Data = (Data + 1) % max_size(); + } + } + + /// Replaces the most recent entry in the history. + /// + /// \param V value to replace the most current value with + void replaceFront(const T &V) noexcept override { + (*this)[(Space - 1) % max_size()] = V; + } }; /// Adds a new entry to a \c rosa::agent::History instance. /// /// \note The result of \c rosa::agent::History::addEntry is ignored. /// /// \tparam T type of values stored in \p H /// \tparam N number of values \p H is able to store /// \tparam P retention policy followed by \p H when capacity is reached /// /// \param H to add a new entry to /// \param V value to add to \p H /// /// \return \p H after adding \p V to it template -History &operator<<(History &H, const T &V) noexcept { +StaticLengthHistory &operator<<(StaticLengthHistory &H, + const T &V) noexcept { H.addEntry(V); return H; } +/// Implements *DynamicLengthHistory* by recording and storing values. +/// +/// \note Not thread-safe implementation, which should not be a problem as any +/// instance of \c rosa::agent::Functionality is an internal component of a +/// \c rosa::Agent, which is the basic unit of concurrency. +/// +/// \tparam T type of values to store +/// \tparam P retention policy to follow when capacity is reached +template +class DynamicLengthHistory : public History, private std::vector { + + // Bring into scope inherited functions that are used. + using std::vector::erase; + using std::vector::begin; + using std::vector::end; + using std::vector::size; + using std::vector::max_size; + using std::vector::resize; + using std::vector::push_back; + using std::vector::pop_back; + using std::vector::operator[]; + + /// The current length of the DynamicLengthHistory. + size_t Length; + +public: + using History::policy; + using History::empty; + using History::full; + using History::addEntry; + using History::trend; + using History::averageAbsDiff; + + /// Creates an instances by setting an initial length + DynamicLengthHistory(size_t Length) noexcept : Length(Length) { + this->resize(Length); + } + + /// Destroys \p this object. + ~DynamicLengthHistory(void) override = default; + + /// Tells how many entries may be recorded by \c this object. + /// + /// \note The number of entries that are actually recorded may be smaller. + /// + /// \return \c rosa::agent::DynamicLengthHistory::N + size_t maxLength(void) const noexcept override { return Length; } + + /// Tells how many entries are currently recorded by \p this object. + /// + /// \return number of entries currently recorded by \p this object. + /// + /// \post The returned value cannot be larger than the capacity of \p this + /// object:\code + /// 0 <= numberOfEntries() && numberOfEntries <= + /// lengthOfHistory() \endcode + size_t numberOfEntries(void) const noexcept { return size(); } + + /// Gives a constant lvalue reference to an entry stored in \p this object. + /// + /// \note The recorded entries are indexed starting from the latest one. + /// + /// \param I the index at which the stored entry to take from + /// + /// \pre \p I is a valid index:\code + /// 0 <= I && I < numberOfEntries() + /// \endcode + const T &entry(const size_t I = 0) const noexcept override { + ASSERT(0 <= I && I < numberOfEntries()); // Boundary check. + return this->operator[](size() - I - 1); + } + + /// Removes all entries recorded in \p this object. + void clear() noexcept override { erase(begin(), end()); } + +private: + /// Pushes a new entry into the circular buffer. + /// + /// \note The earliest entry gets overwritten if the buffer is full. + /// + /// \param V value to push into the buffer + void pushBack(const T &V) noexcept override { + if (full()) { + erase(begin()); + } + push_back(V); + } + + /// Replaces the most recent entry in the history. + /// + /// \param V value to replace the most current value with + void replaceFront(const T &V) noexcept override { + (void)pop_back(); + push_back(V); + } + +public: + /// Resizes the History length. If the new length is smaller than the number + /// of currently stored values, values are deleted according to the + /// HistoryPolicy. + /// + /// @param NewLength The new Length of the History. + void setLength(size_t NewLength) noexcept { + Length = NewLength; + if (NewLength < numberOfEntries()) { + switch (P) { + default: + ROSA_CRITICAL("unkown HistoryPolicy"); + case HistoryPolicy::LIFO: + case HistoryPolicy::SRWF: + // Delete last numberOfEntries() - NewLength items from the back + erase(begin() + NewLength, end()); + break; + case HistoryPolicy::FIFO: + // Delete last numberOfEntries() - NewLength items from the front + erase(begin(), begin() + (numberOfEntries() - NewLength)); + break; + } + } + this->resize(Length); + } +}; + +/// Adds a new entry to a \c rosa::agent::DynamicLengthHistory instance. +/// +/// \note The result of \c rosa::agent::DynamicLengthHistory::addEntry is +/// ignored. +/// +/// \tparam T type of values stored in \p H +/// \tparam P retention policy followed by \p H when capacity is reached +/// +/// \param H to add a new entry to +/// \param V value to add to \p H +/// +/// \return \p H after adding \p V to it +template +DynamicLengthHistory &operator<<(DynamicLengthHistory &H, + const T &V) noexcept { + H.addEntry(V); + return H; +} } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_HISTORY_HPP diff --git a/include/rosa/agent/RangeConfidence.hpp b/include/rosa/agent/RangeConfidence.hpp index 83ba518..f441972 100644 --- a/include/rosa/agent/RangeConfidence.hpp +++ b/include/rosa/agent/RangeConfidence.hpp @@ -1,90 +1,114 @@ //===-- rosa/agent/RangeConfidence.hpp --------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/agent/RangeConfidence.hpp /// /// \author Benedikt Tutzer (benedikt.tutzer@tuwien.ac.at) /// /// \date 2019 /// /// \brief Definition of *RangeConfidence* *functionality*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_RANGECONFIDENCE_HPP #define ROSA_AGENT_RANGECONFIDENCE_HPP -#include "rosa/agent/Functionality.h" #include "rosa/agent/Abstraction.hpp" #include "rosa/agent/FunctionAbstractions.hpp" +#include "rosa/agent/Functionality.h" #include "rosa/support/debug.hpp" #include -#include #include #include +#include namespace rosa { namespace agent { -/// Evaluates a vector of Abstractions at a given value and returns the results -/// as a vector +/// 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 vectors of another arithmetic type, which is -/// statically enforced. +/// arithmetic types into maps whose values are of another arithmetic type, +/// which is statically enforced. /// -/// \tparam T type to abstract from -/// \tparam A type to abstract a vector of to -template -class RangeConfidence : protected Abstraction>, - private std::map>{ +/// \tparam D type to abstract from +/// \tparam I type the type of the ID's +/// \tparam R type of the range +template +class RangeConfidence : protected Abstraction>, + private std::map> { // Make sure the actual type arguments are matching our expectations. - STATIC_ASSERT((std::is_arithmetic::value), "abstracting not arithmetic"); - STATIC_ASSERT((std::is_arithmetic::value), - "abstracting not to arithmetic"); + STATIC_ASSERT((std::is_arithmetic::value), "abstracting not arithmetic"); + STATIC_ASSERT((std::is_arithmetic::value), + "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 RangeAbstraction. + /// Creates an instance by Initializing the underlying \c Abstraction and + /// \c std::map. /// /// \param Abstractions the Abstractions to be evaluated - RangeConfidence(const std::map> &Abstractions, - bool IgnoreDefaults = false) - : Abstraction>({}), - std::map>(Abstractions), - IgnoreDefaults(IgnoreDefaults){ - } + /// \param IgnoreDefaults wether to include default results in the result-map + /// or not (defaults to false). + RangeConfidence(const std::map> &Abstractions, + bool IgnoreDefaults = false) + : Abstraction>({}), std::map>( + Abstractions), + IgnoreDefaults(IgnoreDefaults) {} /// Destroys \p this object. ~RangeConfidence(void) = default; - /// Evaluates an Abstraction from type \p T to type \p A based on the set - /// mapping. + /// 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. /// - /// 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. + /// \return true, if all Abstractions evaluate to default + bool isDefaultAt(const D &V) const noexcept override { + for (auto const &P : ((std::map>)*this)) { + if (!P.second.isDefaultAt(V)) + return false; + } + 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 the abstracted value based on the set mapping - std::map operator()(const T &V) const noexcept override { - std::map ret; - for (auto const& p : ((std::map>)*this)){ - if(!IgnoreDefaults || !p.second.isDefaultAt(V)) - ret.insert(std::pair(p.first, p.second(V))); + /// \return a \c std::map containing the results of the stored Abstractions, + /// indexable by the key's the Abstractions are associated with + std::map operator()(const D &V) const noexcept override { + std::map Ret; + for (auto const &P : ((std::map>)*this)) { + if (!IgnoreDefaults || !P.second.isDefaultAt(V)) + Ret.insert(std::pair(P.first, P.second(V))); } - return ret; + return Ret; } }; } // End namespace agent } // End namespace rosa #endif // ROSA_AGENT_RANGECONFIDENCE_HPP diff --git a/include/rosa/agent/namespace.h b/include/rosa/agent/namespace.h index 2cd458c..70a305b 100755 --- a/include/rosa/agent/namespace.h +++ b/include/rosa/agent/namespace.h @@ -1,28 +1,34 @@ //===-- rosa/agent/namespace.h ----------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/agent/namespace.h /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Documentation for namespace \c rosa::agent. /// //===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_NAMESPACE_H #define ROSA_AGENT_NAMESPACE_H namespace rosa { /// Contains functionalities that are supposed to be useful for implementing /// *agents*. namespace agent {} } // End namespace rosa #endif // ROSA_AGENT_NAMESPACE_H diff --git a/include/rosa/config/config.h b/include/rosa/config/config.h index 89794fa..6090667 100644 --- a/include/rosa/config/config.h +++ b/include/rosa/config/config.h @@ -1,72 +1,86 @@ //===-- rosa/config/config.h ------------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/config/config.h /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \brief Configuration information on the build of the library. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CONFIG_CONFIG_H #define ROSA_CONFIG_CONFIG_H #include "rosa/config/rosa_config.h" #include // This OS-specific block defines one of the following: // - ROSA_LINUX // - ROSA_WINDOWS // It also defines ROSA_POSIX for POSIX-compatible systems. #if defined(__linux__) #define ROSA_LINUX #elif defined(WIN32) || defined(_WIN32) #define ROSA_WINDOWS #else #error Platform and/or compiler not supported #endif #if defined(ROSA_LINUX) #define ROSA_POSIX #endif // Defining filenames in a project-relative way based on absolute paths. #include "rosa/config/project_path.hpp" /// The project-relative path of the current source file. #define __FILENAME__ (__FILE__ + project_relative_path_index(__FILE__)) // Convenience macros. /// No-op. #define ROSA_VOID_STMT static_cast(0) -/// Ignors anything. +/// Ignors an expression. +/// +/// \param x expression +#define ROSA_IGNORE_UNUSED_EXPR(x) static_cast(x) + +/// Ignors a statement (not an expression). /// -/// \param x anything -#define ROSA_IGNORE_UNUSED(x) static_cast(x) +/// \param s statement +#define ROSA_IGNORE_UNUSED_STMT(s) \ + while (false) { \ + s; \ + } \ /// Prints an error message and aborts execution. /// /// \param error the error message to print #define ROSA_CRITICAL(error) \ do { \ std::cerr << "[FATAL] " << __func__ << "@" << __FILENAME__ << ":" \ << __LINE__ << ": critical error: '" << (error) << "'" \ << std::endl; \ ::abort(); \ } while (false) /// Raises a runtime error in the program. /// /// \param msg message describing the error /// /// \throws std::runtime_error #define ROSA_RAISE_ERROR(msg) throw std::runtime_error(msg) #endif // ROSA_CONFIG_CONFIG_H diff --git a/include/rosa/config/namespace.h b/include/rosa/config/namespace.h index ce87454..d2006ad 100755 --- a/include/rosa/config/namespace.h +++ b/include/rosa/config/namespace.h @@ -1,23 +1,29 @@ //===-- rosa/config/namespace.h ---------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/config/namespace.h /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Documentation for the namespace \c rosa. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CONFIG_NAMESPACE_H #define ROSA_CONFIG_NAMESPACE_H /// Base namespace used by the RoSA framework. namespace rosa {} #endif // ROSA_CONFIG_NAMESPACE_H diff --git a/include/rosa/config/project_path.hpp b/include/rosa/config/project_path.hpp index b4849ae..a719fae 100644 --- a/include/rosa/config/project_path.hpp +++ b/include/rosa/config/project_path.hpp @@ -1,66 +1,72 @@ //===-- rosa/config/project_path.hpp ----------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/config/project_path.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Facility for compile-time manipulation of paths. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CONFIG_PROJECT_PATH_HPP #define ROSA_CONFIG_PROJECT_PATH_HPP #include "rosa/config/rosa_config.h" #include namespace rosa { /// Nested namespace with implementation of the provided features, /// consider it private. namespace { /// Tells the index of the project-relative part of an absolute path. /// /// \param path absolute path to check /// \param project the absolute path of the project /// \param index number of leading characters already checked and found /// matching /// /// \return index of the part relative to \c project in \p path; \c 0 if \p path /// is not under \p project constexpr size_t project_relative_path_index_impl(const char *const path, const char *const project = ROSA_SRC_DIR, const size_t index = 0) { return project[index] == '\0' ? index // Found it. : (path[index] == '\0' || project[index] != path[index]) ? 0 // Path is not under project. : project_relative_path_index_impl( path, project, index + 1); // Continue searching... } } // End namespace /// Tells the index of the project-relative part of an absolute path. /// /// \param path absolute path to check /// /// \return index of the project-relative part of \p path; \c 0 if \p path is /// not under the RoSA siource directory. constexpr size_t project_relative_path_index(const char *const path) { return project_relative_path_index_impl(path); } } // End namespace rosa #endif // ROSA_CONFIG_PROJECT_PATH_HPP diff --git a/include/rosa/config/rosa_config.h.cmake b/include/rosa/config/rosa_config.h.cmake index 933d260..5d9ce0a 100644 --- a/include/rosa/config/rosa_config.h.cmake +++ b/include/rosa/config/rosa_config.h.cmake @@ -1,57 +1,63 @@ //===-- rosa/config/rosa_config.h -------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// // // The file enumerates variables from the RoSA configurations so that they can // be in exported headers. // //===----------------------------------------------------------------------===// #ifndef ROSA_CONFIG_ROSA_CONFIG_H #define ROSA_CONFIG_ROSA_CONFIG_H // clang-format off #define CMAKE_SYSTEM "${CMAKE_SYSTEM}" #define CMAKE_GENERATOR "${CMAKE_GENERATOR}" #define CMAKE_C_COMPILER_ID "${CMAKE_C_COMPILER_ID}" #define CMAKE_C_COMPILER_VERSION "${CMAKE_C_COMPILER_VERSION}" #define CMAKE_CXX_COMPILER_ID "${CMAKE_CXX_COMPILER_ID}" #define CMAKE_CXX_COMPILER_VERSION "${CMAKE_CXX_COMPILER_VERSION}" #define ROSA_VERSION_MAJOR ${ROSA_VERSION_MAJOR} #define ROSA_VERSION_MINOR ${ROSA_VERSION_MINOR} #define ROSA_VERSION_PATCH ${ROSA_VERSION_PATCH} #define PACKAGE_NAME "${PACKAGE_NAME}" #define PACKAGE_STRING "${PACKAGE_STRING}" #define PACKAGE_VERSION "${PACKAGE_VERSION}" #define PACKAGE_BUGREPORT "${PACKAGE_BUGREPORT}" #define BUILD_DATE __DATE__ " " __TIME__ #define ROSA_SRC_DIR "${ROSA_MAIN_SRC_DIR}" #if ${ROSA_LOG_LEVEL_INT} != -1 #define ROSA_LOG_LEVEL ${ROSA_LOG_LEVEL} #endif #if ${ROSA_ENABLE_ASSERTIONS_INT} != -1 #define ROSA_ENABLE_ASSERTIONS #endif // clang-format on #endif // ROSA_CONFIG_ROSA_CONFIG_H diff --git a/include/rosa/config/version.h b/include/rosa/config/version.h index 3583d92..502b41f 100644 --- a/include/rosa/config/version.h +++ b/include/rosa/config/version.h @@ -1,37 +1,43 @@ //===-- rosa/config/version.h -----------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/config/version.h /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Version information about the build of the library. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CONFIG_VERSION_H #define ROSA_CONFIG_VERSION_H #include "rosa/config/rosa_config.h" #include // NOLINT namespace rosa { /// Returns a string containing the name of the library followed by its version. std::string library_string(void); /// Returns a string containing the version number of the library. std::string version(void); /// Returns a multi-line string containing verbose information on the library. std::string verbose_version(void); } // End namespace rosa #endif // ROSA_CONFIG_VERSION_H diff --git a/include/rosa/core/AbstractAgent.hpp b/include/rosa/core/AbstractAgent.hpp index bbb1a52..8396c37 100644 --- a/include/rosa/core/AbstractAgent.hpp +++ b/include/rosa/core/AbstractAgent.hpp @@ -1,150 +1,156 @@ //===-- rosa/core/AbstractAgent.hpp -----------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/core/AbstractAgent.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Declaration of an abstract interface for *Agents*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_ABSTRACTAGENT_HPP #define ROSA_CORE_ABSTRACTAGENT_HPP #include "rosa/core/Message.hpp" #include "rosa/core/forward_declarations.h" #include "rosa/support/debug.hpp" #include namespace rosa { /// Abstract class declaring an interface for *Agents*. /// /// \tparam Ref type of the derived class implementing \c rosa::AbstractAgent /// for referencing \p this object /// /// \note \p Ref is reference for \c rosa::AbstractAgent, whose actual value /// must be a class derived from \c rosa::AbstractAgent. /// /// \note It can be statically checked if \p Ref is derived from /// \c rosa::AbstractAgent, but the static assertion cannot be defined /// directly in the class body. That is because a class \c C derived from /// \c rosa::AbstractAgent is not complete when the static assertion in the /// definition of \c rosa::AbstractAgent would be evaluated. Thus, the static /// assertion is placed in the constructor of \c rosa::AbstractAgent. template class AbstractAgent { protected: /// Creates a new instance of \c rosa::AbstractAgent. /// /// \note The constructor is protected, thus restricting class instantiation /// for derived classes only. /// /// \pre \p Ref is derived from \c rosa::AbstractAgent:\code /// std::is_base_of, Ref>::value /// \endcode AbstractAgent(void) noexcept; public: /// Destroys \p this object. virtual ~AbstractAgent(void) = default; /// Tells if \p this object is in a valid state. /// /// \return if \p this object is in a valid state virtual operator bool(void) const noexcept = 0; /// Tells if a given reference refers to \p this object. /// /// \param R reference to another object /// /// \return if \p R refers to \p this object virtual bool operator==(const Ref &R) const noexcept = 0; /// Compares \p this object to a given reference. /// /// The operator can be used to sort references. Standard containers storing /// entries in an ordered way use \c std::less for comparing entries, which /// utilizes this operator. /// /// \param R reference to another object /// /// \return if \p this object compares less to \p R virtual bool operator<(const Ref &R) const noexcept = 0; /// Returns a reference to \p this object. /// /// \return a reference to \p this object virtual Ref self(void) noexcept = 0; /// Sends a \c rosa::message_t instance to \p this object. /// /// \param M message to send /// /// \pre \p this object is in a valid state:\code /// bool(*this) /// \endcode virtual void sendMessage(message_t &&M) noexcept = 0; /// Sends a message -- created from given constant lvalue references -- to /// \p this object. /// /// \note The message must consists of at least one value. /// /// \tparam Type type of the first mandatory value /// \tparam Types types of any further values /// /// \param T the first value to include in the message /// \param Ts optional further values to include in the message /// /// \pre \p this object is in a valid state:\code /// bool(*this) /// \endcode template void send(const Type &T, const Types &... Ts) noexcept; /// Sends a message -- created from given rvalue references -- to \p this /// object. /// /// \note The message must consists of at least one value. /// /// \tparam Type type of the first mandatory value /// \tparam Types types of any further values /// /// \param T the first value to include in the message /// \param Ts optional further values to include in the message /// /// \pre \p this object is in a valid state:\code /// bool(*this) /// \endcode template void send(Type &&T, Types &&... Ts) noexcept; }; template AbstractAgent::AbstractAgent(void) noexcept { STATIC_ASSERT((std::is_base_of, Ref>::value), "not derived Agent"); // Sanity check. } template template void AbstractAgent::send(const Type &T, const Types &... Ts) noexcept { sendMessage(Message::create(T, Ts...)); } template template void AbstractAgent::send(Type &&T, Types &&... Ts) noexcept { sendMessage(Message::create(std::move(T), std::move(Ts)...)); } } // End namespace rosa #endif // ROSA_CORE_ABSTRACTAGENT_HPP diff --git a/include/rosa/core/Agent.hpp b/include/rosa/core/Agent.hpp index 07524fb..bb32cd5 100644 --- a/include/rosa/core/Agent.hpp +++ b/include/rosa/core/Agent.hpp @@ -1,146 +1,152 @@ //===-- rosa/core/Agent.hpp -------------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/core/Agent.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Declaration of the base \c rosa::Agent class. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_AGENT_HPP #define ROSA_CORE_AGENT_HPP #include "rosa/core/AgentHandle.hpp" #include "rosa/core/MessageHandler.hpp" #include "rosa/core/MessagingSystem.hpp" #include "rosa/core/Unit.h" #include "rosa/support/log.h" namespace rosa { /// Implements an *Agent* that is a special \c rosa::Unit owned by a /// \c rosa::MessagingSystem, capable of handling \c rosa::Message instances /// as \c rosa::MessageHandler, and provides the \c rosa::AbstractAgent /// interface with \c rosa::AgentHandle as reference type. class Agent : public Unit, public MessageHandler, public AbstractAgent { friend class AgentHandle; ///< \c rosa::AgentHandle is our friend. protected: /// A handle for \p this object. const AgentHandle Self; public: /// Creates a new instance by instantiating all the base-classes. /// /// \tparam Fun type of the first mandatory function for handling messages /// \tparam Funs types of any further functions for handling messages /// /// \param Kind kind of the new \c rosa::Unit instance /// \param Id unique identifier of the new \c rosa::Unit instance /// \param Name name of the new \c rosa::Unit instance /// \param S \c rosa::MessagingSystem owning the new instance /// \param F the first mandatory function for handling messages /// \param Fs optional further functions for handling messages template Agent(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, Fun &&F, Funs &&... Fs); /// Destroys \p this object. ~Agent(void); /// Tells if \p this object is in a valid state. /// /// \note A \c rosa::Agent instance is always valid. /// /// \return if \p this object is in a valid state operator bool(void) const noexcept override; /// Tells if a given reference refers to \p this object. /// /// \param H reference to another object /// /// \return if \p H refers to \p this object bool operator==(const AgentHandle &H) const noexcept override; /// Comapres \p this object to a given reference. /// /// The comparison is based on the memory addresses. /// /// \param H reference to another object /// /// \return if \p this object compares less to \p H bool operator<(const AgentHandle &H) const noexcept override; /// Returns a reference to \p this object. /// /// \return a reference to \p this object AgentHandle self(void) noexcept override; /// Sends a given \c rosa::message_t instance to \p this object. /// /// \param M message to send /// /// \note Since a \c rosa::Agent instance is always valid, there is no /// precondition for this function. /// \see \c rosa::AbstractAgent::sendMessage and /// `rosa::Agent::operator bool() const` void sendMessage(message_t &&M) noexcept override; /// Dumps \p this object into a \c std::string for tracing purposes. /// /// \return \c std::string representing the state of \p this object std::string dump(void) const noexcept override; protected: /// Returns a reference to the \c rosa::MessagingSystem owning \p this object. /// /// \return reference of \c rosa::Unit::S MessagingSystem &system(void) const noexcept override; /// Gives the referenced \c rosa::Agent instance for a \c rosa::AgentHandle. /// /// \note Intended for derived classes to be able to inspect /// \c rosa::AgentHandle instances. /// /// \param H \c rosa::AgentHandle to take the referenced \c rosa::Agent from /// /// \return reference to the \c rosa::Agent instance from \p H static Agent &unwrapAgent(const AgentHandle &H) noexcept { return H.A; } /// Gives the owning \c rosa::MessagingSystem of a \c rosa::Agent instance /// for a \c rosa::AgentHandle. /// /// \note Intended for for derived classes to be able to inspect /// \c rosa::AgentHandle instances. /// /// \param H \c rosa::AgentHandle to take the owning /// \c rosa::MessagingSystem from /// /// \return reference to the \c rosa::MessagingSystem owning the /// \c rosa::Agent instance from \p H static MessagingSystem &unwrapSystem(const AgentHandle &H) noexcept { return H.S; } }; template Agent::Agent(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, Fun &&F, Funs &&... Fs) : Unit(Kind, Id, Name, S), MessageHandler(std::move(F), std::move(Fs)...), Self(*this, /*valid*/ true) { LOG_TRACE("Agent is created."); } } // End namespace rosa #endif // ROSA_CORE_AGENT_HPP diff --git a/include/rosa/core/AgentHandle.hpp b/include/rosa/core/AgentHandle.hpp index 0a57953..13f4cd3 100644 --- a/include/rosa/core/AgentHandle.hpp +++ b/include/rosa/core/AgentHandle.hpp @@ -1,313 +1,319 @@ //===-- rosa/core/AgentHandle.hpp -------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/core/AgentHandle.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Declaration of a handle for \c rosa::Agent. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_AGENTHANDLE_HPP #define ROSA_CORE_AGENTHANDLE_HPP #include "rosa/core/AbstractAgent.hpp" namespace rosa { /// Wraps an actual \c rosa::Agent to decouple its public interface. /// \note Such decoupling might be necessary when operating with remote /// *systems*, sometime in the future. class AgentHandle : public AbstractAgent { /// \c rosa::Agent and \c rosa::MessagingSystem are our friends, they may /// inspect the private member fields of the class. ///@{ friend class Agent; friend class MessagingSystem; ///@} /// The wrapped \c rosa::Agent instance. Agent &A; /// The \c rosa::MessagingSystem owning \c A. MessagingSystem &S; /// Creates a new instance without validating the state of the wrapped /// \c rosa::Agent. /// /// \note Used by a \c rosa::Agent instance to create a reference to itself /// during construction, when its state is not valid yet. /// /// \param A \c rosa::Agent to wrap /// /// \note There a second argument, which is ignored, that is only present to /// separate this constructor from the public constructor taking only a /// \c rosa::Agent to wrap. AgentHandle(Agent &A, bool) noexcept; public: /// Creates a new instance validating the state of the wrapped \p rosa::Agent. /// /// \note The wrapped \c rosa::Agent must be in a valid state to instantiate /// \c rosa::AgentHandle with this constructor. /// /// \param A \c rosa::Agent to wrap /// /// \pre \p A is registered in its owning *system*:\code /// A.system().isUnitRegistered(A) /// \endcode AgentHandle(Agent &A); /// Destroys \p this object. /// /// The destructor has nothing to take care of. ~AgentHandle(void) = default; /// Tells if the wrapped \c rosa::Agent is in a valid state. /// /// \note A \c rosa::AgentHandler belongs to a \c rosa::MessagingSystem. /// Working with a \c rosa::AgentHandler whose originating /// \c rosa::MessagingSystem has already been destroyed results in *undefined* /// behavior. /// /// \return if the wrapped \c rosa::Agent is in a valid state operator bool(void) const noexcept override; /// Tells if another \c rosa::AgentHandle wraps the same \c rosa::Agent as /// \p this object. /// /// \param H \c rosa::AgentHandle whose wrapped \c rosa::Agent to check /// /// \return if \p H wraps \c A like \p this object bool operator==(const AgentHandle &H) const noexcept override; /// Compares \p this object to another \c rosa::AgentHandle instance. /// /// The comparison is based on the memory addresses of the wrapped /// \c rosa::Agent instances. /// /// \param H \c rosa::AgentHandle to compare to /// /// \return if \p this object a \c rosa::Agent instance whose address is less /// than that of \p H bool operator<(const AgentHandle &H) const noexcept override; /// Returns a reference to the wrapped \c rosa::Agent. /// /// \return a reference to \c A AgentHandle self(void) noexcept override; /// Sends a given \c rosa::message_t instance to the wrapped \c rosa::Agent. /// /// \param M message to send /// /// \pre The wrapped \c rosa::Agent instance is in a valid state:\code /// bool(*this) /// \endcode void sendMessage(message_t &&M) noexcept override; }; /// Template specialization for optionally storing \c rosa::AgentHandle /// instances. /// /// \ingroup Optional /// /// Due to \c rosa::AgentHandle not supporting copying and moving of instances, /// the member functions in this class fall back to destroying the old stored /// object and creating a new one whenever the stored value is to be changed. template <> class Optional { public: using Type = AgentHandle; /// Creates an instance without value. /// /// \note Use it with its default parameter. Optional(const none_t & = none) : Valid(false) {} /// Creates a valid instance with value. /// /// \param X value to store in the object Optional(AgentHandle X) : Valid(false) { cr(std::move(X)); } /// Creates an instance as a copy of another one. /// /// \param Other the instance whose state to copy Optional(const Optional &Other) : Valid(false) { if (Other.Valid) { cr(Other.Value); } } /// Creates an instance as a copy of another one. /// /// \param Other the instance whose state to obtain Optional(Optional &&Other) noexcept : Valid(false) { if (Other.Valid) { cr(std::move(Other.Value)); } } /// Destroys \p this object. ~Optional(void) { destroy(); } /// Updates \p this object by copying the state of another one. /// /// \param Other the instance whose state to copy /// /// \return reference of the updated instance Optional &operator=(const Optional &Other) { if (Valid) { destroy(); } if (Other.Valid) { cr(Other.Value); } return *this; } /// Updates \p this object by copying the state of another one. /// /// \param Other the instance whose state to obtain /// /// \return reference of the updated instance Optional &operator=(Optional &&Other) noexcept { if (Valid) { destroy(); } if (Other.Valid) { cr(std::move(Other.Value)); } return *this; } /// Checks whether \p this object contains a value. /// /// \return if \p this object contains a value explicit operator bool(void) const { return Valid; } /// Checks whether \p this object does not contain a value. /// /// \return if \p this object does not contain a value bool operator!(void)const { return !Valid; } /// Returns the value stored in \p this object. /// /// \return reference of the stored value /// /// \pre \p this object contains a value AgentHandle &operator*(void) { ASSERT(Valid); return Value; } /// Returns the value stored in \p this object. /// /// \return reference of the stored value /// /// \pre \p this object contains a value const AgentHandle &operator*(void)const { ASSERT(Valid); return Value; } /// Returns the value stored in \p this object. /// /// \return pointer to the stored value /// /// \pre \p this object contains a value const AgentHandle *operator->(void)const { ASSERT(Valid); return &Value; } /// Returns the value stored in \p this object. /// /// \return pointer of the stored value /// /// \pre \p this object contains a value AgentHandle *operator->(void) { ASSERT(Valid); return &Value; } /// Returns the value stored in \p this object. /// /// \return reference of the stored value /// /// \pre \p this object contains a value AgentHandle &value(void) { ASSERT(Valid); return Value; } /// Returns the value stored in \p this object. /// /// \return reference of the stored value /// /// \pre \p this object contains a value const AgentHandle &value(void) const { ASSERT(Valid); return Value; } /// Returns the stored value or a default. /// /// If \p this object contains a value, then the stored value is returned. A /// given default value is returned otherwise. /// /// \param DefaultValue the value to return if \p this object does not contain /// a value /// /// \return reference to either the stored value or \p DefaultValue if \p this /// object does not contain a value const AgentHandle &valueOr(const AgentHandle &DefaultValue) const { return Valid ? Value : DefaultValue; } private: /// Deallocates the stored value if any. void destroy(void) { if (Valid) { Value.~AgentHandle(); Valid = false; } } /// Updates the state of \p this object by copying a value into it. /// /// \tparam V type of \p X /// /// \param X value to copy /// /// \pre \p this object does not contain a value template void cr(V &&X) { ASSERT(!Valid); Valid = true; new (&Value) AgentHandle(std::forward(X)); } /// Denotes if \p this object contains a value. bool Valid; /// Holds the stored value if any. union { AgentHandle Value; ///< The stored value. }; }; } // End namespace rosa #endif // ROSA_CORE_AGENTHANDLE_HPP diff --git a/include/rosa/core/Invoker.hpp b/include/rosa/core/Invoker.hpp index 9f35ca3..63ab835 100644 --- a/include/rosa/core/Invoker.hpp +++ b/include/rosa/core/Invoker.hpp @@ -1,258 +1,264 @@ //===-- rosa/core/Invoker.hpp -----------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/core/Invoker.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Facilities for providing actual arguments for functions as /// \c rosa::Messageobjects. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_INVOKER_HPP #define ROSA_CORE_INVOKER_HPP #include "rosa/core/MessageMatcher.hpp" #include "rosa/support/log.h" #include "rosa/support/sequence.hpp" #include #include namespace rosa { /// Wraps a function and provides a simple interface to invoke the stored /// function by passing actual arguments as a \c rosa::Message object. /// /// \note A \c rosa::Invoker instance is supposed to be owned by a /// \c rosa::MessageHandler instance, and not being used directly from user /// code. class Invoker { protected: /// Creates an instance. /// /// \note Protected constructor restricts instantiation to derived classes. Invoker(void) noexcept; public: /// Destroys \p this object. virtual ~Invoker(void); /// Possible results of an invocation. enum class Result { NoMatch, ///< The wrapped function could not be invoked Invoked ///< The wrapped function has been invoked }; /// Type alias for a smart-pointer for \c rosa::Invoker. using invoker_t = std::unique_ptr; /// Type alias for \c rosa::Invoker::Result. using result_t = Result; /// Tells if a \c rosa::Message object can be used to invoke the function /// wrapped in \p this object. /// /// \param Msg \c rosa::Message to check /// /// \return whether \p Msg can be used to invoke the wrapped function virtual bool match(const Message &Msg) const noexcept = 0; /// Tries to invoke the wrapped function with a \c rosa::Message object. /// /// The wrapped function is invoked if the actual \c rosa::Message object can /// be used to invoke it. /// /// \param Msg \c rosa::Message to try to invoke the wrapped function with /// /// \return whether the wrapped function could be invoked with \p Msg virtual result_t operator()(const Message &Msg) const noexcept = 0; /// Instantiates an implementation of \c rosa::Invoker with the given /// function. /// /// \note As there is no empty \c rosa::Message, no \c rosa::Invoker wraps a /// function without any argument. /// /// \todo Enforce F does not potentially throw exception. /// /// \tparam T type of the first mandatory argument /// \tparam Ts types of any further arguments /// /// \param F function to wrap /// /// \return new \c rosa::Invoker::invoker_t object created from the given /// function template static invoker_t wrap(std::function &&F) noexcept; /// Convenience template alias for casting callable stuff to function objects /// for wrapping. /// /// \tparam Ts types of arguments /// /// \todo Should make it possible to avoid using an explicit conversion for /// the arguments of wrap. template using F = std::function; /// Convenience template for preparing non-static member functions into /// function objects for wrapping. /// /// \tparam C type whose non-static member the function is /// \tparam Ts types of arguments /// /// \see \c THISMEMBER template static inline F M(C *O, void (C::*Fun)(Ts...) noexcept) noexcept; }; /// Convenience preprocessor macro for the typical use of \c rosa::Invoker::M. /// It can be used inside a class to turn a non-static member function into a /// function object capturing this pointer, so using the actual object when /// handling a \c rosa::Message. /// /// \param FUN the non-static member function to wrap /// /// \note Inside the class \c MyClass, use\code /// THISMEMBER(fun) /// \endcode instead of\code /// Invoker::M(this, &MyClass::fun) /// \endcode #define THISMEMBER(FUN) \ Invoker::M(this, &std::decay::type::FUN) /// Nested namespace with implementation of \c rosa::Invoker and helper /// templates, consider it private. namespace { /// \defgroup InvokerImpl Implementation for rosa::Invoker /// /// Implements the \c rosa::Invoker interface for functions with different /// signatures. /// ///@{ /// Declaration of \c rosa::InvokerImpl implementing \c rosa::Invoker. /// /// \tparam Fun function to wrap template class InvokerImpl; /// Implementation of \c rosa::InvokerImpl for \c std::function. /// /// \tparam T type of the first mandatory argument /// \tparam Ts types of further arguments /// /// \note As there is no empty \c rosa::Message, no \c rosa::Invoker wraps a /// function without any argument, i.e., no /// \c std::function. template class InvokerImpl> final : public Invoker { /// Type alias for the stored function. using function_t = std::function; /// Type alias for correctly typed argument-tuples as obtained from /// \c rosa::Message. using args_t = std::tuple; /// Alias for \c rosa::MessageMatcher for the arguments of the stored /// function. using Matcher = MsgMatcher; /// The wrapped function. const function_t F; /// Invokes \c InvokerImpl::F by unpacking arguments from a \c std::tuple with /// the help of the actual template arguments. /// /// \tparam S sequence of numbers indexing \c std::tuple for arguments /// /// \param Args arguments to invoke \c InvokerImpl::F with /// /// \pre the length of \p S and size of \p Args are matching:\code /// sizeof...(S) == std::tuple_size::value /// \endcode template inline void invokeFunction(Seq, const args_t &Args) const noexcept; public: /// Creates an instance. /// /// \param F function to wrap /// /// \pre \p F is valid:\code /// bool(F) /// \endcode InvokerImpl(function_t &&F) noexcept : F(F) { ASSERT(bool(F)); // Sanity check. } /// Destroys \p this object. ~InvokerImpl(void) = default; /// Tells if a \c rosa::Message object can be used to invoke the function /// wrapped in \p this object. /// /// \param Msg \c rosa::Message to check /// /// \return whether \p Msg can be used to invoke the wrapped function bool match(const Message &Msg) const noexcept override { return Matcher::doesStronglyMatch(Msg); - }; + } /// Tries to invoke the wrapped function with a \c rosa::Message object. /// /// The wrapped function is invoked if the actual \c rosa::Message object can /// be used to invoke it. /// /// \param Msg \c rosa::Message to try to invoke the wrapped function with /// /// \return whether the wrapped function could be invoked with \p Msg result_t operator()(const Message &Msg) const noexcept override { if (match(Msg)) { LOG_TRACE("Invoking with matching arguments"); - invokeFunction(typename GenSeq::Type(), - Matcher::extractedValues(Msg)); + invokeFunction(seq_t(), Matcher::extractedValues(Msg)); return result_t::Invoked; } else { LOG_TRACE("Tried to invoke with non-matching arguments"); return result_t::NoMatch; } } }; template template void InvokerImpl>::invokeFunction( Seq, const args_t &Args) const noexcept { - ASSERT(sizeof...(S) == std::tuple_size::value); // Sanity check. + STATIC_ASSERT(sizeof...(S) == std::tuple_size::value, + "wrong number of type parameters"); F(std::get(Args)...); } ///@} } // End namespace template Invoker::invoker_t Invoker::wrap(std::function &&F) noexcept { return std::unique_ptr( new InvokerImpl>(std::move(F))); } template Invoker::F Invoker::M(C *O, void (C::*Fun)(Ts...) noexcept) noexcept { return [ O, Fun ](Ts... Vs) noexcept->void { (O->*Fun)(Vs...); }; } } // End namespace rosa #endif // ROSA_CORE_INVOKER_HPP diff --git a/include/rosa/core/Message.hpp b/include/rosa/core/Message.hpp index ce9e6ec..4c7e788 100644 --- a/include/rosa/core/Message.hpp +++ b/include/rosa/core/Message.hpp @@ -1,258 +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 +/// \date 2017-2019 /// /// \brief Declaration of \c rosa::Message base-class. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_MESSAGE_HPP #define ROSA_CORE_MESSAGE_HPP #include "rosa/support/log.h" #include "rosa/support/tokenized_storages.hpp" #include "rosa/core/forward_declarations.h" namespace rosa { /// *Message* interface. /// /// The interface provides means to check the type of the stored values, but /// actual data is to be managed by derived implementations. /// /// A \c rosa::Message instance is an immutable data object that obtains its /// data upon creation and provides only constant references for the stored /// values. /// /// \note Any reference obtained from a \c rosa::Message instance remains valid /// only as long as the owning \c rosa::Message object is not destroyed. /// /// \todo Some member functions of \c rosa::Message duplicate member functions /// of \c rosa::TokenizedStorage, which cannot be easily factored out into a /// common base class due to eventual diamond inheritance issues in derived /// classes. Could this duplication be avoided? class Message { protected: /// Creates a new instance. /// /// \note No implementation for empty list. /// /// \tparam Type type of the mandatory first argument /// \tparam Types types of any further arguments /// /// \note the actual arguments are ignored by the constructor it is only /// their type that matters. The actual values are supposed to be handled by /// any implementation derived from \c rosa::Message. /// /// \pre \p Type and \p Types are all built-in types and the number of stored /// values does not exceed \c rosa::token::MaxTokenizableListSize. template Message(const Type &, const Types &...) noexcept; /// No copying and moving of \c rosa::Message instances. ///@{ Message(const Message &) = delete; Message(Message &&) = delete; Message &operator=(const Message &) = delete; Message &operator=(Message &&) = delete; ///@} public: /// Creates a \c rosa::message_t object from constant lvalue references. /// /// \tparam Type type of the mandatory first argument /// \tparam Types types of any further arguments /// /// \param T the first value to include in the \c rosa::Message /// \param Ts optional further values to include in the \c rosa::Message /// /// \return new \c rosa::message_t object created from the given arguments template static message_t create(const Type &T, const Types &... Ts) noexcept; /// Creates a \c rosa::message_t object from rvalue references. /// /// \tparam Type type of the mandatory first argument /// \tparam Types types of any further arguments /// /// \param T the first value to include in the \c rosa::Message /// \param Ts optional further values to include in the \c rosa::Message /// /// \return new \c rosa::message_t object created from the given arguments template static message_t create(Type &&T, Types &&... Ts) noexcept; /// Represents the types of the values stored in \p this object. /// /// A valid, non-empty \c rosa::Token representing the types of the values /// stored in \p this object. const Token T; /// The number of values stored in \p this object. /// /// That is the number of types encoded in \c rosa::Message::T. - const size_t Size; + const token_size_t Size; /// Destroys \p this object. virtual ~Message(void); /// Tells if the value stored at a given index is of a given type. /// /// \note Any \c rosa::AtomConstant is encoded in \c rosa::Token as /// the \c rosa::AtomValue wrapped into it. /// /// \tparam Type type to match against /// /// \param Pos index the type of the value at is to be matched against \p Type /// /// \return if the value at index \p Pos of type \p Type /// /// \pre \p Pos is a valid index:\code /// Pos < Size /// \endcode - template bool isTypeAt(const size_t Pos) const noexcept; + template bool isTypeAt(const token_size_t Pos) const noexcept; /// Gives a constant reference of a value of a given type stored at a given /// index. /// /// \tparam Type type to give a reference of /// /// \param Pos index to set the reference for /// /// \return constant reference of \p Type for the value stored at index \p Pos /// /// \pre \p Pos is a valid index and the value at index \p Pos is of type /// \p Type: /// \code /// Pos < Size && isTypeAt(Pos) /// \endcode - template const Type &valueAt(const size_t Pos) const noexcept; + template + const Type &valueAt(const token_size_t Pos) const noexcept; protected: /// Provides an untyped pointer for the value at a given index. /// /// \param Pos index to take a pointer for /// /// \return untyped pointer for the value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code /// Pos < Size /// \endcode - virtual const void *pointerTo(const size_t Pos) const noexcept = 0; + virtual const void *pointerTo(const token_size_t Pos) const noexcept = 0; }; /// Nested namespace with implementation for \c rosa::Message, consider it /// private. namespace { /// Template class for an implementation of \c rosa::Message. /// /// \tparam Types types whose values are to be stored template class LocalMessage; /// Implementation of the template \c rosa::LocalMessage providing facilities /// for storing values as a \c rosa::Message object. /// /// \tparam Type type of the first mandatory value of the \c rosa::Message /// \tparam Types of any further values template class LocalMessage final : public Message, private TokenizedStorage { public: /// Creates an instance from constant lvalue references. /// /// \param T the mandatory first value to store in the \c rosa::Message object /// \param Ts optional further values to store in the \c rosa::Message object LocalMessage(const Type &T, const Types &... Ts) noexcept : Message(T, Ts...), TokenizedStorage(T, Ts...) { ASSERT(this->T == this->ST && Size == this->size()); // Sanity check. } /// Creates an instance from rvalue references. /// /// \param T the mandatory first value to store in the \c rosa::Message object /// \param Ts optional further values to store in the \c rosa::Message object LocalMessage(Type &&T, Types &&... Ts) noexcept : Message(T, Ts...), TokenizedStorage(std::move(T), std::move(Ts)...) { ASSERT(this->T == this->ST && Size == this->size()); // Sanity check. } /// Provides an untyped pointer for the constant value stored at a position. /// /// \param Pos the index of the value to return an untyped pointer for /// /// \return untyped pointer for the constant value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code /// Pos < Size /// \endcode - const void *pointerTo(const size_t Pos) const noexcept override { + const void *pointerTo(const token_size_t Pos) const noexcept override { ASSERT(Pos < Size); return TokenizedStorage::pointerTo(Pos); } /// Aborts the program! /// /// Since \c rosa::Message instances are supposed to be immutable, the /// non-const inherited function is overridden so that it aborts execution. - void *pointerTo(const size_t) noexcept override { + void *pointerTo(const token_size_t) noexcept override { ROSA_CRITICAL("Unallowed operation of rosa::LocalMessage"); } }; } // End namespace template Message::Message(const Type &, const Types &...) noexcept : T(TypeToken::type, typename std::decay::type...>::Value), Size(lengthOfToken(T)) { ASSERT(validToken(T) && lengthOfToken(T) == (1 + sizeof...(Types))); // Sanity check. - LOG_TRACE("Creating Message with Token(" + to_string(T) + ")"); + LOG_TRACE("Creating Message with Token(" + std::to_string(T) + ")"); } /// \note The implementation instantiates a private local template class /// \c LocalMessage. template message_t Message::create(const Type &T, const Types &... Ts) noexcept { return message_t(new LocalMessage(T, Ts...)); } /// \note The implementation instantiates a private local template class /// \c LocalMessage. template message_t Message::create(Type &&T, Types &&... Ts) noexcept { return message_t( new LocalMessage(std::move(T), std::move(Ts)...)); } template -bool Message::isTypeAt(const size_t Pos) const noexcept { +bool Message::isTypeAt(const token_size_t Pos) const noexcept { ASSERT(Pos < Size); Token TT = T; dropNOfToken(TT, Pos); return isHeadOfTokenTheSameType(TT); } template -const Type &Message::valueAt(const size_t Pos) const noexcept { +const Type &Message::valueAt(const token_size_t Pos) const noexcept { ASSERT(Pos < Size && isTypeAt(Pos)); return *static_cast(pointerTo(Pos)); } } // End namespace rosa #endif // ROSA_CORE_MESSAGE_HPP diff --git a/include/rosa/core/MessageHandler.hpp b/include/rosa/core/MessageHandler.hpp index 3a19c57..a770b32 100644 --- a/include/rosa/core/MessageHandler.hpp +++ b/include/rosa/core/MessageHandler.hpp @@ -1,169 +1,175 @@ //===-- rosa/core/MessageHandler.hpp ----------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/core/MessageHandler.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Facility for combining \c rosa::Invoker instances and applying /// \c rosa::Message intances to them. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_MESSAGEHANDLER_HPP #define ROSA_CORE_MESSAGEHANDLER_HPP #include "rosa/core/Invoker.hpp" #include "rosa/support/log.h" #include namespace rosa { /// Handles \c rosa::Message instances. /// /// A \c rosa::MessageHandler stores \c rosa::Invoker instances and tries to /// apply \c rosa::Message objects to them in the order of definition.The first /// matching \c rosa::Invoker instance is invoked with the \c rosa::Message /// object, after which handling of that \c rosa::Message object is completed. /// /// For example, consider the following snippet: \code /// rosa::MessageHandler { /// rosa::Invoker::F([](uint8_t) { /* ... */ }), /// rosa::Invoker::F([](uint8_t) { /* Never invoked */ }) /// }; /// \endcode /// Applying a \c rosa::Message with \c rosa::TypeList invokes the /// first function, and the second function would never be invoked because any /// matching \c rosa::Message object had already been handled by the first one. class MessageHandler { /// Type alias to bring \c rosa::Invoker::invoker_t to the local scope. using invoker_t = Invoker::invoker_t; /// Type alias for a \c std::vector storing \c rosa::Invoker instances. using invokers_t = std::vector; /// Stores \c rosa::Invoker instances. const invokers_t Invokers; /// Creates a container with \c rosa::Invoker instances from functions. /// /// \tparam Fun type of the mandatory first function /// \tparam Funs types of further functions /// /// \param F the mandatory first function /// \param Fs optional further functions /// /// \return \c rosa::MessageHandler::invokers_t object storing /// \c rosa::Invoker instances created from the \p F and \p Fs... template static inline invokers_t createInvokers(Fun &&F, Funs &&... Fs) noexcept; /// Updates an \c rosa::MessageHandler::invokers_t object with a new /// \c rosa::Invoker instance and handles further functions recursively. /// /// \tparam Fun type of the first function /// \tparam Funs types of further functions /// /// \param I \c rosa::MessageHandler::invokers_t to update /// \param Pos index at which to store the new \c rosa::Invoker instance /// \param F function to wrap and store into \p I at index \p Pos /// \param Fs further functions to handle later /// /// \pre \p Pos is a valid index:\code /// Pos < I.size() /// \endcode template static inline void wrapFun(invokers_t &I, const size_t Pos, Fun &&F, Funs &&... Fs) noexcept; /// Terminal case for the template \c rosa::MessageHandler::wrapFun. /// /// \param I \c rosa::MessageHandler::invokers_t which is now complete /// \param Pos size of \p I /// /// \pre \p Pos is the size of \p I:\code /// Pos == I.size(); /// \endcode static inline void wrapFun(invokers_t &I, const size_t Pos) noexcept; public: /// Creates an instance. /// /// The constructor stores the given functions into the new /// \c rosa::MessageHandler instance. /// /// \tparam Fun type of the mandatory first function /// \tparam Funs types of further functions /// /// \param F the first function to store /// \param Fs optional further functions to store template MessageHandler(Fun &&F, Funs &&... Fs) noexcept; /// Destroys \p this object. virtual ~MessageHandler(void); /// Tells if a \c rosa::Message object can be handled by \p this object. /// /// \param Msg \c rosa::Message to check /// /// \return whether \p this object stores a \c rosa::Invoker instance that can /// handle \p Msg bool canHandle(const Message &Msg) const noexcept; /// Applies a \c rosa::Message object to the first stored \c rosa::Invoker /// that can handle it, and tells if there was any. /// /// \note This operator finds the first applicable \c rosa::Invoker and /// invokes it with the given \c rosa::Message object, while the member /// function \c rosa::MessageHandler::canHandle only checks if there is any /// \c rosa::Invoker that can be invoked with a given \c rosa::Message object. /// /// \param Msg \c rosa::Message to use in invoking a matching \c rosa::Invoker /// /// \return whether there was a matching \c rosa::Invoker found and invoked /// with \p Msg bool operator()(const Message &Msg) const noexcept; }; template MessageHandler::MessageHandler(Fun &&F, Funs &&... Fs) noexcept : Invokers(createInvokers(std::move(F), std::move(Fs)...)) { LOG_TRACE("MessageHandler is created"); } template MessageHandler::invokers_t MessageHandler::createInvokers(Fun &&F, Funs &&... Fs) noexcept { // Create a container with the required size and get all the functions // wrapped. invokers_t I(1 + sizeof...(Funs)); wrapFun(I, 0, std::move(F), std::move(Fs)...); return I; } template void MessageHandler::wrapFun(invokers_t &I, const size_t Pos, Fun &&F, Funs &&... Fs) noexcept { ASSERT(Pos < I.size()); // Sanity check. // Wrap the current function and continue with the rest. I[Pos] = Invoker::wrap(std::move(F)); wrapFun(I, Pos + 1, std::move(Fs)...); } void MessageHandler::wrapFun(invokers_t &I, const size_t Pos) noexcept { ASSERT(Pos == I.size()); // Sanity check. // Nothing to do here. } } // End namespace rosa #endif // ROSA_CORE_MESSAGEHANDLER_HPP diff --git a/include/rosa/core/MessageMatcher.hpp b/include/rosa/core/MessageMatcher.hpp index 71bd69f..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 +/// \date 2017-2019 /// /// \brief Facilities for checking and matching types of values stored in /// \c rosa::Message instances. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_MESSAGEMATCHER_HPP #define ROSA_CORE_MESSAGEMATCHER_HPP #include "rosa/core/Message.hpp" #include namespace rosa { /// Provides features to type-check a \c rosa::Message instance and extract /// stored values from it into an \c std::tuple instance with matching type /// arguments. /// /// \tparam List \c rosa::TypeList to check the stored values against template struct MessageMatcher; /// Definition of \c rosa::MessageMatcher for non-empty lists of types, like /// \c rosa::Message itself. /// /// \tparam Type first mandatory type /// \tparam Types any further types template struct MessageMatcher> { /// \c rosa::Token associated to the given \c rosa::TypeList. static constexpr Token T = TypeToken::Value; /// Tells if the values stored in a \c rosa::Message instance are matching /// types given as \c rosa::TypeList, considering /// \c rosa::AtomConstant instead of \c rosa::AtomValue. /// /// \param Msg \c rosa::Message to match /// /// \return whether the types of values stored in \p Msg matches /// \c rosa::TypeList static inline bool doesStronglyMatch(const Message &Msg) noexcept; /// Gives a \c std::tuple with references to the values stored in a /// type-matching instance of \c rosa::Message. /// /// \param Msg \c rosa::Message to extract values from /// /// \return \c std::tuple with references to the values stored in \p Msg /// /// \pre Types of the values stored in \p Msg matches /// \c rosa::TypeList:\code /// doesStronglyMatch(Msg) /// \endcode static inline std::tuple extractedValues(const Message &Msg) noexcept; }; /// Turns a list of types into a \c rosa::TypeList for \c rosa::MessageMatcher. template using MsgMatcher = MessageMatcher>; /// Nested namespace with implementation for features of /// \c rosa::MessageMatcher, consider it private. namespace { /// \defgroup MessageMatcherImpl Implementation for rosa::MessageMatcher /// /// An implementation of type-checking and value extraction for /// \c rosa::MessageMatcher. /// ///@{ /// Template declaration of \c MessageMatcherImpl. /// /// \tparam List \c rosa::TypeList to match against template struct MessageMatcherImpl; /// Specialization for \c rosa::EmptyTypeList. template <> struct MessageMatcherImpl { static bool doesStronglyMatchFrom(const Message &Msg, - const size_t Pos) noexcept { + 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 size_t Pos) noexcept { + const token_size_t Pos) noexcept { // It is valid to extract an empty list only if we reached the end of // stored values. ASSERT(doesStronglyMatchFrom(Msg, Pos)); return std::tie(); } }; /// Specialization for \c rosa::AtomValue in the head. template struct MessageMatcherImpl, Ts...>> { static bool doesHeadStronglyMatchAt(const Message &Msg, - const size_t Pos) noexcept { + const token_size_t Pos) noexcept { // Matching a \c rosa::AtomConstant in the head if there is a type stored at // \p Pos, the stored type is \c rosa::AtomValue, and the corresponding // value matches the \c rosa::AtomValue \p V. return Pos < Msg.Size && Msg.isTypeAt(Pos) && Msg.valueAt(Pos) == V; } static bool doesStronglyMatchFrom(const Message &Msg, - const size_t Pos) noexcept { + const token_size_t Pos) noexcept { // Matching a non-empty list if the head is matching and the rest of the // list is matching. return doesHeadStronglyMatchAt(Msg, Pos) && MessageMatcherImpl>::doesStronglyMatchFrom(Msg, Pos + 1); } static std::tuple &, const Ts &...> - extractedValuesFrom(const Message &Msg, const size_t Pos) noexcept { + extractedValuesFrom(const Message &Msg, const token_size_t Pos) noexcept { // Extracting for a non-empty list with a matching \c rosa::AtomConstant in // the head by getting the encoded \c rosa::AtomConstant and concatenating // it with values extracted for the rest of the list. ASSERT(doesHeadStronglyMatchAt(Msg, Pos)); return std::tuple_cat( std::tie(AtomConstant::Value), MessageMatcherImpl>::extractedValuesFrom(Msg, Pos + 1)); } }; /// Definition for the general case when a regular built-in type (not a /// \c rosa::AtomConstant) is in the head. template struct MessageMatcherImpl> { static bool doesHeadStronglyMatchAt(const Message &Msg, - const size_t Pos) noexcept { + const token_size_t Pos) noexcept { // Matching the head if there is a type stored at \p Pos, and the stored // type is \p T. return Pos < Msg.Size && Msg.isTypeAt(Pos); } static bool doesStronglyMatchFrom(const Message &Msg, - const size_t Pos) noexcept { + const token_size_t Pos) noexcept { // Matching a non-empty list if the head is matching and the rest of the // list is matching. return doesHeadStronglyMatchAt(Msg, Pos) && MessageMatcherImpl>::doesStronglyMatchFrom(Msg, Pos + 1); } static std::tuple - extractedValuesFrom(const Message &Msg, const size_t Pos) noexcept { + extractedValuesFrom(const Message &Msg, const token_size_t Pos) noexcept { // Extracting for a non-empty list with a matching head by getting the // value for the head and concatenating it with values extracted for the // rest of the list. ASSERT(doesHeadStronglyMatchAt(Msg, Pos)); return std::tuple_cat( std::tie(Msg.valueAt(Pos)), MessageMatcherImpl>::extractedValuesFrom(Msg, Pos + 1)); } }; ///@} } // End namespace template bool MessageMatcher>::doesStronglyMatch( const Message &Msg) noexcept { // \note Fail quick on \c rosa::MessageMatcher::T, then match against list // with squashed integers the way \c rosa::Token is generated. return T == Msg.T && MessageMatcherImpl>::Type>::doesStronglyMatchFrom(Msg, 0); } template std::tuple MessageMatcher>::extractedValues( const Message &Msg) noexcept { ASSERT(doesStronglyMatch(Msg)); // \note Match against a list with squashed integers as \c rosa::Token is // generated. return MessageMatcherImpl>::Type>::extractedValuesFrom(Msg, 0); } } // End namespace rosa #endif // ROSA_CORE_MESSAGEMATCHER_HPP diff --git a/include/rosa/core/MessagingSystem.hpp b/include/rosa/core/MessagingSystem.hpp index 7e9b3cf..6ddd4cd 100644 --- a/include/rosa/core/MessagingSystem.hpp +++ b/include/rosa/core/MessagingSystem.hpp @@ -1,201 +1,207 @@ //===-- rosa/core/MessagingSystem.hpp ---------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/core/MessagingSystem.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Declaration of an interface extending \c rosa::System with messaging. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_MESSAGINGSYSTEM_HPP #define ROSA_CORE_MESSAGINGSYSTEM_HPP #include "rosa/core/AgentHandle.hpp" #include "rosa/core/System.hpp" #include "rosa/support/atom.hpp" namespace rosa { /// Extends the \c rosa::System interface with features to create \c rosa::Agent /// instancess and register \c rosa::Message objects for them. class MessagingSystem : public System { friend class AgentHandle; ///< \c rosa::AgentHandle is our friend. public: /// Returns an object implementing the \c rosa::MessagingSystem interface. /// /// \param Name name of the new instance /// /// \return \c std::unique_ptr for the new instance of /// \c rosa::MessagingSystem static std::unique_ptr createSystem(const std::string &Name) noexcept; private: /// Kind for categorizing \c rosa::Unit instances as *agents*. static constexpr AtomValue AgentKind = atom("agent"); protected: /// Creates a new instance. /// /// \note Protected constructor restricts instantiation for subclasses. MessagingSystem(void) noexcept = default; protected: /// Creates a \c rosa::Agent instance owned by \p this object and returns a /// \c rosa::AgentHandle for it. /// /// \tparam T type of the actual \c rosa::Agent to instantiate /// \tparam Funs types of the functions to instantiate \c rosa::Agent with /// /// \note \c rosa::Agent requires at least one function for its constructor, /// but derived classes may do not need that. That's the reason of allowing /// zero \p Funs for this template function. /// /// \param Name name of the new \c rosa::Unit instance /// \param Fs functions to instantiate \c rosa::Unit with /// /// \return handle for the new \c rosa::Agent instance /// /// \pre Statically, \p T is a subclass of \c rosa::Agent: /// \code /// std::is_base_of::value /// \endcode template AgentHandle createAgent(const std::string &Name, Funs &&... Fs); /// Unregisters and destroys a \c rosa::Agent referred by a /// \c rosa::AgentHandle. /// /// The function uses \c rosa::System::destroyUnit. /// /// \param H refers to the \c rosa::Agent to destroy /// /// \pre The referred \c rosa::Agent is registered. /// /// \post The referred \c rosa::Agent is not registered and also destroyed. void destroyAgent(const AgentHandle &H) noexcept; /// Gives the referenced \c rosa::Agent instance for a \c rosa::AgentHandle. /// /// \note Intended for derived classes to be able to inspect /// \c rosa::AgentHandle instances. /// /// \param H \c rosa::AgentHandle to take the referenced \c rosa::Agent from /// /// \return reference to the \c rosa::Agent instance from \p H static Agent &unwrapAgent(const AgentHandle &H) noexcept { return H.A; } /// Gives the owning \c rosa::MessagingSystem of a \c rosa::Agent instance /// for a \c rosa::AgentHandle. /// /// \note Intended for for derived classes to be able to inspect /// \c rosa::AgentHandle instances. /// /// \param H \c rosa::AgentHandle to take the owning /// \c rosa::MessagingSystem from /// /// \return reference to the \c rosa::MessagingSystem owning the /// \c rosa::Agent instance from \p H static MessagingSystem &unwrapSystem(const AgentHandle &H) noexcept { return H.S; } public: /// Sends a \c rosa::message_t instance to the \c rosa::Agent instance /// referred by a \c rosa::AgentHandle. /// /// \note If the given \c rosa::Message object cannot be handled by the /// referred \c rosa::Agent instance, the \c rosa::Message object is simply /// ignored. /// /// \param H refers to the \c rosa::Agent instance to send to /// \param M message to send /// /// \pre The referred \c rosa::Agent instance is owned by \p this object and /// also registered: \code /// &unwrapSystem(H) == this && isUnitRegistered(unwrapAgent(H)) /// \endcode virtual void send(const AgentHandle &H, message_t &&M) noexcept = 0; /// Sends a message -- created from given constant lvalue references -- /// to the \c rosa::Agent instance referred by a \c rosa::AgentHandle. /// /// \note If the given \c rosa::Message object cannot be handled by the /// referred \c rosa::Agent instance, the \c rosa::Message object is simply /// ignored. /// /// \note The message must consists of at least one value. /// /// \tparam Type type of the first mandatory value /// \tparam Types types of any further values /// /// \param H refers to the \c rosa::Agent instance to send to /// \param T the first value to include in the message /// \param Ts optional further values to include in the message /// /// \pre The referred \c rosa::Agent instance is owned by \p this object and /// also registered: \code /// &unwrapSystem(H) == this && isUnitRegistered(unwrapAgent(H)) /// \endcode template void send(const AgentHandle &H, const Type &T, const Types &... Ts) noexcept; /// Sends a message -- created from given rvalue references -- /// to the \c rosa::Agent instance referred by a \c rosa::AgentHandle. /// /// \note If the given \c rosa::Message object cannot be handled by the /// referred \c rosa::Agent instance, the \c rosa::Message object is simply /// ignored. /// /// \note The message must consists of at least one value. /// /// \tparam Type type of the first mandatory value /// \tparam Types types of any further values /// /// \param H refers to the \c rosa::Agent instance to send to /// \param T the first value to include in the message /// \param Ts optional further values to include in the message /// /// \pre The referred \c rosa::Agent instance is owned by \p this object and /// also registered: \code /// &unwrapSystem(H) == this && isUnitRegistered(unwrapAgent(H)) /// \endcode template void send(const AgentHandle &H, Type &&T, Types &&... Ts) noexcept; }; template AgentHandle MessagingSystem::createAgent(const std::string &Name, Funs &&... Fs) { STATIC_ASSERT((std::is_base_of::value), "not an Agent"); Agent &A = createUnit([&](const id_t Id, MessagingSystem &S) noexcept { return new T(AgentKind, Id, Name, S, std::move(Fs)...); }); return {A}; } template void MessagingSystem::send(const AgentHandle &H, const Type &T, const Types &... Ts) noexcept { send(H, Message::create(T, Ts...)); } template void MessagingSystem::send(const AgentHandle &H, Type &&T, Types &&... Ts) noexcept { send(H, Message::create(std::move(T), std::move(Ts)...)); } } // End namespace rosa #endif // ROSA_CORE_MESSAGINGSYSTEM_HPP diff --git a/include/rosa/core/System.hpp b/include/rosa/core/System.hpp index b9869bc..77ac39b 100644 --- a/include/rosa/core/System.hpp +++ b/include/rosa/core/System.hpp @@ -1,242 +1,248 @@ //===-- rosa/core/System.hpp ------------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/core/System.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Declaration of *System* interface. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_SYSTEM_HPP #define ROSA_CORE_SYSTEM_HPP #include "rosa/config/config.h" #include "rosa/core/forward_declarations.h" #include "rosa/support/debug.hpp" #include "rosa/support/log.h" #include #include #include namespace rosa { /// Base interface for actual agent-systems. /// /// The class provides facilities to keep track of \c rosa::Unit instances owned /// by a \c rosa::System. /// /// \note Any subclass is supposed to provide thread-safe implementation. /// /// \note The class declares only an interface to avoid trouble with multiple /// inheritance in various subclasses as in derived interfaces and derived /// implementations. /// /// \note Actual implementations are supposed to derive from \c rosa::SystemBase /// implenenting a base feature-set. class System { public: /// Signature of creator functions for \c rosa::Unit instances. /// /// \note The function is to be \c noexcept. /// /// \tparam T type derived from \c rosa::Unit /// \tparam S type derived from \c rosa::System template using UnitCreator = std::function; /// Returns an object implementing the \c rosa::System interface. /// /// \param Name name of the new instance /// /// \return \c std::unique_ptr for a new instance of \c rosa::System static std::unique_ptr createSystem(const std::string &Name) noexcept; protected: /// Creates an instance. /// /// \note Protected constructor restricts instantiation for subclasses. System(void) noexcept = default; /// No copying and moving of \c rosa::System. ///@{ System(const System &) = delete; System(System &&) = delete; System &operator=(const System &) = delete; System &operator=(System &&) = delete; ///@} public: /// Destroys \p this object. /// /// \note Any implementation makes sure that a \c rosa::System can be /// destroyed only if it is marked *cleaned* /// \see \c rosa::System::isSystemCleaned virtual ~System(void) = default; /// Tells whether \p this object is the same as \p Other. /// /// \note Whenever checking equality of two objects, use the one with the /// more specialized static type on the left-hand side of the operator. The /// static type of the object on the right-hand side is better to be /// \c rosa::System, ambiguous conversion might happen otherwise. /// /// \param Other another \c rosa::System instance to compare to /// /// \return whether \p this object and \p Other is the same virtual bool operator==(const System &Other) const noexcept = 0; /// Tells whether \p this object is not the same as \p Other. /// /// \note Whenever checking inequality of two objects, use the one with the /// more specialized static type on the left-hand side of the operator. The /// static type of the object on the right-hand side is better to be /// \c rosa::System, ambiguous conversion might happen otherwise. /// /// \param Other another \c rosa::System instance to compare to /// /// \return whether \p this object and \p Other is not the same bool operator!=(const System &Other) const noexcept { return !operator==(Other); } protected: /// Tells the next unique identifier to be used for a newly created /// \c rosa::Unit. /// /// \return \c rosa::id_t which is unique within the context of \p this /// object. /// /// \note Never returs the same value twice. virtual id_t nextId(void) noexcept = 0; /// Tells if \p this object has been marked cleaned and is ready for /// destruction. /// /// \return if \p this object is marked clean. virtual bool isSystemCleaned(void) const noexcept = 0; /// Marks \p this object cleaned. /// /// \note Can be called only once when the System does not have any live /// \c rosa::Unit instances. /// /// \pre \p this object has not yet been marked as cleaned and it has no /// \c rosa::Unit instances registered:\code /// !isSystemCleaned() && empty() /// \endcode /// /// \post \p this object is marked cleaned:\code /// isSystemCleaned() /// \endcode virtual void markCleaned(void) noexcept = 0; /// Registers a \c rosa::Unit instance to \p this object. /// /// \param U \c rosa::Unit to register /// /// \pre \p this object has not yet been marked as cleaned and \p U is not /// registered yet:\code /// !isSystemCleaned() && !isUnitRegistered(U) /// \endcode /// /// \post \p U is registered:\code /// isUnitRegistered(U) /// \endcode virtual void registerUnit(Unit &U) noexcept = 0; /// Unregisters and destroys a registered \c rosa::Unit instance. /// /// \param U \c rosa::Unit to destroy /// /// \pre \p U is registered:\code /// isUnitRegistered(U) /// \endcode /// /// \post \p U is not registered and also destroyed. virtual void destroyUnit(Unit &U) noexcept = 0; /// Tells if a \c rosa::Unit is registered in \p this object. /// /// \param U \c rosa::Unit to check /// /// \return whether \p U is registered in \p this object virtual bool isUnitRegistered(const Unit &U) const noexcept = 0; /// Creates a \c rosa::Unit instance with the given /// \c rosa::System::UnitCreator and registers the new instance. /// /// \tparam T type of the actual \c rosa::Unit to instantiate /// \tparam S type of the actual \c rosa::System instantiating /// /// \param C function creating an instance of type \p T /// /// \note \p S must be the actual subclass that wants to instantiate /// \c rosa::Unit. That cannot be statically enforced, it is the /// reponsibility of the caller to provide the proper \c rosa::System /// subclass. /// /// \pre Statically, \p T is a subclass of \c rosa::Unit and \p S is a /// subclass of \c rosa::System: /// \code /// std::is_base_of::value && std::is_base_of::value /// \endcode /// Dynamically, \p this object has not yet been marked cleaned:\code /// !isSystemCleaned() /// \endcode template T &createUnit(UnitCreator C) noexcept; public: /// Tells the name of \p this object /// /// \note The returned reference remains valid as long as \p this object is /// not destroyed. /// /// \return name of \p this object virtual const std::string &name(void) const noexcept = 0; /// Tells the number of \c rosa::Unit instances constructed in the context of /// \p this object so far, including those being already destroyed. /// /// \return number of \c rosa::Unit instances created so far virtual size_t numberOfConstructedUnits(void) const noexcept = 0; /// Tells the number of live \c rosa::Unit instances in the context \p this /// object, those being constructed and not destroyed yet. /// /// \return number of \c rosa::Unit instances alive virtual size_t numberOfLiveUnits(void) const noexcept = 0; /// Tells if \p this object has no live \c rosa::Unit instances. /// /// \return whether \p this object has any live \c rosa::Unit instances virtual bool empty(void) const noexcept = 0; }; template T &System::createUnit(UnitCreator C) noexcept { STATIC_ASSERT((std::is_base_of::value), "not a Unit"); STATIC_ASSERT((std::is_base_of::value), "not a System"); if (isSystemCleaned()) { ROSA_CRITICAL("Trying to create a Unit in a cleaned System '" + name() + "'"); } const id_t Id = nextId(); T *U = C(Id, static_cast(*this)); registerUnit(*U); LOG_TRACE("Unit created and registered '" + U->FullName + "'"); return *U; } } // End namespace rosa #endif // ROSA_CORE_SYSTEM_HPP diff --git a/include/rosa/core/SystemBase.hpp b/include/rosa/core/SystemBase.hpp index a5ca0d2..68aba64 100644 --- a/include/rosa/core/SystemBase.hpp +++ b/include/rosa/core/SystemBase.hpp @@ -1,138 +1,144 @@ //===-- rosa/core/SystemBase.hpp -----------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/core/SystemBase.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Base implementation of the \c rosa::System interface. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_SYSTEMBASE_HPP #define ROSA_CORE_SYSTEMBASE_HPP #include "rosa/core/System.hpp" #include namespace rosa { /// Base implementation of the \c rosa::System interface. /// /// This implementation provides only equality checking and *name* for /// \c rosa::System, identifiers for \c rosa::Unit instances, and marking the /// \c rosa::System cleaned for destruction. /// /// \note Actual implementations of \c rosa::System and derived interfaces are /// supposed to inherit from this implementation. class SystemBase : public System { protected: /// Creates an instance. /// /// \note Protected constructor restrict instantiation for subclasses. /// /// \param Name name of the new instance SystemBase(const std::string &Name) noexcept; public: /// Destroys \p this object. /// /// \pre \p this object is marked cleaned:\code /// isSystemCleaned() /// \endcode ~SystemBase(void); /// Tells whether \p this object is the same as \p Other. /// /// Two \c rosa::System instances are considered equal if they share a common /// \c rosa::SystemBase::Name member field. That should do among various /// subclasses. /// /// \param Other another \c rosa::System instance to compare to /// /// \return whether \p this object and \p Other is the same bool operator==(const System &Other) const noexcept override; protected: /// The textual name of \p this object implementing \c rosa::System. const std::string Name; private: /// Number of \c rosa::Unit instances constructed by \p this object. /// /// \note Should never be decremented! std::atomic UnitCount; /// Indicates that \p this object has been cleaned and is ready for /// destruction. /// /// The field is initialized as \c false and can be set by /// \c rosa::SystemBase::markCleaned. /// /// \note Subclasses must set the flag upon destructing their instances, which /// indicates to the destructor of the base-class that all the managed /// resources has been properly released. std::atomic SystemIsCleaned; public: /// Tells the name of \p this object /// /// \note The returned reference remains valid as long as \p this object is /// not destroyed. /// /// \return reference to \c rosa::SystemBase::Name const std::string &name(void) const noexcept override; protected: /// Tells the next unique identifier to be used for a newly created /// \c rosa::Unit. /// /// The functions takes the current value of the internal counter /// \c rosa::SystemBase::UnitCount and then increments it. /// /// \note This is the only function modifying /// \c rosa::SystemBase::UnitCount. /// /// \return \c rosa::id_t which is unique within the context of \p this /// object. id_t nextId(void) noexcept override; /// Tells if \p this object has been marked cleaned and is ready for /// destruction. /// /// \return if \p this object is marked clean. bool isSystemCleaned(void) const noexcept override; /// Marks \p this object cleaned by setting /// \c rosa::SystemBase::SystemIsCleaned. /// /// \note Can be called only once when the System does not have any live /// \c rosa::Unit instances. /// /// \pre \p this object has not yet been marked as cleaned and it has no /// \c rosa::Unit instances registered:\code /// !isSystemCleaned() && empty() /// \endcode /// /// \post \p this object is marked cleaned:\code /// isSystemCleaned() /// \endcode void markCleaned(void) noexcept override; /// Tells the number of \c rosa::Unit instances constructed in the context of /// \p this object so far, including those being already destroyed. /// /// \return current value of \c rosa::SystemBase::UnitCount that is the number /// of \c rosa::Unit instances created so far size_t numberOfConstructedUnits(void) const noexcept override; }; } // End namespace rosa -#endif // ROSA_LIB_CORE_SYSTEMBASE_HPP +#endif // ROSA_CORE_SYSTEMBASE_HPP diff --git a/include/rosa/core/Unit.h b/include/rosa/core/Unit.h index 377443a..e4ccf46 100644 --- a/include/rosa/core/Unit.h +++ b/include/rosa/core/Unit.h @@ -1,114 +1,120 @@ //===-- rosa/core/Unit.h ----------------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/core/Unit.h /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Declaration of \c rosa::Unit base-class. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_UNIT_H #define ROSA_CORE_UNIT_H #include "rosa/support/atom.hpp" #include "rosa/core/forward_declarations.h" #include #include namespace rosa { /// Base class for every entity in a \c rosa::System that has to be identified /// and traced. /// /// \note Life-cycle of \c rosa::Unit instances is supposed to be managed by the /// \c rosa::System owning the instance, do not create and destroy any /// \c rosa::Unit directly. class Unit { public: /// Identifies the *kind* of \p this object. /// /// \note Kind is dependent on the \c rosa::System owning \p this object. const AtomValue Kind; /// Unique identifier for \p this object. /// /// \note The unique identifier is assigned by the \c rosa::System owning /// \p this object upon creation. const id_t Id; /// Textual identifier of \p this object. /// /// \note Textual identifiers of \c rosa::Unit instances are not necessarily /// unique in their owning \c rosa::System. const std::string Name; protected: /// The \c rosa::System owning \p this object. System &S; public: - /// Full qualified name of \p this object. + /// Fully qualified name of \p this object. const std::string FullName; public: /// Creates a new instnace. /// /// \param Kind the kind of the new instance /// \param Id the unique identifier of the new instance /// \param Name the name of the new instance /// \param S \c rosa::System owning the new instance /// /// \pre \p Name is not empty:\code /// !Name.empty() /// \endcode Unit(const AtomValue Kind, const id_t Id, const std::string &Name, System &S) noexcept; /// No copying and moving of \c rosa::Unit instances is possible. ///@{ Unit(const Unit &) = delete; Unit(Unit &&) = delete; Unit &operator=(const Unit &) = delete; Unit &operator=(Unit &&) = delete; ///@} /// Destroys \p this object. virtual ~Unit(void); /// Dumps \p this object into a \c std::string for tracing purposes. /// /// Subclasses are supposed to override this function. /// /// \return \c std::string representing the state of \p this object virtual std::string dump(void) const noexcept; protected: /// Returns a reference to the \c rosa::System owning \p this object. /// /// \note Subclasses may override the function to return a reference of a /// subtype of \c rosa::System. /// /// \return reference of \c rosa::Unit::S virtual System &system() const noexcept; }; /// Dumps a \c rosa::Unit instance to a given \c std::ostream. /// /// \param [in,out] OS output stream to dump to /// \param U \c rosa::Unit to dump /// /// \return \p OS after dumping \p U to it std::ostream &operator<<(std::ostream &OS, const Unit &U); } // End namespace rosa #endif // ROSA_CORE_UNIT_H diff --git a/include/rosa/core/forward_declarations.h b/include/rosa/core/forward_declarations.h index f11f2e0..9a8d40f 100644 --- a/include/rosa/core/forward_declarations.h +++ b/include/rosa/core/forward_declarations.h @@ -1,40 +1,46 @@ //===-- rosa/core/forward_declarations.h ------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/core/forward_declarations.h /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Necessary forward declarations of types in the *Core* library. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_FORWARD_DECLARATIONS_H #define ROSA_CORE_FORWARD_DECLARATIONS_H #include // NOLINT namespace rosa { // Forward declarations of classes. class Agent; class Message; class MessagingSystem; class System; class Unit; /// Type alias used for \c rosa::Unit identifiers. using id_t = uint64_t; /// Type of a \c std::unique_ptr for an immutable *Message*, \c rosa::Message /// instance. using message_t = std::unique_ptr; } // End namespace rosa #endif // ROSA_CORE_FORWARD_DECLARATIONS_H diff --git a/include/rosa/deluxe/DeluxeAgent.hpp b/include/rosa/deluxe/DeluxeAgent.hpp old mode 100755 new mode 100644 index 0e83a7c..d61a424 --- a/include/rosa/deluxe/DeluxeAgent.hpp +++ b/include/rosa/deluxe/DeluxeAgent.hpp @@ -1,673 +1,1459 @@ //===-- rosa/deluxe/DeluxeAgent.hpp -----------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeAgent.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Specialization of \c rosa::Agent for *agent* role of the *deluxe /// interface*. /// /// \see \c rosa::deluxe::DeluxeContext /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXEAGENT_HPP #define ROSA_DELUXE_DELUXEAGENT_HPP #include "rosa/core/Agent.hpp" #include "rosa/deluxe/DeluxeAtoms.hpp" +#include "rosa/deluxe/DeluxeExecutionPolicy.h" +#include "rosa/deluxe/DeluxeTuple.hpp" #include /// Local helper macros to deal with built-in types. /// ///@{ /// Creates function name for member functions in \c rosa::deluxe::DeluxeAgent. /// /// \param N name suffix to use -#define DAHANDLERNAME(N) handleSlave_##N +#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 DAHANDLERDEFN(T, N) \ - void DAHANDLERNAME(N)(atoms::Slave, id_t SlaveId, T Value) noexcept { \ - saveInput(SlaveId, Value); \ +#define DASLAVEHANDLERDEFN(T, N) \ + void DASLAVEHANDLERNAME(N)(atoms::Slave, id_t SlaveId, token_size_t Pos, \ + T Value) noexcept { \ + saveInput(SlaveId, Pos, Value); \ } -/// Convenience macro for \c DAHANDLERDEFN with identical arguments. +/// Defines member functions for handling messages from *master* in +/// \c rosa::deluxe::DeluxeAgent. /// -/// \see \c DAHANDLERDEFN +/// \see \c DeluxeAgentMasterInputHandlers /// -/// This macro can be used instead of \c DAHANDLERDEFN if the actual value of -/// \p T can be used as a part of a valid identifier. +/// \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 -#define DAHANDLERDEF(T) DAHANDLERDEFN(T, T) +/// \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 DAHANDLERDEFN. +/// \c DAMASTERHANDLERDEFN. /// /// Used in the constructor of \c rosa::deluxe::DeluxeAgent to initialize super -/// class \c rosa::Agent with member function defined by \c DAHANDLERDEFN. +/// class \c rosa::Agent with member function defined by \c DAMASTERHANDLERDEFN. /// -/// \see \c DAHANDLERDEFN, \c THISMEMBER +/// \see \c DAMASTERHANDLERDEFN, \c THISMEMBER /// /// \param N name suffix for the function identifier -#define DAHANDLERREF(N) THISMEMBER(DAHANDLERNAME(N)) +#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 All input-related container objects have a size matching -/// \c rosa::deluxe::DeluxeAgent::NumberOfInputs, thus having a corresponding -/// entry for each input. Types of input values are consistent throughout all -/// the input-related containers. No *slave* is registered at more than one -/// input position. *Slave* registrations and corresponding reverse lookup +/// \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; -public: - /// Template alias for function objects used to process input and generate - /// output for \c rosa::deluxe::DeluxeAgent. - /// - /// The output generated by the function is optional as an agent may decide - /// not to output anything at some situation. - /// - /// \note The function used for \c D is to be \c noexcept. - /// - /// \tparam T type of output - /// \tparam As types of input values - template - using D = std::function(std::pair...)>; + /// The \c rosa::deluxe::DeluxeExecutionPolicy that controls the execution of + /// \c this object. + std::unique_ptr ExecutionPolicy; +public: /// The type of values produced by \p this object. /// - /// That is the type of values \p this object sends to its *master*. + /// 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 TypeNumber OutputType; + const Token OutputType; /// Number of inputs processed by \p this object. const size_t NumberOfInputs; -private: + /// 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::TypeNumber values stored here match the corresponding - /// values in \c rosa::deluxe::DeluxeAgent::InputValues. + /// \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 type in the \c std::vector indicates which - /// argument of \p this object's processing function it belongs to. See also - /// \c rosa::deluxe::DeluxeAgent::D. - const std::vector InputTypes; + /// \note The position of a \c rosa::Token in the \c std::vector indicates + /// which argument of \p this object's processing function it belongs to. See + /// also \c rosa::deluxe::DeluxeAgent::DeluxeAgent. + const std::vector InputTypes; + + /// Indicates which element of an input is expected from any particular + /// *slave*. + /// + /// The *slave* is supposed to send one \c rosa::deluxe::DeluxeTuple value + /// element by element in their order of definition. This member field tells + /// the element at which position in the tuple should be received next from + /// the *slave* at a given position. + /// + /// \p this object is supposed to be triggered only when input values has been + /// received completely, that is all values in the field should hold the value + /// `0`. + /// + /// \see \c rosa::deluxe::DeluxeAgent::handleTrigger + /// \c rosa::deluxe::DeluxeAgent::saveInput + std::vector InputNextPos; /// Indicates whether any particular input value has been changed since the /// last trigger received from the system. /// /// All the flags are reset to \c false upon handling a trigger and then set /// to \c true by \c rosa::deluxe::DeluxeAgent::saveInput when storing a new /// input value in \c rosa::deluxe::DeluxeAgent::InputValues. /// /// \note The position of a flag in the \c std::vector indicates which /// argument of \p this object's processing function it belongs to. See also - /// \c rosa::deluxe::DeluxeAgent::D. + /// \c rosa::deluxe::DeluxeAgent::DeluxeAgent. std::vector InputChanged; /// Stores the actual input values. /// /// \note The types of stored values match the corresponding - /// \c rosa::TypeNumber values in \c rosa::deluxe::DeluxeAgent::InputTypes. + /// \c rosa::TypeNumber values (in \c rosa::Token in order) in \c + /// rosa::deluxe::DeluxeAgent::InputTypes. + /// + /// \note The position of a \c rosa::AbstractTokenizedStorage in the \c + /// std::vector indicates which argument of \p this object's processing + /// function the tuple is; and the position of the value in the \c + /// rosa::AbstractTokenizedStorage indicates which element of that tuple the + /// value is. See also \c rosa::deluxe::DeluxeAgent::DeluxeAgent. + const std::vector> InputValues; + + /// Indicates which element of the master-input is expected from the *master*. + /// + /// The *master* is supposed to send one \c rosa::deluxe::DeluxeTuple value + /// element by element in their order of definition. This member field tells + /// the element at which position should be received next. + /// + /// \p this object is supposed to be triggered only when a complete + /// master-input has been received, that is the field should hold the value + /// `0`. + /// + /// \see \c rosa::deluxe::DeluxeAgent::handleTrigger + /// \c rosa::deluxe::DeluxeAgent::saveMasterInput + token_size_t MasterInputNextPos; + + /// Indicates whether the input value from the *master* has been changed since + /// the last trigger received from the system. + /// + /// The flag is reset to \c false upon handling a trigger and then set to \c + /// true by \c rosa::deluxe::DeluxeAgent::saveMasterInput when storig a new + /// input value in \c rosa::deluxe::DeluxeAgent::MasterInputValue. + bool MasterInputChanged; + + /// Stores the actual input value from *master*. /// - /// \note The position of a value in the \c rosa::AbstractTokenizedStorage - /// indicates which argument of \p this object's processing function it is. - /// See also \c rosa::deluxe::DeluxeAgent::D. - const std::unique_ptr InputValues; + /// \note The type of the stored value matches the types indicated by \c + /// rosa::deluxe::DeluxeAgent::MasterInputType. + const std::unique_ptr MasterInputValue; + + /// Types of output values produced by \p this object for its *slaves*. + /// + /// That is the types of values \p this object sends to its *slaves* in a \c + /// rosa::deluxe::DeluxeTuple. + /// + /// \note The position of a type in the \c std::vector indicates which + /// *slave* of \p this object the type belongs to. See also + /// \c rosa::deluxe::DeluxeAgent::DeluxeAgent. + const std::vector MasterOutputTypes; /// Alias for function objects used as trigger handler for /// \c rosa::deluxe::DeluxeAgent. /// /// \note The function used for \c H is to be \c noexcept. /// /// \see \c rosa::deluxe::DeluxeAgent::FP using H = std::function; /// Handles trigger from the system. /// - /// The actual function processing *slave* inputs and generating optional - /// output to *master* is captured in a lambda expression that is in turn - /// wrapped in a \c std::function object. The lambda expression calls the - /// processing function with the actual input data and sends its result -- if - /// any -- to *master* by calling \c rosa::deluxe::DeluxeAgent::sendToMaster. - /// Also, all the flags stored in \c rose::deluxe::DeluxeAgent::InputChanged - /// are reset when the current input values are processed. The function - /// \c rosa::deluxe::DeluxeAgent::handleTrigger needs only to call the + /// 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::triggerHandlerFromProcessingFunction + /// \see \c + /// rosa::deluxe::DeluxeAgent::triggerHandlerFromProcessingFunctions const H FP; /// The *master* to send values to. /// /// \note *Masters* are set dynamically, hence it is possible that a /// \c rosa::deluxe::DeluxeAgent instance does not have any *master* at a /// given moment. Optional Master; /// The *slaves* sending input to \p this object. /// /// \note The position of a *slave* in the \c std::vector indicates which /// argument of \p this object's processing function it belongs to. See also - /// \c rosa::deluxe::DeluxeAgent::D. + /// \c rosa::deluxe::DeluxeAgent::DeluxeAgent. /// /// \note *Slaves* are set dynamically, hence it is possible that a /// \c rosa::deluxe::DeluxeAgent instance does have input positions without /// any *slave* associated to them. /// /// \note Reverse lookup information is maintained in /// \c rosa::deluxe::DeluxeAgent::SlaveIds, which is to be kept in sync with /// the *slaves* stored here. std::vector> Slaves; /// Associates \c rosa::id_t values to corresponding indices of registered /// *slaves*. /// /// \see \c rosa::deluxe::DeluxeAgent::Slaves std::map SlaveIds; - /// Tells whether types \p As... match the input types of \p this object. + /// Tells the unique identifier of the *master* of \p this object, if any + /// registered. /// - /// \tparam As types to match against values in - /// \c rosa::deluxe::DeluxeAgent::InputTypes + /// \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. /// - /// \return if types \p As... match \c rosa::TypeNumber values stored in + /// \tparam As \c rosa::TypeList containing types to match against values in /// \c rosa::deluxe::DeluxeAgent::InputTypes - template bool inputTypesMatch(void) const noexcept; + /// + /// \note Instatiation of the template fails if \p As is not \c + /// rosa::TypeList. + /// + /// \return if types in \p As are instances of \c rosa::deluxe::DeluxeTuple + /// and their types match \c rosa::Token values stored in \c + /// rosa::deluxe::DeluxeAgent::InputTypes + template bool inputTypesMatch(void) const noexcept; + + /// Tells whether types stored in \c rosa::TypeList \p Ts match the + /// master-output types of \p this object. + /// + /// \tparam Ts \c rosa::TypeList containing types to match against values in + /// \c rosa::deluxe::DeluxeAgent::MasterOutputTypes + /// + /// \note Instatiation of the template fails if \p As is not \c + /// rosa::TypeList. + /// + /// \return if types in \p Ts match \c rosa::Token and in turn \c + /// rosa::TypeNumber values stored in \c + /// rosa::deluxe::DeluxeAgent::MasterOutputTypes + template bool masterOutputTypesMatch(void) const noexcept; + + /// Gives the current input value for slave position \p Pos. + /// + /// \tparam Pos slave position to get input value for + /// \tparam Ts types of elements of the input value + /// \tparam S0 indices for accessing elements of the input value + /// + /// \note The arguments provide types and indices statically as template + /// arguments \p Ts... \p S0..., respectively, so their actual values are + /// ignored. + /// + /// \return current input value for slave position \p Pos + /// + /// \pre Statically, the provided indices \p S0... match the length of \p + /// Ts...: \code + /// sizeof...(Ts) == sizeof...(S0) + /// \endcode Dynamically, \p Pos is a valid slave position and type arguments + /// \p Ts... match the corresponding input value: \code + /// Pos < NumberOfInputs && DeluxeTuple::TT == InputTypes[Pos] + /// \endcode + template + DeluxeTuple prepareInputValueAtPos(TypeList, Seq) const + noexcept; /// Gives an \c std::tuple containing the current input values and their /// change flags so that they can be used for the processing function. /// /// \tparam As types of the input values /// \tparam S0 indices for accessing input values and their change flags /// /// \note The only argument provides indices statically as template arguments /// \p S0..., so its actual value is ignored. /// /// \return current input values and their change flags prepared for invoking /// the processing function with them /// - /// \pre The type arguments \p As... match the input types of \p this object - /// and the provided indices \p S0... constitute a proper sequence for - /// accessing input values and their change flags: \code - /// inputTypesMatch() && sizeof...(As) == sizeof...(S0) + /// \pre Statically, all type arguments \p As... are instances of \c + /// rosa::deluxe::DeluxeTuple and the provided indices \p S0... match the + /// length of \p As...: \code + /// TypeListAllDeluxeTuple>::Value && + /// sizeof...(As) == sizeof...(S0) + /// \endcode Dynamically, type arguments \p As... match the input types of \p + /// this object: \code + /// inputTypesMatch>() /// \endcode template std::tuple...> prepareCurrentInputs(Seq) const noexcept; - /// Invokes a processing function matching the output and input types of - /// \p this object with actual arguments provided in a \c std::tuple. + /// Invokes a processing function matching the input, output, and + /// master-output types of \p this object with actual arguments provided in a + /// \c std::tuple. /// /// \note \p Args providing the actual arguments for \p F is to be created by /// \c rosa::deluxe::DeluxeAgent::prepareCurrentInputs. /// /// \tparam T output type of the processing function + /// \tparam Ts types of master-output values of the processing function /// \tparam As types of inputs for the processing function /// \tparam S0 indices starting with `0` for extracting actual arguments from /// \p Args /// /// \param F the processing function to invoke /// \param Args the actual arguments to invoke \p F with /// /// \note The last argument provides indices statically as template arguments /// \p S0..., so its actual value is ignored. /// /// \return the result of \p F for actual arguments \p Args /// /// \pre The provided sequence of indices \p S0... constitutes a proper /// sequence for extracting all actual arguments for /// \p F from \p Args: \code /// sizeof...(As) == sizeof...(S0) /// \endcode - template - static Optional invokeWithTuple(D F, - std::tuple...> Args, - Seq) noexcept; + template + static std::tuple, Optional...> + invokeWithTuple(std::function, Optional...>( + std::pair...)> + F, + const std::tuple...> Args, + Seq) noexcept; + + /// Handles a master-output value for a particular *slave* position. + /// + /// \p Value is a \c rosa::Optional resulted by a processing function and + /// contains a master-output value for the *slave* at position \p Pos. The + /// function takes the master-output value and sends its actual value, if any, + /// to the corresponding *slave*. + /// + /// \note A master-output of type \c rosa::deluxe::EmptyDeluxeTuple indicates + /// no actual output and hence no message is generated for a position whose + /// corresponding master-output type is \c rosa::deluxe::EmptyDeluxeTuple. + /// + /// \note The function provides position-based implementation for \c + /// rosa::deluxe::DeluxeAgent::handleMasterOutputs. + /// + /// \tparam Pos the position of the master-output to send \p Value for + /// \tparam Ts types of elements in \p Value + /// + /// \param Value \c rosa::deluxe::DeluxeTuple resulted by the processing + /// function for *slave* position \p Pos + /// + /// \pre \p Pos is a valid master-output position and \p Value matches the + /// master-output type of \p this object at position \p Pos: \code + /// Pos < NumberOfMasterOutputs && + /// DeluxeTuple::TT == MasterOutputTypes[Pos] + /// \endcode + template + void + handleMasterOutputAtPos(const Optional> &Value) noexcept; + + /// Handles master-output values from \p Output. + /// + /// \p Output is a \c std::tuple resulted by a processing function and + /// contains master-output values starting at position \p Offset. The function + /// takes master-output values and sends each actual value to the + /// corresponding *slave*. + /// + /// \tparam Offset index of the first master-output value in \p Output + /// \tparam Ts output types stored in \p Output + /// \tparam S0 indices starting with `0` for extracting master-output values + /// from \p Output + /// + /// \note Instantiation fails if any of the type arguments \p Ts... starting + /// at position \p Offset is not an instance of \c rosa::deluxe::DeluxeTuple + /// or the number of types \p Ts... is not consistent with the other template + /// arguments. + /// + /// \param Output \c std::tuple resulted by a processing function + /// + /// \pre Statically, type arguments \p Ts... starting at position \p Offset + /// are instances of \c rosa::deluxe::DeluxeTuple and the number of types \p + /// Ts... is consistent with the other template arguments: \code + /// TypeListAllDeluxeTuple< + /// typename TypeListDrop>::Type>::Value && + /// sizeof...(Ts) == Offset + sizeof...(S0) + /// \endcode Dynamically, \p Output matches the master-output types \p this + /// object was created with and the provided sequence of indices \p S0... + /// constitues a proper sequence for extracting all master-output values from + /// \p Output: \code + /// masterOutputTypesMatch>::Type>() && + /// sizeof...(S0) == NumberOfMasterOutputs + /// \endcode + template + void handleMasterOutputs(const std::tuple...> &Output, + Seq) noexcept; - /// Wraps a processing function into a trigger handler. + /// 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 /// - /// \pre Template arguments \p T and \p As... match the corresponding - /// types \p this object was created with: \code - /// OutputType == TypeNumberOf::Value && inputTypesMatch() + /// \note The last argument provides indices statically as template + /// arguments \p S0..., so its actual value is ignored. + /// + /// \note A master-input type of \c rosa::deluxe::EmptyDeluxeTuple indicates + /// that \p this object does not receive master-input, \p MF is never called + /// if \p MTs is empty. + /// + /// \return trigger handler function based on \p F and \p MF + /// + /// \pre Statically, type arguments \p T, \p Ts..., and \p As... are + /// instances of \c rosa::deluxe::DeluxeTuple and the indices match + /// master-input elements: \code + /// TypeListAllDeluxeTuple>::Value && + /// sizeof...(MTs) == sizeof...(S0) + /// \endcode Dynamically, template arguments \p MTs..., \p T, \p Ts..., and + /// \p As... match the corresponding types \p this object was created with: + /// \code + /// MasterInputType == DeluxeTuple::TT && OutputType == T::TT && + /// inputTypesMatch>() && + /// masterOutputTypesMatch>() /// \endcode - template - H triggerHandlerFromProcessingFunction(D &&F) noexcept; + template + H triggerHandlerFromProcessingFunctions( + std::function...>( + std::pair, bool>)> &&MF, + std::function< + std::tuple, Optional...>(std::pair...)> &&F, + Seq) noexcept; public: /// Creates a new instance. /// /// The constructor instantiates the base-class with functions to handle /// messages as defined for the *deluxe interface*. /// - /// \todo Enforce F does not potentially throw exception. + /// 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 T and \p As... - /// is not a built-in type. + /// \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 F function to process input values and generate output with - /// - /// \pre Statically, all of the type arguments \p T and \p As... is a - /// built-in type: \code - /// TypeListSubsetOf, BuiltinTypes>::Value - /// \endcode Dynamically, the instance is created as of kind - /// \c rosa::deluxe::atoms::AgentKind: \code + /// \param MF function to process master-input values and generate + /// master-output with + /// \param F function to process input values and generate output and + /// master-output with + /// + /// \pre Statically, all the type arguments \p MT, \p T, \p Ts..., and \p + /// As... are instances of \c rosa::deluxe::DeluxeTuple, with \p T and \p + /// As... containing at least one element, and the number of input and + /// master-output types are equal: \code + /// TypeListAllDeluxeTuple::Value && + /// T::Length > 0 && (true && ... && As::Length > 0) && + /// sizeof...(Ts) == sizeof...(As) + ///\endcode + /// Dynamically, the instance is created as of kind \c + /// rosa::deluxe::atoms::AgentKind: \code /// Kind == rosa::deluxe::atoms::AgentKind /// \endcode - template , BuiltinTypes>::Value>> - DeluxeAgent(const AtomValue Kind, const id_t Id, const std::string &Name, - MessagingSystem &S, D &&F) noexcept; + TypeListAllDeluxeTuple>::Value && + (T::Length > 0) && (true && ... && (As::Length > 0)) && + sizeof...(Ts) == sizeof...(As)>> + DeluxeAgent( + const AtomValue Kind, const id_t Id, const std::string &Name, + MessagingSystem &S, + std::function...>(std::pair)> &&MF, + std::function, Optional...>( + std::pair...)> &&F) noexcept; /// Destroys \p this object. ~DeluxeAgent(void) noexcept; + /// Returns the current execution policy of \p this object. + /// + /// \see \c rosa::deluxe::DeluxeExecutionPolicy + /// + /// \note The returned reference is valid only as long as \c + /// rosa::deluxe::DeluxeAgent::setExecutionPolicy() is not called and \p this + /// object is not destroyed. + /// + /// \return \c rosa::deluxe::DeluxeAgent::ExecutionPolicy + const DeluxeExecutionPolicy &executionPolicy(void) const noexcept; + + /// Sets the current execution policy of \p this object to \p EP. + /// + /// \see \c rosa::deluxe::DeluxeExecutionPolicy + /// + /// \note \p EP is set only if it can handle \p this object. + /// + /// \param EP the new execution policy for \p this object + /// + /// \return if \p EP was successfully set for \p this object. + bool setExecutionPolicy(std::unique_ptr &&EP) noexcept; + /// The *master* of \p this object, if any is registered. /// /// \see \c rosa::deluxe::DeluxeAgent::registerMaster /// /// \return the *master* registered for \p this object Optional master(void) const noexcept; /// Registers a *master* for \p this object. /// /// The new *master* is registered by overwriting the reference to any /// already registered *master*. One can clear the registered reference by /// passing an *empty* \c rosa::Optional object as actual argument. /// /// \note The role of the referred *master* is validated by checking its /// *kind*. /// + /// \note Any call to \c rosa::deluxe::DeluxeAgent::registerMaster should be + /// paired with a corresponding call of \c + /// rosa::deluxe::DeluxeAgent::registerSlave, which validates that + /// input/output types of master and slave matches. + /// /// \param _Master the *master* to register /// /// \pre \p _Master is empty or of kind \c rosa::deluxe::atoms::AgentKind: /// \code /// !_Master || unwrapAgent(*_Master).Kind == rosa::deluxe::atoms::AgentKind /// \endcode void registerMaster(const Optional _Master) noexcept; - /// Tells the type of values consumed from the *slave* at a position. + /// 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 by its - /// *slave* registered at position \p Pos. + /// 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::TypeNumber representing the type of values consumed from + /// \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 - TypeNumber inputType(const size_t Pos) const noexcept; + Token inputType(const size_t Pos) const noexcept; + + /// Tells the types of values produced for the *slave* at a position. + /// + /// That is the types of values \p this object potentially sends in a \c + /// rosa::deluxe::DeluxeTuple to its *slave* registered at position \p Pos. + /// + /// \see \c rosa::deluxe::DeluxeAgent::slave + /// + /// \param Pos position of *slave* + /// + /// \return \c rosa::Token representing the types of values produced for + /// the *slave* at position \p Pos + /// + /// \pre \p Pos is a valid index of input: \code + /// Pos < NumberOfMasterOutputs + /// \endcode + Token masterOutputType(const size_t Pos) const noexcept; /// The *slave* of \p this object registered at a position, if any. /// /// \see \c rosa::deluxe::DeluxeAgent::registerSlave /// /// \param Pos position of *slave* /// /// \return the *slave* registered for \p this object at position \p Pos /// /// \pre \p Pos is a valid index of input: \code /// Pos < NumberOfInputs /// \endcode Optional slave(const size_t Pos) const noexcept; /// Registers a *slave* for \p this object at a position. /// /// The new *slave* is registered by overwriting the reference to any already /// registered *slave* at position \p Pos. One can clear the registered /// reference by passing an *empty* \c rosa::Optional object as actual /// argument. If \p Slave is already registered for another position, the /// other position gets cleared. /// /// \note The role of the referred *slave* is validated by checking its /// *kind*. /// /// \note The type of values produced by the referred *slave* is validated by /// matching its `OutputType` against the corresponding value in /// \c rosa::deluxe::DeluxeAgent::InputTypes. /// + /// \note The type of master-input values processed by the referred *slave* is + /// validated by matching its `MasterInputType` against the corresponding + /// value in \c rosa::deluxe::DeluxeAgent::MasterOutputTypes. + /// /// \param Pos position to register \p Slave at /// \param Slave the *slave* to register /// /// \pre \p Pos is a valid index of input, \p Slave is empty or of kind /// \c rosa::deluxe::atoms::AgentKind or \c rosa::deluxe::atoms::SensorKind, /// and \p Slave -- if not empty -- produces values of types matching the - /// expected input type at position \p Pos: + /// expected input type at position \p Pos and processes values of types + /// matching the produced master-output type at position \p Pos: /// \code /// Pos < NumberOfInputs && /// (!Slave || /// (unwrapAgent(*Slave.)Kind == rosa::deluxe::atoms::SensorKind && /// static_cast(unwrapAgent(*Slave)).OutputType == - /// InputTypes[Pos]) || + /// InputTypes[Pos] && + /// (emptyToken(MasterOutputTypes[Pos]) || + /// static_cast(unwrapAgent(*Slave)).MasterInputType + /// == MasterOutputTypes[Pos])) || /// (unwrapAgent(*Slave).Kind == rosa::deluxe::atoms::AgentKind && /// static_cast(unwrapAgent(*Slave)).OutputType == - /// InputTypes[Pos])) + /// InputTypes[Pos] && + /// (emptyToken(MasterOutputTypes[Pos]) || + /// static_cast(unwrapAgent(*Slave)).MasterInputType == + /// MasterOutputTypes[Pos]))) /// \endcode void registerSlave(const size_t Pos, const Optional Slave) noexcept; /// Tells the position of a registered *slave*. /// /// \param Slave \c rosa::AgentHandle for the *slave* to check /// /// \return position of \p Slave if it is registered and found, /// \c rosa::deluxe::DeluxeAgent::NumberOfInputs otherwise. size_t positionOfSlave(AgentHandle Slave) const noexcept; private: /// Sends a value to the *master* of \p this object. /// /// \p Value is getting sent to \c rosa::deluxe::DeluxeAgent::Master if it /// contains a valid handle for a \c rosa::deluxe::DeluxeAgent. The function /// does nothing otherwise. /// - /// \tparam T type of the value to send + /// 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 /// - /// \pre \p T matches \c rosa::deluxe::DeluxeiAgent::OutputType: \code - /// OutputType == TypeNumberOf::Value + /// \note The second argument provides indices statically as template + /// arguments \p S0..., so its actual value is ignored. + /// + /// \pre Statically, the indices match the elements: \code + /// sizeof...(Ts) == sizeof...(S0) + /// \endcode Dynamically, \p Ts match \c + /// rosa::deluxe::DeluxeiAgent::OutputType: \code + /// OutputType == TypeToken::Value /// \endcode - template void sendToMaster(const T &Value) noexcept; + template + void sendToMaster(const DeluxeTuple &Value, Seq) noexcept; + + /// Sends a value to a *slave* of \p this object at position \p Pos. + /// + /// \p Value is getting sent to \c rosa::deluxe::DeluxeAgent::Slaves[Pos] if + /// it contains a valid handle. The function does nothing otherwise. + /// + /// The elements from \p Value are sent one by one in separate messages to the + /// *slave*. + /// + /// \tparam Ts types of the elements in \p Value + /// \tparam S0 indices for accessing elements of \p Value + /// + /// \param Pos the position of the *slave* to send \p Value to + /// \param Value value to send + /// + /// \pre Statically, the indices match the elements: \code + /// sizeof...(Ts) == sizeof...(S0) + /// \endcode Dynamically, \p Pos is a valid *slave* position and \p Ts match + /// \c rosa::deluxe::DeluxeiAgent::MasterOutputTypes[Pos]: \code + /// Pos < NumberOfMasterOutputs && + /// MasterOutputTypes[Pos] == TypeToken::Value + /// \endcode + template + void sendToSlave(const size_t Pos, const DeluxeTuple &Value, + Seq) noexcept; /// Generates the next output by processing current input values upon trigger /// from the system. /// /// Executes \c rosa::deluxe::DeluxeAgent::FP. /// /// \note The only argument is a \c rosa::AtomConstant, hence its actual /// value is ignored. + /// + /// \pre Master-input and all input from *slaves* are supposed to be + /// completely received upon triggering: \code + /// MasterInputNextPos == 0 && + /// std::all_of(InputNextPos.begin(), InputNextPos.end(), + /// [](const token_size_t &I){return I == 0;}) + /// \endcode void handleTrigger(atoms::Trigger) noexcept; /// Stores a new input value from a *slave*. /// - /// The function stores \p Value 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 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 and the input from it is - /// expected to be of type \p T: \code + /// \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() && - /// InputTypes[SlaveIds.find(Id)->second] == TypeNumberOf::Value + /// Pos == InputNextPos[SlaveIds.find(Id)->second] && + /// typeAtPositionOfToken(InputTypes[SlaveIds.find(Id)->second], Pos) == + /// TypeNumberOf::Value /// \endcode - template void saveInput(id_t Id, T Value) noexcept; + template + void saveInput(id_t Id, token_size_t Pos, T Value) noexcept; - /// \defgroup DeluxeAgentInputHandlers Input handlers of rosa::deluxe::DeluxeAgent + /// Stores a new input value from the *master*. + /// + /// The function stores \p Value at position \p Pos in \c + /// rosa::deluxe::DeluxeAgent::MasterInputValue and also sets the + /// flag \c rosa::deluxe::DeluxeAgent::MasterInputChanged. The function also + /// takes care of checking and updating \c + /// rosa::deluxe::DeluxeAgent::MasterInputNextPos: increments its value and + /// reset to `0` when the last element is received. + /// + /// \note Utilized by member functions of group \c + /// DeluxeAgentMasterInputHandlers. + /// + /// \tparam T type of input to store + /// + /// \param Id unique identifier of the *master* + /// \param Pos position of the value in the \c rosa::deluxe::DeluxeTuple + /// \param Value the input value to store + /// + /// \pre The *master* with \p Id is registered, \p Pos is the expected + /// position of master-input, and the input from the *master* at position \p + /// Pos is expected to be of type \p T: \code + /// Master && masterId() == Id && Pos == MasterInputNextPos && + /// typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf::Value + /// \endcode + template + void saveMasterInput(id_t Id, token_size_t Pos, T Value) noexcept; + + /// \defgroup DeluxeAgentInputHandlers Input handlers of + /// rosa::deluxe::DeluxeAgent /// /// Definition of member functions handling messages from *slaves* with /// different types of input /// /// A *master* generally needs to be prepared to deal with values of any - /// built-in type. 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. + /// 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 DAHANDLERDEF. + /// \note The member functions in this group are defined by \c + /// DASLAVEHANDLERDEF. /// /// \note Keep these definitions in sync with \c rosa::BuiltinTypes. /// ///@{ - DAHANDLERDEF(AtomValue) - DAHANDLERDEF(int16_t) - DAHANDLERDEF(int32_t) - DAHANDLERDEF(int64_t) - DAHANDLERDEF(int8_t) - DAHANDLERDEFN(long double, long_double) - DAHANDLERDEFN(std::string, std__string) - DAHANDLERDEF(uint16_t) - DAHANDLERDEF(uint32_t) - DAHANDLERDEF(uint64_t) - DAHANDLERDEF(uint8_t) - DAHANDLERDEF(unit_t) - DAHANDLERDEF(bool) - DAHANDLERDEF(double) - DAHANDLERDEF(float) + 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::inputTypesMatch, consider it private. +/// 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 { -/// Template \c struct whose specializations provide a recursive implementation -/// for \c rosa::deluxe::DeluxeAgent::inputTypesMatch. +/// Creates storages for data of types \p Ts... in a \c std::vector of \c +/// rosa::TokenizedStorage. +/// +/// \note Utilized by \c rosa::deluxe::DeluxeAgnet::DeluxeAgent to initialize \c +/// rosa::deluxe::DeluxeAgent::InputValues. That is due to not being able to use +/// an initializer list directly; the initializer list always copies but \c +/// std::unique_ptr is not copyable. +/// +/// \tparam Ts types to create storages for /// -/// \note Matching a list of types \p As... against a \c std::vector of -/// \c rosa::TypeNumber values, \c InputTypes, like \code -/// bool match = InputTypesMatchImpl::f(InputTypes, 0); +/// \note Instantiation fails if any of the type arguments \p Ts... is not an +/// instance of \c rosa::deluxe::DeluxeTuple. +/// +/// \return \c std::vector with pointers for the created storage objects +/// +/// \pre Statically, all the type arguments \p Ts... are instances of \c +/// rosa::deluxe::DeluxeTuple: \code +/// TypeListAllDeluxeTuple>::Value /// \endcode +template +std::vector> +makeInputStorages(void) noexcept { + std::vector> InputStorages; + (InputStorages.push_back( + std::make_unique::Type>::Type>()), + ...); + return InputStorages; +} + +/// Template \c struct whose specializations provide a recursive implementation +/// for \c TypesMatchList. /// /// \tparam As types to match -template struct InputTypesMatchImpl; +template struct TypesMatchImpl; -/// Template specialization for the general case, when at least one type is to -/// be matched. +/// 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 A first type to match +/// \tparam Ts types of elements in the \c rosa::deluxe::DeluxeTuple to match /// \tparam As further types to match -template struct InputTypesMatchImpl { - /// Tells whether types \p A, \p As... match \c rosa::TypeNumber values - /// stored in \p InputTypes starting at position \p Pos. +template +struct TypesMatchImpl, As...> { + /// Tells whether types \c rosa::deluxe::DeluxeTuple and \p As... match + /// \c rosa::Token values stored in \p Tokens starting at position \p Pos. /// /// The function has got a recursive implementation: it matches the first - /// type \p A against \c rosa::TypeNumber at position \p Pos of \p - /// InputTypes, then further types \p As.. are matched recursively starting - /// at position \c (Pos + 1). - /// - /// \param InputTypes container of \c rosa::TypeNumber values to match - /// types against - /// \param Pos position in \p InputTypes to start matching at - /// - /// \return if types \p A, \p As... match \c rosa::TypeNumber values stored - /// in \p InputTypes starting at position \p Pos - static bool f(const std::vector &InputTypes, - size_t Pos) noexcept { - return Pos < InputTypes.size() && - TypeNumberOf::Value == InputTypes[Pos] && - InputTypesMatchImpl::f(InputTypes, Pos + 1); + /// type \c rosa::deluxe::DeluxeTuple against \c rosa::Token at + /// position \p Pos of \p Tokens, then further types \p As... are matched + /// recursively starting at position \c (Pos + 1). + /// + /// \param Tokens container of \c rosa::Token values to match types against + /// \param Pos position in \p Tokens to start matching at + /// + /// \return if types \c rosa::deluxe::DeluxeTuple and \p As... match \c + /// rosa::Token values stored in \p Tokens starting at position \p Pos + static bool f(const std::vector &Tokens, size_t Pos) noexcept { + return Pos < Tokens.size() && TypeToken::Value == Tokens[Pos] && + TypesMatchImpl::f(Tokens, Pos + 1); } }; +/// Template specialization for the case, when at least one type is to +/// be matched and that is *not* an instance of \c rosa::deluxe::DeluxeTuple. +/// +/// \tparam T first type to match +/// \tparam As further types to match +template +struct TypesMatchImpl { + /// Tells whether types \p T and \p As... match \c rosa::Token values stored + /// in \p Tokens starting at position \p Pos. + /// + /// This specialization is used only when \p T is not an instance of \c + /// rosa::deluxe::DeluxeTuple, in which case the match is not successful. + /// + /// \note The function takes two parameters to match the general signature but + /// the actual values are ignored. + /// + /// \return `false` + static bool f(const std::vector &, size_t) noexcept { return false; } +}; + /// Template specialization for the terminal case, when no type remains to /// check. -template <> struct InputTypesMatchImpl<> { - /// Tells whether \p Pos is the number of values stored in \p InputTypes. +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 matchi because all the + /// 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 InputTypes. That is true if \p Pos points exactly to the end of - /// \p InputTypes. + /// \p Tokens. That is true if \p Pos points exactly to the end of \p Tokens. /// - /// \param InputTypes container of \c rosa::TypeNumber values to match - /// types against - /// \param Pos position in \p InputTypes to start matching at + /// \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 InputTypes - static bool f(const std::vector &InputTypes, - size_t Pos) noexcept { - return Pos == InputTypes.size(); + /// \return if \p Pos is the number of values stored in \p Tokens + static bool f(const std::vector &Tokens, size_t Pos) noexcept { + return Pos == Tokens.size(); + } +}; + +/// Template \c struct that provides an implementation for \c +/// rosa::deluxe::DeluxeAgent::inputTypesMatch and \c +/// rosa::deluxe::DeluxeAgent::masterOutputTypesMatch. +/// +/// \note Match a list of types \p List against a \c std::vector of +/// \c rosa::Token values, \c Tokens, like \code +/// bool match = TypesMatchList::f(Tokens); +/// \endcode +/// If any type in \c rosa::TypeList \p Listis not an instance of \c +/// rosa::deluxe::DeluxeTuple, the match gives a negative result. +/// +/// \tparam List \c rosa::TypeList that contains types to match +template struct TypesMatchList; + +/// Template specialization implementing the feature. +/// +/// \tparam As types to match +template struct TypesMatchList> { + /// Tells whether types \p As... match \c rosa::Token values stored in \p + /// Tokens. + /// + /// The function unwraps the types from \c rosa::TypeList and utilizes \c + /// TypesMatchImpl to do the check. + /// + /// \param Tokens container of \c rosa::Token values to match types against + /// + /// \return if types \p As... match \c rosa::Token values stored in \p Tokens + static bool f(const std::vector &Tokens) noexcept { + return TypesMatchImpl::f(Tokens, 0); } }; } // End namespace -template +template bool DeluxeAgent::inputTypesMatch(void) const noexcept { - return InputTypesMatchImpl::f(InputTypes, 0); + return TypesMatchList::f(InputTypes); +} + +template +bool DeluxeAgent::masterOutputTypesMatch(void) const noexcept { + return TypesMatchList::f(MasterOutputTypes); +} + +template +DeluxeTuple DeluxeAgent::prepareInputValueAtPos(TypeList, + Seq) const + noexcept { + using T = DeluxeTuple; + STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent type arguments"); + ASSERT(inv() && Pos < NumberOfInputs && T::TT == InputTypes[Pos]); + + // The below should hold because of the above, just leave it for sanity check. + ASSERT((true && ... && + (static_cast(static_cast(S0)) == S0))); + + const auto &SlaveInput = InputValues[Pos]; + + // Get all elements of the tuple in a fold expression. + return T(*static_cast( + SlaveInput->pointerTo(static_cast(S0)))...); } template std::tuple...> - DeluxeAgent::prepareCurrentInputs(Seq) const noexcept { - // Need to indirectly reference \c rosa::deluxe::DeluxeAgent::inputTypesMatch - // inside \c ASSERT because of the comma in its template argument list. - auto MFP = &DeluxeAgent::inputTypesMatch; - ASSERT(inv() && (this->*MFP)() && sizeof...(As) == sizeof...(S0)); - - return std::make_tuple( - std::make_pair(*static_cast(InputValues->pointerTo(S0)), - InputChanged[S0])...); +DeluxeAgent::prepareCurrentInputs(Seq) const noexcept { + STATIC_ASSERT(TypeListAllDeluxeTuple>::Value, + "not tuple types"); + STATIC_ASSERT(sizeof...(As) == sizeof...(S0), "inconsistent type arguments"); + ASSERT(inv() && inputTypesMatch>()); + + return std::make_tuple(std::make_pair( + prepareInputValueAtPos(typename UnwrapDeluxeTuple::Type(), + seq_t()), + InputChanged[S0])...); } -template -Optional DeluxeAgent::invokeWithTuple( - D F, - std::tuple...> Args, - Seq) noexcept { - ASSERT(sizeof...(As) == sizeof...(S0)); +template +std::tuple, Optional...> DeluxeAgent::invokeWithTuple( + std::function< + std::tuple, Optional...>(std::pair...)> + F, + const std::tuple...> Args, Seq) noexcept { + STATIC_ASSERT(sizeof...(As) == sizeof...(S0), + "wrong number of type parameters"); return F(std::get(Args)...); } -template -DeluxeAgent::H -DeluxeAgent::triggerHandlerFromProcessingFunction(D &&F) noexcept { - // Need to indirectly reference \c rosa::deluxe::DeluxeAgent::inputTypesMatch - // inside \c ASSERT because of the comma in its template argument list. - auto MFP = &DeluxeAgent::inputTypesMatch; - ASSERT(OutputType == TypeNumberOf::Value && (this->*MFP)()); - - return [ this, F ]() noexcept { - using Indices = typename GenSeq::Type; - auto Args = prepareCurrentInputs(Indices()); - std::fill(InputChanged.begin(), InputChanged.end(), false); - Optional R = invokeWithTuple(F, Args, Indices()); - if (R) { - sendToMaster(*R); +template +void DeluxeAgent::handleMasterOutputAtPos( + const Optional> &Value) noexcept { + using MOT = DeluxeTuple; + ASSERT(inv() && Pos < NumberOfMasterOutputs && + MOT::TT == MasterOutputTypes[Pos]); + // Do not do anything for master-output of type \c + // rosa::deluxe::EmptyDeluxeTuple and when \p Value is empty. + if constexpr (!std::is_same::value) { + if (Value) { + sendToSlave(Pos, *Value, seq_t()); + } + } else { + (void)Value; + } + ASSERT(inv()); +} + +template +void DeluxeAgent::handleMasterOutputs(const std::tuple...> &Output, + Seq) noexcept { + using MOTs = typename TypeListDrop>::Type; + STATIC_ASSERT(TypeListAllDeluxeTuple::Value, + "not tuple type arguments"); + STATIC_ASSERT(sizeof...(Ts) == Offset + sizeof...(S0), + "inconsistent arguments"); + ASSERT(inv() && masterOutputTypesMatch() && + sizeof...(S0) == NumberOfMasterOutputs); + // Handle each master-output position in a fold expression. + (handleMasterOutputAtPos(std::get(Output)), ...); + ASSERT(inv()); +} + +template +DeluxeAgent::H DeluxeAgent::triggerHandlerFromProcessingFunctions( + std::function< + std::tuple...>(std::pair, bool>)> &&MF, + std::function< + std::tuple, Optional...>(std::pair...)> &&F, + Seq) noexcept { + using MT = DeluxeTuple; + STATIC_ASSERT((TypeListAllDeluxeTuple>::Value), + "not tuple type arguments"); + STATIC_ASSERT(sizeof...(MTs) == sizeof...(S0), "inconsistent arguments"); + ASSERT(MasterInputType == MT::TT && OutputType == T::TT && + inputTypesMatch>() && + masterOutputTypesMatch>()); + + return [ this, MF, F ]() noexcept { + // \note These indices work for both inputs and master-outputs. + using SlaveIndices = seq_t; + + // Handle master-input. + // Do not do anything for master-input type \c + // rosa::deluxe::EmptyDeluxeTuple. + if (!std::is_same::value) { + LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " handles master-input." + << std::endl; + // The assert must hold if \p this object was successfuuly constructed. + ASSERT((true && ... && + (static_cast(static_cast(S0)) == S0))); + const auto MasterInputArg = std::make_pair( + // Get all elements of the tuple in a fold expression. + MT(*static_cast( + MasterInputValue->pointerTo(static_cast(S0)))...), + MasterInputChanged); + MasterInputChanged = false; + const std::tuple...> MasterOutput = MF(MasterInputArg); + handleMasterOutputs<0>(MasterOutput, SlaveIndices()); + } + + // Handle inputs. + // Call the processing function only if \p ExecutionPolicy allows. + if (ExecutionPolicy->shouldProcess(InputChanged)) { + LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " handles input." + << std::endl; + const auto InputArgs = prepareCurrentInputs(SlaveIndices()); + std::fill(InputChanged.begin(), InputChanged.end(), false); + const std::tuple, Optional...> Output = + invokeWithTuple(F, InputArgs, SlaveIndices()); + const auto OutputToMaster = std::get<0>(Output); + if (OutputToMaster) { + sendToMaster(*OutputToMaster, seq_t()); + } + handleMasterOutputs<1>(Output, SlaveIndices()); + } else { + LOG_TRACE_STREAM << "DeluxeAgent " << Name << " skips input." + << std::endl; } }; } -template -DeluxeAgent::DeluxeAgent(const AtomValue Kind, const id_t Id, - const std::string &Name, MessagingSystem &S, - D &&F) noexcept +template +DeluxeAgent::DeluxeAgent( + const AtomValue Kind, const id_t Id, const std::string &Name, + MessagingSystem &S, + std::function...>(std::pair)> &&MF, + std::function, Optional...>( + std::pair...)> &&F) noexcept : Agent(Kind, Id, Name, S, THISMEMBER(handleTrigger), - DAHANDLERREF(AtomValue), DAHANDLERREF(int16_t), - DAHANDLERREF(int32_t), DAHANDLERREF(int64_t), DAHANDLERREF(int8_t), - DAHANDLERREF(long_double), DAHANDLERREF(std__string), - DAHANDLERREF(uint16_t), DAHANDLERREF(uint32_t), - DAHANDLERREF(uint64_t), DAHANDLERREF(uint8_t), DAHANDLERREF(unit_t), - DAHANDLERREF(bool), DAHANDLERREF(double), DAHANDLERREF(float)), - OutputType(TypeNumberOf::Value), - NumberOfInputs(sizeof...(As)), - InputTypes({TypeNumberOf::Value...}), - InputChanged(NumberOfInputs, false), - InputValues(new TokenizedStorage()), - FP(triggerHandlerFromProcessingFunction(std::move(F))), + DASLAVEHANDLERREF(AtomValue), DASLAVEHANDLERREF(int16_t), + DASLAVEHANDLERREF(int32_t), DASLAVEHANDLERREF(int64_t), + DASLAVEHANDLERREF(int8_t), DASLAVEHANDLERREF(long_double), + DASLAVEHANDLERREF(std__string), DASLAVEHANDLERREF(uint16_t), + DASLAVEHANDLERREF(uint32_t), DASLAVEHANDLERREF(uint64_t), + DASLAVEHANDLERREF(uint8_t), DASLAVEHANDLERREF(unit_t), + DASLAVEHANDLERREF(bool), DASLAVEHANDLERREF(double), + DASLAVEHANDLERREF(float), DAMASTERHANDLERREF(AtomValue), + DAMASTERHANDLERREF(int16_t), DAMASTERHANDLERREF(int32_t), + DAMASTERHANDLERREF(int64_t), DAMASTERHANDLERREF(int8_t), + DAMASTERHANDLERREF(long_double), DAMASTERHANDLERREF(std__string), + DAMASTERHANDLERREF(uint16_t), DAMASTERHANDLERREF(uint32_t), + DAMASTERHANDLERREF(uint64_t), DAMASTERHANDLERREF(uint8_t), + DAMASTERHANDLERREF(unit_t), DAMASTERHANDLERREF(bool), + DAMASTERHANDLERREF(double), DAMASTERHANDLERREF(float)), + ExecutionPolicy(DeluxeExecutionPolicy::decimation(1)), OutputType(T::TT), + NumberOfInputs(sizeof...(As)), MasterInputType(MT::TT), + NumberOfMasterOutputs(NumberOfInputs), InputTypes({As::TT...}), + InputNextPos(NumberOfInputs, 0), InputChanged(NumberOfInputs, false), + InputValues(makeInputStorages()), MasterInputNextPos(0), + MasterInputChanged(false), + MasterInputValue(new typename TokenizedStorageForTypeList< + typename UnwrapDeluxeTuple::Type>::Type()), + MasterOutputTypes({Ts::TT...}), + FP(triggerHandlerFromProcessingFunctions(std::move(MF), std::move(F), + seq_t())), Slaves(NumberOfInputs) { ASSERT(Kind == atoms::AgentKind); - LOG_TRACE("DeluxeAgent is created."); + LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " is created." << std::endl; ASSERT(inv()); } -template -void DeluxeAgent::sendToMaster(const T &Value) noexcept { - ASSERT(inv() && OutputType == TypeNumberOf::Value); +template +void DeluxeAgent::sendToMaster(const DeluxeTuple &Value, + Seq) noexcept { + STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments"); + ASSERT(inv() && OutputType == TypeToken::Value); + + // The assert must hold if \p this object was successfuuly constructed. + ASSERT((true && ... && + (static_cast(static_cast(S0)) == S0))); + // Create a static constant array for these indices to be available as lvalue + // references when creating messages below. \c S0... when used directly in a + // fold expression is a temporary value, which would result in \c + // rosa::Message instances being created with rvalue references. Further, all + // other values would to copied into a temporary variable for making them + /// available as rvalue references (they are constant lvalue references here). + static constexpr std::array Indices{{S0...}}; + + LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id + << ") sends to master (" + << static_cast(Master && *Master) << "): " << Value + << " (" << sizeof...(S0) << ")" << std::endl; // There is a handle and the referred *master* is in a valid state. if (Master && *Master) { - Master->sendMessage(Message::create(atoms::Slave::Value, Id, Value)); + // Handle each element of the tuple in a fold expression. + (Master->sendMessage(Message::create(atoms::Slave::Value, Id, Indices[S0], + std::get(Value))), + ...); + } + ASSERT(inv()); +} + +template +void DeluxeAgent::sendToSlave(const size_t Pos, const DeluxeTuple &Value, + Seq) noexcept { + STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments"); + ASSERT(inv() && Pos < NumberOfMasterOutputs && + MasterOutputTypes[Pos] == TypeToken::Value); + + // The assert must hold if \p this object was successfuuly constructed. + ASSERT((true && ... && + (static_cast(static_cast(S0)) == S0))); + // Create a static constant array for these indices to be available as lvalue + // references when creating messages below. \c S0... when used directly in a + // fold expression is a temporary value, which would result in \c + // rosa::Message instances being created with rvalue references. Further, all + // other values would to copied into a temporary variable for making them + /// available as rvalue references (they are constant lvalue references here). + static constexpr std::array Indices{{S0...}}; + + // There is a handle and the referred *slave* is in a valid state. + auto Slave = Slaves[Pos]; + + LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id + << ") sends to slave (" << static_cast(Slave && *Slave) + << ") at position " << Pos << ": " << Value << " (" + << sizeof...(S0) << ")" << std::endl; + + if (Slave && *Slave) { + // Handle each element of the tuple in a fold expression. + (Slave->sendMessage(Message::create(atoms::Master::Value, Id, Indices[S0], + std::get(Value))), + ...); } } -template void DeluxeAgent::saveInput(id_t Id, T Value) noexcept { +template +void DeluxeAgent::saveInput(id_t Id, token_size_t Pos, T Value) noexcept { ASSERT(inv() && SlaveIds.find(Id) != SlaveIds.end() && - InputTypes[SlaveIds.find(Id)->second] == TypeNumberOf::Value); + Pos == InputNextPos[SlaveIds.find(Id)->second] && + typeAtPositionOfToken(InputTypes[SlaveIds.find(Id)->second], Pos) == + TypeNumberOf::Value); + + const size_t SlavePos = SlaveIds.at(Id); - size_t Pos = SlaveIds.at(Id); - *static_cast(InputValues->pointerTo(Pos)) = Value; - InputChanged[Pos] = true; + LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id + << ") saves value from slave at position " << SlavePos + << ": (" << static_cast(Pos) << ") " << Value + << std::endl; + + // Save value. + *static_cast(InputValues[SlavePos]->pointerTo(Pos)) = Value; + + // Update position of next value. + if (++InputNextPos[SlavePos] == lengthOfToken(InputTypes[SlavePos])) { + InputNextPos[SlavePos] = 0; + } + + // Set flag. + InputChanged[SlavePos] = true; ASSERT(inv()); } +template +void DeluxeAgent::saveMasterInput(id_t Id, token_size_t Pos, T Value) noexcept { + ASSERT(inv() && Master && masterId() == Id && Pos == MasterInputNextPos && + typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf::Value); + + LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id + << ") saves value from master: (" << static_cast(Pos) + << ") " << Value << std::endl; + + // Save value. + *static_cast(MasterInputValue->pointerTo(Pos)) = Value; + + // Update position of next value. + if (++MasterInputNextPos == lengthOfToken(MasterInputType)) { + MasterInputNextPos = 0; + } + + // Set flag. + MasterInputChanged = true; + + ASSERT(inv()); +} } // End namespace deluxe } // End namespace rosa -#undef DAHANDLEREF -#undef DAHANDLEDEF -#undef DAHANDLEDEFN -#undef DAHANDLENAME +#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 a2aab57..23b8ade 100755 --- a/include/rosa/deluxe/DeluxeAtoms.hpp +++ b/include/rosa/deluxe/DeluxeAtoms.hpp @@ -1,61 +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 +/// \date 2017-2019 /// /// \brief Definition of \c rosa::AtomValue values and \c rosa::AtomConstant /// types for the implementation of the *deluxe interface*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXEATOMS_HPP #define ROSA_DELUXE_DELUXEATOMS_HPP #include "rosa/support/atom.hpp" namespace rosa { namespace deluxe { /// Contains some definitions used in the implementation of the *deluxe /// interface* to denote various roles and events /// /// \see \c rosa::deluxe::DeluxeContext /// /// \note Do not apply `using namespace` to this namespace as that may result in /// some identifiers in the original namespace being hidden by those of /// \c rosa::deluxe::atoms. namespace atoms { /// Value to be used as the *kind* of \c rosa::deluxe::DeluxeSensor. /// /// \see \c rosa::Unit::Kind constexpr AtomValue SensorKind = atom("dl_sensor"); /// Value to be used as the *kind* of \c rosa::deluxe::DeluxeAgent. /// /// \see \c rosa::Unit::Kind constexpr AtomValue AgentKind = atom("dl_agent"); /// Type alias denoting system trigger messages. using Trigger = AtomConstant; /// Type alias denoting messages from a slave. using Slave = AtomConstant; /// Type alias denoting messages from a master. -/// -/// \note This one is not used at the moment. using Master = AtomConstant; } // End namespace atoms } // End namespace deluxe } // End namespace rosa #endif // ROSA_DELUXE_DELUXEATOMS_HPP diff --git a/include/rosa/deluxe/DeluxeContext.hpp b/include/rosa/deluxe/DeluxeContext.hpp old mode 100755 new mode 100644 index 29794a1..f2e8340 --- a/include/rosa/deluxe/DeluxeContext.hpp +++ b/include/rosa/deluxe/DeluxeContext.hpp @@ -1,294 +1,941 @@ //===-- rosa/deluxe/DeluxeContext.hpp ---------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeContext.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Public interface for the *deluxe interface* for working with agent /// systems. /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXECONTEXT_HPP #define ROSA_DELUXE_DELUXECONTEXT_HPP #include "rosa/deluxe/DeluxeSystem.hpp" #include "rosa/support/types.hpp" #include #include #include /// Local helper macro to log and return a /// \c rosa::deluxe::DeluxeContext::ErrorCode value. /// /// Creates a debug message with the stringified value and returns the value. /// /// \param Err \c rosa::deluxe::DeluxeContext::ErrorCode value to log and /// return #define DCRETERROR(Err) \ { \ LOG_DEBUG(#Err); \ return Err; \ } namespace rosa { namespace deluxe { /// Defines the *deluxe interface*. +/// +/// \todo The classes \c rosa::deluxe::DeluxeSensor and \c +/// rosa::deluxe::DeluxeAgent share some common features in relation to their +/// *slave* role in the *deluxe interface*. But their definitions are completely +/// independent. It could be investigated how to lift their common parts into a +/// new *deluxe slave* class, which would serve as base for both, to avoid code +/// duplication. class DeluxeContext { /// A system owned by \p this object. /// /// \note The reference is kept in a \c std::shared_ptr because of the member /// function \c rosa::deluxe::DeluxeContext::getSystem. std::shared_ptr System; /// References to all *sensors* and *agents* created by \p this object. std::set DeluxeUnits; public: /// Errors that may be resulted by some of the member functions of the class. enum struct ErrorCode { NoError, TypeMismatch, NotSensor, NotAgent, + NotUnit, WrongPosition, AlreadyHasSlave, AlreadyHasMaster, - AlreadyHasValueStream + AlreadyHasValueStream, + UnsuitableExecutionPolicy }; /// Returns a new instance of \c rosa::deluxe::DeluxeContext. /// /// \param Name name of the underlying \c rosa::DeluxeSystem /// /// \return \c std::unique_ptr for the new instance of /// \c rosa::deluxe::DeluxeContext with a new, empty \c rosa::DeluxeSystem static std::unique_ptr create(const std::string &Name) noexcept; private: /// Creates a new instance. /// /// \note Private constructor restricts instantiation to member functions of /// the class. /// /// \param Name name of the underlying \c rosa::MessagingSystem DeluxeContext(const std::string &Name) noexcept; public: /// Destroys \p this object. ~DeluxeContext(void) noexcept; /// Returns a reference for the underlying \c rosa::MessagingSystem. /// /// \note One cannot do much with a \c rosa::MessagingSystem currently, this /// is for future use. /// /// \return reference for the underlying \c rosa::MessagingSystem. std::weak_ptr getSystem(void) const noexcept; +private: /// Creates a new *sensor* in the context of \p this object. /// + /// The new *sensor* handles master-input by \p MF. + /// + /// \tparam MT type of master-input the new *sensor* handles /// \tparam T type of data the new *sensor* operates on /// + /// \note Instantiation fails if any of the type arguments \p MT and \p T + /// is not an instance of \c rosa::deluxe::DeluxeTuple or \p T is \c + /// rosa::deluxe::EmptyDeluxeTuple. + /// + /// \param Name name of the new *sensor* + /// \param MF function for the new *sensors* to process master-input + /// values with + /// \param F function for the new *sensor* to generate the next value with + /// during normal operation + /// + /// \note \p F is not used during simulation, in which case + /// \c rosa::deluxe::DeluxeContext::registerSensorValues is used to + /// register an alternative simulation data source with \c + /// rosa::deluxe::DeluxeSensor::registerSimulationDataSource. One may + /// safely keep relying on the default value of \p F as long as only + /// simulation of the system is to be done. + /// + /// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor. + /// + /// \return \c rosa::AgentHandle for the new *sensor* + template >::Value && + !std::is_same::value>> + AgentHandle createSensorImpl(const std::string &Name, + std::function)> &&MF, + std::function &&F) noexcept; + +public: + /// Creates a new *sensor* in the context of \p this object. + /// + /// The new *sensor* does not receive master-input. + /// + /// \tparam T type of data the new *sensor* operates on + /// + /// \note Instantiation fails if type argument \p T is neither a built-in type + /// nor an instance of \c rosa::deluxe::DeluxeTuple with at least one element. + /// /// \param Name name of the new *sensor* /// \param F function for the new *sensor* to generate the next value with /// during normal operation /// /// \note \p F is not used during simulation, in which case /// \c rosa::deluxe::DeluxeContext::registerSensorValues is used to register /// an alternative simulation data source with /// \c rosa::deluxe::DeluxeSensor::registerSimulationDataSource. One may /// safely keep relying on the default value of \p F as long as only /// simulation of the system is to be done. /// + /// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor. + /// + /// \return \c rosa::AgentHandle for the new *sensor* + template ::Value || + (IsDeluxeTuple::Value && + !std::is_same::value)>> + AgentHandle createSensor( + const std::string &Name, + std::function &&F = [](void) { return T(); }) noexcept; + + /// Creates a new *sensor* in the context of \p this object. + /// + /// The new *sensor* handles master-input by \p MF. + /// + /// \tparam MT type of master-input the new *sensor* handles + /// \tparam T type of data the new *sensor* operates on + /// + /// \note The type arguments \p MT and \p T must be either all built-in types + /// or all instances of \c rosa::deluxe::DeluxeTuple. Moreover, \p T cannot be + /// \c rosa::deluxe::EmptyDeluxeTuple. Instantiation fails if these conditions + /// do not hold. + /// + /// \param Name name of the new *sensor* + /// \param MF function for the new *sensors* to process master-input + /// values with \param F function for the new *sensor* to generate + /// the next value with during normal operation + /// + /// \note \p F is not used during simulation, in which case + /// \c rosa::deluxe::DeluxeContext::registerSensorValues is used to + /// register an alternative simulation data source with \c + /// rosa::deluxe::DeluxeSensor::registerSimulationDataSource. One may + /// safely keep relying on the default value of \p F as long as only + /// simulation of the system is to be done. + /// + /// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor. + /// /// \return \c rosa::AgentHandle for the new *sensor* - template - AgentHandle createSensor(const std::string &Name, - DeluxeSensor::D &&F = [](void) { - return T(); - }) noexcept; + template , BuiltinTypes>::Value || + (TypeListAllDeluxeTuple>::Value && + !std::is_same::value)>> + AgentHandle createSensor( + const std::string &Name, std::function)> &&MF, + std::function &&F = [](void) { return T(); }) noexcept; + +private: + /// Creates a new *agent* in the context of \p this object. + /// + /// The new *agent* receives master-input by \p MF and produces + /// master-output. + /// + /// \tparam MT type of master-input the new *agent* handles + /// \tparam T type of data the new *agent* outputs + /// \tparam Ts types of master-output the new *agent* produces + /// \tparam As types of inputs the new *agent* takes + /// + /// \note Instantiation fails if any of the type arguments \p MT, \p T, \p + /// Ts..., and \p As... is not an instance of \c rosa::deluxe::DeluxeTuple or + /// any of \p T and \p As... is \c rosa::deluxe::EmptyDeluxeTuple. + /// + /// \param Name name of the new *agent* + /// \param MF function for the new *agent* to process master-input + /// values with \param F function for the new *agent* to process + /// input values and generate output with + /// + /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. + /// + /// \return \c rosa::AgentHandle for the new *agent* + template >::Value && + !std::is_same::value && + (true && ... && (!std::is_same::value))>> + AgentHandle createAgentImpl( + const std::string &Name, + std::function...>(std::pair)> &&MF, + std::function, Optional...>( + std::pair...)> &&F) noexcept; + +public: + /// Creates a new *agent* in the context of \p this object. + /// + /// The new *agent* neither receives master-input nor produces + /// master-output. + /// + /// \tparam T type of data the new *agent* outputs + /// \tparam As types of inputs the new *agent* takes + /// + /// \note The type arguments \p T and \p As... must be either all built-in + /// types or all instances of \c rosa::deluxe::DeluxeTuple. Moreover, none of + /// them can be \c rosa::deluxe::EmptyDeluxeTuple. Instantiation fails if + /// these conditions do not hold. + /// + /// \param Name name of the new *agent* + /// \param F function for the new *agent* to process input values and + /// generate output with + /// + /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. + /// + /// \return \c rosa::AgentHandle for the new *agent* + template < + typename T, typename... As, + typename = std::enable_if_t< + TypeListSubsetOf, BuiltinTypes>::Value || + (TypeListAllDeluxeTuple>::Value && + !std::is_same::value && + (true && ... && (!std::is_same::value)))>> + AgentHandle + createAgent(const std::string &Name, + std::function(std::pair...)> &&F) noexcept; + + /// Creates a new *agent* in the context of \p this object. + /// + /// The new *agent* receives master-input by \p MF but does not + /// produce master-output. + /// + /// \tparam MT type of master-input the new *agent* handles + /// \tparam T type of data the new *agent* outputs + /// \tparam As types of inputs the new *agent* takes + /// + /// \note The type arguments \p MT, \p T, and \p As... must be either all + /// built-in types or all instances of \c rosa::deluxe::DeluxeTuple. Moreover, + /// none of \p T and \p As... can be \c rosa::deluxe::EmptyDeluxeTuple. + /// Instantiation fails if these conditions do not hold. + /// + /// \param Name name of the new *agent* + /// \param MF function for the new *agent* to process master-input + /// values with + /// \param F function for the new *agent* to process input values and + /// generate output with + /// + /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. + /// + /// \return \c rosa::AgentHandle for the new *agent* + template < + typename MT, typename T, typename... As, + typename = std::enable_if_t< + TypeListSubsetOf, BuiltinTypes>::Value || + (TypeListAllDeluxeTuple>::Value && + !std::is_same::value && + (true && ... && (!std::is_same::value)))>> + AgentHandle + createAgent(const std::string &Name, + std::function)> &&MF, + std::function(std::pair...)> &&F) noexcept; + + /// Creates a new *agent* in the context of \p this object. + /// + /// The new *agent* does not receive master-input but produces + /// master-output. + /// + /// \tparam T type of data the new *agent* outputs + /// \tparam Ts types of master-output the new *agent* produces + /// \tparam As types of inputs the new *agent* takes + /// + /// \note The type arguments \p T, \p Ts, and \p As... must be either all + /// built-in types or all instances of \c rosa::deluxe::DeluxeTuple. Moreover, + /// none of \p T and \p As... can be \c rosa::deluxe::EmptyDeluxeTuple. + /// Instantiation fails if these conditions do not hold. + /// + /// \param Name name of the new *agent* + /// \param F function for the new *agent* to process input values and + /// generate output with + /// + /// \note \p F does not produce master-output for a given position if the + /// corresponding type is \c rosa::deluxe::EmptyDeluxeTuple. It is not + /// possible to disable master-output at any position by using built-in types. + /// + /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. + /// + /// \return \c rosa::AgentHandle for the new *agent* + template < + typename T, typename... Ts, typename... As, + typename = std::enable_if_t< + TypeListSubsetOf, BuiltinTypes>::Value || + (TypeListAllDeluxeTuple>::Value && + !std::is_same::value && + (true && ... && (!std::is_same::value)))>> + AgentHandle + createAgent(const std::string &Name, + std::function, Optional...>( + std::pair...)> &&F) noexcept; /// Creates a new *agent* in the context of \p this object. /// + /// The new *agent* receives master-input by \p MF and produces + /// master-output. + /// + /// \tparam MT type of master-input the new *agent* handles /// \tparam T type of data the new *agent* outputs + /// \tparam Ts types of master-output the new *agent* produces /// \tparam As types of inputs the new *agent* takes /// + /// \note The type arguments \p MT, \p T, \p Ts, and \p As... must be either + /// all built-in types or all instances of \c rosa::deluxe::DeluxeTuple. + /// Moreover, none of \p T and \p As... can be \c + /// rosa::deluxe::EmptyDeluxeTuple. Instantiation fails if these conditions + /// do not hold. + /// /// \param Name name of the new *agent* + /// \param MF function for the new *agent* to process master-input + /// values with /// \param F function for the new *agent* to process input values and /// generate output with /// + /// \note \p F does not produce master-output for a given position if the + /// corresponding type is \c rosa::deluxe::EmptyDeluxeTuple. It is not + /// possible to disable master-output at any position by using built-in types. + /// + /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. + /// /// \return \c rosa::AgentHandle for the new *agent* - template - AgentHandle createAgent(const std::string &Name, - DeluxeAgent::D &&F) noexcept; + template < + typename MT, typename T, typename... Ts, typename... As, + typename = std::enable_if_t< + TypeListSubsetOf, + BuiltinTypes>::Value || + (TypeListAllDeluxeTuple>::Value && + !std::is_same::value && + (true && ... && (!std::is_same::value)))>> + AgentHandle createAgent( + const std::string &Name, + std::function...>(std::pair)> &&MF, + std::function, Optional...>( + std::pair...)> &&F) noexcept; - /// Connectes a *sensor* to an *agent* in the context of \p this object. + /// Returns the current execution policy of the referred \p Unit + /// + /// \see \c rosa::deluxe::DeluxeExecutionPolicy + /// + /// \note The referred \p Unit is either *sensor* or *agent*. + /// + /// \note The returned reference is valid only as long as \c + /// rosa::deluxe::DeluxeContext::setExecutionPolicy() is not called with the + /// *unit* referred by \p Unit and the *unit* is not destroyed. + /// + /// \param Unit the *unit* whose execution policy is to be obtained + /// + /// \return the \c rosa::deluxe::DeluxeExecutionPolicy from \p Unit if \p Unit + /// is valid + Optional + getExecutionPolicy(AgentHandle Unit) const noexcept; + + /// Sets the current execution policy of the referred \p Unit to \p + /// ExecutionPolicy. + /// + /// \see \c rosa::deluxe::DeluxeExecutionPolicy + /// + /// \note The referred \p Unit is either *sensor* or *agent*. + /// + /// \param Unit the *unit* whose execution policy is to be set + /// \param ExecutionPolicy the new execution policy for \p Unit + /// + /// \return how successful setting \p ExecutionPolicy for \p Unit was + /// + /// \note The function may return the following + /// \c rosa::deluxe::DeluxeContext::ErrorCode values: + /// `ErrorCode` | Comment + /// ----------- | ------- + /// `NoError` | Success + /// `NotUnit` | Referred \p Unit is not valid + /// `UnsuitableExecutionPolicy` | \p ExecutionPolicy cannot handle \p Unit + ErrorCode setExecutionPolicy( + AgentHandle Unit, + std::unique_ptr &&ExecutionPolicy) noexcept; + + /// Connects a *sensor* to an *agent* in the context of \p this object. /// /// \param Agent the *agent* to connect to /// \param Pos the index of slot of \p Agent to connect \p Sensor to /// \param Sensor the *sensor* to connect /// \param Description optional textual description of the connection /// - /// \return how successfull connecting \p Sensor to \p Agent at slot index - /// \p Pos was + /// \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 than the output type of \p Sensor + /// `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 + /// \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 + /// `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. + /// 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; - /// Registers a stream providing values for a *sensor* during simulation. +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 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 + /// \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 + /// `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 + template ::Value || IsTuple::Value>> ErrorCode registerSensorValues(AgentHandle Sensor, Iterator &&Start, const Iterator &End, T Default = {}) noexcept; /// Performs the system contained by \p this object. /// - /// The function performs \p NumCycles cycle of simulation. In each cycle, - /// all the *agents* and *sensors* registered in - /// \c rosa::deluxe::DeluxeContext::DeluxeUnits are trigged for execution. + /// 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. + /// \pre All the *sensors* in the system contained by \p this object + /// generate their output from simulation data sources. void simulate(const size_t NumCycles) const noexcept; }; +/// Anonymous namespace with helper features for implementing +/// \c rosa::deluxe::DeluxeContext, consider it private. +namespace { + +/// Maps any type \p T to \c rosa::deluxe::EmptyDeluxeTuple. +template struct MapToEmptyDeluxeTuple { + using Type = EmptyDeluxeTuple; +}; + +/// Convenience template alias for \c MapToEmptyDeluxeTuple. template -AgentHandle DeluxeContext::createSensor(const std::string &Name, - DeluxeSensor::D &&F) noexcept { - AgentHandle H = System->createSensor(Name, std::move(F)); +using empty_deluxe_t = typename MapToEmptyDeluxeTuple::Type; + +/// Converts a \c std::tuple of \c rosa::Optional built-in types into a +/// corresponding \c std::tuple of \c rosa::Optional with each actual value +/// wrapped in \c rosa::deluxe::DeluxeTuple. +/// +/// \tparam Ts types of the values +/// \tparam S0 indices for accessing values in \p Values +/// +/// \param Values the \c std::tuple of \c rosa::Optional with built-in values +/// +/// \note The second argument provides indices statically as template arguments +/// \p S0..., so its actual value is ignored. +/// +/// \return a \c std::tuple of \c rosa::Optional corresponding to \p Values +/// with each actual value wrapped in \c rosa::deluxe::DeluxeTuple +/// +/// \pre Statically, all type arguments \p Ts... are built-in types and the +/// provided indices \p S0... match the length of \p Ts...: \code +/// TypeListSubsetOf, BuiltinTypes>::Value && +/// sizeof...(Ts) == sizeof...(S0) +/// \endcode +template +std::tuple>...> +wrapBuiltinInDeluxeTuple(const std::tuple...> &Values, + Seq) noexcept { + STATIC_ASSERT((TypeListSubsetOf, BuiltinTypes>::Value), + "not built-in types"); + STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent type arguments"); + + return std::make_tuple(std::get(Values) + ? Optional>( + make_deluxe_tuple(*std::get(Values))) + : Optional>()...); +} + +} // End namespace + +template +AgentHandle +DeluxeContext::createSensorImpl(const std::string &Name, + std::function)> &&MF, + std::function &&F) noexcept { + AgentHandle H = System->createSensor(Name, std::move(MF), std::move(F)); DeluxeUnits.emplace(H); return H; } -template -AgentHandle DeluxeContext::createAgent(const std::string &Name, - DeluxeAgent::D &&F) noexcept { - AgentHandle H = System->createAgent(Name, std::move(F)); +template +AgentHandle DeluxeContext::createSensor(const std::string &Name, + std::function &&F) noexcept { + auto EmptyMF = std::function)>( + [](std::pair) {}); + + if constexpr (TypeListContains::Value) { + using OutputType = DeluxeTuple; + return createSensorImpl( + Name, std::move(EmptyMF), + std::function( + [F{std::move(F)}](void) { return OutputType(F()); })); + + } else if constexpr (IsDeluxeTuple::Value && + !std::is_same::value) { + return createSensorImpl(Name, std::move(EmptyMF), std::move(F)); + + } else { + ASSERT(false && "Unexpected type argument"); + } +} + +template +AgentHandle +DeluxeContext::createSensor(const std::string &Name, + std::function)> &&MF, + std::function &&F) noexcept { + + if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { + using MasterInputType = DeluxeTuple; + using OutputType = DeluxeTuple; + return createSensorImpl( + Name, + std::function)>( + [MF{std::move(MF)}](std::pair Arg) { + MF({std::get<0>(Arg.first), Arg.second}); + }), + std::function( + [F{std::move(F)}](void) { return OutputType(F()); })); + + } else if constexpr (TypeListAllDeluxeTuple>::Value && + !std::is_same::value) { + return createSensorImpl(Name, std::move(MF), std::move(F)); + + } else { + ASSERT(false && "Unexpected type arguments"); + } +} + +template +AgentHandle DeluxeContext::createAgentImpl( + const std::string &Name, + std::function...>(std::pair)> &&MF, + std::function, Optional...>( + std::pair...)> &&F) noexcept { + AgentHandle H = System->createAgent(Name, std::move(MF), std::move(F)); DeluxeUnits.emplace(H); return H; } -template +template +AgentHandle DeluxeContext::createAgent( + const std::string &Name, + std::function(std::pair...)> &&F) noexcept { + + using NoMasterOutputType = std::tuple>...>; + auto EmptyMF = + std::function)>( + [](std::pair) { + return NoMasterOutputType(); + }); + + if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { + using OutputType = DeluxeTuple; + return createAgentImpl( + Name, std::move(EmptyMF), + std::function< + std::tuple, Optional>...>( + std::pair, bool>...)>( + [F{std::move(F)}](std::pair, bool>... Args) { + const auto Result = F({std::get<0>(Args.first), Args.second}...); + return std::tuple_cat( + wrapBuiltinInDeluxeTuple(std::tuple(Result), seq_t<1>()), + NoMasterOutputType()); + })); + + } else if constexpr (TypeListAllDeluxeTuple>::Value && + !std::is_same::value && + (true && ... && + (!std::is_same::value))) { + return createAgentImpl( + Name, std::move(EmptyMF), + std::function, Optional>...>( + std::pair...)>( + [F{std::move(F)}](std::pair... Args) { + const auto Result = F(Args...); + return std::tuple_cat(std::tuple(Result), NoMasterOutputType()); + })); + + } else { + ASSERT(false && "Unexpected type arguments"); + } +} + +template +AgentHandle DeluxeContext::createAgent( + const std::string &Name, std::function)> &&MF, + std::function(std::pair...)> &&F) noexcept { + + using NoMasterOutputType = std::tuple>...>; + + if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { + using MasterInputType = DeluxeTuple; + using OutputType = DeluxeTuple; + return createAgentImpl( + Name, + std::function)>( + [MF{std::move(MF)}](std::pair Arg) { + MF({std::get<0>(Arg.first), Arg.second}); + return NoMasterOutputType(); + }), + std::function< + std::tuple, Optional>...>( + std::pair, bool>...)>( + [F{std::move(F)}](std::pair, bool>... Args) { + const auto Result = F({std::get<0>(Args.first), Args.second}...); + return std::tuple_cat( + wrapBuiltinInDeluxeTuple(std::tuple(Result), seq_t<1>()), + NoMasterOutputType()); + })); + + } else if constexpr (TypeListAllDeluxeTuple>::Value && + !std::is_same::value && + (true && ... && + (!std::is_same::value))) { + return createAgentImpl( + Name, + std::function)>( + [MF{std::move(MF)}](std::pair Arg) { + MF(Arg); + return NoMasterOutputType(); + }), + std::function, Optional>...>( + std::pair...)>( + [F{std::move(F)}](std::pair... Args) { + const auto Result = F(Args...); + return std::tuple_cat(std::tuple(Result), NoMasterOutputType()); + })); + + } else { + ASSERT(false && "Unexpected type arguments"); + } +} + +template +AgentHandle DeluxeContext::createAgent( + const std::string &Name, + std::function, Optional...>( + std::pair...)> &&F) noexcept { + + if constexpr (TypeListSubsetOf, + BuiltinTypes>::Value) { + using MasterOutputType = std::tuple>...>; + using OutputType = DeluxeTuple; + return createAgentImpl( + Name, + std::function)>( + [](std::pair) { + return MasterOutputType(); + }), + std::function< + std::tuple, Optional>...>( + std::pair, bool>...)>( + [F{std::move(F)}](std::pair, bool>... Args) { + const auto Result = F({std::get<0>(Args.first), Args.second}...); + return wrapBuiltinInDeluxeTuple(Result, + seq_t<1 + sizeof...(Ts)>()); + })); + + } else if constexpr (TypeListAllDeluxeTuple< + TypeList>::Value && + !std::is_same::value && + (true && ... && + (!std::is_same::value))) { + using MasterOutputType = std::tuple...>; + return createAgentImpl( + Name, + std::function)>( + [](std::pair) { + return MasterOutputType(); + }), + std::function, Optional...>( + std::pair...)>( + [F{std::move(F)}](std::pair... Args) { + const auto Output = F(Args...); + return Output; + })); + + } else { + ASSERT(false && "Unexpected type arguments"); + } +} + +template +AgentHandle DeluxeContext::createAgent( + const std::string &Name, + std::function...>(std::pair)> &&MF, + std::function, Optional...>( + std::pair...)> &&F) noexcept { + + if constexpr (TypeListSubsetOf, + BuiltinTypes>::Value) { + using MasterInputType = DeluxeTuple; + using MasterOutputType = std::tuple>...>; + using OutputType = DeluxeTuple; + return createAgentImpl( + Name, + std::function)>( + [MF{std::move(MF)}](std::pair Arg) { + const auto Result = MF({std::get<0>(Arg.first), Arg.second}); + return wrapBuiltinInDeluxeTuple(Result, seq_t()); + }), + std::function< + std::tuple, Optional>...>( + std::pair, bool>...)>( + [F{std::move(F)}](std::pair, bool>... Args) { + const auto Result = F({std::get<0>(Args.first), Args.second}...); + return wrapBuiltinInDeluxeTuple(Result, + seq_t<1 + sizeof...(Ts)>()); + })); + + } else if constexpr (TypeListAllDeluxeTuple< + TypeList>::Value && + !std::is_same::value && + (true && ... && + (!std::is_same::value))) { + using MasterOutputType = std::tuple...>; + return createAgentImpl( + Name, + std::function)>( + [MF{std::move(MF)}](std::pair Arg) { + const auto Output = MF(Arg); + return Output; + }), + std::function, Optional...>( + std::pair...)>( + [F{std::move(F)}](std::pair... Args) { + const auto Output = F(Args...); + return Output; + })); + + } else { + ASSERT(false && "Unexpected type arguments"); + } +} + +template DeluxeContext::ErrorCode DeluxeContext::registerSensorValues(AgentHandle Sensor, Iterator &&Start, const Iterator &End, T Default) noexcept { // Get the type of values provided by \p Iterator. STATIC_ASSERT((std::is_same::value), "type mismatch"); // Make sure preconditions are met. if (!System->isDeluxeSensor(Sensor)) { DCRETERROR(ErrorCode::NotSensor); } auto S = System->getDeluxeSensor(Sensor); ASSERT(S); // Sanity check. - if (S->OutputType != TypeNumberOf::Value) { - DCRETERROR(ErrorCode::TypeMismatch); - } else if (S->simulationDataSourceIsSet()) { + + if (S->simulationDataSourceIsSet()) { DCRETERROR(ErrorCode::AlreadyHasValueStream); } - // Register input stream. - // \note Need to capture parameters by value so having local copies. - S->registerSimulationDataSource( - DeluxeSensor::D([=](void) mutable noexcept { - if (Start != End) { - LOG_TRACE_STREAM << "Reading next value for sensor '" << S->FullName - << "': " << *Start << '\n'; - return *Start++; - } else { - LOG_TRACE_STREAM << "Providing default value for sensor '" - << S->FullName << "': " << Default << '\n'; - return Default; - } - })); + if constexpr (TypeListContains::Value) { + if (S->OutputType != TypeToken::Value) { + DCRETERROR(ErrorCode::TypeMismatch); + } + + // Register input stream. + // \note Need to capture parameters by value so having local copies. + S->registerSimulationDataSource(std::function(void)>([= + ](void) mutable noexcept->DeluxeTuple { + if (Start != End) { + LOG_TRACE_STREAM << "Reading next value for sensor '" << S->FullName + << "': " << *Start << '\n'; + return make_deluxe_tuple(*Start++); + } else { + LOG_TRACE_STREAM << "Providing default value for sensor '" + << S->FullName << "': " << Default << '\n'; + return make_deluxe_tuple(Default); + } + })); + + } else if constexpr (IsTuple::Value) { + + using TT = matching_deluxe_tuple_t; + if (std::is_same::value || S->OutputType != TT::TT) { + DCRETERROR(ErrorCode::TypeMismatch); + } + + // Register input stream. + // \note Need to capture parameters by value so having local copies. + S->registerSimulationDataSource( + std::function([=](void) mutable noexcept->TT { + if (Start != End) { + const TT DV(*Start++); + LOG_TRACE_STREAM << "Reading next value for sensor '" << S->FullName + << "': " << DV << '\n'; + return DV; + } else { + static const TT DD(Default); + LOG_TRACE_STREAM << "Providing default value for sensor '" + << 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 new file mode 100644 index 0000000..223ca13 --- /dev/null +++ b/include/rosa/deluxe/DeluxeExecutionPolicy.h @@ -0,0 +1,201 @@ +//===-- rosa/deluxe/DeluxeExecutionPolicy.h ---------------------*- C++ -*-===// +// +// The RoSA Framework +// +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/deluxe/DeluxeExecutionPolicy.h +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Public interface of *execution policies* in the *deluxe interface*. +/// +//===----------------------------------------------------------------------===// + +#ifndef ROSA_DELUXE_DELUXEEXECUTIONPOLICY_H +#define ROSA_DELUXE_DELUXEEXECUTIONPOLICY_H + +#include "rosa/core/AgentHandle.hpp" + +#include +#include +#include +#include + +namespace rosa { +namespace deluxe { + +// Forward declaration of DeluxeSystem. Do not include the corresponding header +// in this file because of cyclic dependency. +class DeluxeSystem; + +/// *Execution policy* that controls how *agents* and *sensors* call their +/// processing functions. +/// +/// An *execution policy* can be applied to a deluxe *unit* only if \c +/// deluxe::rosa::DeluxeExecutionPolicy::canHandle() allows it. Each deluxe +/// *unit* must have a compatible *execution policy* associated to it, and the +/// *unit* queries \c rosa::deluxe::DeluxeExecutionPolicy::shouldProcess() on each +/// triggering and calls its processing funtion only if it is allowed by the +/// *execution policy*. +/// +/// \see rosa::deluxe::DeluxeExecutionPolicy::decimation() +/// \see rosa::deluxe::DeluxeExecutionPolicy::awaitAll() +/// \see rosa::deluxe::DeluxeExecutionPolicy::awaitAny() +/// +/// \todo Extend the interface with query functions about what kind of +/// execution policy is behind the interface. This can be done in relation +/// to the existing factory functions; for example, if the actual object is +/// decimation and with what rate. +class DeluxeExecutionPolicy { +protected: + + /// Protected constructor, only implementations can instantiate the class. + DeluxeExecutionPolicy(void) noexcept = default; + +private: + /// No instance can be copy-constructed, move-constructed, copied, and moved. + /// + ///@{ + DeluxeExecutionPolicy(const DeluxeExecutionPolicy &) = delete; + DeluxeExecutionPolicy(DeluxeExecutionPolicy &&) = delete; + DeluxeExecutionPolicy &operator=(const DeluxeExecutionPolicy &) = delete; + DeluxeExecutionPolicy &operator=(DeluxeExecutionPolicy &&) = delete; + ///@} + +public: + /// Virtual destructor for subclasses. + virtual ~DeluxeExecutionPolicy(void) noexcept = default; + + /// Creates an *execution policy* that allows execution with decimation of + /// triggering. + /// + //// *Decimation* can handle both *agents* and *sensors*. + /// Processing functions are executed only on every \p D th + /// triggering. In the case of *sensors* in simulation, the simulation data + /// source is read on each triggering as it provides values with respect to + /// the highest execution frequency, but output is generated by the *sensor* + /// only on every \p D th triggering. + /// + /// \note A rate of \c 0 is allowed as actual argument and is treated as rate + /// \c 1 (i.e., execute processing functions on each triggering). + /// + /// \param D the rate of *decimation* + /// + /// \return an *execution policy* implementing *decimation* with rate \p D + static std::unique_ptr decimation(const size_t D); + + /// Creates an *execution policy* that allows execution only if all defined + /// *slave* positions has new input. + /// + /// *Await all* can handle only *agents* and only if the particular *agent* + /// has at least as many *slave* positions as the largest position defined in + /// \p S. Processing functions are executed only if new input has been + /// received for all defined *slave* positions. + /// + /// \param S set of *slave* positions to await input from + /// + /// \return an *execution policy* implementing *awaiting all* input from set + /// \p S + static std::unique_ptr + awaitAll(const std::set &S); + + /// Creates an *execution policy* that allows execution if any of the defined + /// *slave* positions has new input. + /// + /// *Await any* can handle only *agents* and only if the particular *agent* + /// has at least as many *slave* positions as the largest position defined in + /// \p S. Processing functions are executed if new input has been received for + /// any of the defined *slave* positions. + /// + /// \param S set of *slave* positions to await input from + /// + /// \return an *execution policy* implementing *awaiting any* input from set + /// \p S + static std::unique_ptr + awaitAny(const std::set &S); + + /// Tells if \p this object can handle the deluxe *unit* referred by \p H. + /// + /// The *execution policy* implemented by \p this object is applicable to the + /// given deluxe *unit* referred by \p H only if the function returns \c true. + /// + /// \param H reference to the *unit* to check + /// \param S the system owning the *unit* referred by \p H + /// + /// \return if \p this object can handle the *unit* referred by \p H + virtual bool canHandle(const AgentHandle H, const DeluxeSystem &S) const + noexcept = 0; + + /// Tells if processing function should be executed on the current triggering. + /// + /// The function is to be called on each triggering of the deluxe *unit*. + /// Decision about execution of processing function is done by \p this object + /// according to the implemented *execution policy*. + /// + /// \param InputChanged flags indicating whether new input has been received + /// at *slave* positions + /// + /// \return if to execute processing function + virtual bool shouldProcess(const std::vector &InputChanged) noexcept = 0; + + /// Dumps \p this object into textual representation. + /// + /// \return textual representation of \p this object + virtual std::string dump(void) const noexcept = 0; + +protected: + /// Tells whether the *unit* referred by \p H is a \c + /// rosa::deluxe::DeluxeAgent. + /// + /// \param H reference to the *unit* to check + /// \param S the system owning the *unit* referred by \p H + /// + /// \return if the *unit* referred by \p H is a \c rosa::deluxe::DeluxeAgent + bool isDeluxeAgent(const AgentHandle H, const DeluxeSystem &S) const noexcept; + + /// Tells the number of inputs handled by the *unit* referred by \p H. + /// + /// If \p H refers to a \c rosa::deluxe::DeluxeAgent, the function returns the + /// number of inputs (i.e., *slave* positions) of the *agent*. Otherwise, the + /// function returns \c 0. + /// + /// \param H reference to the *unit* to check + /// \param S the system owning the *unit* referred by \p H + /// + /// \return the number of inputs handled by the *unit* referred by \p H + size_t numberOfDeluxeAgentInputs(const AgentHandle H, + const DeluxeSystem &S) const noexcept; +}; + +} // End namespace deluxe +} // End namespace rosa + +namespace std { + +/// Converts a \c rosa::deluxe::DeluxeExecutionPolicy into \c std::string. +/// +/// \param EP \c rosa::deluxe::DeluxeExecutionPolicy to convert +/// +/// \return \c std::string representing \p EP +string to_string(const rosa::deluxe::DeluxeExecutionPolicy &EP); + +/// Dumps a \c rosa::deluxe::DeluxeExecutionPolicy to a given \c std::ostream. +/// +/// \param [in,out] OS output stream to dump to +/// \param EP \c rosa::deluxe::DeluxeExecutionPolicy to dump +/// +/// \return \p OS after dumping \p EP to it +ostream &operator<<(ostream &OS, const rosa::deluxe::DeluxeExecutionPolicy &EP); + +} // End namespace std + +#endif // ROSA_DELUXE_DELUXEEXECUTIONPOLICY_H diff --git a/include/rosa/deluxe/DeluxeSensor.hpp b/include/rosa/deluxe/DeluxeSensor.hpp old mode 100755 new mode 100644 index 46ca0c1..afa2693 --- a/include/rosa/deluxe/DeluxeSensor.hpp +++ b/include/rosa/deluxe/DeluxeSensor.hpp @@ -1,257 +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 { -public: - /// Template alias for function objects used as data source for - /// \c rosa::deluxe::DeluxeSensor. + /// Checks whether \p this object holds the class invariant. /// - /// \note The function used for \c D is to be \c noexcept. + /// \see Invariant of the class \c rosa::deluxe::DeluxeSensor /// - /// \tparam T type of data provided by the function - template using D = std::function; + /// \return if \p this object holds the class invariant + bool inv(void) const noexcept; + + /// The \c rosa::deluxe::DeluxeExecutionPolicy that controls the execution of + /// \c this object. + std::unique_ptr ExecutionPolicy; +public: /// The type of values produced by \p this object. /// - /// That is the type of values \p this object sends to its *master*. + /// 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 TypeNumber OutputType; + const Token OutputType; + + /// The type of values \p this object processes from its *master*. + /// + /// That is the types of values \p this object receives from its *master* in a + /// \c rosa::deluxe::DeluxeTuple. + /// + /// \see \c rosa::deluxe::DeluxeSensor::master + const Token MasterInputType; private: + /// Indicates which element of the master-input is expected from the *master*. + /// + /// The *master* is supposed to send one \c rosa::deluxe::DeluxeTuple value + /// element by element in their order of definition. This member field tells + /// the element at which position should be received next. + /// + /// \p this object is supposed to be triggered only when a complete + /// master-input has been received, that is the field should hold the value + /// `0`. + /// + /// \see \c rosa::deluxe::DeluxeSensor::handleTrigger + /// \c rosa::deluxe::DeluxeSensor::saveMasterInput + token_size_t MasterInputNextPos; + + /// Indicates whether the input value from the *master* has been changed since + /// the last trigger received from the system. + /// + /// The flag is reset to \c false upon handling a trigger and then set to \c + /// true by \c rosa::deluxe::DeluxeSensor::saveMasterInput when storig a new + /// input value in \c rosa::deluxe::DeluxeSensor::MasterInputValue. + bool MasterInputChanged; + + /// Stores the actual input value from *master*. + /// + /// \note The type of the stored value matches the types indicated by \c + /// rosa::deluxe::DeluxeSensor::MasterInputType. + const std::unique_ptr MasterInputValue; + /// Alias for function objects used as trigger handler for /// \c rosa::deluxe::DeluxeSensor. /// /// \note The function used for \c H is to be \c noexcept. /// /// \see \c DeluxeSensorTriggerHandlers using H = std::function; - /// \defgroup DeluxeSensorTriggerHandlers Trigger handlers of rosa::deluxe::DeluxeSensor + /// \defgroup DeluxeSensorTriggerHandlers Trigger handlers of + /// rosa::deluxe::DeluxeSensor /// /// \brief Trigger handler functions of \c rosa::deluxe::DeluxeSensor /// - /// The actual data source functions are captured in a lambda expression that - /// is in turn wrapped in a \c std::function object. The lambda expression - /// calls the data source function to obtain the next sensory value and sends - /// it to *master* by calling \c rosa::deluxe::DeluxeSensor::sendToMaster. The - /// function \c rosa::deluxe::DeluxeSensor::handleTrigger needs only to call - /// the proper function object. + /// 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. - /// Handles trigger during normal execution. + /// 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; - /// Handles trigger during simulation. + /// Produces the next sensory value during simulation. /// /// \ingroup DeluxeSensorTriggerHandlers /// /// The function is empty by default. The simulation environment sets it to be /// used during simulation. H SFP; /// The *master* to send values to. /// /// \note *Masters* are set dynamically, hence it is possible that a /// \c rosa::deluxe::DeluxeSensor instance does not have any *master* at a /// given moment. Optional Master; + /// Tells the unique identifier of the *master* of \p this object, if any + /// registered. + /// + /// \return the unique identifier of the *master* + /// + /// \pre A *master* is registered for \p this object: \code + /// Master + /// \endcode + id_t masterId(void) const noexcept; + + /// Wraps a master-input processing function into a trigger handler. + /// + /// \see \c rosa::deluxe::DeluxeSensor::MFP and \c DeluxeSensorTriggerHandlers + /// + /// \tparam Ts types of elements of master-input processed by \p MF + /// \tparam S0 indices for accessing master-input values + /// + /// \param MF function that processes master-input + /// + /// \note The second argument provides indices statically as template + /// arguments \p S0..., so its actual value is ignored. + /// + /// \note A master-input type of \c rosa::deluxe::EmptyDeluxeTuple indicates + /// that \p this object does not receive master-input, \p MF is never called + /// if \p Ts is empty. + /// + /// \return trigger handler function based on \p MF + /// + /// \pre Statically, the indices match the elements: \code + /// sizeof...(Ts) == sizeof...(S0) + /// \endcode Dynamically, \p Ts... match \c + /// rosa::deluxe::DeluxeSensor::MasterInputType: \code + /// MasterInputType == DeluxeTuple::TT + /// \endcode + template + H triggerHandlerFromProcessingFunction( + std::function, bool>)> &&MF, + Seq) noexcept; + /// Wraps a data source function into a trigger handler. /// - /// \see \c DeluxeSensorTriggerHandlers + /// \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 \p T matches \c rosa::deluxe::DeluxeSensor::OutputType: \code - /// OutputType == TypeNumberOf::Value + /// \pre Statically, the type agument \p T is an instance of \c + /// rosa::deluxe::DeluxeTuple: \code + /// IsDeluxeTuple::Value + /// \endcode Dynamically, \p T matches \c + /// rosa::deluxe::DeluxeSensor::OutputType: \code + /// OutputType == T::TT /// \endcode - template H triggerHandlerFromDataSource(D &&F) noexcept; + template + H triggerHandlerFromDataSource(std::function &&F, + bool inSimulation) noexcept; public: /// Creates a new instance. /// /// The constructor instantiates the base-class with functions to handle /// messages as defined for the *deluxe interface*. /// - /// \todo Enforce F does not potentially throw exception. + /// \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 T is a built-in type:\code - /// TypeListContains::Value + /// \pre Statically, \p MT and \p T are instances of \c + /// rosa::deluxe::DeluxeTuple and \p T contains at least one element:\code + /// TypeListAllDeluxeTuple>::Value && T::Length > 0 /// \endcode /// Dynamically, the instance is created as of kind /// \c rosa::deluxe::atoms::SensorKind: /// \code /// Kind == rosa::deluxe::atoms::SensorKind /// \endcode - template ::Value>> + /// + /// \see \c rosa::deluxe::DeluxeTuple + template < + typename MT, typename T, + typename = std::enable_if_t< + TypeListAllDeluxeTuple>::Value && (T::Length > 0)>> DeluxeSensor(const AtomValue Kind, const id_t Id, const std::string &Name, - MessagingSystem &S, D &&F) noexcept; + MessagingSystem &S, + std::function)> &&MF, + std::function &&F) noexcept; /// Destroys \p this object. ~DeluxeSensor(void) noexcept; + /// Returns the current execution policy of \p this object. + /// + /// \see \c rosa::deluxe::DeluxeExecutionPolicy + /// + /// \note The returned reference is valid only as long as \c + /// rosa::deluxe::DeluxeSensor::setExecutionPolicy() is not called and \p this + /// object is not destroyed. + /// + /// \return \c rosa::deluxe::DeluxeSensor::ExecutionPolicy + const DeluxeExecutionPolicy &executionPolicy(void) const noexcept; + + /// Sets the current execution policy of \p this object to \p EP. + /// + /// \see \c rosa::deluxe::DeluxeExecutionPolicy + /// + /// \note \p EP is set only if it can handle \p this object. + /// + /// \param EP the new execution policy for \p this object + /// + /// \return if \p EP was successfully set for \p this object. + bool setExecutionPolicy(std::unique_ptr &&EP) noexcept; + /// The *master* of \p this object, if any. /// /// \see \c rosa::deluxe::DeluxeSensor::registerMaster /// /// \return the *master* registered for \p this object Optional master(void) const noexcept; /// Registers a *master* for \p this object. /// /// The new *master* is registered by overwriting the reference to any /// already registered *master*. One can clear the registered reference by /// passing an *empty* \c rosa::Optional object as actual argument. /// /// \note The role of the referred *master* is validated by checking its /// *kind*. /// + /// \note Any call to \c rosa::deluxe::DeluxeSensor::registerMaster should be + /// paired with a corresponding call of \c + /// rosa::deluxe::DeluxeAgent::registerSlave, which validates that + /// input/output types of master and slave matches. + /// /// \param _Master the *master* to register /// /// \pre \p Master is empty or of kind \c rosa::deluxe::atoms::AgentKind: /// \code /// !_Master || unwrapAgent(*_Master).Kind == rosa::deluxe::atoms::AgentKind /// \endcode void registerMaster(const Optional _Master) noexcept; /// Clears the simulation trigger handler of \p this object. /// /// The function assigns \c rosa::deluxe::DeluxeSensor::SFP with \c nullptr. void clearSimulationDataSource(void) noexcept; /// Tells whether a simulation trigger handler is set for \p this object. /// /// The function returns whether \c rosa::deluxe::DeluxeSensor::SFP is not /// \c nullptr. /// /// \return if a simulation trigger handler is set for \p this object. bool simulationDataSourceIsSet(void) const noexcept; /// Registers a simulation data source for \p this object. /// /// A new simulation trigger handler wrapping \p SF is stored in /// \c rosa::deluxe::DeluxeSensor::SFP by overwriting any already registered /// simulation data source. /// /// \todo Enforce SF does not potentially throw exception. /// - /// \tparam T type of data provided by \p SF + /// \tparam Ts types of elements of values provided by \p SF /// /// \param SF function to generate value with /// - /// \pre \p T matches \c rosa::deluxe::DeluxeSensor::OutputType: \code - /// OutputType == TypeNumberOf::Value + /// \pre \p Ts... match \c rosa::deluxe::DeluxeSensor::OutputType: \code + /// OutputType == TypeToken::Value /// \endcode - template void registerSimulationDataSource(D &&SF) noexcept; + template + void registerSimulationDataSource( + std::function(void)> &&SF) noexcept; private: /// Sends a value to the *master* of \p this object. /// /// \p Value is getting sent to \c rosa::deluxe::DeluxeSensor::Master if it /// contains a valid handle for a \c rosa::deluxe::DeluxeAgent. The function /// does nothing otherwise. /// - /// \tparam T type of the value to send + /// 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 /// - /// \pre \p T matches \c rosa::deluxe::DeluxeSensor::OutputType: \code - /// OutputType == TypeNumberOf::Value + /// \note The second argument provides indices statically as template + /// arguments \p S0..., so its actual value is ignored. + /// + /// \pre Statically, the indices match the elements: \code + /// sizeof...(Ts) == sizeof...(S0) + /// \endcode Dynamically, \p Ts match \c + /// rosa::deluxe::DeluxeSensor::OutputType: \code + /// OutputType == TypeToken::Value /// \endcode - template void sendToMaster(const T &Value) noexcept; + template + void sendToMaster(const DeluxeTuple &Value, Seq) noexcept; - /// Generates the next sensory value upon trigger from the system. + /// Handles master-input and generates the next sensory value upon trigger + /// from the system. /// - /// Executes \c rosa::deluxe::DeluxeSensor::FP or - /// \c rosa::deluxe::DeluxeSensor::SFP if set. + /// Executes \c rosa::deluxe::DeluxeSensor::MFP for processing master-input + /// and data generating function \c rosa::deluxe::DeluxeSensor::FP or \c + /// rosa::deluxe::DeluxeSensor::SFP if set. /// /// \note The only argument is a \c rosa::AtomConstant, hence its actual /// value is ignored. + /// + /// \pre Master-input is supposed to be completely received upon triggering: + /// \code + /// MasterInputNextPos == 0 + /// \endcode void handleTrigger(atoms::Trigger) noexcept; + + /// Stores a new input value from the *master*. + /// + /// The function stores \p Value at position \p Pos in \c + /// rosa::deluxe::DeluxeSensor::MasterInputValue and also sets the + /// flag \c rosa::deluxe::DeluxeSensor::MasterInputChanged. The function also + /// takes care of checking and updating \c + /// rosa::deluxe::DeluxeSensor::MasterInputNextPos: increments its value and + /// resets it to `0` when the last element is received. + /// + /// \note Utilized by member functions of group \c + /// DeluxeSensorMasterInputHandlers. + /// + /// \tparam T type of input to store + /// + /// \param Id unique identifier of the *master* + /// \param Pos position of the value in the \c rosa::deluxe::DeluxeTuple + /// \param Value the input value to store + /// + /// \pre The *master* with \p Id is registered, \p Pos is the expected + /// position of master-input, and the input from the *master* at position \p + /// Pos is expected to be of type \p T: \code + /// Master && masterId() == Id && Pos == MasterInputNextPos && + /// typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf::Value + /// \endcode + template + void saveMasterInput(id_t Id, token_size_t Pos, T Value) noexcept; + + /// \defgroup DeluxeSensorMasterInputHandlers Master-input handlers of + /// rosa::deluxe::DeluxeSensor + /// + /// Definition of member functions handling messages from the *master* with + /// different types of input + /// + /// A *slave* generally needs to be prepared to deal with values of any + /// built-in type to handle messages from its *master*. Each type requires a + /// separate message handler, which are implemented by these functions. The + /// functions instantiate \c rosa::deluxe::DeluxeSensor::saveMasterInput with + /// the proper template argument and pass the content of the message on for + /// processing. + /// + /// \note The member functions in this group are defined by \c + /// DSMASTERHANDLERDEF. + /// + /// \note Keep these definitions in sync with \c rosa::BuiltinTypes. + /// + ///@{ + + DSMASTERHANDLERDEF(AtomValue) + DSMASTERHANDLERDEF(int16_t) + DSMASTERHANDLERDEF(int32_t) + DSMASTERHANDLERDEF(int64_t) + DSMASTERHANDLERDEF(int8_t) + DSMASTERHANDLERDEFN(long double, long_double) + DSMASTERHANDLERDEFN(std::string, std__string) + DSMASTERHANDLERDEF(uint16_t) + DSMASTERHANDLERDEF(uint32_t) + DSMASTERHANDLERDEF(uint64_t) + DSMASTERHANDLERDEF(uint8_t) + DSMASTERHANDLERDEF(unit_t) + DSMASTERHANDLERDEF(bool) + DSMASTERHANDLERDEF(double) + DSMASTERHANDLERDEF(float) + + /// @} }; +template +DeluxeSensor::H DeluxeSensor::triggerHandlerFromProcessingFunction( + std::function, bool>)> &&MF, + Seq) noexcept { + using MT = DeluxeTuple; + STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments"); + ASSERT(MasterInputType == MT::TT); + + // NOTE: Clang 6 warns about unused lambda captures; we suppress that + // warning (those variables need to be captured). +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-lambda-capture" +#endif // defined __clang__ + return [ this, MF ](void) noexcept { + // Do not do anything for master-input type \c + // rosa::deluxe::EmptyDeluxeTuple. + if constexpr (!std::is_same::value) { + LOG_TRACE_STREAM << "DeluxeSensor " << FullName + << " handles master-input." << std::endl; + // The assert must hold if \p this object was successfuuly constructed. + ASSERT((true && ... && + (static_cast(static_cast(S0)) == S0))); + const auto MasterInputArg = std::make_pair( + // Get all elements of the tuple in a fold expression. + DeluxeTuple(*static_cast( + MasterInputValue->pointerTo(static_cast(S0)))...), + MasterInputChanged); + MasterInputChanged = false; + MF(MasterInputArg); + } + }; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif // defined __clang__ +} + template -DeluxeSensor::H DeluxeSensor::triggerHandlerFromDataSource(D &&F) noexcept { - ASSERT(OutputType == TypeNumberOf::Value); - return [this, F](void) noexcept { sendToMaster(F()); }; +DeluxeSensor::H +DeluxeSensor::triggerHandlerFromDataSource(std::function &&F, + bool inSimulation) noexcept { + STATIC_ASSERT(IsDeluxeTuple::Value, "not tuple type argument"); + ASSERT(OutputType == T::TT); + return [ this, F, inSimulation ](void) noexcept { + // Get value and send it to master only if \p ExecutionPolicy allows it. + if (ExecutionPolicy->shouldProcess({})) { + LOG_TRACE_STREAM << "DeluxeSensor " << Name << " obtains next value." + << std::endl; + sendToMaster(F(), seq_t()); + } else { + LOG_TRACE_STREAM << "DeluxeSensor " << Name << " skips next value." + << std::endl; + if (inSimulation) { + // But read input value in Simulation anyway as input values are + // provided for the highest execution frequency for simulation + (void)F(); + } + } + }; } -template +template DeluxeSensor::DeluxeSensor(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, - D &&F) noexcept - : Agent(Kind, Id, Name, S, THISMEMBER(handleTrigger)), - OutputType(TypeNumberOf::Value), - FP(triggerHandlerFromDataSource(std::move(F))), - SFP(nullptr) { + std::function)> &&MF, + std::function &&F) noexcept + : Agent(Kind, Id, Name, S, THISMEMBER(handleTrigger), + DSMASTERHANDLERREF(AtomValue), DSMASTERHANDLERREF(int16_t), + DSMASTERHANDLERREF(int32_t), DSMASTERHANDLERREF(int64_t), + DSMASTERHANDLERREF(int8_t), DSMASTERHANDLERREF(long_double), + DSMASTERHANDLERREF(std__string), DSMASTERHANDLERREF(uint16_t), + DSMASTERHANDLERREF(uint32_t), DSMASTERHANDLERREF(uint64_t), + DSMASTERHANDLERREF(uint8_t), DSMASTERHANDLERREF(unit_t), + DSMASTERHANDLERREF(bool), DSMASTERHANDLERREF(double), + DSMASTERHANDLERREF(float)), + ExecutionPolicy(DeluxeExecutionPolicy::decimation(1)), OutputType(T::TT), + MasterInputType(MT::TT), MasterInputNextPos(0), MasterInputChanged(false), + MasterInputValue(new typename TokenizedStorageForTypeList< + typename UnwrapDeluxeTuple::Type>::Type()), + MFP(triggerHandlerFromProcessingFunction(std::move(MF), + seq_t())), + FP(triggerHandlerFromDataSource(std::move(F), false)), SFP(nullptr) { ASSERT(Kind == atoms::SensorKind); - LOG_TRACE("DeluxeSensor is created."); + LOG_TRACE_STREAM << "DeluxeSensor " << FullName << " is created." + << std::endl; + ASSERT(inv()); } -template -void DeluxeSensor::registerSimulationDataSource(D &&SF) noexcept { - ASSERT(OutputType == TypeNumberOf::Value); - SFP = triggerHandlerFromDataSource(std::move(SF)); +template +void DeluxeSensor::registerSimulationDataSource( + std::function(void)> &&SF) noexcept { + ASSERT(OutputType == TypeToken::Value); + SFP = triggerHandlerFromDataSource(std::move(SF), true); + ASSERT(inv()); } -template -void DeluxeSensor::sendToMaster(const T &Value) noexcept { - ASSERT(OutputType == TypeNumberOf::Value); +template +void DeluxeSensor::sendToMaster(const DeluxeTuple &Value, + Seq) noexcept { + STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments"); + ASSERT(OutputType == TypeToken::Value); + + // The assert must hold if \p this object was successfuuly constructed. + ASSERT((true && ... && + (static_cast(static_cast(S0)) == S0))); + // Create a static constant array for these indices to be available as lvalue + // references when creating messages below. \c S0... when used directly in a + // fold expression is a temporary value, which would result in \c + // rosa::Message instances being created with rvalue references. Further, all + // other values would to copied into a temporary variable for making them + /// available as rvalue references (they are constant lvalue references here). + static constexpr std::array Indices{{S0...}}; + + LOG_TRACE_STREAM << "DeluxeSensor " << FullName << "(" << Id + << ") sends to master(" + << static_cast(Master && *Master) << "): " << Value + << std::endl; // There is a handle and the referred *master* is in a valid state. if (Master && *Master) { - Master->sendMessage(Message::create(atoms::Slave::Value, Id, Value)); + // Handle each element of the tuple in a fold expression. + (Master->sendMessage(Message::create(atoms::Slave::Value, Id, Indices[S0], + std::get(Value))), + ...); } + ASSERT(inv()); +} + +template +void DeluxeSensor::saveMasterInput(id_t Id, token_size_t Pos, + T Value) noexcept { + ASSERT(Master && masterId() == Id && Pos == MasterInputNextPos && + typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf::Value); + + LOG_TRACE_STREAM << "DeluxeSensor " << FullName << "(" << Id + << ") saves value from master: (" << static_cast(Pos) + << ") " << Value << std::endl; + + // Save value. + *static_cast(MasterInputValue->pointerTo(Pos)) = Value; + + // Update position of next value. + if (++MasterInputNextPos == lengthOfToken(MasterInputType)) { + MasterInputNextPos = 0; + } + + // Set flag. + MasterInputChanged = true; } } // End namespace deluxe } // End namespace rosa +#undef DSMASTERHANDLEREF +#undef DSMASTERHANDLEDEF +#undef DSMASTERHANDLEDEFN +#undef DSMASTERHANDLENAME + #endif // ROSA_DELUXE_DELUXESENSOR_HPP diff --git a/include/rosa/deluxe/DeluxeSystem.hpp b/include/rosa/deluxe/DeluxeSystem.hpp old mode 100755 new mode 100644 index 4388a4e..2364d2b --- a/include/rosa/deluxe/DeluxeSystem.hpp +++ b/include/rosa/deluxe/DeluxeSystem.hpp @@ -1,211 +1,246 @@ //===-- rosa/deluxe/DeluxeSystem.hpp ----------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeSystem.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Specialization of \c rosa::MessagingSystem for the *deluxe /// interface*. /// /// \see \c rosa::deluxe::DeluxeContext /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXESYSTEM_HPP #define ROSA_DELUXE_DELUXESYSTEM_HPP #include "rosa/core/MessagingSystem.hpp" #include "rosa/deluxe/DeluxeAgent.hpp" #include "rosa/deluxe/DeluxeSensor.hpp" namespace rosa { namespace deluxe { /// Implements and extends the \c rosa::MessagingSystem interface to be /// used by \c rosa::deluxe::DeluxeContext. /// /// The class is a specialization of \c rosa::MessagingSystem, where objects /// of two specialized subtypes of \c rosa::Agent, \c rosa::deluxe::DeluxeSensor /// and \c rosa::deluxe::DeluxeAgent, constitute a system. The class extends the /// \c rosa::MessagingSystem interface with features required to implement the /// *deluxe interface*. /// /// \see rosa::deluxe::DeluxeContext class DeluxeSystem : public MessagingSystem { friend class DeluxeContext; + friend class DeluxeExecutionPolicy; public: /// Returns an object implementing the \c rosa::deluxe::DeluxeSystem /// interface. /// /// \param Name name of the new instance /// /// \return \c std::unique_ptr for the new instance of /// \c rosa::DeluxeSystem static std::unique_ptr createSystem(const std::string &Name) noexcept; protected: /// Creates a new instance. /// /// \note Protected constructor restricts instantiation for subclasses. DeluxeSystem(void) noexcept = default; public: /// Creates a \c rosa::deluxe::DeluxeSensor instance owned by \p this object /// and returns a \p rosa::AgentHandle for it. /// + /// \tparam MT type of master-input the new \c rosa::deluxe::DeluxeSensor + /// receives /// \tparam T type of data the new \c rosa::deluxe::DeluxeSensor operates on /// + /// \note Type arguments \p MT and \p T must be instances of \c + /// rosa::deluxe::DeluxeTuple. + /// /// \param Name name of the new \c rosa::deluxe::DeluxeSensor + /// \param MF function to process master-input values /// \param F function to generate the next value with during normal operation /// + /// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor. + /// /// \return \c rosa::AgentHandle for new \c rosa::deluxe::DeluxeSensor - template + template AgentHandle createSensor(const std::string &Name, - DeluxeSensor::D &&F) noexcept; + std::function)> &&MF, + std::function &&F) noexcept; /// Creates a \c rosa::deluxe::DeluxeAgent instance owned by \p this object /// and returns a \c rosa::AgentHandle for it. /// + /// \tparam MT type of master-input the new \c rosa::deluxe::DeluxeAgent + /// receives /// \tparam T type of data the new \c rosa::deluxe::DeluxeAgent outputs + /// \tparam Ts types of master-output the new \c rosa::deluxe::DeluxeAgent + /// produces /// \tparam As types of inputs the new \c rosa::deluxe::DeluxeAgent takes /// + /// \note Type arguments \p MT, \p T, \p Ts..., and \p As... must be + /// instances of \c rosa::deluxe::DeluxeTuple. + /// /// \param Name name of the new \c rosa::deluxe::DeluxeAgent + /// \param MF function for the new \c rosa::deluxe::DeluxeAgent to process + /// master-input values and generate master-output with /// \param F function for the new \c rosa::deluxe::DeluxeAgent to process - /// input values and generate output with + /// input values and generate output and master-output with + /// + /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. /// /// \return \c rosa::AgentHandle for new \c rosa::deluxe::DeluxeAgent - template - AgentHandle createAgent(const std::string &Name, - DeluxeAgent::D &&F) noexcept; + template + AgentHandle createAgent( + const std::string &Name, + std::function...>(std::pair)> &&MF, + std::function, Optional...>( + std::pair...)> &&F) noexcept; protected: /// Tells whether a \c rosa::AgentHandle refers to a /// \c rosa::deluxe::DeluxeSensor owned by \p this object. /// /// \param H \c rosa::AgentHandle to check /// /// \return whether \p H refers to a \c rosa::deluxe::DeluxeSensor owned by /// \p this object virtual bool isDeluxeSensor(const AgentHandle &H) const noexcept = 0; /// Extracts a const qualified \c rosa::deluxe::DeluxeSensor reference from a /// const qualified \c rosa::AgentHandle if possible. /// /// The function returns a \c rosa::Optional object containing a const /// qualified reference to a \c rosa::deluxe::DeluxeSensor object extracted /// from a const qualified \c rosa::AgentHandle instance if the referred /// object is of type \c rosa::deluxeDeluxeSensor and owned by \p this object. /// The returned \c rosa::Optional object is empty otherwise. /// /// \see rosa::deluxe::DeluxeSystem::isDeluxeSensor /// /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeSensor /// from /// /// \return const qualified reference to \c rosa::deluxe::DeluxeSensor if /// \p H refers to an object which is of that type and is owned by \p this /// object Optional getDeluxeSensor(const AgentHandle &H) const noexcept; /// Extracts a \c rosa::deluxe::DeluxeSensor reference from a /// \c rosa::AgentHandle if possible. /// /// The function returns a \c rosa::Optional object containing a reference to /// a \c rosa::deluxe::DeluxeSensor object extracted from a /// \c rosa::AgentHandle instance if the referred object is of type /// \c rosa::deluxeDeluxeSensor and owned by \p this object. The returned /// \c rosa::Optional object is empty otherwise. /// /// \see rosa::deluxe::DeluxeSystem::isDeluxeSensor /// /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeSensor /// from /// /// \return reference to \c rosa::deluxe::DeluxeSensor if \p H refers to an /// object which is of that type and is owned by \p this object Optional getDeluxeSensor(AgentHandle &H) const noexcept; /// Tells whether a \c rosa::AgentHandle refers to a /// \c rosa::deluxe::DeluxeAgent owned by \p this object. /// /// \param H \c rosa::AgentHandle to check /// /// \return whether \p H refers to a \c rosa::deluxe::DeluxeAgent owned by /// \p this object virtual bool isDeluxeAgent(const AgentHandle &H) const noexcept = 0; /// Extracts a const qualified \c rosa::deluxe::DeluxeAgent reference from a /// const qualified \c rosa::AgentHandle if possible. /// /// The function returns a \c rosa::Optional object containing a const /// qualified reference to a \c rosa::deluxe::DeluxeAgent object extracted /// from a const qualified \c rosa::AgentHandle instance if the referred /// object is of type \c rosa::deluxeDeluxeAgent and owned by \p this object. /// The returned \c rosa::Optional object is empty otherwise. /// /// \see rosa::deluxe::DeluxeSystem::isDeluxeAgent /// /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeAgent /// from /// /// \return const qualified reference to \c rosa::deluxe::DeluxeAgent if \p H /// refers to an object which is of that type and is owned by \p this object Optional getDeluxeAgent(const AgentHandle &H) const noexcept; /// Extracts a \c rosa::deluxe::DeluxeAgent reference from a /// \c rosa::AgentHandle if possible. /// /// The function returns a \c rosa::Optional object containing a reference to /// a \c rosa::deluxe::DeluxeAgent object extracted from a /// \c rosa::AgentHandle instance if the referred object is of type /// \c rosa::deluxeDeluxeAgent and owned by \p this object. The returned /// \c rosa::Optional object is empty otherwise. /// /// \see rosa::deluxe::DeluxeSystem::isDeluxeAgent /// /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeAgent /// from /// /// \return reference to \c rosa::deluxe::DeluxeAgent if \p H refers to an /// object which is of that type and is owned by \p this object Optional getDeluxeAgent(AgentHandle &H) const noexcept; }; -template -AgentHandle DeluxeSystem::createSensor(const std::string &Name, - DeluxeSensor::D &&F) noexcept { +template +AgentHandle +DeluxeSystem::createSensor(const std::string &Name, + std::function)> &&MF, + std::function &&F) noexcept { Agent &DS = createUnit( [&](const id_t Id, MessagingSystem &S) { - return new DeluxeSensor(atoms::SensorKind, Id, Name, S, std::move(F)); + return new DeluxeSensor(atoms::SensorKind, Id, Name, S, std::move(MF), + std::move(F)); }); return {DS}; } -template -AgentHandle -DeluxeSystem::createAgent(const std::string &Name, - DeluxeAgent::D &&F) noexcept { - +template +AgentHandle DeluxeSystem::createAgent( + const std::string &Name, + std::function...>(std::pair)> &&MF, + std::function, Optional...>( + std::pair...)> &&F) noexcept { Agent &DA = createUnit( [&](const id_t Id, DeluxeSystem &S) { - return new DeluxeAgent(atoms::AgentKind, Id, Name, S, std::move(F)); + 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 new file mode 100644 index 0000000..5e999e0 --- /dev/null +++ b/include/rosa/deluxe/DeluxeTuple.hpp @@ -0,0 +1,425 @@ +//===-- rosa/deluxe/DeluxeTuple.hpp -----------------------------*- C++ -*-===// +// +// The RoSA Framework +// +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/deluxe/DeluxeTuple.hpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Facilities for handling multiple input/output values for connections +/// in the *deluxe interface*. +/// +/// \see \c rosa::deluxe::DeluxeContext +/// +//===----------------------------------------------------------------------===// + +#ifndef ROSA_DELUXE_DELUXETUPLE_HPP +#define ROSA_DELUXE_DELUXETUPLE_HPP + +#include "rosa/support/sequence.hpp" +#include "rosa/support/type_token.hpp" +#include +#include + +namespace rosa { +namespace deluxe { + +/// A tuple to manage multiple input/output values in the *deluxe interface*. +/// +/// \tparam Ts types of elements of the tuple +/// +/// \note The template may be instantiated only with built-in types and the +/// number of those type may not exceed the capacity of a \c rosa::Token. +template +struct DeluxeTuple : public std::tuple { + // Statically enforce that the class template is instantiated only with + // built-in types. + STATIC_ASSERT((TypeListSubsetOf, BuiltinTypes>::Value), + "not built-in types"); + // Statically enforce that the class template is instantiated with not too + // many types. + // \note Instantiation would fail on \c rosa::deluxe::DeluxeTuple::TT if there + // are too many types; this assertion is for more readable error reporting. + STATIC_ASSERT(sizeof...(Ts) <= token::MaxTokenizableListSize, + "Too many types"); + + /// How many elements the instance has. + static constexpr token_size_t Length = sizeof...(Ts); + + /// What types the class contains. + /// + /// Type information encoded as \c rosa::Token. + static constexpr Token TT = TypeToken::Value; + + /// Default constructor, zero-initializes elements. + DeluxeTuple(void) = default; + + /// Constructor, initializes the underlying \c std::tuple with lvalue + /// references. + /// + /// \param Args value references to the values to store + DeluxeTuple(const std::decay_t &... Args) : std::tuple(Args...) {} + + /// Constructor, initializes the underlying \c std::tuple with rvalue + /// references. + /// + /// \param Args rvalue references to the values to store + DeluxeTuple(std::decay_t &&... Args) + : std::tuple(std::move(Args)...) {} + + /// Contructor, initializes the underlying \c std::tuple from another matching + /// \c std::tuple. + DeluxeTuple(const std::tuple &Args) : std::tuple(Args) {} + + /// Default copy-constructor. + DeluxeTuple(const DeluxeTuple &) = default; + + /// Default move-constructor. + DeluxeTuple(DeluxeTuple &&) = default; + + /// Default copy-assignment. + DeluxeTuple &operator=(const DeluxeTuple &) = default; + + /// Default move-assignment. + DeluxeTuple &operator=(DeluxeTuple &&) = default; + +private: + /// Dumps \p this object to a given \c std::ostream. + /// + /// \note Provides implementation for \c rosa::deluxe::DeluxeTuple::dump. + /// + /// \tparam S0 Indices for accessing elements. + /// + /// \param [in,out] OS output stream to dump to + /// + /// \note The second argument provides indices statically as template + /// arguments \p S0..., so its actual value is ignored. + /// + /// \pre Statically, \p S0... matches number of types \p this object was + /// created: \code + /// sizeof...(S0) == sizeof...(Ts) + /// \endcode + template + void dump(std::ostream &OS, Seq) const noexcept; + +public: + /// Dumps \p this object to a given \c std::ostream. + /// + /// \param [in,out] OS output stream to dump to + void dump(std::ostream &OS) const noexcept; +}; + +template +template +void DeluxeTuple::dump(std::ostream &OS, Seq) const noexcept { + STATIC_ASSERT(sizeof...(S0) == sizeof...(Ts), "inconsistent type arguments"); + // Convert value to std::string with std::to_string except for a value of + // std::string that does not need conversion. + auto dump_to_string = [](const auto &V) { + if constexpr (std::is_same, std::string>::value) { + return V; + } else { + return std::to_string(V); + } + }; + OS << "{"; + (OS << ... << (" " + dump_to_string(std::get(*this)))); + OS << " }"; +} + +template +void DeluxeTuple::dump(std::ostream &OS) const noexcept { + dump(OS, seq_t()); +} + +/// Type alias for a \c rosa::deluxe::DeluxeTuple that contains no elements. +using EmptyDeluxeTuple = DeluxeTuple<>; + +/// Template specialization for \c rosa::deluxe::EmptyDeluxeTuple. +template <> struct DeluxeTuple<> : public std::tuple<> { + /// How many elements the instance has. + static constexpr token_size_t Length = 0; + + /// What types the class contains. + /// + /// Type information encoded as \c rosa::Token. + static constexpr Token TT = TypeToken<>::Value; + + /// Constructor, initializes the underlying \c std::tuple. + DeluxeTuple(void) : std::tuple<>() {} + + /// Default copy-constructor. + DeluxeTuple(const DeluxeTuple &) = default; + + // Default move-constructor. + DeluxeTuple(DeluxeTuple &&) = default; + + /// Default copy-assignment. + DeluxeTuple &operator=(const DeluxeTuple &) = default; + + // Default move-assignment, + DeluxeTuple &operator=(DeluxeTuple &&) = default; + + /// Dumps \p this object to a given \c std::ostream. + /// + /// \param [in,out] OS output stream to dump to + static void dump(std::ostream &OS) noexcept; +}; + +/// Creates a \c rosa::deluxe::DeluxeTuple instance from the given lvalues +/// references. +/// +/// \tparam Ts types of elements of the tuple +/// +/// \see \c rosa::deluxe::DeluxeTuple +/// +/// \param Args values to store in the tuple +/// +/// \return an instance of \c rosa::deluxe::DeluxeTuple with \p Args as +/// elements +template +inline DeluxeTuple make_deluxe_tuple(const Ts &... Args) noexcept { + return DeluxeTuple(Args...); +} + +/// Creates a \c rosa::deluxe::DeluxeTuple instance from the given rvalue +/// references. +/// +/// \tparam Ts types of elements of the tuple +/// +/// \see \c rosa::deluxe::DeluxeTuple +/// +/// \param Args values to store in the tuple +/// +/// \return an instance of \c rosa::deluxe::DeluxeTuple with \p Args as +/// elements +template +inline DeluxeTuple make_deluxe_tuple(Ts&&... Args) noexcept { + return DeluxeTuple(std::move(Args)...); +} + +/// Creates a \c rosa::deluxe::DeluxeTuple instance from the given \c std::tuple +/// reference. +/// +/// \tparam Ts types of elements of the tuple +/// +/// \see \c rosa::deluxe::DeluxeTuple +/// +/// \param Args values to store in the tuple +/// +/// \return an instance of \c rosa::deluxe::DeluxeTuple with \p Args as +/// elements +template +inline DeluxeTuple +make_deluxe_tuple(const std::tuple &Args) noexcept { + return DeluxeTuple(Args); +} + +/// \defgroup UnwrapDeluxeTuple Implementation of +/// rosa::deluxe::UnwrapDeluxeTuple +/// +/// \brief Unwraps element types from an instance of \c +/// rosa::deluxe::DeluxeTuple into a \c rosa::TypeList +/// +/// Types can be unwrapped from a \c rosa::deluxe::DeluxeTuple instance as \code +/// typename UnwrapDeluxeTuple::Type +/// \endcode +/// +/// For example, the following expression evaluates to `true`: \code +/// std::is_same>::Type, +/// TypeList>::value +/// \endcode +///@{ + +/// Declaration of the template. +/// +/// \tparam Tuple \c rosa::deluxe::DeluxeTuple to unwrap +template struct UnwrapDeluxeTuple; + +/// Implementation of the template for \c rosa::deluxe::DeluxeTuple instances. +template struct UnwrapDeluxeTuple> { + using Type = TypeList; +}; + +///@} + +///@} + +/// \defgroup IsTuple Implementation of \c rosa::deluxe::IsTuple +/// +/// \brief Tells if a type is a tuple as in it can be converted to \c +/// rosa::deluxe::DeluxeTuple. +/// +/// \see \c rosa::deluxe::MatchingDeluxeTuple +/// +/// Whether a type \c T is a tuple can be checked as \code +/// IsTuple::Value +/// \endcode +///@{ + +/// Declaration of the template. +/// +/// \tparam T type to check +template struct IsTuple; + +/// Specialization for the case when the type is an instance of \c std::tuple. +template struct IsTuple> { + static constexpr bool Value = true; +}; + +/// Specialization for the case when the type is an instance of \c std::tuple. +template struct IsTuple> { + static constexpr bool Value = true; +}; + +/// Implementation for a general case of type \p T. +template struct IsTuple { static constexpr bool Value = false; }; + +///@} + +/// \defgroup IsDeluxeTuple Implementation of \c rosa::deluxe::IsDeluxeTuple +/// +/// \brief Tells if a type is an instance of \c rosa::deluxe::DeluxeTuple. +/// +/// Whether a type \c T is an instance of \c rosa::deluxe::DeluxeTuple can be +/// checked as \code +/// IsDeluxeTuple::Value +/// \endcode +/// +/// \note `!IsDeluxeTuple::Value || IsTuple::Value` +///@{ + +/// Declaration of the template. +/// +/// \tparam T type to check +template struct IsDeluxeTuple; + +/// Specialization for the case when the type is an instance of \c +/// rosa::deluxe::DeluxeTuple. +template +struct IsDeluxeTuple> { + static constexpr bool Value = true; +}; + +/// Implementation for a general case of type \p T. +template +struct IsDeluxeTuple { + static constexpr bool Value = false; +}; + +///@} + +/// \defgroup MatchingDeluxeTuple Implementation of \c +/// rosa::deluxe::MatchingDeluxeTuple +/// +/// \brief Gives the \c rosa::deluxe::DeluxeTuple type that matches a given +/// tuple type. +/// +/// The matching \c rosa::deluxe::DeluxeTuple type for a tuple type \p T can be +/// obtained as \code +/// typename MatchingDeluxeTuple::Type +/// \endcode +/// If \p T is \c rosa::deluxe::DeluxeTuple, the matching type is \p T itself. +/// If \p T is \c std::tuple, the matching type if \c rosa::deluxe::DeluxeTuple +/// with the same type parameters as \p T. +/// \c rosa::deluxe::MatchingDeluxeTuple is not defined for other type +/// parameters. +/// +/// \note The template is defined for type \p T only if +/// `rosa::deluxe::IsTuple::Value`. Values of such types can be used to +/// construct a new instance of the class \c rosa::deluxe::DeluxeTuple. +/// +///\see \c rosa::deluxe::IsTuple +///@{ + +/// Declaration of the template. +/// +/// \tparam T type to check +template struct MatchingDeluxeTuple; + +/// Specialization for the case when the type is an instance of \c +/// rosa::deluxe::DeluxeTuple. +template struct MatchingDeluxeTuple> { + using Type = DeluxeTuple; +}; + +/// Specialization for the case when the type is an instance of \c +/// std::tuple. +template struct MatchingDeluxeTuple> { + using Type = DeluxeTuple; +}; + +///@} + +/// Convenience template type alias for easy use of \c +/// rosa::deluxe::MatchingDeluxeTuple. +/// +/// Converts a tuple type to the matching \c rosa::deluxe::DeluxeTuple type. +/// +/// \tparam Tuple type to convert +template +using matching_deluxe_tuple_t = typename MatchingDeluxeTuple::Type; + +/// \defgroup TypeListAllDeluxeTuple Implementation of +/// \c rosa::deluxe::TypeListAllDeluxeTuple +/// +/// \brief Tells if all types in a \c rosa::TypeList is an instance of \c +/// rosa::deluxe::DeluxeTuple. +/// +/// Whether a \c rosa::TypeList \c List contains instances of \c +/// rosa::deluxe::DeluxeTuple only can be checked as \code +/// TypeListAllDeluxeTuple::Value +/// \endcode +///@{ + +/// Declaration of the template. +/// +/// \tparam List \c rosa::TypeList to check +template struct TypeListAllDeluxeTuple; + +/// Specialization for \c rosa::EmptyTypeList. +template <> struct TypeListAllDeluxeTuple { + static constexpr bool Value = true; +}; + +/// Implementation for the general case when there is at leasst one element in +/// the list. +template +struct TypeListAllDeluxeTuple> { + static constexpr bool Value = + IsDeluxeTuple::Value && TypeListAllDeluxeTuple>::Value; +}; + +///@} + +} // End namespace deluxe +} // End namespace rosa + +namespace std { + +/// Dumps a \c rosa::deluxe::Deluxe instance to a given \c std::ostream. +/// +/// \param [in,out] OS output stream to dump to +/// \param Tuple \c rosa::deluxe::Deluxe to dump +/// +/// \return \p OS after dumping \p Tuple to it +template +ostream &operator<<(ostream &OS, + const rosa::deluxe::DeluxeTuple &Tuple) { + Tuple.dump(OS); + return OS; +} + +} // End namespace std + +#endif // ROSA_DELUXE_DELUXETUPLE_HPP diff --git a/include/rosa/deluxe/namespace.h b/include/rosa/deluxe/namespace.h index 58f8e7b..d28e32c 100755 --- a/include/rosa/deluxe/namespace.h +++ b/include/rosa/deluxe/namespace.h @@ -1,32 +1,38 @@ //===-- rosa/deluxe/namespace.h ---------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/namespace.h /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Documentation for the namespace \c rosa::deluxe. /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_NAMESPACE_H #define ROSA_DELUXE_NAMESPACE_H namespace rosa { /// Contains a simplified, limited in functionality but flexible in modularity, /// interface to define and easily reconfigure agent systems. /// /// **Deluxe Interface** /// /// TODO write this description here! namespace deluxe {} } // End namespace rosa #endif // ROSA_DELUXE_NAMESPACE_H diff --git a/include/rosa/support/atom.hpp b/include/rosa/support/atom.hpp index 60fbe06..4746211 100644 --- a/include/rosa/support/atom.hpp +++ b/include/rosa/support/atom.hpp @@ -1,179 +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 +/// \date 2017-2019 /// /// \brief Facility for *atoms*, short strings statically encoded as integers. /// /// \note This implementation is based on the \c atom implementation of CAF. -/// \todo Check license. /// /// *Atoms* can be used to turn short string literals into statically generated /// types. The literals may consist of at most \c 10 non-special characters, /// legal characters are \c _0-9A-Za-z and the whitespace character. Special /// characters are turned into whitespace, which may result in different string /// literals being encoded into the same integer value, if any of those contain /// at least one special character. /// /// \note The usage of special characters in the string literals used to create /// *atoms* cannot be checked by the compiler. /// /// Example: /// /// \code /// constexpr AtomValue NameValue = atom("name"); /// using NameAtom = AtomConstant; /// /// [](NameAtom){ std::cout << "Argument of type NameAtom"; }(NameAtom::Value) /// \endcode /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_ATOM_HPP #define ROSA_SUPPORT_ATOM_HPP #include "rosa/support/debug.hpp" namespace rosa { /// Maximal length of valid atom strings. constexpr size_t MaxAtomLength = 10; /// Underlying integer type of atom values. using atom_t = uint64_t; /// Turn \c rosa::atom_t into a strongly typed enumeration. /// /// Values of \c rosa::atom_t casted to \c rosa::AtomValue may be used in a /// type-safe way. enum class AtomValue : atom_t {}; /// Anonymous namespace with implementational details, consider it private. namespace { // clang-format off /// Encodes ASCII characters to 6-bit encoding. constexpr unsigned char AtomEncodingTable[] = { /* ..0 ..1 ..2 ..3 ..4 ..5 ..6 ..7 ..8 ..9 ..A ..B ..C ..D ..E ..F */ /* 0.. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1.. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2.. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 3.. */ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0, 0, /* 4.. */ 0, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, /* 5.. */ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 0, 0, 0, 0, 37, /* 6.. */ 0, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, /* 7.. */ 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 0, 0, 0, 0, 0}; // clang-format on /// Decodes 6-bit characters to ASCII constexpr char AtomDecodingTable[] = " 0123456789" "ABCDEFGHIJKLMNOPQRSTUVWXYZ_" "abcdefghijklmnopqrstuvwxyz"; /// Encodes one character and updates the integer representation. /// /// \param Current an encoded value /// \param CharCode a character to add to \p Current /// /// \return \p Current updated with \p CharCode constexpr atom_t nextInterim(atom_t Current, size_t CharCode) { return (Current << 6) | AtomEncodingTable[(CharCode <= 0x7F) ? CharCode : 0]; } /// Encodes a C-string into an integer value to be used as \c rosa::AtomValue. /// /// \param CStr a string to encode /// \param Interim encoded value to add \p CStr to it /// /// \return \p Interim updated with \p CStr constexpr atom_t atomValue(const char *CStr, atom_t Interim = 0xF) { return (*CStr == '\0') ? Interim : atomValue(CStr + 1, nextInterim(Interim, static_cast(*CStr))); } } // End namespace -/// Converts a \c rosa::AtomValue into \c std::string. -/// -/// \param What value to convert -/// -/// \return \c std::string encoded in \p What -std::string to_string(const AtomValue &What); - /// Converts a \c std::string into a \c rosa::AtomValue. /// /// \param S \c std::string to convert /// /// \return \c rosa::AtomValue representing \p S AtomValue atom_from_string(const std::string &S); /// Converts a string-literal into a \c rosa::AtomValue. /// /// \tparam Size the length of \p Str /// /// \param Str the string-literal to convert /// /// \return \c rosa::AtomValue representating \p Str /// /// \pre \p Str is not too long:\code /// Size <= MaxAtomLength + 1 /// \endcode template constexpr AtomValue atom(char const (&Str)[Size]) { // Last character is the NULL terminator. STATIC_ASSERT(Size <= MaxAtomLength + 1, "Too many characters in atom definition"); return static_cast(atomValue(Str)); } /// Lifts a \c rosa::AtomValue to a compile-time constant. /// /// \tparam V \c rosa::AtomValue to lift template struct AtomConstant { /// Constructor has to do nothing. constexpr AtomConstant(void) {} /// Returns the wrapped value. /// /// \return \p V constexpr operator AtomValue(void) const { return V; } /// Returns the wrapped value as of type \c rosa::atom_t. /// /// \return \c rosa::atom_t value from \p V static constexpr atom_t value() { return static_cast(V); } /// An instance *of this constant* (*not* a \c rosa::AtomValue). static const AtomConstant Value; }; // Implementation of the static member field \c rosa::AtomConstant::Value. template const AtomConstant AtomConstant::Value = AtomConstant{}; +} // End namespace rosa + +namespace std { + +/// Converts a \c rosa::AtomValue into \c std::string. +/// +/// \param What value to convert +/// +/// \return \c std::string encoded in \p What +string to_string(const rosa::AtomValue &What); + +/// Dumps a \c rosa::AtomValue to a given \c std::ostream. +/// +/// \param [in,out] OS output stream to dump to +/// \param A \c rosa::AtomValue to dump +/// +/// \return \p OS after dumping \p N to it +inline ostream &operator<<(ostream &OS, const rosa::AtomValue &A) { + OS << to_string(A); + return OS; +} + /// Converts a \c rosa::AtomConstant into \c std::string. /// /// \tparam V \c rosa::AtomValue to convert /// /// \note The actual argument of type `const rosa::AtomConstant` is ignored /// because the \c rosa::AtomValue to convert is encoded in the type itself. /// /// \return the original string encoded in \p V -template std::string to_string(const AtomConstant &) { +template string to_string(const rosa::AtomConstant &) { return to_string(V); } -} // End namespace rosa +/// Dumps a \c rosa::AtomConstant to a given \c std::ostream. +/// +/// \tparam V the \c rosa::AtomValue to dump +/// +/// \param [in,out] OS output stream to dump to +/// \param A \c rosa::AtomConstant providing \p V +/// +/// \return \p OS after dumping \p V to it +template +inline ostream &operator<<(ostream &OS, const rosa::AtomConstant &A) { + (void)A; // Shut compiler about unused parameter. + OS << to_string(V); + return OS; +} + +} // End namespace std #endif // ROSA_SUPPORT_ATOM_HPP diff --git a/include/rosa/support/csv/CSVReader.hpp b/include/rosa/support/csv/CSVReader.hpp index 0491c17..18bd220 100755 --- a/include/rosa/support/csv/CSVReader.hpp +++ b/include/rosa/support/csv/CSVReader.hpp @@ -1,382 +1,494 @@ //===-- rosa/support/csv/CSVReader.hpp --------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/support/csv/CSVReader.hpp /// -/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// \authors David Juhasz (david.juhasz@tuwien.ac.at), Edwin Willegger (edwin.willegger@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Facitilities to read CSV files. /// /// \note The implementation is based on the solution at /// https://stackoverflow.com/a/1120224 /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_CSV_CSVREADER_HPP #define ROSA_SUPPORT_CSV_CSVREADER_HPP #include "rosa/support/debug.hpp" +#include "rosa/support/sequence.hpp" #include #include #include +#include +#include +#include namespace rosa { namespace csv { +/// Indicating it the CSV file contains any header or not +enum class HeaderInformation { + HasHeader, + HasNoHeader +}; + /// Anonymous namespace providing implementation details for /// \c rosa::csv::CSVIterator, consider it private. namespace { -/// Provides facility for parsing values from one row CSV data. +/// Provides facility for parsing one value from a string. /// -/// \tparam T type of values to parse from the line +/// \tparam T type of value to parse /// \tparam IsSignedInt if \p T is a signed integral type, always use default /// \tparam IsUnsignedInt if \p T is an unsigned integral type, always use /// default /// \tparam IsFloat if \p T is a floating-point type, always use default /// \tparam IsString if \p T is \c std::string, always use default /// -/// \note Specializations of this `struct` are provided for arithmentic types +/// \note Specializations of this struct are provided for arithmentic types /// and \c std::string. -template ::value && - std::is_signed::value), +template ::value && std::is_signed::value), bool IsUnsignedInt = (std::is_integral::value && std::is_unsigned::value), bool IsFloat = std::is_floating_point::value, bool IsString = std::is_same::value> -struct CSVRowParser; - -/// Specialization for signed integral types. -/// -/// \tparam T type of values to parse from the line -/// -/// \pre \p T is a signed integral type:\code -/// std::is_integral::value && std::is_signed::value -/// \endcode -template struct CSVRowParser { - STATIC_ASSERT((std::is_integral::value && std::is_signed::value), - "wrong type"); // Sanity check. +struct ValueParser { - /// Parses a given row of CSV data into a given container. /// - /// \p Data is cleared and then filled with values parsed from \p LineStream. - /// Entries in the line are to be separated by commas, the character `,`. A - /// trailing comma results in an empty entry at the end of the line. No empty - /// entry should be present otherwise. /// - /// \note Parsed values are silently converted to type \p T. + /// \param Cell the \c std::string to parse /// - /// \param [in,out] LineStream the line to parse - /// \param [in,out] Data the container to store the parsed values - static void parse(std::stringstream &LineStream, std::vector &Data) { - std::string Cell; - Data.clear(); - while (std::getline(LineStream, Cell, ',')) { - Data.push_back(static_cast(std::stoll(Cell))); - } - // This checks for a trailing comma with no data after it. - if (!LineStream && Cell.empty()) { - // If there was a trailing comma then add an empty element. - Data.push_back(0); - } + /// \return the parsed value + /// + /// \note The function silently fails if cannot parse \p Cell for type \p T. + static T parse(const std::string &Cell) noexcept; +}; + +template +struct ValueParser { + STATIC_ASSERT((std::is_integral::value && std::is_signed::value), + "wrong type"); // Sanity check. + static T parse(const std::string &Cell) noexcept { + return static_cast(std::stoll(Cell)); } }; -/// Specialization for unsigned integral types. -/// -/// \tparam T type of values to parse from the line -/// -/// \pre \p T is an unsigned integral type:\code -/// std::is_integral::value && std::is_unsigned::value -/// \endcode -template struct CSVRowParser { +template +struct ValueParser { STATIC_ASSERT((std::is_integral::value && std::is_unsigned::value), "wrong type"); // Sanity check. - - /// Parses a given row of CSV data into a given container. - /// - /// \p Data is cleared and then filled with values parsed from \p LineStream. - /// Entries in the line are to be separated by commas, the character `,`. A - /// trailing comma results in an empty entry at the end of the line. No empty - /// entry should be present otherwise. - /// - /// \note Parsed values are silently converted to type \p T. - /// - /// \param [in,out] LineStream the line to parse - /// \param [in,out] Data the container to store the parsed values - static void parse(std::stringstream &LineStream, std::vector &Data) { - std::string Cell; - Data.clear(); - while (std::getline(LineStream, Cell, ',')) { - Data.push_back(static_cast(std::stoull(Cell))); - } - // This checks for a trailing comma with no data after it. - if (!LineStream && Cell.empty()) { - // If there was a trailing comma then add an empty element. - Data.push_back(0); - } + static T parse(const std::string &Cell) noexcept { + return static_cast(std::stoull(Cell)); } }; -/// Specialization for floating-point types. -/// -/// \tparam T type of values to parse from the line -/// -/// \pre \p T is a floating-point type:\code -/// std::is_floating_point::value -/// \endcode -template struct CSVRowParser { +template +struct ValueParser { STATIC_ASSERT((std::is_floating_point::value), "wrong type"); // Sanity check. - - /// Parses a given row of CSV data into a given container. - /// - /// \p Data is cleared and then filled with values parsed from \p LineStream. - /// Entries in the line are to be separated by commas, the character `,`. A - /// trailing comma results in an empty entry at the end of the line. No empty - /// entry should be present otherwise. - /// - /// \note Parsed values are silently converted to type \p T. - /// - /// \param [in,out] LineStream the line to parse - /// \param [in,out] Data the container to store the parsed values - static void parse(std::stringstream &LineStream, std::vector &Data) { - std::string Cell; - Data.clear(); - while (std::getline(LineStream, Cell, ',')) { - Data.push_back(static_cast(std::stold(Cell))); - } - // This checks for a trailing comma with no data after it. - if (!LineStream && Cell.empty()) { - // If there was a trailing comma then add an empty element. - Data.push_back(0); - } + static T parse(const std::string &Cell) noexcept { + return static_cast(std::stold(Cell)); } }; -/// Specialization for \c std::string. -/// -/// \tparam T type of values to parse from the line -/// -/// \pre \p T is \c std::string:\code -/// std::is_same::value -/// \endcode -template struct CSVRowParser { +template +struct ValueParser { STATIC_ASSERT((std::is_same::value), "wrong type"); // Sanity check. - - /// Parses a given row of CSV data into a given container. - /// - /// \p Data is cleared and then filled with values parsed from \p LineStream. - /// Entries in the line are to be separated by commas, the character `,`. A - /// trailing comma results in an empty entry at the end of the line. No empty - /// entry should be present otherwise. - /// - /// \param [in,out] LineStream the line to parse - /// \param [in,out] Data the container to store the parsed values - static void parse(std::stringstream &LineStream, std::vector &Data) { - std::string Cell; - Data.clear(); - while (std::getline(LineStream, Cell, ',')) { - Data.push_back(Cell); - } - // This checks for a trailing comma with no data after it. - if (!LineStream && Cell.empty()) { - // If there was a trailing comma then add an empty element. - Data.push_back(""); - } - } + static T parse(const std::string &Cell) noexcept { return Cell; } }; /// Parses and stores entries from a row of CSV data. /// -/// \tparam T type of values to parse and store, i.e. entries in the row +/// \tparam Ts types of values to parse and store, i.e. entries in the row /// /// \note The implementation relies on \c rosa::csv::CSVRowParser, which is -/// implemented only for `arithmetic` types -- signed and unsigned integral and -/// floating-point types -- and for \c std::string. Those are the valid values -/// for \p T. -template -class CSVRow { -public: - /// Gives a constant reference for an entry at a given position of the row. +/// implemented only for `arithmetic` types -- signed and unsigned integral +/// and floating-point types -- and for \c std::string. Those are the valid +/// values for \p Ts. +template class CSVRow { +private: + /// Parses a given row of CSV data into \c CSVRow::Data. + /// + /// \ CSVRow::Data is filled with values parsed from \p LineStream. Entries + /// in the line are to be separated by commas, the character `,`. + /// + /// \note Parsed values are silently converted to types \p Ts. + /// + /// \note Parsing silently fails if values do not match \p Ts. /// - /// \note No bounds checking is performed. + /// \tparam S0 indices to access tuple elements. /// - /// \param Index the position of the entry + /// \param [in,out] LineStream the line to parse /// - /// \return constant reference for the stored entry at position \p Index - const T &operator[](const size_t Index) const noexcept { return Data[Index]; } + /// \note The last argument is used only to get \p S0, the actual value of + /// the parameter is ignored. + template + void parseRow(std::stringstream &LineStream, char Delimeter, Seq) { + STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), + "Not matching template arguments."); + std::string Cell; + // Get fields and parse the values into the proper element of the tuple + // one by one in a fold expression. + ((std::getline(LineStream, Cell, Delimeter), + std::get(Data) = ValueParser::parse(Cell)), + ...); + + } - /// Tells the number of entries stored in the row. +public: + + /// Constructor with all possible parameters /// - /// \return number of stored entries. - size_t size(void) const noexcept { return Data.size(); } + /// The function creates an instance of an CSVRow object and sets the attributes of the + /// object to the values of the parameters. + /// + /// \param SkipRows the number of data rows to skip, not taking header into account. + /// \param HeaderInfo is the first line of the file a header row or not. + /// \param Delimeter to seperate between the data entries within one row. + CSVRow(const size_t SkipRows = 0, + const HeaderInformation HeaderInfo = HeaderInformation::HasHeader, + const char Delimeter = ',') : + SkipRows(SkipRows), HeaderInfo(HeaderInfo), Delimeter(Delimeter), + RowNumber(0), IsHeaderRead(false) { + } + + /// Parses and stores one row of CSV data. /// /// The function reads one line from \p Str and parses it into /// \c rosa::csv::CSVRow::Data using \c rosa::csv::CSVRowParser. /// /// \param [in,out] Str input stream of a CSV file - void readNextRow(std::istream &Str) { + void readNextRow(std::istream &Str) noexcept { std::string Line; + std::getline(Str, Line); - std::stringstream LineStream(Line); - CSVRowParser::parse(LineStream, Data); + + if(Line.size() > 0){ + std::stringstream LineStream(Line); + parseRow(LineStream, Delimeter, seq_t()); + + RowNumber = RowNumber + 1; + } + } + /// Read header row and stores it as \p std::string. + /// + /// The function reads the first line of the csv file and stores the entries + /// in a vector. + /// + /// \param [in,out] Str input stream of a CSV file + void readHeader(std::istream &Str) noexcept { + std::string Line; + std::getline(Str, Line); + std::stringstream LineStream(Line); + std::string Value; + + while( getline(LineStream, Value, Delimeter) ){ + Header.push_back(Value); + } + + IsHeaderRead = true; + } + + /// The number of rows to skip once. + /// + /// This function returns the number of data rows to skip + /// at the beginning of the file. + /// + /// \return The number of rows to skip at the beginning of a csv file. + inline size_t SkipNumRows() const noexcept { + return this->SkipRows; + } + + /// The current row number within the csv file. + /// + /// This function returns the current row number. The header + /// row is not counted as a row. + /// + /// \returns the current row number within the csv file. + inline size_t CurRow() const noexcept { + return this->RowNumber; + } + + /// Indiciates if the header was already read. + /// + /// This function returns true, if the header of a csv file which contains + /// a header file is already read. + /// The user has to pass in the attribute HeaderInfo the information if the + /// file has in the first row the header row or not. + /// + /// \return if the header of a file is already read. + inline bool IsHeaderReadDone() const noexcept{ + return this->IsHeaderRead; + } + + + /// Indicates if the file contains a header row in the first row. + /// + /// This function returns if the file contains a header row. + /// The information if the file contains a header row or not, has to be passed by the user. + /// The standard value is HeaderInformation::HasHeader + /// + /// \return if the csv file contains a header row in the first line of the file. + inline HeaderInformation HasFileHeader() const noexcept { + return this->HeaderInfo; + } + + /// Set the number of rows to skip. + /// + /// This function sets the number of rows to skip at the beginning of + /// the reading of the file. + /// + /// \param SkipRowsBeginning the number of rows you want to skip at the beginning of the file. + inline void SetSkipRows(const size_t SkipRowsBeginning) noexcept { + this->SkipRows = SkipRowsBeginning; + } + + /// Is the first row a header row or not. + /// + /// This function sets the information, if the first row of the csv file + /// is a header line or not. + /// + /// \param HeaderInf if the first row is a header row or not. + inline void SetHeaderInfo(const HeaderInformation HeaderInf) noexcept { + this->HeaderInfo = HeaderInf; + } + + /// Set the seperator between data entries. + /// + /// This funcction sets the separator between the data entries of the csv file. + /// + /// \param separator the character that separates the data values. + inline void SetDelimeter(char separator) { + this->Delimeter = separator; + } + + + + + /// Gives a constant references for the \c std::tuple containing the values + /// read by \p this object. + /// + /// \return \c CSVRow::Data + const std::tuple &tuple(void) const noexcept { return Data; } + private: - std::vector Data; ///< Stores parsed entries + std::tuple Data; ///< Stores parsed entries + size_t SkipRows; ///< The number of rows to skip at the very beginning of the file. + ///< This number only applies on the number of data rows. + ///< If your file contains a header row and data rows, the skiping + ///< of the header row is not taken into account. + HeaderInformation HeaderInfo; ///< If the file contains a header row or not. + char Delimeter; ///< The seperator between the data entries. + size_t RowNumber; ///< Current row number, counts all row numbers including the header row. + bool IsHeaderRead; ///< Was the header read or not. + std::vector Header; ///< The content of the header row. + }; /// Reads a row of CSV data into \c rosa::csv::CSVRow. /// /// The next line is read from \p Str by calling -/// \c rosa::csv::CSVRow::readNextRow on \p Data. +/// \c rosa::csv::CSVRow::readNextRow on \p Data until all lines are +/// skipped. /// -/// \note A CSV file should contain no empty lines. +/// If the function is called for the first time and the file contains +/// a header than is the header and the first data row read in after the +/// number of rows that the user wants to skip. +/// +/// \tparam Ts type of values to read from the row +/// +/// \note The CSV file should contain a line with fields matching \p Ts... /// /// \param [in,out] Str input stream of a CSV file /// \param [in,out] Data object to read the next line into /// /// \return \p Str after reading one line from it -template -std::istream &operator>>(std::istream &Str, CSVRow &Data) { +template +std::istream &operator>>(std::istream &Str, CSVRow &Data) { + + if( Data.HasFileHeader() == HeaderInformation::HasHeader && !Data.IsHeaderReadDone() ) { + Data.readHeader(Str); + } + + while(Data.CurRow() < (Data.SkipNumRows())){ + Data.readNextRow(Str); + } + + //read the lines after you skipped the number of rows you want to skip Data.readNextRow(Str); + return Str; } } // End namespace -/// Provides `InputIterator` features for iterating over a CSV file in a -/// flat way. +/// Provides `InputIterator` features for iterating over a CSV file. /// -/// The iterator hides rows of the CSV file, and iterates over the entries -/// row-by-row. +/// The iterator parses rows into `std::tuple` values and iterates over the +/// file row by row. /// -/// \note A CSV file should contain no empty lines. +/// \tparam Ts types of values stored in one row of the CSV file /// -/// \tparam T type of values to iterate over, i.e. entries in the CSV file. +/// \note The iterator expects each row to consists of fields matching \p Ts. /// /// \note The implementation relies on \c rosa::csv::CSVRow, which in turn /// relies on \c rosa::csv::CSVRowParser, which is implemented only for /// `arithmetic` types -- signed and unsigned integral types and floating-point -/// types -- and for \c std::string. Those are the valid values for \p T. -template -class CSVFlatIterator { +/// types -- and for \c std::string. Those are the valid values for \p Ts +template class CSVIterator { public: - /// \defgroup CSVFlatIteratorTypedefs Typedefs of rosa::csv::CSVFlatIterator + /// \defgroup CSVIteratorTypedefs Typedefs of rosa::csv::CSVIterator /// /// Standard `typedef`s for iterators. /// ///@{ typedef std::input_iterator_tag - iterator_category; ///< Category of the iterator. - typedef T value_type; ///< Type of values iterated over. - typedef std::size_t difference_type; ///< Type to identify distance. - typedef T *pointer; ///< Pointer to the type iterated over. - typedef T &reference; ///< Reference to the type iterated over. + iterator_category; ///< Category of the iterator. + typedef std::tuple value_type; ///< Type of values iterated over. + typedef std::size_t difference_type; ///< Type to identify distance. + typedef std::tuple *pointer; ///< Pointer to the type iterated over. + typedef std::tuple + &reference; ///< Reference to the type iterated over. ///@} /// Creates a new instance. /// /// \param [in,out] S input stream to iterate over - CSVFlatIterator(std::istream &S) - : Str(S.good() ? &S : nullptr), Pos((size_t)(-1)) { - // \c rosa::csv::CSVFlatIterator::Pos is initialized to `-1` so the first - // incrementation here will set it properly. + /// \param SkipRows the number of rows you want to skip only once at the beginning of the file. + /// If you have an header in the file, it is supposed to be the first row, and it will be always read out. + /// But after this header the next number of Rows will be skipped. + /// \param HeaderInfo is used to know wheter the file contains an header row or not. + /// The header has to be in the first row. + /// \param Delimeter is the separator between the differnt values of the csv file. + CSVIterator(std::istream &S, const size_t SkipRows = 0, + const HeaderInformation HeaderInfo = HeaderInformation::HasHeader, + const char Delimeter = ',') : Str(S.good() ? &S : nullptr), + SkipRows(SkipRows), HeaderInfo(HeaderInfo), Delimeter(Delimeter), Row(){ + + Row.SetSkipRows(SkipRows); + Row.SetHeaderInfo(HeaderInfo); + Row.SetDelimeter(Delimeter); + + // \c rosa::csv::CSVIterator::Row is initialized empty so the first + // incrementation here will read the first row. ++(*this); } /// Creates an empty new instance. - CSVFlatIterator(void) noexcept : Str(nullptr) {} + CSVIterator(void) noexcept + : Str(nullptr), SkipRows(0), HeaderInfo(HeaderInformation::HasHeader), + Delimeter(','), Row() {} /// Pre-increment operator. /// - /// The implementation moves over the entries in the current row and advances - /// to the next row when the end of the current row is reached. If the end of - /// the input stream is reached, the operator becomes empty and has no - /// further effect. + /// The implementation reads the next row. If the end of the input stream is + /// reached, the operator becomes empty and has no further effect. /// /// \return \p this object after incrementing it. - CSVFlatIterator &operator++() { + CSVIterator &operator++() { if (Str) { - ++Pos; - if (Pos == Row.size()) { - if (!((*Str) >> Row)) { - Str = nullptr; - --Pos; // Stay on the last entry forever. - } else { - Pos = 0; - } + if (!((*Str) >> Row)) { + Str = nullptr; } } return *this; } /// Post-increment operator. /// /// The implementation uses the pre-increment operator and returns a copy of /// the original state of \p this object. /// /// \return \p this object before incrementing it. - CSVFlatIterator operator++(int) { - CSVFlatIterator Tmp(*this); + CSVIterator operator++(int) { + CSVIterator Tmp(*this); ++(*this); return Tmp; } /// Returns a constant reference to the current entry. /// /// \note Should not dereference the iterator when it is empty. /// /// \return constant reference to the current entry. - const T &operator*(void)const noexcept { return Row[Pos]; } + const std::tuple &operator*(void)const noexcept { return Row.tuple(); } /// Returns a constant pointer to the current entry. /// /// \note Should not dereference the iterator when it is empty. /// /// \return constant pointer to the current entry. - const T *operator->(void)const noexcept { return &Row[Pos]; } + const std::tuple *operator->(void)const noexcept { + return &Row.tuple(); + } /// Tells if \p this object is equal to another one. /// /// Two \c rosa::csv::CSVReader instances are equal if and only if they are /// the same or both are empty. /// /// \param RHS other object to compare to /// /// \return whether \p this object is equal with \p RHS - bool operator==(const CSVFlatIterator &RHS) const noexcept { + bool operator==(const CSVIterator &RHS) const noexcept { return ((this == &RHS) || ((this->Str == nullptr) && (RHS.Str == nullptr))); } /// Tells if \p this object is not equal to another one. /// /// \see rosa::csv::CSVReader::operator== /// /// \param RHS other object to compare to /// /// \return whether \p this object is not equal with \p RHS. - bool operator!=(const CSVFlatIterator &RHS) const noexcept { + bool operator!=(const CSVIterator &RHS) const noexcept { return !((*this) == RHS); } + /// Set the delimeter used in the csv file. + /// \param Separator the character which separates the values in the csv file. + inline void setDelimeter(char Separator) noexcept { + this->Delimeter = Separator; + } + + /// get the delimeter currently set to separate the values in the csv file. + /// \return the current character, which is used to separte teh values in the csv file. + inline char getDelimeter() const noexcept { + return this->Delimeter; + } + private: - std::istream *Str; ///< Input stream of a CSV file to iterate over. - CSVRow Row; ///< Content of the current row iterating over. - size_t Pos; ///< Current position within the current row. + std::istream *Str; ///< Input stream of a CSV file to iterate over. + size_t SkipRows; ///< Number of Rows to skip only once at the beginning of the file. + HeaderInformation HeaderInfo; ///< does the csv file contain a header or not, if this information is + ///< not given correclty, the reading of the header would result in + ///< in an error. + char Delimeter; ///< Delimeter between the entries in the csv file. + CSVRow Row; ///< Content of the current row + }; } // End namespace csv } // End namespace rosa #endif // ROSA_SUPPORT_CSV_CSVREADER_HPP diff --git a/include/rosa/support/csv/CSVWriter.hpp b/include/rosa/support/csv/CSVWriter.hpp index b67de6a..9dd6b9f 100755 --- a/include/rosa/support/csv/CSVWriter.hpp +++ b/include/rosa/support/csv/CSVWriter.hpp @@ -1,99 +1,227 @@ //===-- rosa/support/csv/CSVWriter.hpp --------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/support/csv/CSVWriter.hpp /// -/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// \authors David Juhasz (david.juhasz@tuwien.ac.at) +/// Edwin Willegger (edwin.willegger@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \brief Facitilities to write CSV files. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_CSV_CSVWRITER_HPP #define ROSA_SUPPORT_CSV_CSVWRITER_HPP +#include #include +#include +#include +#include + +#include "rosa/support/log.h" + namespace rosa { namespace csv { /// Provides facilities to write values into a CSV file. /// /// The writer emits a comma, the character `,`, between each written values. /// The resulted stream is a flat CSV file as it consists of onlyone row, no new /// line is emitted. /// /// \tparam T type of values to write template class CSVWriter { public: /// Creates a new instance. /// /// \param [in,out] S output stream to write to /// /// \note The writer operates on non-binary outputs as long as \p S is in /// good state. CSVWriter(std::ostream &S) : Str(S.good() && !(S.flags() & std::ios::binary) ? &S : nullptr), IsFirst(true) {} /// Tells if the last operation was successful. /// /// \return if the last operation was successful bool good(void) const noexcept { return Str != nullptr; } /// Writes an entry to the output stream. /// /// The implementation does anything only if the last operation was /// successful. If so, \p V is written to \c rosa::csv::CSVWriter::Str. /// The emitted value is preceded with a comma if the actual call is not the /// first one for \p this object. Success of the operation is checked at the /// end. /// /// \param V value to write void write(const T &V) { if (Str) { if (!IsFirst) { *Str << ','; } else { IsFirst = false; } *Str << V; if (!Str->good()) { Str = nullptr; } } } private: std::ostream *Str; ///< Output stream to write to. bool IsFirst; ///< Denotes if the next write would be the first one. }; +/// Writes a tuple of values into a CSV file +/// +/// \tparam Ts types of values to write +template class CSVTupleWriter { + +public: + +// typedef value_type ; ///< Type of values written. + typedef std::tuple value_type; + + /// Creates a new instance. + /// + /// \param [in,out] S output stream to write to + /// + /// \note The writer operates on non-binary outputs as long as \p S is in + /// good state. + CSVTupleWriter(std::ostream &S) + : Str(S.good() && !(S.flags() & std::ios::binary) ? &S : nullptr), + IsHeaderWritten(false), IsDataWritten(false) {} + + /// Tells if the last operation was successful. + /// + /// \return if the last operation was successful + bool good(void) const noexcept { + return Str != nullptr; + } + + + /// Write the values of a tuple to a CSV file with \c rosa::csv::CSVTupleWriter. + /// + /// \see rosa::csv::CSVTupleWriter + /// + /// + /// \param [in,out] values tuple, which values are written in a recusive fashion into a stream. + template + void write(const std::tuple &values) { + constexpr size_t size = sizeof...(Ts); + + LOG_TRACE_STREAM << "Writing tuple values into file \n"; + LOG_TRACE_STREAM << " Tuple has " << std::to_string(size) << " elements. \n"; + + LOG_TRACE_STREAM << " Value is " << std::get(values); + + if(Str){ + /// Write the current element of the tuple into the stream and add a separtor after it, + /// and call the function for the next element in the tuple. + if constexpr(i+1 != sizeof...(Ts)){ + *Str << std::get(values) << ", "; + write(values); + /// If the last element is written into the stream than begin a new line. + }else if constexpr(i + 1 == sizeof...(Ts)){ + *Str << std::get(values) << '\n'; + /// every time the last data value of a line is written, the flag indicates that data was already written into the file. + IsDataWritten = true; + } + } + } + + /// Write the header values to a CSV file with \c rosa::csv::CSVTupleWriter. + /// + /// \note The function has no effect if anything has already been written + /// to the output stream either by \c + /// rosa::csv::CSVTupleWriter::writeHeader() or \c + /// rosa::csv::CSVTupleWriter::write(). + /// + /// \see rosa::csv::CSVTupleWriter + /// + /// \param header the content of the header line. + void writeHeader(const std::array &header){ + size_t index = 0; + /// write into the stream only, if it is not a nullptr, and if no data and no header was already written into it. + if(Str && IsDataWritten == false && IsHeaderWritten == false){ + index = 0; + for (auto i = header.begin(); i != header.end(); ++i){ + index = index + 1; + /// write into the stream every entry with a delimeter, in this case ", " until + /// the last entry + if(index != header.size()){ + *Str << *i << ", "; + /// write the last entry into the stream, without any delimeter + }else { + *Str << *i; + } + } + /// finish the header line and start a new line. + *Str << '\n'; + /// now it is not possible to write additional header lines. + IsHeaderWritten = true; + } + } + +private: + std::ostream *Str; ///< Output stream to write to. + bool IsHeaderWritten; ///< If an header line was already written into the stream. If set than no additional header could be written. + bool IsDataWritten; ///< If one line of data has already been written into the stream, than no headerline could be added. +}; + +/// Writes all values of a tuple to a CSV file with \c rosa::csv::CSVTupleWriter. +/// +/// \see rosa::csv::CSVTupleWriter +/// +/// \tparam Ts types of values to write +/// +/// \param [in,out] W object to write with +/// \param V values to write +/// +/// \return \p W after writing \p V with it +template +CSVTupleWriter &operator<<(CSVTupleWriter &W, const std::tuple &V) +{ + W.write(V); + return W; +} + /// Writes a value to a CSV file with \c rosa::csv::CSVWriter. /// /// \see rosa::csv::CSVWriter /// /// \tparam T type of value to write /// /// \param [in,out] W object to write with /// \param V value to write /// /// \return \p W after writing \p V with it template CSVWriter &operator<<(CSVWriter &W, const T& V) { W.write(V); return W; } } // End namespace csv } // End namespace rosa #endif // ROSA_SUPPORT_CSV_CSVWRITER_HPP diff --git a/include/rosa/support/csv/namespace.h b/include/rosa/support/csv/namespace.h index 85ad49e..b077a57 100755 --- a/include/rosa/support/csv/namespace.h +++ b/include/rosa/support/csv/namespace.h @@ -1,25 +1,31 @@ //===-- rosa/support/csv/namespace.h ----------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/support/csv/namespace.h /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Documentation for the namespace \c rosa::csv. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_CSV_NAMESPACE_H #define ROSA_SUPPORT_CSV_NAMESPACE_H namespace rosa { /// Provides facilities to work with CSV files. namespace csv {} } // End namespace rosa #endif // ROSA_SUPPORT_CSV_NAMESPACE_H diff --git a/include/rosa/support/debug.hpp b/include/rosa/support/debug.hpp index ca3b966..c8230a9 100644 --- a/include/rosa/support/debug.hpp +++ b/include/rosa/support/debug.hpp @@ -1,127 +1,133 @@ //===-- rosa/support/debug.hpp ----------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/support/debug.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \brief Facility for debugging /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_DEBUG_HPP #define ROSA_SUPPORT_DEBUG_HPP #include "rosa/config/config.h" #include "rosa/support/terminal_colors.h" #include "rosa/support/type_helper.hpp" #include #include namespace rosa { /// Returns an output stream to use for debugging. std::ostream &dbgs(void); /// Prints a \c std::array to a \c std::ostream. /// /// \tparam T type of values stored in \p A /// \tparam Size number of elements in \p A /// /// \param [in,out] OS \c std::ostream to print to /// \param A \c std::array to print to \p OS /// /// \return \p OS after printing \p A template std::ostream &operator<<(std::ostream &OS, const std::array &A) { OS << '['; for (unsigned I = 0; I < Size; ++I) { if (I) { OS << ','; } OS << PRINTABLE(A[I]); } OS << ']'; return OS; } } // End namespace rosa /// \def ASSERT(stmt) /// \brief Enforces an assertion. /// /// \note Takes effect only when \c ROSA_ENABLE_ASSERTIONS is defined. /// /// Checks if \p stmt evaluates to true. If not, prints an error message about /// violating the assertion and aborts execution. /// /// \param stmt statement to evaluate, needs to be convertable to \c bool #ifndef ROSA_ENABLE_ASSERTIONS -#define ASSERT(stmt) ROSA_IGNORE_UNUSED(stmt) +#define ASSERT(stmt) ROSA_IGNORE_UNUSED_EXPR(stmt) #elif defined(ROSA_WINDOWS) #define ASSERT(stmt) \ if (static_cast(stmt) == false) { \ rosa::dbgs() << rosa::terminal::Color::Default << __FILENAME__ << ":" \ << __LINE__ << ": requirement failed: '" << #stmt << "'" \ << std::endl; \ ::abort(); \ } \ ROSA_VOID_STMT #else // defined(ROSA_LINUX) #include #define ASSERT(stmt) \ if (static_cast(stmt) == false) { \ rosa::dbgs() << rosa::terminal::Color::Default << __FILENAME__ << ":" \ << __LINE__ << ": requirement failed: '" << #stmt << "'" \ << std::endl; \ void *array[20]; \ auto bt_size = ::backtrace(array, 20); \ ::backtrace_symbols_fd(array, bt_size, 2); \ ::abort(); \ } \ ROSA_VOID_STMT #endif // defined(ROSA_ENABLE_ASSERTIONS) /// \def DEBUG(X) /// \brief Executes the given piece of code only if \c NDEBUG is not defined. /// /// \param X the code to execute /// \def DEBUGVAR(V) /// \brief Dumps the given variable to the default debug output. /// /// \param V the variable to dump /// /// \sa \c rosa::dbgs() #ifndef NDEBUG #define DEBUG(X) \ do { \ X; \ } while (false) #define DEBUGVAR(V) \ do { \ rosa::dbgs() << rosa::terminal::Color::Default << #V << " (" \ << __FILENAME__ << ":" << __LINE__ << "): " << (V) \ << std::endl; \ } while (false) #else // defined(NDEBUG) -#define DEBUG(X) ROSA_IGNORE_UNUSED(X) -#define DEBUGVAR(X) ROSA_IGNORE_UNUSED(X) +#define DEBUG(X) ROSA_IGNORE_UNUSED_STMT(X) +#define DEBUGVAR(X) ROSA_IGNORE_UNUSED_EXPR(X) #endif // defined(NDEBUG) /// Enforces static assertion. /// /// \param COND the condition to evaluate, must be statically evaluable /// \param DIAG error message if \p COND does not evaluate to \c true #define STATIC_ASSERT(COND, DIAG) static_assert((COND), DIAG) #endif // ROSA_SUPPORT_DEBUG_HPP diff --git a/include/rosa/support/iterator/namespace.h b/include/rosa/support/iterator/namespace.h new file mode 100644 index 0000000..027978d --- /dev/null +++ b/include/rosa/support/iterator/namespace.h @@ -0,0 +1,25 @@ +//===-- rosa/support/iterator/namespace.h -----------------------*- C++ -*-===/ +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===/ +/// +/// \file rosa/support/iterator/namespace.h +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Documentation for the namespace \c rosa::iterator. +/// +//===----------------------------------------------------------------------===/ + +#ifndef ROSA_SUPPORT_ITERATOR_NAMESPACE_H +#define ROSA_SUPPORT_ITERATOR_NAMESPACE_H + +namespace rosa { +/// Provides facilities to work with iterators. +namespace iterator {} +} // End namespace rosa + +#endif // ROSA_SUPPORT_ITERATOR_NAMESPACE_H diff --git a/include/rosa/support/iterator/split_tuple_iterator.hpp b/include/rosa/support/iterator/split_tuple_iterator.hpp new file mode 100644 index 0000000..f5af821 --- /dev/null +++ b/include/rosa/support/iterator/split_tuple_iterator.hpp @@ -0,0 +1,469 @@ +//===-- rosa/support/iterator/split_tuple_iterator.hpp ----------*- C++ -*-===/ +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===/ +/// +/// \file rosa/support/iterator/split_tuple_iterator.hpp +/// +/// \authors David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Facilities to split iterators of \c std::tuple into iterators of +/// their elements. +/// +//===----------------------------------------------------------------------===/ + +#ifndef ROSA_SUPPORT_ITERATOR_SPLIT_TUPLE_ITERATOR_HPP +#define ROSA_SUPPORT_ITERATOR_SPLIT_TUPLE_ITERATOR_HPP + +#include "rosa/support/debug.hpp" +#include "rosa/support/sequence.hpp" + +#include +#include +#include +#include + +namespace rosa { +namespace iterator { + +/// Anonymous namespace providing implementation for splitting tuple iterators; +/// consider it private. +namespace { + +/// Iterator of values provided by a buffer based on an index +/// +/// \tparam TB buffer providing values to iterate +/// \tparam I index of the values to iterator from \p TB +/// +/// \note \p TB is expected to implemnet an interface matching that of \c +/// TupleIteratorBuffer. +template +class SplittedElementIterator { +public: + /// Type alias for the values the iterator iterates. + using T = typename TB::template element_type; + + /// \defgroup SplittedElementIteratorTypedefs Typedefs of + /// \c SplittedElementIterator + /// + /// Standard `typedef`s for iterators. + /// + ///@{ + typedef std::input_iterator_tag + iterator_category; ///< Category of the iterator. + typedef T value_type; ///< Type of values iterated over. + typedef std::size_t difference_type; ///< Type to identify distance. + typedef T *pointer; ///< Pointer to the type iterated over. + typedef T &reference; ///< Reference to the type iterated ver. + ///@} + + /// Creates a new instance. + /// + /// \param Buffer buffer providing values for \p this object + /// + /// \note \p Buffer is captured as a \c std::shared_ptr because it is shared + /// among iterators for its element positions. + SplittedElementIterator(std::shared_ptr Buffer) + : Buffer(Buffer), Value() { + // \c Value is initialized empty so the first incrementation here will read + // the first value. + ++(*this); + } + + /// Creates an empty new instance. + SplittedElementIterator(void) noexcept : Buffer(), Value() {} + + /// Pre-increment operator. + /// + /// The implementation reads the next value from \c Buffer. If no more values + /// can be provided by \c Buffer, \p this object becomes empty and the + /// operator has no further effect. + /// + /// \return \p this object after incrementing it. + SplittedElementIterator &operator++() { + if (Buffer->template hasMore()) { + Value = Buffer->template next(); + } else { + // Reached end of range, remove reference of \c Buffer. + Buffer.reset(); + } + return *this; + } + + /// Post-increment operator. + /// + /// The implementation uses the pre-increment operator and returns a copy of + /// the original state of \p this object. + /// + /// \return \p this object before incrementing it. + SplittedElementIterator operator++(int) { + SplittedElementIterator Tmp(*this); + ++(*this); + return Tmp; + } + + /// Returns a constant reference to the current value. + /// + /// \note Should not dereference the iterator when it is empty. + /// + /// \return constant reference to the current value. + const T &operator*(void)const noexcept { return Value; } + + /// Returns a constant pointer to the current value. + /// + /// \note Should not dereference the iterator when it is empty. + /// + /// \return constant pointer to the current value. + const T *operator->(void)const noexcept { return &Value; } + + /// Tells if \p this object is equal to another one. + /// + /// Two \c SplittedElementIterator instances are equal if and only if they are + /// the same or both are empty. + /// + /// \param RHS other object to compare to + /// + /// \return whether \p this object is equal with \p RHS + bool operator==(const SplittedElementIterator &RHS) const noexcept { + return ((this == &RHS) || (!Buffer && !RHS.Buffer)); + } + + /// Tells if \p this object is not equal to another one. + /// + /// \see SplittedElementIterator::operator== + /// + /// \param RHS other object to compare to + /// + /// \return whether \p this object is not equal with \p RHS. + bool operator!=(const SplittedElementIterator &RHS) const noexcept { + return !((*this) == RHS); + } + +private: + std::shared_ptr Buffer; ///< Buffer providing values for \p this object + T Value; ///< The current value of the iterator +}; + +///\defgroup TupleBufferContainer Type converter turning a \c std::tuple of +///types into a \c std::tuple of container of those types. +/// +/// The new type is used for buffering elements of tuples separately. +/// +///@{ + +/// Template declaration. +/// +/// \tparam T type to convert +/// +/// \note The template is defined only when \p T is a \c std::tuple. +/// +/// Usage for a type \c Tuple as:\code +/// typename TupleBufferContainer::Type +/// \endcode +template struct TupleBufferContainer; + +/// Template definition for \c std::tuple. +template struct TupleBufferContainer> { + /// The converted type. + using Type = std::tuple...>; +}; + +///@} + +/// Convenience template type alias for easy use of \c TupleBufferContainer. +/// +/// Converts a \c std::tuple of types into a \c std::tuple of container of those +/// types. +/// +/// \tparam Tuple type to convert +template +using tuple_buffer_container_t = typename TupleBufferContainer::Type; + +/// Buffer for element values for iterator of \c std::tuple. +/// +/// The class can be instantiated with a range of iterator of \c std::tuple and +/// provides an interface for accessing elements of the iterated tuples one by +/// one. +/// +/// \note The class is utilized by \c SplittedElementIterator. +/// +/// \tparam TupleIterator the iterator type that provides values of \c +/// std::tuple +/// +/// \todo Consider thread safety of the class. +template +class TupleIteratorBuffer { +public: + /// Type alias for the value type of \p TupleIterator. + /// \note The type is expected to be \c std::tuple. + using iterator_value_type = typename TupleIterator::value_type; + + /// The number of elements of \c iterator_value_type. + static constexpr size_t iterator_value_size = + std::tuple_size_v; + + /// Template type alias to get element types of \c iterator_value_type by + /// index. + /// \tparam I the index of the element + template + using element_type = + typename std::tuple_element::type; + + /// Type alias for index sequence for accessing elements of \c + /// iterator_value_type. + using element_idx_seq_t = seq_t; + + /// Creates a new instance. + /// + /// \param Begin the beginning of the iterator range to buffer + /// \param End the end of the iterator range to buffer + TupleIteratorBuffer(TupleIterator &&Begin, const TupleIterator &End) noexcept + : Iterator(Begin), End(End), Buffer() {} + + /// Tells whether there is any more value in the handled range for index \p I. + /// + /// \tparam I the index of the element to check + /// + /// \return whether there is more value for index \p I + template bool hasMore(void) const noexcept { + const auto &ElementBuffer = std::get(Buffer); + // There is more value if some has already been buffered or the end of the + // handled range is not reached yet. + return !ElementBuffer.empty() || Iterator != End; + } + + /// Gives the next value from the handled range for index \p I. + /// + /// \note The function should not be called if there is no more value for + /// index \p I (i.e., \c hasMore() returns \c false). If it is called in + /// that situation, it returns the default value of \c element_type. + /// + /// \tparam I the index of the element to fetch next value for + /// + /// \return the next value for index \p I + template element_type next(void) noexcept { + auto &ElementBuffer = std::get(Buffer); + if (ElementBuffer.empty()) { + // This position needs a new value from Iterator if there is more. + if (Iterator != End) { + // Push next value for all elements. + push(*Iterator++, element_idx_seq_t()); + } else { + // No more values; called when hasMore() is false. + // Return default value. + return element_type(); + } + } + // Buffer is not empty here, return the next value. + ASSERT(!ElementBuffer.empty() && "Unexpected condition"); + // Get the next value and also pop it from the buffer. + const element_type Elem = ElementBuffer.front(); + ElementBuffer.pop(); + return Elem; + } + +private: + /// Buffers next tuple value elementwise into element containers. + /// + /// \tparam S0 indices for accessing elements of \c std::tuple + /// + /// \param Tuple the values to buffer + /// + /// \note The second parameter provides indices as template parameter \p S0 + /// and its actual value is ignored. + template + void push(const iterator_value_type &Tuple, Seq) noexcept { + (std::get(Buffer).push(std::get(Tuple)), ...); + } + + TupleIterator Iterator; ///< Current input iterator + const TupleIterator End; ///< End of iterator range to handle + tuple_buffer_container_t + Buffer; ///< Container for elementwise buffering +}; + +/// Template type alias for iterator of an element based on buffered iterator of +/// \c std::tuple. +/// +/// The alias utilizes \c SplittedElementIterator for iterating over the values +/// provided by \p TB. +/// +/// \tparam TB buffer providing values to iterate +/// \tparam I index of the values to iterator from \p TB +template +using element_iterator_t = SplittedElementIterator; + +/// Template type alias for iterator-range of an element based on buffered +/// iterator of \c std::tuple. +/// +/// \tparam TB buffer providing values to iterate +/// \tparam I index of the values to iterator from \p TB +template +using element_iterator_range_t = + std::pair, element_iterator_t>; + +///\defgroup ElementIteratorRanges Type converter turning a buffer of \c +/// std::tuple into a \c std::tuple of corresponding \c +/// element_iterator_range_t. +/// +///@{ + +/// Template declaration. +/// +/// \tparam TB buffer providing values +/// \tparam S type providing indices for accessing elements of \c std::tuple +/// +/// \note \p TB is expected to implement an interface matching that of \c +/// TupleIteratorBuffer. +/// +/// \note The template is defined only when \p S is a \c rosa::Seq. +/// +/// Usage for a proper buffer type \c TB:\code +/// typename ElementIteratorRanges::Type +/// \endcode +template struct ElementIteratorRanges; + +/// Template definition. +template +struct ElementIteratorRanges> { + /// The converted type. + using Type = std::tuple...>; +}; + +///@} + +/// Convenience template type alias for easy use of \c ElementIteratorRanges. +/// +/// Converts a buffer of \c std::tuple into a \c std::tuple of corresponding \c +/// element_iterator_range_t. +/// +/// \tparam TB buffer providing values +/// +/// \note \p TB is expected to implement an interface matching that of \c +/// TupleIteratorBuffer. +template +using element_iterator_ranges_t = + typename ElementIteratorRanges::Type; + +/// Template type alias for turning an iterator of \c std::tuple into +/// corresponding \c element_iterator_ranges_t. +/// +/// The alias utilizes \c TupleIteratorBuffer for buffering the values provided +/// by \p TupleIterator. The elementwise iterators are \c +/// SplittedElementIterator. +/// +/// \tparam TupleIterator iterator of \c std::tuple +template +using splitted_tuple_iterator_ranges_t = + element_iterator_ranges_t>; + +/// Creates elementwise iterator ranges for an iterator range of \c std::tuple. +/// +/// \note Implementation for \c rosa::iterator::splitTupleIterator. +/// +/// \tparam TupleIterator iterator of \c std::tuple +/// \tparam S0 indices for accessing elements of \c std::tuple +/// +/// \param Begin the beginning of the iterator range to handle +/// \param End the end of the iterator range to handle +/// +/// \note The last parameter provides indices as template parameter \p S0 and +/// its actual value is ignored. +/// +/// \return \c std::tuple of elementwise iterator ranges corresponding to \p +/// Begin and \p End +template +splitted_tuple_iterator_ranges_t +splitTupleIteratorImpl(TupleIterator &&Begin, const TupleIterator &End, + Seq) noexcept { + using TB = TupleIteratorBuffer; + // Create a buffer in shared pointer. The buffer will be destructed once + // all corresponding element iterators (created below) have reached the end of + // the handled range or have been destructed. + auto Buffer = std::make_shared(std::move(Begin), End); + return {std::make_pair(element_iterator_t(Buffer), + element_iterator_t())...}; +} + +} // End namespace + +/// Gives the beginning of a range created by \c splitTupleIterator(). +/// +/// \see rosa::iterator::splitTupleIterator() +/// +/// \note The signature of the template function uses implementation details. +/// +/// \tparam TB buffer providing values to iterate +/// \tparam I index of the values to iterator from \p TB +/// +/// \param Range range to get the beginning of +/// +/// \return beginning of \p Range +template +element_iterator_t &begin(element_iterator_range_t &Range) { + return Range.first; +} + +/// Gives the end of a range created by \c splitTupleIterator(). +/// +/// \see rosa::iterator::splitTupleIterator() +/// +/// \note The signature of the template function uses implementation details. +/// +/// \tparam TB buffer providing values to iterate +/// \tparam I index of the values to iterator from \p TB +/// +/// \param Range range to get the end of +/// +/// \return end of \p Range +template +element_iterator_t &end(element_iterator_range_t &Range) { + return Range.second; +} + +/// Creates elementwise iterator ranges for an iterator range of \c std::tuple. +/// +/// \note The implementation utilizes \c splitTupleIteratorImpl. +/// +/// Obtain element iterator ranges for an iterator range \c Begin and \c End of +/// iterator type \c TupleIterator of \c std::tuple as \code +/// auto [T1Range, T2Range, T3Range] = splitTupleIterator(std::move(Begin), End); +/// auto [T1Begin, T1End] = T1Range; +/// auto [T2Begin, T2End] = T2Range; +/// auto [T3Begin, T3End] = T3Range; +/// \endcode +/// One range can also be dissected by dedicated functions like \code +/// auto T1Begin = begin(T1Range); +/// auto T1End = end(T1Range); +/// \endcode +/// +/// The function returns a tuple of ranges (i.e., \c std::pair of iterators), +/// which can be assigned to variables using structured binding. The ranges can +/// be dissected into begin and end iterators by separate structured bindings. +/// +/// The function moves its first argument (i.e., \p Begin cannot be used +/// directly after splitting it). If the first argument is a variable, it needs +/// to be moved explicitly by \c std::move() as in the example. +/// +/// \tparam TupleIterator iterator of \c std::tuple +/// +/// \param Begin the beginning of the iterator range to handle +/// \param End the end of the iterator range to handle +/// +/// \return \c std::tuple of elementwise iterator ranges corresponding to \p +/// Begin and \p End +template +splitted_tuple_iterator_ranges_t +splitTupleIterator(TupleIterator &&Begin, const TupleIterator &End) noexcept { + return splitTupleIteratorImpl( + std::move(Begin), End, + seq_t>()); +} + +} // End namespace iterator +} // End namespace rosa + +#endif // ROSA_SUPPORT_ITERATOR_SPLIT_TUPLE_ITERATOR_HPP diff --git a/include/rosa/support/log.h b/include/rosa/support/log.h index 9573bfb..c5b60ce 100644 --- a/include/rosa/support/log.h +++ b/include/rosa/support/log.h @@ -1,259 +1,265 @@ //===-- rosa/support/log.h --------------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/support/log.h /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \brief Facility for logging. /// /// \note One call for the various logging macros is supposed to be used /// for registering one log entry. That goes natural with the non-stream /// implementations, which accept one string as argument. A more flexible way /// for printing log entries, for example for colorizing text, is to use macros /// providing a log stream. It is important to note, however, that the stream /// obtained from one macro evaluation fits for printing one log entry, without /// nested/overlapping log entry emissions and the entry being closed with a /// newline. Should this simple recommendation not being followed, the result /// becomes hard to read due to missing line breaks and overlapping entries. /// /// \todo Thread-safety is another issue, which need to be addressed for proper /// logging. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_LOG_H #define ROSA_SUPPORT_LOG_H #include "rosa/config/config.h" #include "rosa/support/terminal_colors.h" #include #include /* **************************************************************************** * Log Levels * * ****************************************************************************/ namespace rosa { /// Type-safe definition of log levels, use this in code. /// \note Keep values in sync with the corresponding preprocessor definitions. enum class LogLevel { Error, ///< Log errors only Warning, ///< Like \c rosa::LogLevel::Error and also log warnings Info, ///< Like \c rosa::LogLevel::Warning and also log general infos Debug, ///< Like \c rosa::LogLevel::Info and also log debug infos Trace, ///< Like \c rosa::LogLevel::Debug and also log trace infos NumLogLevels ///< Number of log levels }; /// Converts a \c rosa::LogLevel to its textual representation. /// /// \param logLevel \c rosa::LogLevel to convert /// /// \return \c std::string representing \p logLevel /// /// \pre `logLevel` is valid:\code /// logLevel != LogLevel::NumLogLevels /// \endcode std::string logLevelToString(const LogLevel logLevel); /// Prints colorized tag for the given \c rosa::LogLevel. /// /// \param [in,out] OS \c std::ostream to print to /// \param logLevel \c rosa::LogLevel to print tag for /// /// \return \p OS after printing a tag for \p logLevel /// /// \pre \p logLevel is valid:\code /// logLevel != LogLevel::NumLogLevels /// \endcode std::ostream &operator<<(std::ostream &OS, const LogLevel logLevel); } // End namespace rosa /// \name Valid log level constants /// \note Only for preprocessor definitions in this file. /// \note Keep the defintions in sync with the values of \c rosa::LogLevel. ///@{ #define ROSA_LOG_LEVEL_ERROR \ 0 ///< Value corresponding to \c rosa::LogLevel::Error #define ROSA_LOG_LEVEL_WARNING \ 1 ///< Value corresponding to \c rosa::LogLevel::Warning #define ROSA_LOG_LEVEL_INFO \ 2 ///< Value corresponding to \c rosa::LogLevel::Info #define ROSA_LOG_LEVEL_DEBUG \ 3 ///< Value corresponding to \c rosa::LogLevel::Debug #define ROSA_LOG_LEVEL_TRACE \ 4 ///< Value corresponding to \c rosa::LogLevel::Trace ///@} /* **************************************************************************** * Logger Implementation * * ****************************************************************************/ /// Stream to print logs to /// /// \todo Make it configurable, e.g. printing into a file. #define ROSA_LOG_OSTREAM std::clog /// Prints a log message to \c ROSA_LOG_OSTREAM. /// /// \param level \c rosa::LogLevel of the log entry /// \param output message to print #define ROSA_LOG_IMPL(level, output) \ do { \ ROSA_LOG_OSTREAM << (level) << " " << __func__ << "@" << __FILENAME__ \ << ":" << __LINE__ << ": " << (output) << '\n'; \ } while (false) /// Returns a stream to print a log message to. /// /// \param level \c rosa::LogLevel of the log entry that is about to be printed #define ROSA_LOG_STREAM_IMPL(level) \ ([](void) -> std::ostream & { \ return ROSA_LOG_OSTREAM << (level) << " " << __func__ << "@" \ << __FILENAME__ << ":" << __LINE__ << ": "; \ }()) namespace rosa { /// Dummy \c std::ostream printing to nowhere. extern std::ostream LogSink; } // End namespace rosa /// An output stream ignoring all its input. #define ROSA_LOG_STREAM_IGNORE rosa::LogSink /* **************************************************************************** * Logging Interface * * ****************************************************************************/ /// \name Logging interface /// /// Preprocesser macros for convenience. ///@{ /// \def LOG_ERROR_STREAM /// \brief Returns a stream to print a log entry of level /// \c rosa::LogLevel::Error. /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Error. /// \def LOG_ERROR(output) /// \brief Prints a log entry of level \c rosa::LogLevel::Error. /// \param output the message to print /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Error. /// \def LOG_WARNING_STREAM /// \brief Returns a stream to print a log entry of level /// \c rosa::LogLevel::Warning. /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Warning. /// \def LOG_WARNING(output) /// \brief Prints a log entry of level \c rosa::LogLevel::Warning. /// \param output the message to print /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Warning. /// \def LOG_INFO_STREAM /// \brief Returns a stream to print a log entry of level /// \c rosa::LogLevel::Info. /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Info. /// \def LOG_INFO(output) /// \brief Prints a log entry of level \c rosa::LogLevel::Info. /// \param output the message to print /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Info. /// \def LOG_DEBUG_STREAM /// \brief Returns a stream to print a log entry of level /// \c rosa::LogLevel::Debug. /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Debug. /// \def LOG_DEBUG(output) /// \brief Prints a log entry of level \c rosa::LogLevel::Debug. /// \param output the message to print /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Debug. /// \def LOG_TRACE_STREAM /// \brief Returns a stream to print a log entry of level /// \c rosa::LogLevel::Trace. /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Trace. /// \def LOG_TRACE(output) /// \brief Prints a log entry of level \c rosa::LogLevel::Trace. /// \param output the message to print /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Trace. ///@} // Define logging macros if logging is enabled. #ifdef ROSA_LOG_LEVEL #define LOG_ERROR_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Error) #define LOG_ERROR(output) ROSA_LOG_IMPL(rosa::LogLevel::Error, output) #if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_WARNING #define LOG_WARNING_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Warning) #define LOG_WARNING(output) ROSA_LOG_IMPL(rosa::LogLevel::Warning, output) #endif #if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_INFO #define LOG_INFO_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Info) #define LOG_INFO(output) ROSA_LOG_IMPL(rosa::LogLevel::Info, output) #endif #if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_DEBUG #define LOG_DEBUG_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Debug) #define LOG_DEBUG(output) ROSA_LOG_IMPL(rosa::LogLevel::Debug, output) #endif #if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_TRACE #define LOG_TRACE_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Trace) #define LOG_TRACE(output) ROSA_LOG_IMPL(rosa::LogLevel::Trace, output) #endif #endif // defined ROSA_LOG_LEVEL // Define all disabled logging features as void. #ifndef LOG_ERROR #define LOG_ERROR_STREAM ROSA_LOG_STREAM_IGNORE -#define LOG_ERROR(output) ROSA_IGNORE_UNUSED(output) +#define LOG_ERROR(output) ROSA_IGNORE_UNUSED_EXPR(output) #endif #ifndef LOG_WARNING #define LOG_WARNING_STREAM ROSA_LOG_STREAM_IGNORE -#define LOG_WARNING(output) ROSA_IGNORE_UNUSED(output) +#define LOG_WARNING(output) ROSA_IGNORE_UNUSED_EXPR(output) #endif #ifndef LOG_INFO #define LOG_INFO_STREAM ROSA_LOG_STREAM_IGNORE -#define LOG_INFO(output) ROSA_IGNORE_UNUSED(output) +#define LOG_INFO(output) ROSA_IGNORE_UNUSED_EXPR(output) #endif #ifndef LOG_DEBUG #define LOG_DEBUG_STREAM ROSA_LOG_STREAM_IGNORE -#define LOG_DEBUG(output) ROSA_IGNORE_UNUSED(output) +#define LOG_DEBUG(output) ROSA_IGNORE_UNUSED_EXPR(output) #endif #ifndef LOG_TRACE #define LOG_TRACE_STREAM ROSA_LOG_STREAM_IGNORE -#define LOG_TRACE(output) ROSA_IGNORE_UNUSED(output) +#define LOG_TRACE(output) ROSA_IGNORE_UNUSED_EXPR(output) #endif #endif // ROSA_SUPPORT_LOG_H diff --git a/include/rosa/support/math.hpp b/include/rosa/support/math.hpp index f88a13d..0ca3262 100644 --- a/include/rosa/support/math.hpp +++ b/include/rosa/support/math.hpp @@ -1,57 +1,63 @@ //===-- rosa/support/math.hpp -----------------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/support/math.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Math helpers. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_MATH_HPP #define ROSA_SUPPORT_MATH_HPP #include #include #include #include namespace rosa { /// Computes log base 2 of a number. /// /// \param N the number to compute log base 2 for /// /// \return log base 2 of \p N constexpr size_t log2(const size_t N) { return ((N < 2) ? 1 : 1 + log2(N / 2)); } /// Tells the next representable floating point value. /// /// \tparam T type to operate on /// /// \note The second type argument enforces \p T being a floating point type, /// always use the default value! /// /// \param V value to which find the next representable one /// /// \return the next representable value of type \p T after value \p V /// /// \pre Type \p T must be a floating point type, which is enforced by /// `std::enable_if` in the second type argument. template ::value>> T nextRepresentableFloatingPoint(const T V) { return std::nextafter(V, std::numeric_limits::infinity()); } } // End namespace rosa #endif // ROSA_SUPPORT_MATH_HPP diff --git a/include/rosa/support/sequence.hpp b/include/rosa/support/sequence.hpp index 5764a80..a2ba12a 100755 --- a/include/rosa/support/sequence.hpp +++ b/include/rosa/support/sequence.hpp @@ -1,51 +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 +/// \date 2017-2019 /// /// \brief Template facilities to statically generate a sequence of numbers. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_SEQUENCE_HPP #define ROSA_SUPPORT_SEQUENCE_HPP #include namespace rosa { /// \defgroup Seq Implementation of rosa::Seq /// /// Facility to statically generate sequences of numbers. /// ///@{ /// Template with an empty struct to store a sequence of numbers in compile time /// as template arguments. /// /// Generate a sequence of numbers from `0` up to (including) `(N - 1)` like /// \code /// typename GenSeq::Type /// \endcode template struct Seq {}; /// Sequence generator, the general case when counting down by extending the /// sequence. template struct GenSeq : GenSeq {}; /// Sequence generator, the terminal case when storing the generated sequence /// into \c Seq. template struct GenSeq<0, S...> { using Type = Seq; }; ///@} +/// Convenience template alias for using \c rosa::GenSeq to obtain an instance + /// of \c rosa::Seq. + /// + /// \see \c rosa::Seq and \c rosa::GenSeq + template using seq_t = typename GenSeq::Type; } // 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 48df20a..3e1c031 100644 --- a/include/rosa/support/squashed_int.hpp +++ b/include/rosa/support/squashed_int.hpp @@ -1,132 +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 +/// \date 2017-2019 /// /// \brief Facilities for squashing integer types into standard equivalents. /// /// \note This implementation is partially based on the \c squashed_int /// implementation of CAF. -/// \todo Check license. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_SQUASHED_INT_HPP #define ROSA_SUPPORT_SQUASHED_INT_HPP #include "rosa/support/type_list.hpp" #include "rosa/support/type_pair.hpp" namespace rosa { /// Compile-time list of integer types. /// /// \note This list is used to select a proper type as \c rosa::type_nr_t, /// always make sure that \c rosa::type_nr_t remains correct whenever changing /// the list. using IntegerTypesBySize = TypeList< // bytes none_t, // 0 TypePair, // 1 TypePair, // 2 none_t, // 3 TypePair, // 4 none_t, // 5 none_t, // 6 none_t, // 7 TypePair // 8 >; -/// Squashes integer types into \c [u]int_[8|16|32|64]_t equivalents. +/// Squashes integral types (except \c bool) into \c [u]int_[8|16|32|64]_t +/// equivalents. /// /// The squashed type for a type \c T can be obtained as \code /// typename SquashedInt::Type /// \endcode /// -/// \tparam T the integer type to squash +/// \tparam T the integral type to squash /// /// \pre \p T is an integral type:\code /// std::is_integral::value /// \endcode template struct SquashedInt { - STATIC_ASSERT((std::is_integral::value), "squashing a non-integral type"); + STATIC_ASSERT((std::is_integral::value && !std::is_same::value), + "squashing a non-integral type or bool"); using TPair = typename TypeListAt::Type; using Type = typename std::conditional::value, typename TPair::First, typename TPair::Second>::type; }; /// Convenience alias for obtaining a squashed integer type. template using squashed_int_t = typename SquashedInt::Type; /// \defgroup SquashedType Implementation for squashing types /// /// \brief Squashes a type. /// /// The squashed type for a type \c T can be obtained as \code /// typename SquashedType::Type /// \endcode /// The resulting type is squashed with \c rosa::SquashedInt if \c T is -/// integral, and remains \p T otherwise. +/// integral but not \c bool, and remains \p T otherwise. ///@{ /// Definition for the general case, when squashing a non-integral type. /// /// \tparam T the type to squash /// \tparam IsIntegral Always use the default value! template ::value> struct SquashedType { using Type = T; }; /// Specialization for the case when squashing an integral type. /// /// \tparam T the type to squash template struct SquashedType { using Type = squashed_int_t; }; +/// Specialization for the type \c bool. +/// +/// \note The type \c bool is an integral type and would be squashed by the +/// general case to \c uint8_t without this specialization. +template <> struct SquashedType { using Type = bool; }; + ///@} /// Convenience alias for obtaining a squashed type. template using squashed_t = typename SquashedType::Type; /// \defgroup SquashedTypeList Implementation for squashing lists of types /// /// \brief Squashes a \c rosa::TypeList elementwise. /// /// Replaces all types in a \c rosa::TypeList with their corresponding squashed /// types by using \c rosa::SquashedType. The squashed \c rosa::TypeList /// corresponding to \c List can be obtained as \code /// typename SquashedTypeList::Type /// \endcode ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to squash template struct SquashedTypeList; // Specialization for \c rosa::EmptyTypeList. template <> struct SquashedTypeList { using Type = EmptyTypeList; }; /// Specialization for non-empty \c rosa::TypeList. template struct SquashedTypeList> { using Type = typename TypeListPush< squashed_t, typename SquashedTypeList>::Type>::Type; }; ///@} } // End namespace rosa #endif // ROSA_SUPPORT_SQUASHED_INT_HPP diff --git a/include/rosa/support/terminal_colors.h b/include/rosa/support/terminal_colors.h index 5f21ab8..2c8ddbe 100644 --- a/include/rosa/support/terminal_colors.h +++ b/include/rosa/support/terminal_colors.h @@ -1,70 +1,76 @@ //===-- rosa/support/terminal_colors.h --------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/support/terminal_colors.h /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Facility for printing colorized text to terminals supporting it. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_TERMINAL_COLORS_H #define ROSA_SUPPORT_TERMINAL_COLORS_H #include namespace rosa { /// Encloses entities related to terminal I/O. namespace terminal { /// Text colors for colorizable terminals. enum class Color { Default, Black, Red, Green, Yellow, Blue, Magenta, Cyan, Lightgrey, Darkgrey, Lightred, Lightgreen, Lightyellow, Lightblue, LightMagenta, Lightcyan, White, NumColors ///< Number of \c rosa::terminal::Color values }; /// Handles \c rosa::terminal::Color values sent to output streams. /// /// The operator sends terminal commands through \p os to the /// associated terminal to change text color to \p color. /// /// \note If \p os is not a terminal output, the terminal commands simply appear /// as text in the stream. /// /// \param [in,out] os \c std::ostream to apply \p color to /// \param color \c rosa::terminal::Color to apply for \p os /// /// \return \p os after applying \p color to it /// /// \pre \p color is valid:\code /// color != Color::NumColors /// \endcode std::ostream &operator<<(std::ostream &os, const Color color); } // End namespace terminal } // End namespace rosa #endif // ROSA_SUPPORT_TERMINAL_COLORS_H diff --git a/include/rosa/support/tokenized_storages.hpp b/include/rosa/support/tokenized_storages.hpp index 0f5bdbf..3353284 100755 --- a/include/rosa/support/tokenized_storages.hpp +++ b/include/rosa/support/tokenized_storages.hpp @@ -1,510 +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 +/// \date 2017-2019 /// /// \brief Definition of storage helper template for storing values in a /// type-safe way based on type tokens. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_TOKENIZED_STORAGES_HPP #define ROSA_SUPPORT_TOKENIZED_STORAGES_HPP #include "rosa/support/type_token.hpp" #include #include namespace rosa { /// Defines a simple interface for storing and accessing values of different /// types. /// /// While the interface provides features to access values and know their /// types, it is the users responsibility to use particular values according to /// their actual types. No facilities for type-safe access of values is /// provided by the class. /// /// \see \c rosa::TokenizedStorage for a type-safe specialization of the /// interface. class AbstractTokenizedStorage { protected: /// Protected constructor restricts instantiation for derived classes. AbstractTokenizedStorage(void) noexcept = default; public: /// No copying and moving of \c rosa::AbstractTokenizedStorage instances. ///@{ AbstractTokenizedStorage(const AbstractTokenizedStorage&) = delete; AbstractTokenizedStorage &operator=(const AbstractTokenizedStorage&) = delete; AbstractTokenizedStorage(AbstractTokenizedStorage&& Other) = delete; AbstractTokenizedStorage &operator=(AbstractTokenizedStorage&&) = delete; ///@} /// Destroys \p this object. virtual ~AbstractTokenizedStorage(void) noexcept = default; /// Tells how many values are stored in \p this object. /// /// \return number of values stored in \p this object virtual size_t size(void) const noexcept = 0; /// Tells the type of the value stored at a position. /// /// \param Pos the index of the value whose type is to returned /// /// \return \c rosa::TypeNumber for the value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code /// Pos < size() /// \endcode - virtual TypeNumber typeAt(const size_t Pos) const noexcept = 0; + 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 size_t Pos) noexcept = 0; + 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 size_t Pos) const noexcept = 0; + virtual const void *pointerTo(const token_size_t Pos) const noexcept = 0; }; /// Template class storing values and providing dynamic type-safe access to /// them in a lightweight way based on type tokens. /// /// \see rosa/support/type_token.hpp /// /// \tparam Types types whose values are to be stored template class TokenizedStorage; +/// \defgroup TokenizedStorageForTypeList Implementation of +/// rosa::TokenizedStorageForTypeList +/// +/// \brief Transforms a \c rosa::TypeList instance to the corresponding +/// \c rosa::TokenizedStorage instance. +/// +/// A \c rosa::TypeList \c List instance can be turned into a corresponding \c +/// rosa::TokenizedStorage instance as \code +/// typename TokenizedStorageForTypeList::Type +/// \endcode +/// +/// For example, the following expression evaluates to `true`: \code +/// std::is_same>::Type, +/// TokenizedStorage>::value +/// \endcode +///@{ + +/// Declaration of the template. +/// +/// \tparam List \c rosa::TypeList to transform +template struct TokenizedStorageForTypeList; + +/// Implementation of the template for \c rosa::TypeList instances. +template +struct TokenizedStorageForTypeList> { + using Type = TokenizedStorage; +}; + +///@} + /// Nested namespace with implementation for \c rosa::TokenizedStorage, consider /// it private. namespace { /// Initializes a pre-allocated memory area with values from constant lvalue /// references. /// /// \tparam Types types whose values are to be stored /// /// \param Arena pre-allocated memory area to store values to /// \param Ts the values to store in \p Arena /// /// \note \p Arena needs to be a valid pointer to a memory area big enough for /// values of \p Types. template inline void createArenaElements(void *const Arena, const Types &... Ts) noexcept; /// \defgroup createLvalueArenaElement Implementation of creating lvalue arena elements /// /// Stores values from constant lvalue references into a pre-allocated memory /// area. /// /// \note To be used by the implementation of \c createArenaElements. /// /// \todo Document these functions. ///@{ /// \note This terminal case is used for both constant lvalue references and /// value references. template inline void createArenaElement(void *const, const std::vector &Offsets) { ASSERT(Pos == Offsets.size()); } template inline void createArenaElement(void *const Arena, const std::vector &Offsets, const Type &T, const Types &... Ts) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); new (static_cast(static_cast(static_cast(Arena) + Offsets[Pos]))) Type(T); createArenaElement(Arena, Offsets, Ts...); } template inline void createArenaElement(void *const Arena, const std::vector &Offsets, const AtomConstant &, const Types &... Ts) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); *static_cast( static_cast(static_cast(Arena) + Offsets[Pos])) = V; createArenaElement(Arena, Offsets, Ts...); } ///@} /// Implementation of the template. /// -/// \tparam Type the type of the mandatory first value to store -/// \tparam Types types of any further values to store +/// \tparam Types types of values to store /// /// \param Arena pre-allocated memory area to store values to -/// \param T the first value to store in \p Arena˛ -/// \param Ts optional further values to store in \p Arena +/// \param Ts values to store in \p Arena /// /// \pre \p Arena is not \p nullptr. -template -inline void createArenaElements(void *const Arena, const Type &T, +template +inline void createArenaElements(void *const Arena, const Types &... Ts) noexcept { ASSERT(Arena != nullptr); - createArenaElement<0>(Arena, TokenizedStorage::Offsets, T, - Ts...); + createArenaElement<0>(Arena, TokenizedStorage::Offsets, Ts...); } /// Initializes a pre-allocated memory area with values from rvalue references. /// /// \tparam Types types whose values are to be stored /// /// \param Arena pre-allocated memory area to store values to /// \param Ts the values to store in \p Arena /// /// \note \p Arena needs to be a valid pointer to a memory area big enough for /// values of \p Types. template inline void createArenaElements(void *const Arena, Types &&... Ts) noexcept; /// \defgroup createRvalueArenaElement Implementation of creating rvalue arena elements /// /// Stores values from rvalue references into a pre-allocated memory area. /// /// \note To be used by the implementation of \c createArenaElements. /// /// \todo Document these functions. ///@{ template inline void createArenaElement(void *const Arena, const std::vector &Offsets, Type &&T, Types &&... Ts) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); new (static_cast(static_cast( static_cast(Arena) + Offsets[Pos]))) Type(std::move(T)); createArenaElement(Arena, Offsets, std::move(Ts)...); } template inline void createArenaElement(void *const Arena, const std::vector &Offsets, AtomConstant &&, Types &&... Ts) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); *static_cast( static_cast(static_cast(Arena) + Offsets[Pos])) = V; createArenaElement(Arena, Offsets, std::move(Ts)...); } ///@} /// Implementation of the template. /// -/// \tparam Type the type of the mandatory first value to store -/// \tparam Types types of any further values to store +/// \tparam Types types of values to store /// /// \param Arena pre-allocated memory area to store values to -/// \param T the first value to store in \p Arena -/// \param Ts optional further values to store in \p Arena +/// \param Ts values to store in \p Arena /// /// \pre \p Arena is not \c nullptr. -template -inline void createArenaElements(void *const Arena, Type &&T, - Types &&... Ts) noexcept { +template +inline void createArenaElements(void *const Arena, Types &&... Ts) noexcept { ASSERT(Arena != nullptr); - createArenaElement<0>(Arena, TokenizedStorage::Offsets, - std::move(T), std::move(Ts)...); + createArenaElement<0>(Arena, TokenizedStorage::Offsets, + std::move(Ts)...); } /// Destroys values allocated by \c createArenaElements. /// -/// \tparam Type type of the mandatory first value stored in \p Arena -/// \tparam Types futher types whose values are stored in \p Arena +/// \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 +template inline void destroyArenaElements(void *const Arena) noexcept; /// \defgroup destroyArenaElement Implementation of destroying arena elements /// /// Destroys values from a memory area. /// /// \note To be used by the implementation of \c destroyArenaElements. /// /// \todo Document these functions. ///@{ template inline void destroyArenaElement(void *const, const std::vector &Offsets) noexcept { ASSERT(Pos == Offsets.size()); } template inline void destroyArenaElement(void *const Arena, const std::vector &Offsets) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); static_cast( static_cast(static_cast(Arena) + Offsets[Pos])) ->~Type(); destroyArenaElement(Arena, Offsets); } ///@} /// Implementation of the template. /// -/// \tparam Type the type of the mandatory first value to destroy -/// \tparam Types types of any further values to destroy +/// \tparam Types types of values to destroy /// /// \param Arena the memory area to destroy values from /// /// \pre \p Arena is not \c nullptr. -template +template inline void destroyArenaElements(void *const Arena) noexcept { ASSERT(Arena != nullptr); - destroyArenaElement<0, Type, Types...>( - Arena, TokenizedStorage::Offsets); + destroyArenaElement<0, Types...>(Arena, TokenizedStorage::Offsets); } } // End namespace /// Implementation of the template \c rosa::TokenizedStorage as a /// specialization of \c rosa::AbstractTokenizedStorage. /// /// The class provides facilities for storing values and providing type-safe /// access to them. /// -/// \tparam Type type of the first mandatory value to store -/// \tparam Types of any further values to store -template -class TokenizedStorage : public AbstractTokenizedStorage { +/// \tparam Types types of values to store +template +class TokenizedStorage : public AbstractTokenizedStorage { public: /// \c rosa::Token for the stored values. - static constexpr Token ST = - TypeToken::type, - typename std::decay::type...>::Value; + static constexpr Token ST = TypeToken...>::Value; /// Byte offsets to access stored values in \c rosa::TokenizedStorage::Arena. static const std::vector Offsets; private: /// A BLOB storing all the values one after the other. void *const Arena; /// Generates byte offsets for accessing values stored in /// \c rosa::TokenizedStorage::Arena. /// /// \return \c std::vector containing byte offsets for accessing values stored /// in \c rosa::TokenizedStorage::Arena static std::vector offsets(void) noexcept { Token T = ST; // Need a mutable copy. - const size_t N = lengthOfToken(T); // Number of types encoded in \c T. - size_t I = 0; // Start indexing from position \c 0. + const token_size_t N = lengthOfToken(T); // Number of types encoded in \c T. std::vector O(N); // Allocate vector of proper size. - 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; + // 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); } - 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 \c Type - /// and \c Types... are default constructible. + /// \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, Type(), Types()...); + createArenaElements(Arena, Types()...); } /// Creates an instance from constant lvalue references. /// - /// \param T the mandatory first value to store - /// \param Ts optional further values to store - TokenizedStorage(const Type &T, const Types &... Ts) noexcept + /// \param Ts values to store + TokenizedStorage(const std::decay_t &... Ts) noexcept : Arena(::operator new(sizeOfValuesOfToken(ST))) { ASSERT(Arena != nullptr); // Sanity check. - createArenaElements(Arena, T, Ts...); + createArenaElements(Arena, Ts...); } /// Creates an instance from rvalue references. /// - /// \param T the mandatory first value to store - /// \param Ts optional further values to store - TokenizedStorage(Type &&T, Types &&... Ts) noexcept + /// \param Ts values to store + TokenizedStorage(std::decay_t &&... Ts) noexcept : Arena(::operator new(sizeOfValuesOfToken(ST))) { ASSERT(Arena != nullptr); // Sanity check. - createArenaElements(Arena, std::move(T), std::move(Ts)...); + createArenaElements(Arena, std::move(Ts)...); } /// No copying and moving of \c rosa::TokenizedStorage instances. /// /// \note This restriction may be relaxed as moving should be easy to /// implement, only requires the possiblity to validate Arena pointer. ///@{ TokenizedStorage(const TokenizedStorage&) = delete; TokenizedStorage &operator=(const TokenizedStorage&) = delete; TokenizedStorage(TokenizedStorage&& Other) = delete; TokenizedStorage &operator=(TokenizedStorage&&) = delete; ///@} // Destroys \p this object. ~TokenizedStorage(void) { - destroyArenaElements(Arena); + destroyArenaElements...>(Arena); ::operator delete(Arena); } /// Tells how many values are stored in \p this object. /// /// \return number of values stored in \p this object size_t size(void) const noexcept override { return Offsets.size(); } /// Tells the type of the value stored at a position. /// /// \param Pos the index of the value whose type is to returned /// /// \return \c rosa::TypeNumber for the value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code /// Pos < size() /// \endcode - TypeNumber typeAt(const size_t Pos) const noexcept override { + 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 size_t Pos) noexcept override { + void *pointerTo(const token_size_t Pos) noexcept override { ASSERT(Pos < size()); return static_cast(Arena) + Offsets[Pos]; } /// Provides a constant untyped pointer for the value stored at a position. /// /// \param Pos the index of the value to return an untyped pointer for /// /// \return constant untyped pointer for the value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code /// Pos < Offsets.size() /// \endcode - const void *pointerTo(const size_t Pos) const noexcept override { + const void *pointerTo(const token_size_t Pos) const noexcept override { ASSERT(Pos < size()); return static_cast(Arena) + Offsets[Pos]; } /// Tells if the value stored at a given index is of a given type. /// /// \note Any \c rosa::AtomConstant is encoded in \c rosa::Token as /// the \c rosa::AtomValue wrapped into it. /// /// \tparam T type to match against /// /// \param Pos index the type of the value at is to be matched against \p Type /// /// \return if the value at index \p Pos of type \p T /// /// \pre \p Pos is a valid index:\code /// Pos < Offsets.size() /// \endcode template bool isTypeAt(const size_t Pos) const noexcept { ASSERT(Pos < size()); Token TT = ST; dropNOfToken(TT, Pos); return isHeadOfTokenTheSameType(TT); } /// Gives a reference of a value of a given type stored at a given index. /// /// \note The constant variant of the function relies on this implementation, /// the function may not modify \p this object! /// /// \tparam T type to give a reference of /// /// \param Pos index to set the reference for /// - /// \return reference of \p Type for the value stored at index \p Pos + /// \return reference of \p T for the value stored at index \p Pos /// /// \pre \p Pos is a valid index and the value at index \p Pos is of type /// \p T: /// \code /// Pos < Size && isTypeAt(Pos) /// \endcode - template Type &valueAt(const size_t Pos) noexcept { - ASSERT(Pos < size() && isTypeAt(Pos)); - return *static_cast(pointerTo(Pos)); + template T &valueAt(const token_size_t Pos) noexcept { + ASSERT(Pos < size() && isTypeAt(Pos)); + return *static_cast(pointerTo(Pos)); } /// Gives a constant reference of a value of a given type stored at a given /// index. /// /// \tparam T type to give a reference of /// /// \param Pos index to set the reference for /// - /// \return constant reference of \p Type for the value stored at index \p Pos + /// \return constant reference of \p T for the value stored at index \p Pos /// /// \pre \p Pos is a valid index and the value at index \p Pos is of type /// \p T: /// \code /// Pos < Size && isTypeAt(Pos) /// \endcode - template const Type &valueAt(const size_t Pos) const noexcept { + template + const T &valueAt(const token_size_t Pos) const noexcept { // \note Just use the non-const implementation as that does not modify // \p this object. return const_cast(this)->valueAt(Pos); } }; // Implementation of the static member field \c rosa::TokenizedStorage::Offsets. -template -const std::vector TokenizedStorage::Offsets = - TokenizedStorage::offsets(); +template +const std::vector + TokenizedStorage::Offsets = TokenizedStorage::offsets(); + +/// Specialization of the template \c rosa::TokenizedStorage for storing +/// nothing. +/// +/// \note The specialization implements the interface defined by \c +/// rosa::AbstractTokenizedStorage but most of the functions cannot be called +/// because nothing is stored in instances of the class. +template <> class TokenizedStorage<> : public AbstractTokenizedStorage { +public: + /// \c rosa::Token for the stored values. + static constexpr Token ST = TypeToken<>::Value; + + /// Byte offsets to access stored values in \c rosa::TokenizedStorage::Arena. + static const std::vector Offsets; + + /// Creates an instance. + TokenizedStorage(void) noexcept {} + + /// No copying and moving of \c rosa::TokenizedStorage instances. + /// + /// \note This restriction may be relaxed as moving should be easy to + /// implement, only requires the possiblity to validate Arena pointer. + ///@{ + TokenizedStorage(const TokenizedStorage &) = delete; + TokenizedStorage &operator=(const TokenizedStorage &) = delete; + TokenizedStorage(TokenizedStorage &&Other) = delete; + TokenizedStorage &operator=(TokenizedStorage &&) = delete; + ///@} + + // Destroys \p this object. + ~TokenizedStorage(void) {} + + /// Tells how many values are stored in \p this object. + /// + /// \return `0` + size_t size(void) const noexcept override { return 0; } + + /// Tells the type of the value stored at a position. + /// + /// \pre Do not call. + TypeNumber typeAt(const token_size_t) const noexcept override { + ASSERT(false); + return TypeNumber(0); + } + + /// Provides an untyped pointer for the value stored at a position. + /// + /// \pre Do not call. + void *pointerTo(const token_size_t) noexcept override { + ASSERT(false); + return nullptr; + } + + /// Provides a constant untyped pointer for the value stored at a position. + /// + /// \pre Do not call. + const void *pointerTo(const token_size_t) const noexcept override { + ASSERT(false); + return nullptr; + } + + /// Tells if the value stored at a given index is of a given type. + /// + /// \pre Do not call. + template bool isTypeAt(const size_t) const noexcept { + ASSERT(false); + return false; + } + + /// Gives a reference of a value of a given type stored at a given index. + /// + /// \tparam T type to give a reference of + /// \pre Do not call. + template T &valueAt(const token_size_t) noexcept { + ASSERT(false); + return *static_cast(nullptr); + } + + /// Gives a constant reference of a value of a given type stored at a given + /// index. + /// + /// \tparam T type to give a reference of + /// + /// \pre Do not call. + template const T &valueAt(const token_size_t) const noexcept { + // \note Just use the non-const implementation as that does not modify + // \p this object. + return *static_cast(nullptr); + } +}; } // End namespace rosa #endif // ROSA_SUPPORT_TOKENIZED_STORAGES_HPP diff --git a/include/rosa/support/type_helper.hpp b/include/rosa/support/type_helper.hpp index 5e683d0..82021ee 100644 --- a/include/rosa/support/type_helper.hpp +++ b/include/rosa/support/type_helper.hpp @@ -1,137 +1,143 @@ //===-- rosa/support/type_helper.hpp ----------------------------*- C++ -*-===// // // The RoSA Framework // +// Distributed under the terms and conditions of the Boost Software License 1.0. +// See accompanying file LICENSE. +// +// If you did not receive a copy of the license file, see +// http://www.boost.org/LICENSE_1_0.txt. +// //===----------------------------------------------------------------------===// /// /// \file rosa/support/type_helper.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Helper facilities for type-related stuff. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_TYPE_HELPER_HPP #define ROSA_SUPPORT_TYPE_HELPER_HPP #include #include namespace rosa { /* ************************************************************************** * * Printable * * ************************************************************************** */ /// \defgroup PrintableType Implementation of rosa::PrintableType /// /// A value of type \c [u]int8_t is treated as a character when being put to an /// output stream, which can result in invisible characters being printed. To /// avoid that, such a value needs to be casted to a wider type. It can be done /// by using the following template to find a target type to cast our value to. /// The template also turns enumerations into their underlying types. Moreover, /// any reference type is turned into the referred type and any non-const type /// is turned into their const-qualified type. /// /// \note It is important to remove references before checking /// const-qualification because constant references are not const-qualified /// types. /// /// The corresponding printable type for a type \c T can be obtained as \code /// typename PrintableType::Type /// \endcode ///@{ /// Definition for the general case /// /// \tparam T type to cast /// \tparam IsReference always use the default value! /// \tparam IsConst always use the default value! /// \tparam IsEnum always use the default value! template ::value, bool IsConst = std::is_const::value, bool IsEnum = std::is_enum::value> struct PrintableType { using Type = T; }; /// Specialization for reference types. template struct PrintableType { using Type = typename PrintableType::type>::Type; }; /// Specialization for non-reference, non-const types. template struct PrintableType { using Type = typename PrintableType::Type; }; /// Specialization for non-reference, const, enum types. template struct PrintableType { using Type = typename PrintableType::type>::Type; }; /// Specialization for \c const uint8_t. template <> struct PrintableType { using Type = const unsigned int; }; /// Specialization for \c const int8_t. template <> struct PrintableType { using Type = const int; }; ///@} /// Convenience template alias for using \c rosa::PrintableType. template using printable_t = typename PrintableType::Type; /// Casts values to their corresponding printable types. /// /// \param V value to cast #define PRINTABLE(V) static_cast>(V) /* ************************************************************************** * * Unsigned * * ************************************************************************** */ /// \defgroup Unsigned Implemtation of rosa::Unsigned /// /// \brief Converts integral types to their corresponding unsigned type. /// /// Provides the unsigned integer type corresponding to \c T` if \c T is an /// integral (except \c bool) or enumeration type. Keeps \c T otherwise. /// /// The corresponding unsigned type for a type \c T can be obtained as \code /// typename Unsigned::Type /// \endcode ///@{ /// Definition for the general case when converting a non-integral type. /// /// \tparam T type to convert /// \tparam IsIntegral always use the default value! template ::value> struct Unsigned { using Type = T; }; /// Specialization for the case when converting an integral type. template struct Unsigned { using Type = typename std::make_unsigned::type; }; ///@} /// Convenience template alias for using \c rosa::Unsigned. template using unsigned_t = typename Unsigned::Type; } // End namespace rosa #endif // ROSA_SUPPORT_TYPE_HELPER_HPP diff --git a/include/rosa/support/type_list.hpp b/include/rosa/support/type_list.hpp index bb50be3..ad06837 100644 --- a/include/rosa/support/type_list.hpp +++ b/include/rosa/support/type_list.hpp @@ -1,444 +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 +/// \date 2017-2019 /// /// \brief Facilities for types representing lists of types. /// /// \note This implementation is partially based on the \c type_list /// implementation of CAF. -/// \todo Check license. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_TYPE_LIST_HPP #define ROSA_SUPPORT_TYPE_LIST_HPP #include "rosa/support/debug.hpp" #include "rosa/support/types.hpp" #include namespace rosa { /// A list of types. /// /// \tparam Ts types to make a list of template struct TypeList { /// Constructor, needs to do nothing. constexpr TypeList(void) {} }; /// The empty \c rosa::Typelist. using EmptyTypeList = TypeList<>; /// \defgroup TypeListAtImpl Implementation of rosa::TypeListAt /// /// \brief Gets the type at index \p Pos from a list of types. /// /// \note Only to be used by the implementation of \c rosa::TypeListAt. ///@{ /// Declaration of the template. /// /// \tparam Pos index to take the element from /// \tparam Ts types template struct TypeListAtImpl; /// Definition for the general case when \p Pos is not \c 0 and there is type in /// the list. template struct TypeListAtImpl { using Type = typename TypeListAtImpl::Type; }; /// Specialization for the case when \p Pos is \c 0. template struct TypeListAtImpl<0, T, Ts...> { using Type = T; }; /// Specialization for the case when there is no more type. /// /// In this case, the found type is \c rosa::none_t. template struct TypeListAtImpl { using Type = none_t; }; ///@} /// \defgroup TypeListAt Definition of rosa::TypeListAt /// /// \brief Gets the element at index \p Pos of \p List. /// /// /// The type at index \c Pos in a \c rosa::TypeList \c List can be obtained as /// \code /// typename TypeListAt::Type /// \endcode /// /// \note The resulting type is \c rosa::none_t if \code /// TypeListSize::Value < Pos /// \endcode ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to take an element from /// \tparam Pos index to take the element from template struct TypeListAt; /// Implementation using \c rosa::TypeListAtImpl. template struct TypeListAt, Pos> { using Type = typename TypeListAtImpl::Type; }; ///@} /// \defgroup TypeListIndexOfImpl Implementation of rosa::TypeListIndexOf /// /// \brief Tells the index of the first occurence of a type in a list of types. /// /// \note Only to be used by the implementation of \c rosa::TypeListIndexOf. ///@{ /// Declaration of the template. /// /// \tparam Pos the number types already being checked from the beginning of the /// list /// \tparam X type to search for /// \tparam Ts remaining list of types template struct TypeListIndexOfImpl; /// Specialization for the case when the list is over. /// /// In this case, the found index is \c -1. template struct TypeListIndexOfImpl { static constexpr int Value = -1; }; /// Specialization for the case when the first type in the remaining list /// is a match. template struct TypeListIndexOfImpl { static constexpr int Value = Pos; }; /// Implementation for the general case when need to continue looking. template struct TypeListIndexOfImpl { static constexpr int Value = TypeListIndexOfImpl::Value; }; ///@} /// \defgroup TypeListIndexOf Definition of rosa::TypeListIndexOf /// /// \brief Tells the index of the first occurence of type in a /// \c rosa::TypeList. /// /// The index of the first occurence of type \c T in \c rosa::TypeList \c List /// can be obtained as \code /// TypeListIndexOf::Value /// \endcode /// /// \note The resulting index is \c -1 if \c T is not present in \c List. ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to search in /// \tparam T type to search for template struct TypeListIndexOf; /// Implementation of the template using \c rosa::TypeListIndexOfImpl. template struct TypeListIndexOf, T> { static constexpr int Value = TypeListIndexOfImpl<0, T, Ts...>::Value; }; ///@} /// \defgroup TypeListHead Implementation of rosa::TypeListHead /// /// \brief Gets the first element of a \c rosa::TypeList. /// /// The first element of a \c rosa::TypeList \c List can be obtained as \code /// typename TypeListHead::Type /// \endcode /// /// \note The resulting type is \c rosa::none_t if \c List is /// \c rosa::EmptyTypeList. ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to get the first element of template struct TypeListHead; /// Specialization for \c rosa::EmptyTypeList. /// /// In this case, the found type is \c rosa::none_t. template <> struct TypeListHead { using Type = none_t; }; /// Implementation for a non-empty \c rosa::TypeList. template struct TypeListHead> { using Type = T; }; ///@} /// \defgroup TypeListTail Implementation of rosa::TypeListTail /// /// \brief Gets the tail of a \c rosa::TypeList. /// /// The tail of a \c rosa::TypeList \c List, that is \c List except for its /// first element, can be obtained as \code /// typename TypeListTail::Type /// \endcode /// /// \note If \c List is \c rosa::EmptyTypeList, then the resulting type is also /// \c rosa::EmptyTypeList. ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to take the tail of template struct TypeListTail; /// Specialization for \c rosa::EmptyTypeList. /// /// In this case, the resulting type is \c rosa::EmptyTypeList. template <> struct TypeListTail { using Type = EmptyTypeList; }; /// Implementation for a non-empty \c rosa::TypeList. template struct TypeListTail> { using Type = TypeList; }; ///@} /// \defgroup TypeListPush Implementation of rosa::TypeListPush /// /// \brief Extends a \c rosa::TypeList with a type. /// /// Whether the new type is pushed in the front or in the back of the /// \c rosa::TypeList depends on the order of template arguments, as shown in /// the following example: \code /// using List = TypeList /// typename TypeListPush::Type; // TypeList /// typename TypeListPush::Type; // TypeList /// \endcode ///@{ /// Declaration of the template. /// /// \tparam P a type if \p Q is a \c rosa::TypeList, a \c rosa::TypeList /// otherwise /// \tparam Q a type if \p P is a \c rosa::TypeList, a \c rosa::TypeList /// otherwise template struct TypeListPush; /// Implementation for the case when pushing at the back of the /// \c rosa::TypeList. template struct TypeListPush, T> { using Type = TypeList; }; /// Implementation for the case when pushing to the front of the /// \c rosa::TypeList. template struct TypeListPush> { using Type = TypeList; }; ///@} /// \defgroup TypeListDrop Implementation of rosa::TypeListDrop /// /// \brief Drops some elements from the beginning of a \c rosa::TypeList. /// /// The first \c N types of a \c rosa::TypeList \c List can be dropped as \code /// typename TypeListDrop::Type /// \endcode ///@{ /// Declaration of the template. /// /// \tparam N number of types to drop /// \tparam List \c rosa::TypeList to drop the first \p N element of template struct TypeListDrop; /// Specialization for \c rosa::EmptyTypeList. template struct TypeListDrop { using Type = EmptyTypeList; }; /// Implementation for a non-empty \c rosa::TypeList. template struct TypeListDrop> { using Type = typename std::conditional< - N == 0, TypeList, + N == 0, TypeList, typename TypeListDrop>::Type>::type; }; ///@} +/// \defgroup TypeListConcat Implementation of rosa::TypeListConcat +/// +/// \brief Concatenates two \c rosa::TypeList instances. +/// +/// Two instances of \c rosa::TypeList \c List1 and \c List2 can be +/// concatenated as \code +/// typename TypeListConcat::Type +/// \endcode +///@{ + +/// Declaration of the template +/// +/// \tparam List1 the first instance of \c rosa::TypeList +/// \tparam List2 the second instance of \c rosa::TypeList +template struct TypeListConcat; + +/// Implementation of the template for \c rosa::TypeList instances. +template +struct TypeListConcat, TypeList> { + using Type = TypeList; +}; + +///@} + /// \defgroup TypeListSize Implementation of rosa::TypeListSize /// /// \brief Tells the number of types stored in a \c rosa::TypeList. /// /// The size of a \c rosa::TypeList \c List can be obtained as \code /// TypeListSize::Value /// \endcode ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to get the size of template struct TypeListSize; /// Implementation of the template. template struct TypeListSize> { static constexpr size_t Value = sizeof...(Ts); }; template constexpr size_t TypeListSize>::Value; ///@} /// Tests whether a \c rosa::TypeList is empty. /// /// \tparam List \c rosa::TypeList to check template struct TypeListEmpty { /// Denotes whether \p List is an empty \c rosa::TypeList or not. static constexpr bool Value = std::is_same::value; }; /// \defgroup TypeListContains Implementation of rosa::TypeListContains /// /// \brief Tells if a \c rosa::TypeList contains a given type. /// /// Whether a \c rosa::TypeList \c List contains the type \c T can be checked as /// \code /// TypeListContains::Value /// \endcode ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to search in /// \tparam T type to search for template struct TypeListContains; /// Implementation of the template. template struct TypeListContains, T> { static constexpr bool Value = std::conditional, T>::Value == -1, std::false_type, std::true_type>::type::value; }; ///@} /// \defgroup TypeListSubsetOf Implementation of rosa::TypeListSubsetOf /// /// \brief Tells if a \c rosa::TypeList is a subset of another one. /// /// Whether a \c rosa::TypeList \c ListA is a subset of another /// \c rosa::TypeList \c ListB can be checked as \code /// TypeListSubsetOf::Value /// \endcode ///@{ /// Declaration of the template. /// /// \tparam ListA \c rosa::TypeList to check if is a subset of \p ListB /// \tparam ListB \c rosa::TypeList to check if is a superset of \p ListA /// \tparam Fwd always use the default value! template struct TypeListSubsetOf; /// Specialization for the case when all the elements of the original \p ListA /// was found in \p ListB. template struct TypeListSubsetOf { static constexpr bool Value = true; }; /// Specializaton for the case when an element of the original \p ListA cannot /// be found in \p ListB. template struct TypeListSubsetOf { static constexpr bool Value = false; }; /// Definition for the general case. template struct TypeListSubsetOf, List> : TypeListSubsetOf, List, TypeListContains::Value> {}; ///@} /// \defgroup TypeListFindImpl Implementation of rosa::TypeListFind /// /// \brief Finds the first type in a list of types that satisfies a predicate. /// /// \note Only to be used by the implementation of \c rosa::TypeListFind. ///@{ /// Declaration of the template. /// /// \tparam Pred the predicate to check types against /// \tparam Ts list of types to check template