diff --git a/cmake/modules/FindClangFormat.cmake b/cmake/modules/FindClangFormat.cmake
index ba19d37..157458b 100755
--- a/cmake/modules/FindClangFormat.cmake
+++ b/cmake/modules/FindClangFormat.cmake
@@ -1,22 +1,29 @@
# CMake find_package() Module for clang-format
#
# Example usage:
#
# find_package(ClangFormat)
#
# If successful the following variables will be defined
# CLANGFORMAT_FOUND
# CLANGFORMAT_EXECUTABLE
-find_program(CLANGFORMAT_EXECUTABLE
- NAMES clang-format
- PATHS ${ROSA_CLANG_FORMAT_PATH}
- DOC "Path to clang-format executable")
+if( ROSA_CLANG_FORMAT_PATH STREQUAL "" )
+ find_program(CLANGFORMAT_EXECUTABLE
+ NAMES clang-format
+ DOC "Path to clang-format executable")
+else()
+ find_program(CLANGFORMAT_EXECUTABLE
+ NAMES clang-format
+ PATHS ${ROSA_CLANG_FORMAT_PATH}
+ DOC "Path to clang-format executable"
+ NO_DEFAULT_PATH)
+endif()
# Handle REQUIRED and QUIET arguments
# this will also set CLANGFORMAT_FOUND to true if CLANGFORMAT_EXECUTABLE exists
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(ClangFormat
"Failed to locate clang-format executable"
CLANGFORMAT_EXECUTABLE)
diff --git a/cmake/modules/FindClangTidy.cmake b/cmake/modules/FindClangTidy.cmake
index 05702ac..3e53599 100755
--- a/cmake/modules/FindClangTidy.cmake
+++ b/cmake/modules/FindClangTidy.cmake
@@ -1,22 +1,29 @@
# CMake find_package() Module for clang-tidy
#
# Example usage:
#
# find_package(ClangTidy)
#
# If successful the following variables will be defined
# CLANGTIDY_FOUND
# CLANGTIDY_EXECUTABLE
-find_program(CLANGTIDY_EXECUTABLE
- NAMES clang-tidy
- PATHS ${ROSA_CLANG_TIDY_PATH}
- DOC "Path to clang-tidy executable")
+if( ROSA_CLANG_TIDY_PATH STREQUAL "" )
+ find_program(CLANGTIDY_EXECUTABLE
+ NAMES clang-tidy
+ DOC "Path to clang-tidy executable")
+else()
+ find_program(CLANGTIDY_EXECUTABLE
+ NAMES clang-tidy
+ PATHS ${ROSA_CLANG_TIDY_PATH}
+ DOC "Path to clang-tidy executable"
+ NO_DEFAULT_PATH)
+endif()
# Handle REQUIRED and QUIET arguments
# this will also set CLANGTIDY_FOUND to true if CLANGTIDY_EXECUTABLE exists
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(ClangTidy
"Failed to locate clang-tidy executable"
CLANGTIDY_EXECUTABLE)
diff --git a/cmake/modules/HandleROSAOptions.cmake b/cmake/modules/HandleROSAOptions.cmake
index ad04f21..6571163 100644
--- a/cmake/modules/HandleROSAOptions.cmake
+++ b/cmake/modules/HandleROSAOptions.cmake
@@ -1,102 +1,102 @@
# 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_COMPILER_IS_GNUCXX )
set(ROSA_COMPILER_IS_GCC_COMPATIBLE ON)
elseif( "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" )
set(ROSA_COMPILER_IS_GCC_COMPATIBLE ON)
elseif( MSVC )
set(ROSA_COMPILER_IS_GCC_COMPATIBLE OFF)
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()
# 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()
-#FIXME: Disable RTTI and exceptions, once gen_agent_test2 is cleared up.
-
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)
+
check_cxx_compiler_flag("-std=c++14" CXX_SUPPORTS_CXX14)
if ( CXX_SUPPORTS_CXX14 )
append("-std=c++14" CMAKE_CXX_FLAGS)
else()
message(FATAL_ERROR "RoSA requires C++14 support but the '-std=c++14' 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()
else ( ROSA_COMPILER_IS_GCC_COMPATIBLE )
# FIXME: set the same things as for GCC-compatible compilers
endif( ROSA_COMPILER_IS_GCC_COMPATIBLE )
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 )
diff --git a/docs/Build.rst b/docs/Build.rst
index 9a06315..1110d85 100755
--- a/docs/Build.rst
+++ b/docs/Build.rst
@@ -1,343 +1,348 @@
===========================
Building the RoSA Framework
===========================
.. contents::
:local:
Build Dependencies
==================
In order to build RoSA, the following tools are required:
* `CMake `_ (minimum version 3.6.0 if clang-tidy is used,
2.8.8 otherwise);
* Build system of your choice that can be targeted by CMake -- including a
compiler supporting *C++14*.
+ * LLVM -- minimum version 3.9.0 (due to :ref:`this issue
+ `)
+
The following additional tools are required to generate documentation:
* `Doxygen `_ -- for generating API documentation;
* `Graphviz `_ -- not necessary, but the API
documentation has nicer graphics when `dot` is available;
* `Sphinx `_ (with *Python 2*) -- for generating
documentation.
The following additional tools are required to check and possibly enforce coding
standard:
* `clang-tidy `_
* `clang-format `_
General notes
=============
* The framework is delivered with a CMake project, which can be used to generate
build projects to build the framework.
* The provided CMake project supports out-of-tree builds only, i.e. one must use
a separate build directory outside of the RoSA source directory.
.. _cmake-variables:
CMake Variables
===============
Beyond the usual CMake variables, the following project-related options are
available:
`ROSA_INCLUDE_TOOLS`
Generate build targets for RoSA tools. The tools are also built when the
option is set to `ON`, which is the default setting.
`ROSA_INCLUDE_EXAMPLES`
Generate build targets for RoSA examples. The examples are also built when the
option is set to `ON`, which is the default setting.
`ROSA_ENABLE_PEDANTIC`
Compile the framework with using `-pedantic`. The option defaults to `ON`.
`ROSA_ENABLE_ASSERTIONS`
Enable assertions for non-Debug builds, which defaults to `OFF`.
Note that assertions are always enabled for Debug builds and this option is
ignored for such builds.
.. _CMake_clang_tidy:
`ROSA_ENABLE_CLANG_TIDY`
Run *clang-tidy* checks when building RoSA, which defaults to `OFF`.
When the variable is enabled, build targets created by *Makefile* and *Ninja*
generators include calls for clang-tidy. Other generators ignore this option.
Note that CMake variables `CMAKE__CLANG_TIDY` are set when enabled.
Settings for clang-tidy are defined by the file `.clang-tidy` in the RoSA
source directory.
Consider the following options when the option is enabled:
`ROSA_CLANG_TIDY_PATH`
Custom path for `clang-tidy` executable.
- In order to use clang-tidy, CMake needs to find the `clang-tidy` executable.
- If `clang-tidy` is available via `PATH`, just leave the option empty
- -- which is default. Set the absolute path of the directory containing the
- `clang-tidy` executable otherwise.
+ In order to use clang-tidy, CMake needs to find the `clang-tidy`
+ executable. If `clang-tidy` to be used is available via `PATH`, just leave
+ the option empty -- which is default. Set the absolute path of the
+ directory containing the `clang-tidy` executable otherwise, in which case
+ no default path is searched for `clang-tidy`.
`ROSA_CLANG_TIDY_FIX`
Apply suggested clang-tidy fixes to the sources, which defaults to `OFF`.
Enable the option only if you know what you are doing.
.. _CMake_clang_format:
`ROSA_INCLUDE_CLANG_FORMAT` **[experimental]**
Generate build target -- `format-rosa` -- for formatting RoSA sources with
*clang-format*, which defatuls to `OFF`.
When the variable is enabled and *CMake is not running on a Windows host*, a
build target is generated which can be used to have all the RoSA sources
formatted with clang-format. Settings for clang-format are defined by the file
`.clang-format` in the RoSA source directory.
Note that executing build target `format-rosa` will reformat all the source
files *inplace*.
Consider the following option when a build target for clang-format is to be
generated:
`ROSA_CLANG_FORMAT_PATH`
Custom path for `clang-format` executable.
In order to use clang-format, CMake needs to find the `clang-format`
- executable. If `clang-format` is available via `PATH`, just leave the option
- empty -- which is default. Set the absolute path of the directory containing
- the `clang-format` executable otherwise.
+ executable. If `clang-format` to be used is available via `PATH`, just
+ leave the option empty -- which is default. Set the absolute path of the
+ directory containing the `clang-format` executable otherwise, in which case
+ no default path is search for `clang-format`.
`ROSA_LOG_LEVEL`
Level of logging to be used, use one of the following valid integer values.
======== =========
Variable Log Level
======== =========
`0` `ERROR`
`1` `WARNING`
`2` `INFO`
`3` `DEBUG`
`4` `TRACE`
`5` *disabled*
======== =========
Level of logging defaults to *disabled*.
`ROSA_INCLUDE_DOCS`
Generate build targets for RoSA documentation, defaults to `ON`.
Note that the automatic execution of the generated build targets is
controlled by the option `ROSA_BUILD_DOCS`. The actual documentations to
build are controlled by the options `ROSA_ENABLE_DOXYGEN` and
`ROSA_ENABLE_SPHINX`.
`ROSA_BUILD_DOCS`
Build RoSA documentation automatically as part of the build process. The
option defaults to `OFF` and takes effect only if the option
`ROSA_INCLUDE_DOCS` is enabled.
.. _CMake_doxygen:
`ROSA_ENABLE_DOXYGEN`
Use *doxygen* to generate RoSA API documentation. The option defaults to `OFF`
and takes effect only if the option `ROSA_INCLUDE_DOCS` is enabled.
Doxygen documentation may be generated by executing build target
`doxygen-rosa`, which is done as part of the default build process if
`ROSA_BUILD_DOCS` is enabled.
Doxygen must be available via `PATH` if the option is enabled.
The following options are also available to tune doxygen:
`ROSA_DOXYGEN_SVG`
Use *svg* instead of *png* files for doxygen graphs. The option defaults to
`OFF` and takes effect if the tool *dot* is available via `PATH` to be used
to generated graph images.
`ROSA_DOXYGEN_EXTERNAL_SEARCH`
Enable doxygen external search, which defatuls to `OFF`.
The following options need to be set if the option is enabled:
`ROSA_DOXYGEN_SEARCHENGINE_URL`
URL to use for external search.
`ROSA_DOXYGEN_SEARCH_MAPPINGS`
Doxygen Search Mappings.
.. _CMake_sphinx:
`ROSA_ENABLE_SPHINX`
Use *Sphinx* to generate RoSA documentation. The option defaults to `OFF` and
takes effect only if the option `ROSA_INCLUDE_DOCS` is enabled.
Sphinx must be available via `PATH` if the option is enabled.
The following options are also available to tune Sphinx:
`SPHINX_OUTPUT_HTML`
Output standalone HTML files. The option defaults to `ON`.
Documentation may be generated by executing build target `docs-rosa-html`,
which is done as part of the default build process if `ROSA_BUILD_DOCS` is
enabled.
`SPHINX_OUTPUT_MAN`
Output man pages for RoSA tools. The option defaults to `ON`.
Man pages may be generated by executing build target `docs-rosa-man`, which
is done as part of the default build process if `ROSA_BUILD_DOCS` is
enabled.
`SPHINX_WARNINGS_AS_ERRORS`
When building documentation, treat Sphinx warnings as errors. The option
defaults to `ON`.
Building RoSA Step-by-Step
==========================
Building on Linux with Make
---------------------------
Configuring and building the framework on Linux using *Make* is a
straightforward process which does not require performing any tricks. The
framework compiles with *Clang 3.8.0*.
Set C and C++ compilers with the variables `CC` and `CXX`, respectively. Use the
CMake variable `CMAKE_BUILD_TYPE` to set the type of build: `Debug`, `Release`.
Follows an example on building the framework. You need to have RoSA sources on
your computer and Clang executables available via `PATH`.::
rosa-src$ cd ..
$ mkdir rosa-build
$ cd rosa-build
rosa-build$ CC=clang CXX=clang++ cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug -DROSA_LOG_LEVEL=4 ../rosa-src/
[CMake configures and generates without errors]
$ make
[Make builds the project]
You just need to re-run Make in order to re-build the project after changing
the source code and the CMake project. In case the CMake project is changed,
Make automatically calls CMake to update the build project.
In order to build documentation and enforce coding standard, refer to
corresponding :ref:`cmake-variables`.
.. _Build_VS:
Building on Windows with Visual Studio
--------------------------------------
Unfortunately, the native MSVC compiler cannot compile the framework. One needs
to use Clang with Visual Studio in order to build the framework. There are two
alternatives to do that:
* using the *Clang/C2* module for *Visual Studio 2017*,
* using *LLVM for Windows* as an external toolset for Visual Studio
(necessary for older versions of Visual Studio).
For using Visual Studio 2017 -- which would be the preferred way -- one needs to
select *Clang/C2 (experimental)* on the *Individual components* tab in *Visual
Studio Installer*.
For using *LLVM for Windows*, one downloads the official binary release from
http://llvm.org and consults with its documentation. The release provides
integration for Visual Studio versions between 2010 and 2014 (as of LLVM 4.0.0).
Having your build system prepared and RoSA sources fetched to your computer,
configure and build the framework like this:
#. Generate Visual Studio solution with CMake:
#. Start CMake.
#. Define *source directory* and a separate *build directory* in CMake.
#. Click *Configure*.
#. Select the proper *generator* for your version of Visual Studio.
#. Define your optional toolset (argument for `-T`):
* Clang/C2 with VS2017: `v141_clang_c2`.
* LLVM for Windows with older VS: `LLVM-vs`.
#. Click *Finish*.
#. Tune CMake variables as you wish.
* Note that Visual Studio Generators are multi-configuration generators,
hence you cannot set `CMAKE_BUILD_TYPE`. You need to select a
configuration to build in Visual Studio.
#. Click *Generate*.
.. _CMake-VS2017-Clang-fix:
#. Fix the generated Visual Studio projects (*applies for Clang/C2*):
* The generated projects define `ExceptionHandling` as `Sync`, which is an
invalid value for Clang/C2 and needs to be changed to `Enabled` in all
Visual Studio project files:
* using GNU command line tools::
rosa-build$ find . -name "*.vcxproj" -print|xargs sed -i -e 's/Sync/Enabled/g'
* using PowerShell::
rosa-build> ls -path . -include *.vcxproj -recurse | %{ $f=$_; (gc $f.PSPath) | %{ $_ -replace "Sync", "Enabled" } | sc $f.PSPath }
* Note that the project files need to be fixed every time CMake regenerates
them, which may happen when building the project in Visual Studio in case
you have the CMake project changed in the source directory.
#. Build the framework with Visual Studio:
#. Open the generated `RoSA.sln` from the build directory with Visual
Studio.
#. Build the project `ALL_BUILD`.
You just need to re-build the project in Visual Studio after changing the
source code and the CMake project.
In case the CMake project is changed, Visual Studio automatically calls CMake
the update the build project. Should you use Clang/C2, do not forget to
:ref:`fix the generated Visual Studio projects `.
Build Result
============
The build process works in the build directory. After a successful build, one
can find the following final outputs there -- besides some intermediate files.
.. _Build_Result_Software:
Software
--------
In the build directory, `include` contains header files which are generated by
CMake and provide configuration-specific information.
The build process generates static libraries in `lib` and executables --
examples and tools -- in `bin`. Projects generated by a multi-configuration
generator result in the actual libraries and executables being located in
subdirectories corresponding to different build configurations.
.. _Build_Result_Documentation:
Documentation
-------------
Documentation is generated in `docs`.
The general documentation can be found in `docs/html`. Man pages for tools can
be found in `docs/man`.
The API documentation can be found in `docs/doxygen/html`.
diff --git a/docs/Dev.rst b/docs/Dev.rst
index 56581df..dbe5980 100755
--- a/docs/Dev.rst
+++ b/docs/Dev.rst
@@ -1,344 +1,353 @@
=============================
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`_.
`tools`
Contains `Tools`_ based on RoSA features.
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`_ and `Tools`_ using those `Libraries`_ are separated from the
implementation of the RoSA features into different directories.
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.
.. _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 | agent |
-+=========+========+=========+======+=======+
-| config | | | | |
-+---------+--------+---------+------+-------+
-| support | | | | |
-+---------+--------+---------+------+-------+
-| core | | × | | |
-+---------+--------+---------+------+-------+
-| agent | | | | |
-+---------+--------+---------+------+-------+
++---------+--------+---------+------+--------+-------+
+| | 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.
Tools
~~~~~
Tools, programs based on the RoSA libraries, are implemented in `tools`.
.. _Coding_Standards:
Coding Standards
----------------
RoSA is implemented in standard *C++14* code. All the software sources are to
be written in accordance to the `LLVM Coding Standards`_.
+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.
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`, and also update `lib/CMakeLists.txt` by adding or
removing a `add_subdirectory` command for the library.
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 `examples` or `tools`, and also update `CMakeLists.txt` in the
containing directory as for libraries.
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` [1]_.
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.
----
.. [1] 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 b2cf67f..c8c8f16 100755
--- a/docs/Issues.rst
+++ b/docs/Issues.rst
@@ -1,32 +1,37 @@
==================================================================
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
============
* CMake
* VS2017 generates intermediate files for the `ZERO_CHECK` project out of the
build directory, see `CMake issue #16458`_.
* VS2017 with `v141_clang_c2` toolset requires
:ref:`fix of the generated Visual Studio projects `.
-* clang-format
+* clang
- * `include/rosa/core/Message.hpp` breaks clang-format as of version 3.8.0.
+ .. _Issues_clang_template_argument_deduction_DeluxeAgent:
+
+ * Clang breaks on template argument deduction when instantiating the class
+ `rosa::deluxe::DeluxeAgent`, the issue is gone with clang version 3.9.0 and
+ newer. Check the documentation on the relevant constructor for more
+ details.
.. _CMake issue #16458: https://gitlab.kitware.com/cmake/cmake/issues/16458
diff --git a/examples/type-facilities/type-facilities.cpp b/examples/type-facilities/type-facilities.cpp
index 44b7379..665a0ce 100644
--- a/examples/type-facilities/type-facilities.cpp
+++ b/examples/type-facilities/type-facilities.cpp
@@ -1,90 +1,90 @@
//===-- examples/type-facilities/type-facilities.cpp ------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file examples/type-facilities/type-facilities.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \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"
#include
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 full size: " << sizeOfValuesOfToken(T_)
- << "\n head type number: " << PRINTABLE_TN(typeNumberOfHeadOfToken(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/core/Agent.hpp b/include/rosa/core/Agent.hpp
index eebe905..344b8f6 100644
--- a/include/rosa/core/Agent.hpp
+++ b/include/rosa/core/Agent.hpp
@@ -1,112 +1,137 @@
//===-- rosa/core/Agent.hpp -------------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \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;
/// 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 6e0f4b4..68df7b0 100644
--- a/include/rosa/core/AgentHandle.hpp
+++ b/include/rosa/core/AgentHandle.hpp
@@ -1,108 +1,302 @@
//===-- rosa/core/AgentHandle.hpp -------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \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);
+ 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;
/// 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 ad6399e..d13057e 100644
--- a/include/rosa/core/Invoker.hpp
+++ b/include/rosa/core/Invoker.hpp
@@ -1,273 +1,256 @@
//===-- rosa/core/Invoker.hpp -----------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/core/Invoker.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \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.
///
/// \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 Seq
-///
-///@{
-
-/// Template with an empty struct to store a sequence of numbers in compile time
-/// as template arguments.
-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; };
-
-///@}
-
/// \defgroup InvokerImpl
///
/// 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));
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.
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 8e7b66e..49e78aa 100644
--- a/include/rosa/core/Message.hpp
+++ b/include/rosa/core/Message.hpp
@@ -1,482 +1,257 @@
//===-- rosa/core/Message.hpp -----------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/core/Message.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \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/type_token.hpp"
+#include "rosa/support/tokenized_storages.hpp"
#include "rosa/core/forward_declarations.h"
-#include
-#include
-
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;
/// 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;
/// 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)
+ /// Pos < Size && isTypeAt(Pos)
/// \endcode
template const Type &valueAt(const 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;
};
/// 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;
-/// 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 createMessageElements(void *const Arena,
- const Types &... Ts) noexcept;
-
-/// \defgroup createMessageElement from const lvalue references
-///
-/// Stores values from constant lvalue references into a pre-allocated memory
-/// area.
-///
-/// \note To be used by the implementation of \c createMessageElements.
-///
-/// \todo Document these functions.
-///@{
-
-/// \note This terminal case is used for both constant lvalue references and
-/// value references.
-template
-inline void createMessageElement(void *const,
- const std::vector &Offsets) {
- ASSERT(Pos == Offsets.size());
-}
-
-template
-inline void createMessageElement(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);
- createMessageElement(Arena, Offsets, Ts...);
-}
-
-template
-inline void
-createMessageElement(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;
- createMessageElement(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
-///
-/// \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
-///
-/// \pre \p Arena is not \p nullptr.
-template
-inline void createMessageElements(void *const Arena, const Type &T,
- const Types &... Ts) noexcept {
- ASSERT(Arena != nullptr);
- createMessageElement<0>(Arena, LocalMessage::Offsets, T,
- 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 createMessageElements(void *const Arena, Types &&... Ts) noexcept;
-
-/// \defgroup createMessageElement from rvalue references
-///
-/// Stores values from rvalue references into a pre-allocated memory area.
-///
-/// \note To be used by the implementation of \c createMessageElements.
-///
-/// \todo Document these functions.
-///@{
-
-template
-inline void createMessageElement(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));
- createMessageElement(Arena, Offsets, std::move(Ts)...);
-}
-
-template
-inline void createMessageElement(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;
- createMessageElement(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
-///
-/// \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
-///
-/// \pre \p Arena is not \c nullptr.
-template
-inline void createMessageElements(void *const Arena, Type &&T,
- Types &&... Ts) noexcept {
- ASSERT(Arena != nullptr);
- createMessageElement<0>(Arena, LocalMessage::Offsets,
- std::move(T), std::move(Ts)...);
-}
-
-/// Destroys values allocated by \c createMessageElements.
-///
-/// \tparam Type type of the mandatory first value stored in \p Arena
-/// \tparam Types futher types whose values are stored in \p Arena
-///
-/// \param Arena the memory area to destroy values from
-///
-/// \note \p Arena needs to be a valid pointer to a memory area where values of
-/// \p Types are stored.
-template
-inline void destroyMessageElements(void *const Arena) noexcept;
-
-/// \defgroup destroyMessageElement
-///
-/// Destroys values from a memory area.
-///
-/// \note To be used by the implementation of \c destroyMessageElements.
-///
-/// \todo Document these functions.
-///@{
-
-template
-inline void destroyMessageElement(void *const,
- const std::vector &Offsets) noexcept {
- ASSERT(Pos == Offsets.size());
-}
-
-template
-inline void destroyMessageElement(void *const Arena,
- const std::vector &Offsets) noexcept {
- ASSERT(Arena != nullptr && Pos < Offsets.size());
- static_cast(
- static_cast(static_cast(Arena) + Offsets[Pos]))
- ->~Type();
- destroyMessageElement(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
-///
-/// \param Arena the memory area to destroy values from
-///
-/// \pre \p Arena is not \c nullptr.
-template
-inline void destroyMessageElements(void *const Arena) noexcept {
- ASSERT(Arena != nullptr);
- destroyMessageElement<0, Type, Types...>(
- Arena, LocalMessage::Offsets);
-}
-
/// 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 : public Message {
-public:
- /// \c rosa::Token for the stored values.
- ///
- /// \note Only for compile-time checks! This static member is not defined
- /// because it must be the same as \c rosa::Message::T.
- static constexpr Token ST =
- TypeToken::type,
- typename std::decay::type...>::Value;
-
- /// Byte offsets to access stored values in \c LocalMessage::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 LocalMessage::Arena.
- ///
- /// \return \c std::vector containing byte offsets for accessing values stored
- /// in \c LocalMessage::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.
- 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;
- }
- ASSERT(I + 1 == O.size() && lengthOfToken(T) == 1);
- return O;
- }
-
+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...),
- Arena(::operator new(sizeOfValuesOfToken(ST))) {
- ASSERT(this->T == ST && Size == Offsets.size() &&
- Arena != nullptr); // Sanity check.
- createMessageElements(Arena, 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...),
- Arena(::operator new(sizeOfValuesOfToken(ST))) {
- ASSERT(this->T == ST && Size == Offsets.size() &&
- Arena != nullptr); // Sanity check.
- createMessageElements(Arena, std::move(T), std::move(Ts)...);
- }
-
- // Destroys \p this object.
- ~LocalMessage(void) {
- destroyMessageElements(Arena);
- ::operator delete(Arena);
+ 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 {
- ASSERT(Pos < Offsets.size());
- return static_cast(Arena) + Offsets[Pos];
+ ASSERT(Pos < Size);
+ return TokenizedStorage::pointerTo(Pos);
}
-};
-// Implementation of the static member field \c LocalMessage::Offsets.
-template
-const std::vector LocalMessage::Offsets =
- LocalMessage::offsets();
+ /// 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 {
+ 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) + ")");
}
/// \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 {
ASSERT(Pos < Size);
Token TT = T;
dropNOfToken(TT, Pos);
return isHeadOfTokenTheSameType(TT);
}
template
const Type &Message::valueAt(const 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/MessagingSystem.hpp b/include/rosa/core/MessagingSystem.hpp
index cb81c44..5b6fd14 100644
--- a/include/rosa/core/MessagingSystem.hpp
+++ b/include/rosa/core/MessagingSystem.hpp
@@ -1,186 +1,198 @@
//===-- rosa/core/MessagingSystem.hpp ---------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \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
///
/// \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);
- /// Gives the references \c rosa::Agent instance for a \c rosa::AgentHandle.
+ /// 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 3470bcb..f104180 100644
--- a/include/rosa/core/System.hpp
+++ b/include/rosa/core/System.hpp
@@ -1,214 +1,212 @@
//===-- rosa/core/System.hpp ------------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/core/System.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \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.
///
/// \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;
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()
/// \encode
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:\code
- /// !isUnitRegistered(U)
- /// \endcode Moreover, \p U is destroyed.
+ /// \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/forward_declarations.h b/include/rosa/core/forward_declarations.h
index 1fcc8e1..f11f2e0 100644
--- a/include/rosa/core/forward_declarations.h
+++ b/include/rosa/core/forward_declarations.h
@@ -1,40 +1,40 @@
//===-- rosa/core/forward_declarations.h ------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \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
+#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;
+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
new file mode 100755
index 0000000..d71db01
--- /dev/null
+++ b/include/rosa/deluxe/DeluxeAgent.hpp
@@ -0,0 +1,667 @@
+//===-- rosa/deluxe/DeluxeAgent.hpp -----------------------------*- C++ -*-===//
+//
+// The RoSA Framework
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file rosa/deluxe/DeluxeAgent.hpp
+///
+/// \author David Juhasz (david.juhasz@tuwien.ac.at)
+///
+/// \date 2017
+///
+/// \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