├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── CONTRIBUTING.md ├── LICENSE.txt ├── README.md ├── benchmarks ├── CMakeLists.txt ├── benchmarkfunctions.cpp ├── benchmarkfunctions.h ├── port_benchmarks.cpp └── range_benchmarks.cpp ├── cmake ├── FindBoost.cmake └── Findbenchmark.cmake ├── docs ├── .gitignore ├── ActiveConnectionProxy.md ├── CMakeLists.txt ├── CoreConcept.md ├── GETTING_STARTED.md ├── Glossary.md ├── ParallelRegion.md ├── ParallelScheduler.md ├── Serialization.md ├── USING.md ├── chainingconnections.md ├── designrationale.md ├── doxyfile ├── doxyfile.in ├── images │ ├── 2015-09-25_scheduler_class_v2ck.png │ ├── 2015-11-02_ActiveConnectionProxy.png │ ├── 2015-11-02_ParallelRegion.png │ ├── 2015-11-10_Scheduler_sequence.png │ ├── CoreClassDiagram_v3.png │ └── CoreClassDiagram_v4.png ├── knownpitfalls.md ├── ports.md └── usertips.md ├── flexcore ├── 3rdparty │ └── adobe │ │ ├── LICENSE_1_0.txt │ │ ├── config.hpp │ │ ├── forest.hpp │ │ ├── readme.txt │ │ ├── reverse.hpp │ │ └── set_next.hpp ├── CMakeLists.txt ├── core │ ├── connectables.hpp │ ├── connection.hpp │ ├── connection_util.hpp │ ├── detail │ │ ├── connection_utils.hpp │ │ └── function_traits.hpp │ ├── exceptions.hpp │ ├── traits.hpp │ └── tuple_meta.hpp ├── extended │ ├── base_node.cpp │ ├── base_node.hpp │ ├── graph │ │ ├── graph.cpp │ │ ├── graph.hpp │ │ ├── graph_connectable.hpp │ │ └── traits.hpp │ ├── node_fwd.hpp │ ├── nodes │ │ ├── buffer.hpp │ │ ├── event_nodes.hpp │ │ ├── generic.hpp │ │ ├── region_worker_node.hpp │ │ ├── state_nodes.hpp │ │ └── terminal.hpp │ ├── ports │ │ ├── connection_buffer.hpp │ │ ├── node_aware.hpp │ │ └── token_tags.hpp │ └── visualization │ │ ├── visualization.cpp │ │ └── visualization.hpp ├── flexcore-config.cmake.in ├── infrastructure.cpp ├── infrastructure.hpp ├── ports.hpp ├── pure │ ├── detail │ │ ├── active_connection_proxy.hpp │ │ ├── port_traits.hpp │ │ └── port_utils.hpp │ ├── event_sinks.hpp │ ├── event_sources.hpp │ ├── mux_ports.hpp │ ├── port_connection.hpp │ ├── pure_node.hpp │ ├── pure_ports.hpp │ ├── state_sink.hpp │ └── state_sources.hpp ├── range │ └── actions.hpp ├── scheduler │ ├── clock.cpp │ ├── clock.hpp │ ├── cyclecontrol.cpp │ ├── cyclecontrol.hpp │ ├── parallelregion.cpp │ ├── parallelregion.hpp │ ├── parallelscheduler.cpp │ ├── parallelscheduler.hpp │ ├── scheduler.hpp │ ├── serialschedulers.cpp │ └── serialschedulers.hpp └── utils │ ├── demangle.cpp │ ├── demangle.hpp │ ├── generic_container.hpp │ ├── logging │ ├── logger.cpp │ └── logger.hpp │ ├── serialisation │ ├── deserializer.hpp │ └── serializer.hpp │ └── settings │ ├── jsonfile_setting_backend.hpp │ ├── settings.hpp │ ├── settings_backend.hpp │ └── settings_container.hpp ├── integration_tests ├── CMakeLists.txt └── main.cpp └── tests ├── CMakeLists.txt ├── core ├── movable_connectable.hpp ├── test_connectables.cpp ├── test_connection.cpp └── test_traits.cpp ├── examples.cpp ├── extended ├── graph │ └── test_graph.cpp ├── nodes │ ├── test_base_node.cpp │ ├── test_infrastructure.cpp │ ├── test_region_worker_node.cpp │ └── test_terminal_node.cpp └── ports │ ├── test_node_aware.cpp │ └── test_region_buffer.cpp ├── logging └── test_logging.cpp ├── nodes ├── owning_node.hpp ├── test_buffer.cpp ├── test_event_nodes.cpp ├── test_generic.cpp ├── test_moving.cpp └── test_state_nodes.cpp ├── pure ├── sink_fixture.hpp ├── test_events.cpp ├── test_moving.cpp ├── test_mux_ports.cpp └── test_state_sinks.cpp ├── range └── test_range.cpp ├── runner.cpp ├── scheduler ├── TestClock.cpp ├── test_cyclecontrol.cpp ├── test_parallel_region.cpp ├── test_parallelscheduler.cpp └── test_serialscheduler.cpp ├── serialisation └── test_deserializer.cpp ├── settings ├── test_setting_backend.cpp └── test_settings.cpp └── util └── test_generic_container.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | /.settings 2 | /.project 3 | /.cproject 4 | Debug 5 | Release 6 | build*/ 7 | build-*/ 8 | CMakeLists.txt.user 9 | 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "flexcore/3rdparty/cereal"] 2 | path = flexcore/3rdparty/cereal 3 | url = ../cereal.git 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED( VERSION 3.0 ) 2 | 3 | PROJECT( flexcore ) 4 | 5 | IF( CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR ) 6 | SET( STANDALONE TRUE ) 7 | ELSE() 8 | SET( STANDALONE FALSE ) 9 | ENDIF() 10 | 11 | SET(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) 12 | 13 | OPTION( FLEXCORE_ENABLE_COVERAGE_ANALYSIS "activate gcov based coverage anlysis" OFF ) 14 | OPTION( FLEXCORE_ENABLE_TESTS "build unit tests" ${STANDALONE} ) 15 | OPTION( FLEXCORE_ENABLE_BENCHMARKS "build micro benchmarks" OFF ) 16 | 17 | IF( FLEXCORE_ENABLE_COVERAGE_ANALYSIS AND NOT CMAKE_BUILD_TYPE STREQUAL "Debug" ) 18 | MESSAGE( WARNING "Build type is not Debug, code coverage information may be wrong" ) 19 | ENDIF() 20 | 21 | ADD_COMPILE_OPTIONS( 22 | "-Wall" 23 | "-Wextra" 24 | "-pedantic" 25 | $<$:-fprofile-arcs> 26 | $<$:-ftest-coverage> 27 | ) 28 | 29 | IF( ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang" ) 30 | ADD_COMPILE_OPTIONS( 31 | "-Wno-unused-local-typedef" 32 | ) 33 | ENDIF() 34 | 35 | SET(THREADS_PREFER_PTHREAD_FLAG ON) 36 | FIND_PACKAGE( Boost 1.55 REQUIRED COMPONENTS log) 37 | FIND_PACKAGE( Threads ) 38 | 39 | ADD_SUBDIRECTORY( flexcore ) 40 | ADD_SUBDIRECTORY( docs ) 41 | IF( FLEXCORE_ENABLE_TESTS ) 42 | ADD_SUBDIRECTORY( tests ) 43 | ADD_SUBDIRECTORY( integration_tests ) 44 | ENDIF() 45 | 46 | IF ( FLEXCORE_ENABLE_BENCHMARKS ) 47 | ADD_SUBDIRECTORY( benchmarks ) 48 | ENDIF() -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guideline and Coding style guide for FlexCore Development 2 | 3 | The first rule of the Contribution Guideline is: Contribute! 4 | We are confident that stylistic inconsitencies etc. can easily be fixed. 5 | This guideline is meant to encourage contribution, not block it. 6 | 7 | ## C++ 8 | 9 | We generally try to adher to the [CppCoreGuidelines](https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md). 10 | 11 | The coding style in general is meant to be close to that of the stl and boost, 12 | since flexcore is used as a foundation library for other domain specific libraries and tools. 13 | 14 | ### Code layout 15 | * Lines are strictly limited to 100 characters 16 | * Identation is done with tabs and tab width is 4 characters 17 | * namespace content is not indented 18 | * Access labels (public, protected, private) do not add an indent level 19 | * [Allman style](https://en.wikipedia.org/wiki/Indent_style#Allman_style) for curly brackets 20 | * Use of whitespaces within a line are left to the coder and shall enhance readability 21 | 22 | ### Identifier 23 | * The language used for identifiers and comments is english. 24 | * Identifiers: 25 | * types and objects are all lower case 26 | * MACROS and only MACROS are all caps. 27 | * Concepts start with an uppercase letter 28 | * Template parameters and alias types commonly have a _t suffix 29 | * Distinction between types, objects, members, nonmembers is the responsibility of the UI, not of the use of upper case characters 30 | 31 | ### Comments 32 | * There is no documentation header in files 33 | * The language used for identifiers and comments is english. 34 | * Comments describing an API should are to be in doxygen format 35 | * If functions have preconditions or postconditions they need to explained in their documentation 36 | * Classes should have documentation explaining their purpose, use and if helpful an example 37 | * Comments are required where they are needed for clarification **but** should not be present where they add no additional information 38 | 39 | ### Other 40 | * Header files have the extension .hpp, Source files .cpp 41 | * Includes are grouped (in this order) in: flexcore includes, standard library, boost, other system libraries. 42 | * Includes may not use "." or ".." -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | flexcore 2 | ======== 3 | 4 | Build fast computational graphs quickly using modern C++! 5 | 6 | Flexcore is: 7 | * A modern C++ library for dataflow programming based on C++14. 8 | * Fast: FlexCore does not contain any hidden performance traps. The template based design does not hide the execution path behind unnecessary indirection and thus allows compilers to optimize the whole dataflow program. 9 | A set of benchmarks is delivered with the library to back up our claims. 10 | * Generic: FlexCore requires only minimal concepts from user defined types and algorithms. 11 | * Robust: FlexCore makes full use of the C++ type system to make it easy to write correct and hard to write incorrect code. 12 | * Well tested: FlexCore is in production use for 3D point cloud scanners. An extensive suite of unit-tests is provided as well. 13 | 14 | Usage 15 | ----- 16 | See the [getting started](docs/GETTING_STARTED.md) page. 17 | 18 | Installation 19 | ------------ 20 | Flexcore uses a cmake based build system. 21 | After cloning run: 22 | 23 | $ git submodule init 24 | $ git submodule update 25 | 26 | To compile and install: 27 | 28 | $ mkdir build && cd build 29 | $ cmake -DFLEXCORE_ENABLE_TESTS=NO .. 30 | $ make install 31 | 32 | The installation location can be customized in the usual cmake way: 33 | 34 | cmake -DCMAKE_INSTALL_PREFIX= 35 | 36 | On 32-bit platforms libraries will be placed in /lib32, and on 64-bit 37 | platforms in /lib64. 38 | 39 | To override the library dir suffix specify the variable -DLIB_SUFFIX= in the 40 | call to cmake. The libraries will then be installed in 41 | /lib${LIB_SUFFIX} 42 | 43 | To use flexcore in a cmake based project check the [usage](docs/USING.md) document. 44 | 45 | To access the documentation in doxygen, execute doxygen from top level directory, not from /docs : 46 | 47 | `doxygen docs/doxyfile` 48 | 49 | Dependencies 50 | ------------ 51 | - C++14 compatible compiler (tested with GCC-5.2, Clang-3.6) 52 | - Boost (>=1.54) - thread/log/system libraries 53 | 54 | External libraries that are included as git submodules, are located under 55 | /src/3rdparty. Currently, flexcore includes the following libraries: 56 | 57 | - adobe stlab forest library - A generic C++ library for graph representation 58 | - cereal - A C++11 library for serialization 59 | 60 | License 61 | ------------ 62 | Copyright 2016 Caspar Kielwein 63 | 64 | Licensed under the Apache License, Version 2.0 (the "License"); 65 | you may not use this file except in compliance with the License. 66 | You may obtain a copy of the License at 67 | 68 | http://www.apache.org/licenses/LICENSE-2.0 69 | 70 | Unless required by applicable law or agreed to in writing, software 71 | distributed under the License is distributed on an "AS IS" BASIS, 72 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 73 | See the License for the specific language governing permissions and 74 | limitations under the License. 75 | See LICENSE.txt or http://www.apache.org/licenses/LICENSE-2.0 76 | 77 | All files under flexcore/3rdparty are licensed by their respective copyright owners. 78 | adobe stlab forest is distributed under the Boost Software License, Version 1.0. 79 | cereal is licensed under the BSD license. 80 | -------------------------------------------------------------------------------- /benchmarks/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 3.0.) 2 | 3 | FIND_PACKAGE(benchmark) 4 | IF (NOT benchmark_FOUND) 5 | message(WARNING "Disabling benchmarks") 6 | return() 7 | ENDIF() 8 | 9 | ADD_EXECUTABLE(flexcore_benchmark 10 | benchmarkfunctions.cpp 11 | range_benchmarks.cpp 12 | port_benchmarks.cpp 13 | ) 14 | 15 | set_property(TARGET flexcore_benchmark PROPERTY CXX_STANDARD 14) 16 | 17 | #TARGET_COMPILE_OPTIONS( flexcore_benchmark PUBLIC 18 | # "-march=native" 19 | # ) 20 | 21 | TARGET_LINK_LIBRARIES( flexcore_benchmark PUBLIC 22 | benchmark 23 | pthread 24 | flexcore 25 | ) -------------------------------------------------------------------------------- /benchmarks/benchmarkfunctions.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * benchmarkfunctions.cpp 3 | * 4 | * Created on: Feb 7, 2017 5 | * Author: ckielwein 6 | */ 7 | 8 | #include "benchmarkfunctions.h" 9 | 10 | namespace fc 11 | { 12 | namespace bench 13 | { 14 | 15 | 16 | float identity_function(const float in) 17 | { 18 | return in; 19 | } 20 | 21 | float inherited::foo(float in) const 22 | { 23 | return in; 24 | } 25 | 26 | identity_node::identity_node() 27 | : owner() 28 | , internal(owner.make_child_named>("bla")) 29 | { 30 | } 31 | 32 | std::unique_ptr make_inherited() 33 | { 34 | return std::make_unique(); 35 | } 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /benchmarks/benchmarkfunctions.h: -------------------------------------------------------------------------------- 1 | /* 2 | * benchmarkfunctions.h 3 | * 4 | * Created on: Feb 7, 2017 5 | * Author: ckielwein 6 | */ 7 | 8 | #ifndef BENCHMARKS_BENCHMARKFUNCTIONS_H_ 9 | #define BENCHMARKS_BENCHMARKFUNCTIONS_H_ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include "../tests/nodes/owning_node.hpp" 16 | 17 | namespace fc 18 | { 19 | namespace bench 20 | { 21 | 22 | float identity_function(float in); 23 | 24 | struct identity_node 25 | { 26 | 27 | identity_node(); 28 | 29 | fc::tests::owning_node owner; 30 | fc::state_terminal& internal; 31 | }; 32 | 33 | struct base_class 34 | { 35 | virtual float foo(float in) const = 0; 36 | virtual ~base_class() = default; 37 | }; 38 | 39 | std::unique_ptr make_inherited(); 40 | 41 | struct inherited : base_class 42 | { 43 | float foo(float in) const override; 44 | }; 45 | 46 | } 47 | } 48 | 49 | #endif /* BENCHMARKS_BENCHMARKFUNCTIONS_H_ */ 50 | -------------------------------------------------------------------------------- /benchmarks/port_benchmarks.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "benchmarkfunctions.h" 7 | 8 | #include 9 | 10 | namespace fc 11 | { 12 | /// Performance benchmarks of flexcore 13 | namespace bench 14 | { 15 | 16 | 17 | // benchmnark of flexcore basic functions, nodes, ports etc. 18 | // tested against std function, function calls, function pointers etc. 19 | 20 | using fc::operator>>; 21 | 22 | 23 | void lambda(benchmark::State& state) { 24 | std::random_device rd; 25 | std::mt19937 gen(rd()); 26 | 27 | 28 | float x = gen(); 29 | 30 | while (state.KeepRunning()) { 31 | benchmark::DoNotOptimize(x); 32 | 33 | const float a = fc::identity{}(x); 34 | 35 | assert(a == x); 36 | benchmark::DoNotOptimize(a); 37 | } 38 | } 39 | 40 | void pure_port(benchmark::State& state) { 41 | std::random_device rd; 42 | std::mt19937 gen(rd()); 43 | 44 | 45 | float x = gen(); 46 | float a = 0.0; 47 | 48 | fc::pure::event_sink sink{[&a](float in){ a = in; }}; 49 | 50 | while (state.KeepRunning()) { 51 | benchmark::DoNotOptimize(x); 52 | 53 | sink(x); 54 | 55 | assert(a == x); 56 | benchmark::DoNotOptimize(a); 57 | } 58 | } 59 | 60 | void virtual_function(benchmark::State& state) { 61 | std::random_device rd; 62 | std::mt19937 gen(rd()); 63 | 64 | 65 | float x = gen(); 66 | 67 | //try to hide the dynmic class 68 | //by constructing it in a different compilation unit. 69 | std::unique_ptr obj 70 | = make_inherited(); 71 | 72 | while (state.KeepRunning()) { 73 | benchmark::DoNotOptimize(x); 74 | 75 | const float a = obj->foo(x); 76 | 77 | assert(a == x); 78 | benchmark::DoNotOptimize(a); 79 | } 80 | } 81 | 82 | void extended_node(benchmark::State& state) { 83 | std::random_device rd; 84 | std::mt19937 gen(rd()); 85 | 86 | 87 | float x = gen(); 88 | 89 | identity_node node{}; 90 | //make sure we create the connection outside of the loop 91 | //we don't want to measure the overhead of creating the connection. 92 | [&x](){ return x;} >> node.internal.in(); 93 | 94 | while (state.KeepRunning()) { 95 | benchmark::DoNotOptimize(x); 96 | 97 | const float a = node.internal.out()(); 98 | 99 | assert(a == x); 100 | benchmark::DoNotOptimize(a); 101 | } 102 | } 103 | 104 | 105 | 106 | BENCHMARK(lambda); 107 | BENCHMARK(virtual_function); 108 | BENCHMARK(pure_port); 109 | BENCHMARK(extended_node); 110 | 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /benchmarks/range_benchmarks.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | namespace fc 10 | { 11 | namespace bench 12 | { 13 | 14 | // Benchmark of flexcore range functions 15 | 16 | using fc::operator>>; 17 | 18 | struct map_loop { 19 | decltype(auto) operator()(std::vector in, float x, float y) { 20 | for (size_t i = 0; i != in.size(); ++i) { 21 | in[i] = y + x * in[i]; 22 | } 23 | return in; 24 | } 25 | }; 26 | 27 | struct fc_map_inline { 28 | decltype(auto) operator()(std::vector in, float x, float y) { 29 | return (fc::actions::map([x](auto in) {return x * in;}) 30 | >> fc::actions::map([y](auto in) {return y + in;}))(std::move(in)); 31 | } 32 | }; 33 | 34 | struct fc_map { 35 | decltype(auto) operator()(std::vector in, float x, float y) { 36 | return fc::actions::map([x,y](auto in) {return y + x * in;})(std::move(in)); 37 | } 38 | }; 39 | 40 | constexpr auto filter_factor = 0.25; 41 | constexpr auto filter_value = filter_factor * 10000; 42 | 43 | struct filter_loop { 44 | decltype(auto) operator()(std::vector in, float x, float y) { 45 | for (size_t i = 0; i != in.size(); ++i) { 46 | if(in[i] > filter_value) 47 | in[i] = y + x * in[i]; 48 | } 49 | return in; 50 | } 51 | }; 52 | 53 | struct fc_filter_map { 54 | decltype(auto) operator()(std::vector in, float x, float y) { 55 | return (fc::actions::filter([](auto in){ return in > filter_value;}) 56 | >> fc::actions::map([x](auto in) {return x * in;}) 57 | >> fc::actions::map([y](auto in) {return y + in;}))(std::move(in)); 58 | } 59 | }; 60 | 61 | constexpr auto benchmark_size = 2 << 15; 62 | 63 | /// Copying a std::vector serves as a simple baseline 64 | static void VectorCopy(benchmark::State& state) { 65 | std::vector vec(state.range(0)); 66 | while (state.KeepRunning()) 67 | { 68 | std::vector copy(vec); 69 | benchmark::DoNotOptimize(copy); 70 | } 71 | } 72 | 73 | template void vector_f(benchmark::State& state) { 74 | T f; 75 | 76 | std::random_device rd; 77 | std::mt19937 gen(rd()); 78 | 79 | std::uniform_real_distribution<> d(0, 10000); 80 | std::vector a(state.range(0)); 81 | std::vector b(state.range(0)); 82 | 83 | std::generate(b.begin(), b.end(), [&]() {return d(gen);}); 84 | 85 | 86 | float x = d(gen); 87 | float y = d(gen); 88 | while (state.KeepRunning()) { 89 | auto tmp = b; 90 | benchmark::DoNotOptimize(tmp); 91 | 92 | a = f(tmp,x,y); 93 | 94 | benchmark::DoNotOptimize(a); 95 | } 96 | } 97 | 98 | BENCHMARK(VectorCopy) 99 | ->RangeMultiplier(2)->Range(64, benchmark_size); 100 | BENCHMARK_TEMPLATE(vector_f, map_loop) 101 | ->RangeMultiplier(2)->Range(64, benchmark_size); 102 | BENCHMARK_TEMPLATE(vector_f, fc_map) 103 | ->RangeMultiplier(2)->Range(64, benchmark_size); 104 | BENCHMARK_TEMPLATE(vector_f, fc_map_inline) 105 | ->RangeMultiplier(2)->Range(64, benchmark_size); 106 | 107 | BENCHMARK_TEMPLATE(vector_f, filter_loop) 108 | ->RangeMultiplier(2)->Range(64, benchmark_size); 109 | BENCHMARK_TEMPLATE(vector_f, fc_filter_map) 110 | ->RangeMultiplier(2)->Range(64, benchmark_size); 111 | 112 | } 113 | } 114 | 115 | BENCHMARK_MAIN() 116 | -------------------------------------------------------------------------------- /cmake/Findbenchmark.cmake: -------------------------------------------------------------------------------- 1 | # FindBenchmark 2 | # --------- 3 | # 4 | # Find google benchmark include dirs and libraries 5 | # 6 | # The following variables are optionally searched for defaults 7 | # benchmark_ROOT_DIR: Base directory where all benchmark components are found 8 | # 9 | # Once done this will define 10 | # benchmark_FOUND - System has benchmark 11 | # benchmark_INCLUDE_DIRS - The benchmark include directories 12 | # benchmark_LIBRARIES - The libraries needed to use benchmark 13 | 14 | find_path(benchmark_INCLUDE_DIR "benchmark/benchmark.h" 15 | PATHS ${benchmark_ROOT_DIR} 16 | PATH_SUFFIXES include 17 | NO_DEFAULT_PATH) 18 | find_path(benchmark_INCLUDE_DIR "benchmark/benchmark.h") 19 | 20 | find_library(benchmark_LIBRARY NAMES "benchmark" 21 | PATHS ${benchmark_ROOT_DIR} 22 | PATH_SUFFIXES lib lib64 23 | NO_DEFAULT_PATH) 24 | find_library(benchmark_LIBRARY NAMES "benchmark") 25 | 26 | include(FindPackageHandleStandardArgs) 27 | # handle the QUIETLY and REQUIRED arguments and set benchmark_FOUND to TRUE 28 | # if all listed variables are TRUE 29 | find_package_handle_standard_args(benchmark FOUND_VAR benchmark_FOUND 30 | REQUIRED_VARS benchmark_LIBRARY 31 | benchmark_INCLUDE_DIR) 32 | 33 | if (benchmark_FOUND AND NOT TARGET benchmark) 34 | add_library(benchmark UNKNOWN IMPORTED) 35 | set_target_properties(benchmark PROPERTIES 36 | IMPORTED_LOCATION "${benchmark_LIBRARY}" 37 | INTERFACE_INCLUDE_DIRECTORIES "${benchmark_INCLUDE_DIR}" 38 | ) 39 | endif() 40 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | /html/ 2 | -------------------------------------------------------------------------------- /docs/ActiveConnectionProxy.md: -------------------------------------------------------------------------------- 1 | #ActiveConnectionProxy 2 | 3 | Active connection proxies are temporary objects used in the creation of connections between ports. 4 | They only appear when an __ActiveConnectable__ is connected. 5 | 6 | Since active_connectables have ownership of their connections, they need to store them. 7 | Active_connectables store the connections as `std::function` objects with signatures matching their own type. 8 | An active source (event source) of type `int` expects no return and thus uses the signature `void(int)`. 9 | 10 | When the active_connectable is connected to a connectable, this connection cannot be stored in the active_connectable yet. 11 | Instead an active connection proxy is created which takes ownership of the connectable and holds a reference to the active connectable. 12 | The connection is completed, when a passive_connectable is connected. 13 | Until then, the intermediate connection of the connectables is built up and stored in the `active_connection_proxy`. 14 | When the proxy reaches a passive_connectable, the connection stored within the proxy is connected to the passive_connectable and the resulting connection gets connected to the active_connectable. The proxy is then destroyed as the active_connectable takes over the full connection. 15 | 16 | Creation and use of active connection proxies is completely symmetric for events and states. The only difference is that for events the source is active, and thus the proxy is built from left to right while for states the sink is active and the proxy thus built from right to left. 17 | 18 | ![2015-11-02_ActiveConnectionProxy](./images/2015-11-02_ActiveConnectionProxy.png) 19 | 20 | Due to operator precedence rules in the typical chain of connections 21 | 22 | passive >> connectable_a >> connectable_b >> active; 23 | 24 | an `active_connection_proxy` will _NOT_ be created, as simple `connection` objects are sufficient. 25 | 26 | Active connection proxy objects may be stored in a variable, but cannot be used to connect more than once. Therefore it is necessary to `std::move` the variable storing the proxy when connecting the next connectable to it. 27 | ~~~{.cpp} 28 | auto proxy = active >> connectable; 29 | // proxy >> passive; // BAD! does not compile 30 | std::move(proxy) >> passive; // GOOD, proxy may not be used again 31 | ~~~ 32 | 33 | -------------------------------------------------------------------------------- /docs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(Doxygen) 2 | if(DOXYGEN_FOUND) 3 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY) 4 | 5 | #Build cereal documentation 6 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../flexcore/3rdparty/cereal 7 | ${CMAKE_CURRENT_BINARY_DIR}/cereal 8 | EXCLUDE_FROM_ALL 9 | ) 10 | 11 | #the working dir is a bit of a hack as it is force set to the flexcore directory via relative path to docs 12 | #this is currently necessary as the docs are only working when doxygen is executed from flexcore top level 13 | add_custom_target(flexcore_doc 14 | ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile 15 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/.. 16 | COMMENT "Generating flexcore API documentation with Doxygen" 17 | VERBATIM 18 | ) 19 | 20 | #add cereal doc target (which is only called doc up to 1.2.1) 21 | add_dependencies(flexcore_doc doc) 22 | 23 | endif(DOXYGEN_FOUND) -------------------------------------------------------------------------------- /docs/CoreConcept.md: -------------------------------------------------------------------------------- 1 | # Concepts 2 | 3 | ## Connection Concepts 4 | 5 | ### Connectable 6 | There is one main concept which forms the foundation of the FlexCore library: 7 | **connectable**. Every type, which fulfils this concept can be connected. 8 | Every edge in the dataflow graph connects to connectable objects. 9 | 10 | The concept **connectable** extends two concepts from the standard library: 11 | 12 | - **callable**, described at http://en.cppreference.com/w/cpp/concept/Callable 13 | - **copyable**, described at: http://en.cppreference.com/w/cpp/types/is_copy_constructible. 14 | 15 | Examples for connectable types are: Lambdas, Function Pointers, std::function, 16 | functors etc. 17 | 18 | There is one exception to the **copyable** requirement: [lvalues][lvalue]. Callable objects which are connected as named variables need not be copyable. They will be wrapped in a `std::ref` object to establish the connection. As a consequence, lvalue connectables are **not** allowed to change address after the connection is established. 19 | 20 | ### Connections 21 | Two connectables are connected by calling the free template function **connect**, 22 | which takes two connectables as parameters. 23 | 24 | This returns an object of type **connection**, which contains both arguments of 25 | connect. A connection object is itself connectable. 26 | ~~~{.cpp} 27 | template //L and R need to fulfil connectable 28 | connection connect(L lhs, R rhs); 29 | connectable_a >> connectable_b; 30 | ~~~ 31 | 32 | Users should not call `connect` directly but instead use the overloaded 33 | `operator>>` which performs type validation and then calls `connect`. 34 | ~~~{.cpp} 35 | using fc::operator>>; 36 | connectable_a >> connectable_b; 37 | ~~~ 38 | 39 | The following UML class diagram shows the interaction between these concepts: 40 | 41 | ![CoreClassDiagram_v3](./images/CoreClassDiagram_v3.png) 42 | 43 | By calling connect on connectables and connections repeatedly we can 44 | build **chains** of connections. 45 | 46 | ### Restrictions: Active- And Passive-Connectable 47 | Two additional concepts complete the design. These are **active_connectable** 48 | and **passive_connectable**. active_connectable and passive_connectable serve 49 | as end points of chains of connections. Types which are active_connectable do 50 | not need to be callable unlike passive_connectable and connectable. connectable 51 | types are both active_connectable and passive_connectable. 52 | 53 | Any type which is neither connectable nor passive_connectable or 54 | active_connectable is called **not_connectable**. This is not a separate 55 | concept, as it is defined purely as the negation of the three other. 56 | 57 | Both active and passive connectable types can appear on the left and right hand 58 | side of a connection. Since they form the end points of a chain of connections, 59 | active_connectable and passive_connectable restrict how connect can be called on 60 | them: 61 | 62 | ~~~{.cpp} 63 | // for push-logic, i.e. sending events (source is active (pusher), sink is passive) 64 | connect(active_connectable, connectable); // returns an active_connectable. 65 | connect(connectable, passive_connectable); // returns a passive_connectable. 66 | connect(active_connectable, passive_connectable); // connection is complete, returns a non_connectable connection. 67 | 68 | // for pull-logic, i.e. receiving states (source is passive, sink is active (puller)) 69 | connect(connectable, active_connectable); // returns an active_connectable. 70 | connect(passive_connectable, connectable); // returns a passive_connectable. 71 | connect(passive_connectable, active_connectable); // connection is complete, returns a non_connectable connection. 72 | 73 | connect(connectable, connectable); // returns a connectable as defined above. 74 | 75 | connect(active_connectable, active_connectable); // illegal. 76 | connect(passive_connectable, passive_connectable); // illegal. 77 | ~~~ 78 | 79 | ![CoreClassDiagram_v4](./images/CoreClassDiagram_v4.png) 80 | 81 | [lvalue]: http://en.cppreference.com/w/cpp/language/value_category 82 | -------------------------------------------------------------------------------- /docs/Glossary.md: -------------------------------------------------------------------------------- 1 | # Glossary 2 | 3 | Definitions of the concepts and terms used in flexcore. The main objective is 4 | to create a common nomenclature to avoid misunderstandings. 5 | 6 | * **Dataflow Programming:** Programming paradigm, where software structure is modelled as a directed graph with data flowing between nodes. See [dataflow][]. 7 | * **flexcore:** This is what all the fuzz is about. A C++ library for dataflow programming. The name flexcore is also used for the development project. 8 | 9 | ### Connectables 10 | * **Node:** Nodes are connected to form the graph of the dataflow program. Functionality is written in the code of the node. Nodes can again consist of several other nodes. This is then called a **compound node**. Nodes communicate using connections established between ports. 11 | * [Parallel Region:](md_docs_ParallelRegion.html) A subgraph of the dataflow graph which allows all computations inside to be executed in parallel to all computations outside is called a parallel region. Parallel Regions can consist of single nodes or compound nodes. 12 | * [Port:](md_docs_ports.html) Ports are the end points of connections, and are accesible to nodes. Ports can be **input ports** (sinks) or **output ports** (sources) for state or events. Nodes can have explicitly declared ports. Ports of a node can also be implicit, when the node is very simple. 13 | * **Pure:** A node that does not have side effects is called pure. A pure node always produces the same output, when given the same inputs. See [pure][] The term must not be confused with the flexcore namespace _pure_, which is used to distinguish low level classes, that are essential for the libraries dataflow concept, and higher order classes, which are primarily extensions to the core concept and are more convenient to use in most applications. 14 | * **Sink:** A sink is a port, which receives tokens over one or more connections. A sink can be seen as a source of data for a node. Sinks can either pull tokens over **states** or receive **events**. 15 | * **Source:** A source is a port, which sends tokens. A source can either be pulled through a state, or send events on its own. 16 | * **Unary:** A node that has exactly one input port and exactly one output port is called unary. 17 | 18 | ### Connections 19 | * **Connection:** Connections are the edges of the dataflow graph. All interaction between nodes is done through **tokens**, which travel over connections. 20 | * **Graph:** The structure of a program written in flexcore is a graph. It is a directed graph, which might, but doesn't need to be acyclic. The graph consists of nodes and edges (called **connection** in flexcore), see [directed-graph][]. 21 | * **active:** In a connection, the port that calls through the connection is called active. 22 | * **passive:** The port, that is being called through a connection is called passive. Each connection connects an active port with a passive port. 23 | 24 | ### Data 25 | * **Token:** Tokens are pieces of data travelling over connections between nodes. Tokens can contain arbitrary (but finite) amounts of data. Tokens can be sent as **events** or be pulled as **states**. Whether a Token is a state or an event is determined by the connection the token flows through. This information is not stored in the token itself, but in the ports trough which a node sends and receives the token. 26 | * **Event:** A token, which is pushed through a connection. flexcore uses Events when one node wants to send a message to other nodes. When an event is sent, the source is **active** and the sink is **passive**. Events in flexcore correspond to the general concept in event driven design. See [event-computing][]. 27 | * **State:** A token that can be pulled through a connection. States are used to model continuous data and communication of state between nodes. When tokens travel as state, the sink is **active** and the source is **passive**. 28 | 29 | [dataflow]: https://en.wikipedia.org/wiki/Dataflow_programming 30 | [pure]: https://en.wikipedia.org/wiki/Pure_function 31 | [event-computing]: https://en.wikipedia.org/wiki/Event_%28computing%29 32 | [directed-graph]: https://en.wikipedia.org/wiki/Directed_graph 33 | -------------------------------------------------------------------------------- /docs/ParallelRegion.md: -------------------------------------------------------------------------------- 1 | # Parallel Regions 2 | 3 | Parallel Regions form the basis for parallel executions of sub-graphs of a flexcore program. 4 | 5 | All nodes inside the region are guaranteed to be executed by a single thread. 6 | All calculations within the region might be performed in a different thread than calculations outside the region. 7 | 8 | Connections entering or leaving the region go through buffers. 9 | These buffers are use double buffering to allow asynchronous access to from within and without the region. 10 | The region controls the buffers and switches their content at the beginning of each cycle. 11 | 12 | Buffers are created automatically and inserted into the connection, if the connection is between ports from two different regions. 13 | 14 | Client code specifies which region a port belongs to as a constructor parameter to the port. 15 | 16 | ![2015-11-02_ParallelRegion](./images/2015-11-02_ParallelRegion.png) 17 | 18 | Each Region has two ticks, which trigger all operations: 19 | 1. The switch tick triggers switching of all buffers of the region. 20 | 2. The work tick triggers the actual calculations inside the nodes. 21 | 22 | These ticks are controlled by the [Parallelscheduler](md_docs_ParallelScheduler.html). 23 | -------------------------------------------------------------------------------- /docs/ParallelScheduler.md: -------------------------------------------------------------------------------- 1 | # Scheduler and Periodic Tasks 2 | 3 | Calculations on nodes of the dataflow graph can generally be done in parallel. (See [Parallelregion](md_docs_ParallelRegion.html) ) 4 | 5 | To efficiently distribute these calculations a Scheduler is used. 6 | The current implementation is based on a thread-pool together with a task-queue. (See [Thread Pool](https://en.wikipedia.org/wiki/Thread_pool_pattern) for an explanation. 7 | 8 | The task queue of the scheduler is fed with cyclic task by a master thread (fc::thread::cycle_control) which makes sure that a cycle is executed once and only once in the duration of its cycle time. 9 | 10 | Cyclecontrol goes through the following steps each cycle. 11 | 1. Advance virtual clock by minimal cycle duration 12 | 2. call switch tick of all parallel regions whose calculations are due. (The cycle duration of the region matches the current time.) 13 | 3. Add Work tick of these regions to worker threads of scheduler 14 | 4. Wait Remaining duration of cycle 15 | 5. Goto 1. 16 | 17 | The switch tick serves as the synchronization point and the work tick does the actual calculations. 18 | 19 | ![2015-11-10_Scheduler_sequence](./images/2015-11-10_Scheduler_sequence.png) 20 | 21 | ![2015-09-25_scheduler_class_v2ck](./images/2015-09-25_scheduler_class_v2ck.png) 22 | -------------------------------------------------------------------------------- /docs/Serialization.md: -------------------------------------------------------------------------------- 1 | Serialization 2 | ============= 3 | 4 | Flexcore uses the [cereal](http://uscilab.github.io/cereal/) serialization 5 | library. The `single_object_serializer` and 6 | `single_object_deserializer` classes are [connectable](CoreConcept#connectable) 7 | wrappers to serialize data from and to flexcore ports. 8 | 9 | Both wrappers overload the function call operator as demanded by the _connectable_ concept. The serializer takes an object of `data_t` and sends a `std::string` containing either plain text or binary data, depending on `archive_t`. The deserializer works accordingly. 10 | 11 | Data Types 12 | ---------- 13 | 14 | The template parameter `data_t` can by default be any primitive and most STL 15 | types. 16 | 17 | For STL types the respective cereal header must be included, e.g. `std::vector` 18 | requires an include of `cereal/types/vector.hpp`. Additional headers can be 19 | found [here][cereal-stl-doc]. 20 | 21 | For own classes, the default constructor must exist and serialization methods 22 | must be implemented, which can either be a single method named `serialize`, 23 | e.g. 24 | 25 | ```cpp 26 | template 27 | void class_name::serialize(Archive & archive) 28 | { 29 | archive(member1, member2, ...); 30 | } 31 | 32 | ``` 33 | 34 | or split `save` and `load` methods, e.g. 35 | 36 | ```cpp 37 | template 38 | void class_name::save(Archive & archive) const 39 | { 40 | archive(member1, member2, ...); 41 | } 42 | 43 | template 44 | void class_name::load(Archive & archive) 45 | { 46 | archive(member1, member2, ...); 47 | } 48 | ``` 49 | 50 | The save method must be declared const, otherwise the code will not compile. 51 | 52 | Non-member serialize or save/load methods may also be provided, e.g. 53 | ```cpp 54 | template 55 | void serialize(Archive& archive, some_class& s) 56 | { 57 | archive(s.member1, s.member2, ...); 58 | } 59 | ``` 60 | 61 | More information on this topic can be found on the [cereal website][cereal-doc]. 62 | 63 | Archive Types 64 | ------------- 65 | 66 | The template parameter `archive_t` can be any of the three cereal archive 67 | types. Take care to provide the serializer with an output archive and the 68 | deserializer with an input archive. 69 | 70 | Cereal provides serialization in binary, json and xml form. The archives are 71 | located in the `cereal` namespace and the respective includes are: 72 | 73 | | Name | Header | 74 | | :-------------------------------------: | :------------------------: | 75 | | BinaryInputArchive, BinaryOutputArchive | cereal/archives/binary.hpp | 76 | | JSONInputArchive, JSONOutputArchive | cereal/archives/json.hpp | 77 | | XMLInputArchive, XMLOutputArchive | cereal/archives/xml.hpp | 78 | 79 | Example 80 | ------- 81 | 82 | Binary serialization of a `std::vector`: 83 | ```cpp 84 | #include 85 | #include 86 | #include 87 | 88 | #include 89 | #include 90 | 91 | #include 92 | 93 | using namespace fc; 94 | 95 | bool serialize_something() 96 | { 97 | single_object_serializer serializer; 98 | single_object_deserializer deserializer; 99 | 100 | auto round_trip = serializer >> deserializer; 101 | 102 | std::vector my_vec = { 103 | 0.0, 1.1, 2.2, 3.3 104 | }; 105 | 106 | return round_trip(my_vec) == my_vec; // returns success 107 | } 108 | 109 | ``` 110 | 111 | [cereal-stl-doc]: http://uscilab.github.io/cereal/assets/doxygen/group__STLSupport.html 112 | [cereal-doc]: http://uscilab.github.io/cereal/serialization_functions.html 113 | 114 | -------------------------------------------------------------------------------- /docs/USING.md: -------------------------------------------------------------------------------- 1 | How to include Flexcore in your project 2 | =================================== 3 | 4 | There are two supported ways to include flexcore in your own project. Both of 5 | these methods assume you use cmake as your build configuration tool. 6 | 7 | git submodule 8 | ------------- 9 | 10 | Add the flexcore repository as a git submodule of your project. 11 | 12 | $ git submodule add flexcore 13 | 14 | The root CMakeLists.txt of your project should then include the line: 15 | 16 | add_subdirectory(flexcore) 17 | 18 | To link flexcore with one of your targets add the commands: 19 | 20 | add_executeable(main main.cpp) 21 | target_link_libraries(main flexcore) 22 | 23 | to CMakeLists.txt. This will add the proper include directories, link flags 24 | and will force building in c++14 mode. 25 | 26 | When checking out your project remember to also checkout submodules: 27 | 28 | $ git submodule init && git submodule update --recursive 29 | 30 | It is not necessary to integrate flexcore as a git submodule; if you know where 31 | the source tree is located you can call add_subdirectory with the full path to 32 | flexcore sources. 33 | 34 | find_package 35 | ------------ 36 | 37 | This is the recommended way of including flexcore. First build and install flexcore. 38 | 39 | $ git clone flexcore 40 | $ mkdir -p flexcore/build && cd flexcore/build 41 | $ cmake .. -DCMAKE_INSTALL_PREFIX= 42 | $ make install 43 | 44 | Once flexcore has been installed, your projects may use flexcore by including 45 | the following in their CMakeLists.txt: 46 | 47 | find_package(flexcore) 48 | add_executable(main main.cpp) 49 | target_link_libraries(main flexcore) 50 | 51 | If you installed flexcore in a custom location then your cmake invocation will 52 | probably need to include a value for flexcore_DIR (the path to the installed 53 | flexcore-config.cmake). 54 | 55 | $ cmake -Dflexcore_DIR=/lib(32|64)/cmake/flexcore 56 | 57 | The prefix is the one specified as CMAKE_INSTALL_PREFIX when installing 58 | flexcore. 59 | -------------------------------------------------------------------------------- /docs/chainingconnections.md: -------------------------------------------------------------------------------- 1 | # Chaining of Connections 2 | 3 | FlexCore allows connections to be chained. 4 | This means that more then two connectables can be used in a linear connection. 5 | 6 | The following line of code chains two connectables (Lambdas in this case) which increment their input in the connection between `source` and `sink`. `sink` will receive the output of source +2. 7 | ~~~{.cpp} 8 | source >> [](int in){ return ++in; } >> [](int in){ return ++in; } >> sink; 9 | ~~~ 10 | 11 | It is not required that the data type of the token travelling through the connection stays the same. 12 | Connectables in between nodes are especially useful to convert data types if the sink requires a different type than the source provides. 13 | 14 | Chains of connectables are built at compile time and can have arbitrary length. 15 | They are made possible by the fact the connections are connectable themselves. See [Core Concepts](CoreConcept). 16 | This means that chains of connections can be optimized very well by the compiler. -------------------------------------------------------------------------------- /docs/designrationale.md: -------------------------------------------------------------------------------- 1 | #Rationale 2 | 3 | The main goal of FlexCore is to provide a library, which allows programmers to easily design and code dataflow programs, which are as fast and robust as expected by any good C++ program. 4 | 5 | To achieve this we chose a template based design as this allows developers to enjoy the full support of the C++ type system and still achieve Genericity and Extensibility without adding unnecessary burdens to runtime performance. Staying within the bounds of a statically typed system moves as many error checks as possible from runtime to compilation. This increases robustness of the resulting program. 6 | FlexCore does not restrict programmers to a specific problem domain or makes them jump through hoops to extend it with their own code. 7 | 8 | We developed FlexCore with applications in Sensor Technology and Automation in mind. 9 | This context influences our design goals, which where: 10 | * Genericity: FlexCore makes as few assumptions about the algorithms and data-types developed and used by client code. 11 | * Robustness: FlexCore enables a lot of compile time error checks by sticking to the full C++ type system. 12 | * Real-time Capability: FlexCore does not contain any hidden performance traps. The template based design does not hide the execution path behind unnecessary indirection and thus allows compilers to optimize the whole dataflow program. 13 | * Extensibility: The generic design runs through the whole library and allows users to easily extend it by specifying their own nodes, ports and connectables. 14 | 15 | Unfortunately, one notable drawback of this approach is the lack of readability of the compiler errors in many cases. 16 | We believe that the benefits outweigh this problem and are confident, that the fast moving support for generic programming in modern C++ will continuously improve this situation. 17 | 18 | Mitigation strategies: 19 | * As many concept checks and static asserts as reasonable possible. Many cases are already checked as close to the user code as possible. Merge Requests with more and better checks are always welcome. 20 | * Compile with a different compiler. Error messages for template code are quickly becoming better. Try to compile the program with the newest version of gcc and clang. -------------------------------------------------------------------------------- /docs/doxyfile.in: -------------------------------------------------------------------------------- 1 | @INCLUDE = @CMAKE_CURRENT_SOURCE_DIR@/doxyfile 2 | 3 | #we are overriding the existing tags with these and doxygen will use the new ones. 4 | OUTPUT_DIRECTORY = @CMAKE_CURRENT_BINARY_DIR@/ 5 | INPUT = @CMAKE_CURRENT_SOURCE_DIR@/../flexcore @CMAKE_CURRENT_SOURCE_DIR@/.. @CMAKE_CURRENT_SOURCE_DIR@ 6 | TAGFILES = @CMAKE_CURRENT_BINARY_DIR@/cereal/cereal.doxytags=@CMAKE_CURRENT_BINARY_DIR@/cereal/doc/html/ 7 | GENERATE_TAGFILE = @CMAKE_CURRENT_BINARY_DIR@/flexcore.doxytags 8 | IMAGE_PATH = @CMAKE_CURRENT_SOURCE_DIR@/images 9 | -------------------------------------------------------------------------------- /docs/images/2015-09-25_scheduler_class_v2ck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlexCoreLib/flexcore/b91324a84d2e21fa50e207223f4514dcb54771c4/docs/images/2015-09-25_scheduler_class_v2ck.png -------------------------------------------------------------------------------- /docs/images/2015-11-02_ActiveConnectionProxy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlexCoreLib/flexcore/b91324a84d2e21fa50e207223f4514dcb54771c4/docs/images/2015-11-02_ActiveConnectionProxy.png -------------------------------------------------------------------------------- /docs/images/2015-11-02_ParallelRegion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlexCoreLib/flexcore/b91324a84d2e21fa50e207223f4514dcb54771c4/docs/images/2015-11-02_ParallelRegion.png -------------------------------------------------------------------------------- /docs/images/2015-11-10_Scheduler_sequence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlexCoreLib/flexcore/b91324a84d2e21fa50e207223f4514dcb54771c4/docs/images/2015-11-10_Scheduler_sequence.png -------------------------------------------------------------------------------- /docs/images/CoreClassDiagram_v3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlexCoreLib/flexcore/b91324a84d2e21fa50e207223f4514dcb54771c4/docs/images/CoreClassDiagram_v3.png -------------------------------------------------------------------------------- /docs/images/CoreClassDiagram_v4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlexCoreLib/flexcore/b91324a84d2e21fa50e207223f4514dcb54771c4/docs/images/CoreClassDiagram_v4.png -------------------------------------------------------------------------------- /docs/knownpitfalls.md: -------------------------------------------------------------------------------- 1 | # Common Pitfalls 2 | 3 | This page contains a list of common pitfalls, which might be hard to detect. 4 | 5 | 1. Ports which have been connected cannot be moved. 6 | If a port is moved after it has been connected, the partner of the connection is left with a dangling reference. This will most likely result in a segfault with a corrupted stack. 7 | If one wants to move a node after it's ports are connected, keep the ports by unique_ptr. 8 | 9 | 2. Connectables which are lvalues and have been connected to ports can not be moved after connecting. This would lead to a dangling reference inside the port. 10 | If this is a problem, connectables should either be copied or allocated on the heap. A local copy helper can be of use 11 | ```cpp 12 | auto source = [] { return 1; }; 13 | auto sink = [](int i) {}; 14 | auto copy = [](auto c) { return c; }; 15 | copy(source) >> copy(sink); 16 | ``` 17 | 3. If a node instantiates an fc::event_sink or fc::state_source with a lambda capturing `this` or a reference to a member of the node, the node must not be moved. 18 | Moving the node would invalidate the reference or pointer. 19 | As a general rule (not flexcore specific): if you capture `this` in a lambda, delete move and copy constructor. -------------------------------------------------------------------------------- /docs/ports.md: -------------------------------------------------------------------------------- 1 | # Ports 2 | 3 | There are four basic kinds of ports in flexcore. 4 | 5 | * `event_source` 6 | * `event_sink` 7 | * `state_source` 8 | * `state_sink` 9 | 10 | `event_source` and `state_sink` are active ports, so they store connections and initiate the communication. `event_sink` and `state_sink` are passive ports, and are needed to complete a connection. 11 | The basic flexcore ports are non-copyable but moveable. Beware of [moving ports that have been connected](knownpitfalls). For this reason only lvalue ports can be used in connections. 12 | 13 | The various ports described below are part of the `fc::pure` namespace. To get these implementations use the line: 14 | ~~~{.cpp} 15 | #include 16 | ~~~ 17 | To take advantage of ports with added functionality (node-aware, graph-connectable), use the ports in the `flexcore` namespace and include the header: 18 | ~~~{.cpp} 19 | #include 20 | ~~~ 21 | 22 | ## active ports 23 | ### event_source 24 | ~~~{.cpp} 25 | template 26 | struct event_source 27 | { 28 | void fire(event_t event); 29 | template 30 | port_connection, conn_t, event> connect(conn_t&&) &; 31 | }; 32 | ~~~ 33 | `event_t` is the type of data that this port sends; it may be void, in which case `fire` takes no arguments. `event_source` may be connected to multiple ports and will send every one of them any event it is fired with. `connect()` can only be called on an lvalue port. 34 | 35 | ### state_sink 36 | ~~~{.cpp} 37 | template 38 | class state_sink 39 | { 40 | data_t get() const; 41 | template 42 | port_connection, data_t> connect(conn_t&&) &; 43 | }; 44 | ~~~ 45 | `data_t` is the type of data that this port provides. The `get()` method calls the connection. A `state_sink` can only be connected to a single other port. Connecting the `state_sink` more than once invokes undefined behaviour. 46 | 47 | ## passive ports 48 | ### event_sink 49 | ~~~{.cpp} 50 | template 51 | struct event_sink 52 | { 53 | explicit event_sink(const std::function& handler); 54 | template 55 | void operator()(T&&); // when event_t != void and T is convertible to event_t. 56 | // or 57 | void operator()(); // when event_t == void. 58 | }; 59 | ~~~ 60 | `event_t` is the type of event that is accepted by this port. `void` is an acceptable type for *poke* style events that don't transfer any data. An `event_sink` will accept any kind of event that is implicitly convertible to its `event_t`. 61 | 62 | The handler function passed to the constructor is a callback that will be called when the port receives an event. Typically this will notify the node that the port belongs to of the incoming event. 63 | 64 | ### state_source 65 | ~~~{.cpp} 66 | template 67 | class state_source 68 | { 69 | explicit state_source(std::function f); 70 | data_t operator()(); 71 | }; 72 | ~~~ 73 | `data_t` is the type of state that the `state_source` returns. The function `f` passed to the constructor is expected to deliver an object of type `data_t` on very call. This function will be called every time when the `state_sink` that this `state_source` is connected to needs a state. 74 | -------------------------------------------------------------------------------- /docs/usertips.md: -------------------------------------------------------------------------------- 1 | # Tips and Guidelines for using FlexCore in your own code base 2 | 3 | 1. **Prefer Connectables over Nodes** 4 | 5 | If a job can be done by a connectables, don't implement a full fledged node. 6 | Connectables are more generic and allow the compiler to generate faster code. 7 | Connectables also make it easier to reason about code as the dataflow stays linear and is clearly visible in the code. 8 | 9 | Connectables can simply be aggregated to nodes if it proves necessary. 10 | If a connectables produces more than one output, it might be useful to return all outputs as an std::tuple and process them together. 11 | 12 | 2. **Prefer to make Nodes and Connectables pure** 13 | 14 | If a Node or Connectable can perform it's responsibility without internal state, let it do so. This makes it easer to reason about the correctness of the code. 15 | 16 | 3. **Make `operator >>` available by `using fc::operator>>;`** 17 | 18 | As soon as one connects two objects which are not from namespace fc, `operator>>` needs to be made available. 19 | Prefer to import only the operator instead of `using namespace fc` to minimize number of symbols imported. 20 | -------------------------------------------------------------------------------- /flexcore/3rdparty/adobe/LICENSE_1_0.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /flexcore/3rdparty/adobe/config.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2008 Adobe Systems Incorporated 3 | Distributed under the MIT License (see accompanying file LICENSE_1_0_0.txt 4 | or a copy at http://stlab.adobe.com/licenses.html) 5 | */ 6 | 7 | /*************************************************************************************************/ 8 | 9 | #ifndef ADOBE_CONFIG_HPP 10 | #define ADOBE_CONFIG_HPP 11 | 12 | /*************************************************************************************************/ 13 | 14 | #include 15 | 16 | /*************************************************************************************************/ 17 | 18 | /* 19 | Caution: 20 | This is the only ASL header that is guarenteed to change with every release. Including 21 | this header will cause a recompile every time a new ASL version is released. 22 | 23 | ADOBE_VERSION % 100 is the sub-minor version 24 | ADOBE_VERSION / 100 % 1000 is the minor version 25 | ADOBE_VERSION / 100000 is the major version 26 | */ 27 | 28 | #define ADOBE_VERSION_MAJOR 1 29 | #define ADOBE_VERSION_MINOR 0 30 | #define ADOBE_VERSION_SUBMINOR 43 31 | 32 | #define ADOBE_VERSION \ 33 | (ADOBE_VERSION_MAJOR * 100000 + ADOBE_VERSION_MINOR * 100 + ADOBE_VERSION_SUBMINOR) 34 | 35 | /*************************************************************************************************/ 36 | 37 | #define ADOBE_IS_DEPRECATED_ERROR(version) \ 38 | ((ADOBE_VERSION - version) > 0 || defined(ADOBE_NO_DEPRECATED)) 39 | 40 | /*************************************************************************************************/ 41 | 42 | // Big thanks to Boost here for doing a majority of the work for us. 43 | 44 | #if defined(__CYGWIN__) 45 | // Cygwin is not Win32 46 | #define ADOBE_PLATFORM_CYGWIN 1 47 | 48 | #elif defined(BOOST_WINDOWS) || defined(__MINGW32__) 49 | // Win32 50 | #define ADOBE_PLATFORM_WIN 1 51 | 52 | #elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) 53 | // MacOS 54 | #define ADOBE_PLATFORM_MAC 1 55 | 56 | #elif defined(__BEOS__) 57 | // BeOS 58 | #define ADOBE_PLATFORM_BEOS 1 59 | 60 | #elif defined(__IBMCPP__) 61 | // IBM 62 | #define ADOBE_PLATFORM_AIX 1 63 | 64 | #elif defined(__amigaos__) 65 | // AmigaOS 66 | #define ADOBE_PLATFORM_AMIGA 1 67 | 68 | #elif defined(sun) || defined(__sun) 69 | // Solaris 70 | #define ADOBE_PLATFORM_SOLARIS 1 71 | 72 | #elif defined(__sgi) 73 | // SGI Irix 74 | #define ADOBE_PLATFORM_IRIX 1 75 | 76 | #elif defined(__hpux) 77 | // HP Unix 78 | #define ADOBE_PLATFORM_HPUX 1 79 | 80 | #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) 81 | // BSD 82 | #define ADOBE_PLATFORM_BSD 1 83 | 84 | #elif defined(linux) || defined(__linux) || defined(__linux__) 85 | // Linux 86 | #define ADOBE_PLATFORM_LINUX 1 87 | 88 | #elif defined(unix) || defined(__unix) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE) 89 | // Generic Unix 90 | #define ADOBE_PLATFORM_UNIX 1 91 | 92 | #else 93 | // Unknown 94 | #error "Unknown platform - please configure and report the results to stlab.adobe.com" 95 | 96 | #endif 97 | 98 | /*************************************************************************************************/ 99 | 100 | #if defined __GNUC__ 101 | 102 | // GNU C++: (including Darwin) 103 | #ifndef ADOBE_COMPILER_GCC 104 | #define ADOBE_COMPILER_GCC 1 105 | #endif 106 | 107 | #endif 108 | 109 | /*************************************************************************************************/ 110 | 111 | #endif 112 | 113 | /*************************************************************************************************/ 114 | -------------------------------------------------------------------------------- /flexcore/3rdparty/adobe/readme.txt: -------------------------------------------------------------------------------- 1 | From Adobe Source Libraries 2 | https://github.com/stlab/adobe_source_libraries.git 3 | Cloned on Dec. 16, 2015 4 | 3eb57ef5699a53b0f5a20775bbd997bd4289e766 5 | -------------------------------------------------------------------------------- /flexcore/3rdparty/adobe/reverse.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013 Adobe 3 | Distributed under the Boost Software License, Version 1.0. 4 | (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | */ 6 | /*************************************************************************************************/ 7 | 8 | #ifndef ADOBE_ALGORITHM_REVERSE_HPP 9 | #define ADOBE_ALGORITHM_REVERSE_HPP 10 | 11 | #include "config.hpp" 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include "set_next.hpp" 18 | 19 | #include 20 | #include 21 | 22 | /*************************************************************************************************/ 23 | 24 | namespace adobe { 25 | 26 | /*************************************************************************************************/ 27 | /*! 28 | \defgroup reverse reverse 29 | \ingroup mutating_algorithm 30 | 31 | \see 32 | - STL documentation for \ref stldoc_reverse 33 | - STL documentation for \ref stldoc_reverse_copy 34 | */ 35 | /*************************************************************************************************/ 36 | 37 | namespace unsafe { 38 | 39 | /*************************************************************************************************/ 40 | 41 | /*! 42 | \ingroup node_algorithm 43 | */ 44 | template // I models NodeIterator 45 | I reverse_append(I first, I last, I result) { 46 | while (first != last) { 47 | I prior(first); 48 | ++first; 49 | adobe::unsafe::set_next(prior, result); 50 | result = prior; 51 | } 52 | return result; 53 | } 54 | 55 | /*! 56 | \ingroup node_algorithm 57 | */ 58 | template 60 | // I models NodeIterator 61 | inline I reverse_append(R& range, I result) { 62 | return adobe::unsafe::reverse_append(boost::begin(range), boost::end(range), result); 63 | } 64 | 65 | /*! 66 | \ingroup node_algorithm 67 | */ 68 | template // I models NodeIterator 69 | inline I reverse_nodes(I first, I last) { 70 | return adobe::unsafe::reverse_append(first, last, last); 71 | } 72 | 73 | /*! 74 | \ingroup node_algorithm 75 | */ 76 | template // R models NodeRange 77 | inline typename boost::range_iterator::type reverse_nodes(R& range) { 78 | return adobe::unsafe::reverse_nodes(boost::begin(range), boost::end(range)); 79 | } 80 | 81 | /*************************************************************************************************/ 82 | 83 | } // namspace unsafe 84 | 85 | /*************************************************************************************************/ 86 | /*! 87 | \ingroup reverse 88 | 89 | \brief reverse implementation 90 | */ 91 | template 92 | inline void reverse(BidirectionalRange& range) { 93 | std::reverse(boost::begin(range), boost::end(range)); 94 | } 95 | 96 | /*! 97 | \ingroup reverse 98 | 99 | \brief reverse implementation 100 | */ 101 | template 102 | inline void reverse_copy(BidirectionalRange& range, OutputIterator result) { 103 | std::reverse_copy(boost::begin(range), boost::end(range), result); 104 | } 105 | 106 | /*! 107 | \ingroup reverse 108 | 109 | \brief reverse implementation 110 | */ 111 | template 112 | inline void reverse_copy(const BidirectionalRange& range, OutputIterator result) { 113 | std::reverse_copy(boost::begin(range), boost::end(range), result); 114 | } 115 | 116 | /*************************************************************************************************/ 117 | /*! 118 | \ingroup reverse 119 | 120 | \brief reverse implementation 121 | */ 122 | template // I models Bidirectional Iterator 123 | std::pair reverse_until(I f, I m, I l) { 124 | while (f != m && m != l) { 125 | --l; 126 | 127 | std::iter_swap(f, l); 128 | 129 | ++f; 130 | } 131 | 132 | return std::pair(f, l); 133 | } 134 | 135 | /*************************************************************************************************/ 136 | 137 | } // namespace adobe 138 | 139 | /*************************************************************************************************/ 140 | 141 | #endif 142 | 143 | /*************************************************************************************************/ 144 | -------------------------------------------------------------------------------- /flexcore/3rdparty/adobe/set_next.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013 Adobe 3 | Distributed under the Boost Software License, Version 1.0. 4 | (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | */ 6 | /*************************************************************************************************/ 7 | 8 | #ifndef ADOBE_ITERATOR_SET_NEXT_HPP 9 | #define ADOBE_ITERATOR_SET_NEXT_HPP 10 | 11 | #include "config.hpp" 12 | 13 | #include 14 | 15 | /*************************************************************************************************/ 16 | 17 | namespace adobe { 18 | 19 | namespace unsafe { 20 | 21 | /*! \addtogroup adobe_iterator 22 | @{ 23 | */ 24 | 25 | /* 26 | Example: 27 | 28 | template <> 29 | stuct set_next_fn 30 | { 31 | void operator()(T x, T y) const 32 | { 33 | //... 34 | } 35 | }; 36 | */ 37 | 38 | template // I models NodeIterator 39 | struct set_next_fn; // Must be specialized 40 | 41 | /*************************************************************************************************/ 42 | 43 | template // I models NodeIterator 44 | inline void set_next(I x, I y) { 45 | set_next_fn()(x, y); 46 | } 47 | 48 | /*************************************************************************************************/ 49 | 50 | /* 51 | location: a valid forward node iterator 52 | first and last - two valid node iterators 53 | 54 | postcondition: location->first...last->next(location) 55 | */ 56 | 57 | template // T models ForwardNodeIterator 58 | inline void splice_node_range(I location, I first, I last) { 59 | I successor(boost::next(location)); 60 | set_next(location, first); 61 | set_next(last, successor); 62 | } 63 | 64 | template // I models ForwardNodeIterator 65 | inline void skip_next_node(I location) { 66 | set_next(location, boost::next(boost::next(location))); 67 | } 68 | 69 | template // I models BidirectionalNodeIterator 70 | inline void skip_node(I location) { 71 | set_next(boost::prior(location), boost::next(location)); 72 | } 73 | 74 | //!@} 75 | 76 | /*************************************************************************************************/ 77 | 78 | } // namespace unsafe 79 | 80 | } // namespace adobe 81 | 82 | /*************************************************************************************************/ 83 | 84 | // ADOBE_ITERATOR_SET_NEXT_HPP 85 | #endif 86 | -------------------------------------------------------------------------------- /flexcore/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_POLICY( SET CMP0028 NEW ) # Alias/Namespace targets 2 | 3 | # creates the executable 4 | ADD_LIBRARY( flexcore 5 | infrastructure.cpp 6 | extended/graph/graph.cpp 7 | utils/logging/logger.cpp 8 | utils/demangle.cpp 9 | extended/base_node.cpp 10 | extended/visualization/visualization.cpp 11 | scheduler/clock.cpp 12 | scheduler/cyclecontrol.cpp 13 | scheduler/parallelregion.cpp 14 | scheduler/parallelscheduler.cpp 15 | scheduler/serialschedulers.cpp ) 16 | 17 | TARGET_COMPILE_OPTIONS( flexcore 18 | PUBLIC "-std=c++1y" ) 19 | TARGET_INCLUDE_DIRECTORIES( flexcore PUBLIC 20 | $ 21 | $ 22 | $ 23 | $ 24 | $ 25 | ) 26 | 27 | IF( FLEXCORE_ENABLE_COVERAGE_ANALYSIS ) 28 | TARGET_LINK_LIBRARIES( flexcore gcov ) 29 | ENDIF() 30 | TARGET_LINK_LIBRARIES( flexcore 31 | Boost::boost 32 | Boost::log 33 | Threads::Threads 34 | ) 35 | 36 | INCLUDE(GNUInstallDirs) 37 | INCLUDE(CMakePackageConfigHelpers) 38 | 39 | CONFIGURE_PACKAGE_CONFIG_FILE(flexcore-config.cmake.in flexcore-config.cmake 40 | INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/flexcore 41 | PATH_VARS CMAKE_INSTALL_INCLUDEDIR CMAKE_INSTALL_LIBDIR) 42 | 43 | INSTALL( TARGETS flexcore 44 | EXPORT flexcore-targets 45 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 46 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) 47 | INSTALL( EXPORT flexcore-targets 48 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/flexcore ) 49 | INSTALL( FILES ${CMAKE_CURRENT_BINARY_DIR}/flexcore-config.cmake ../cmake/FindBoost.cmake 50 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/flexcore ) 51 | INSTALL( DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ 52 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/flexcore 53 | FILES_MATCHING 54 | PATTERN "3rdparty/cereal/*" EXCLUDE 55 | PATTERN "*.hpp" ) 56 | INSTALL( DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/cereal/include/ 57 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/flexcore/3rdparty 58 | FILES_MATCHING PATTERN "*.hpp" 59 | PATTERN "*.h" ) 60 | 61 | -------------------------------------------------------------------------------- /flexcore/core/connectables.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SRC_CORE_CONNECTABLES_HPP_ 2 | #define SRC_CORE_CONNECTABLES_HPP_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace fc 9 | { 10 | 11 | /** 12 | * \defgroup connectables connectables 13 | * \brief A collection of different useful connectables. 14 | * 15 | * A lot of them are names for simple lambdas to make code less verbose. 16 | */ 17 | 18 | /** 19 | * \addtogroup connectables 20 | * @{ 21 | */ 22 | 23 | /// Increments input using prefix operator ++. 24 | struct increment 25 | { 26 | template 27 | constexpr auto operator()(T in) const { return ++in; } 28 | }; 29 | /// Decrements input using prefix operator --. 30 | struct decrement 31 | { 32 | template 33 | constexpr auto operator()(T in) const { return --in; } 34 | }; 35 | /// Returns input unchanged. 36 | struct identity 37 | { 38 | template 39 | constexpr T operator()(T in) const { return in; } 40 | }; 41 | /// Adds a constant addend to inputs. 42 | template 43 | auto add(const T summand) 44 | { 45 | return [summand](auto in){ return in + summand; }; 46 | } 47 | 48 | /// Subtracts a constant subtrahend from inputs. 49 | template 50 | auto subtract (const T subtrahend) 51 | { 52 | return [subtrahend](auto in){ return in - subtrahend; }; 53 | } 54 | 55 | /// Multiples input by a constant factor. (aka gain) 56 | template 57 | auto multiply(const T factor) 58 | { 59 | return [factor](auto in) { return factor * in; }; 60 | } 61 | 62 | /// Divides inputs by a constant divisor. 63 | template 64 | auto divide(const T divisor) 65 | { 66 | return [divisor](auto in) { return in / divisor; }; 67 | } 68 | 69 | /// Returns absolute value on input using std::abs. 70 | struct absolute 71 | { 72 | template 73 | constexpr auto operator()(const T& in) const { return std::abs(in); } 74 | }; 75 | 76 | /// Negates input using unary -. 77 | struct negate 78 | { 79 | template 80 | constexpr auto operator()(const T& in) const { return -in; } 81 | }; 82 | 83 | /// Returns logical not (operator !) of input. 84 | struct logical_not 85 | { 86 | template 87 | constexpr auto operator()(const T& in) const { return !in; } 88 | }; 89 | 90 | /** 91 | * \brief Clamps input to closed range [min, max]. 92 | * 93 | * \pre min <= max 94 | * \post output >= min && output <= max 95 | */ 96 | template 97 | auto clamp(U min, V max) 98 | { 99 | assert(min <= max); 100 | return [min, max](auto in) 101 | { 102 | return in < min ? min: (max < in ? max : in); 103 | }; 104 | } 105 | 106 | /** 107 | * \brief State Source, which returns a given constant every time it is called. 108 | * 109 | * \pre constant value needs to fulfill copy_constructible. 110 | */ 111 | template 112 | auto constant(T x) 113 | { 114 | return [x](){ return x; }; 115 | } 116 | 117 | namespace detail 118 | { 119 | template 120 | struct tee_op 121 | { 122 | template 123 | auto operator()(data_t&& in) -> data_t 124 | { 125 | // call callback with const_ref to make sure it cannot change token 126 | // But token can still be move_only 127 | const auto& temp_ref = in; 128 | callback(temp_ref); 129 | 130 | return std::forward(in); 131 | } 132 | 133 | T callback; 134 | }; 135 | } 136 | 137 | /** 138 | * \brief Calls a given callback and then returns value every time it is called. 139 | * \param op callback which is called before forwarding tokens 140 | * \pre op needs to fulfill copy_constructible or move_constructible. 141 | */ 142 | template 143 | auto tee(T&& op) 144 | { 145 | return detail::tee_op{std::forward(op)}; 146 | } 147 | 148 | /** 149 | * \brief Event_sink, which prints all incoming tokens to given stream. 150 | * 151 | * Ends every token with a new_line character. 152 | * Use this together with tee to print tokens in chains. 153 | * source >> tee(print(std::cout)) >> sink; 154 | * 155 | * \param stream Results are printed to this using operator <<. 156 | * print does not take ownership of stream. 157 | */ 158 | template 159 | auto print(T& stream) 160 | { 161 | return [&](auto in) 162 | { 163 | stream << in << '\n'; 164 | }; 165 | } 166 | 167 | /** @} doxygen group connectables */ 168 | 169 | } // namespace fc 170 | 171 | #endif /* SRC_CORE_CONNECTABLES_HPP_ */ 172 | -------------------------------------------------------------------------------- /flexcore/core/connection_util.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SRC_PORTS_CONNECTION_UTIL_HPP_ 2 | #define SRC_PORTS_CONNECTION_UTIL_HPP_ 3 | 4 | #include 5 | 6 | #include 7 | 8 | namespace fc 9 | { 10 | 11 | /** 12 | * \brief generalization of get_source 13 | * Stopping criterion for recursion 14 | */ 15 | template 16 | constexpr auto get_source(T& s) 17 | -> std::enable_if_t(0), decltype(s)> 18 | { 19 | return s; 20 | } 21 | /** 22 | * \brief recursively extracts the source of a connection 23 | */ 24 | template 25 | constexpr auto get_source(T& c) 26 | -> std::enable_if_t(0), 27 | decltype(get_source(c.source))> 28 | { 29 | return get_source(c.source); 30 | } 31 | 32 | /** 33 | * \brief generalization of get_sink 34 | * Stopping criterion for recursion 35 | */ 36 | template 37 | constexpr auto get_sink(T& s) 38 | -> std::enable_if_t(0), decltype(s)> 39 | { 40 | return s; 41 | } 42 | 43 | /** 44 | * \brief recursively extracts the sink of an object with member "sink" 45 | */ 46 | template 47 | constexpr auto get_sink(T& c) 48 | -> std::enable_if_t(0), 49 | decltype(get_sink(c.sink))> 50 | { 51 | return get_sink(c.sink); 52 | } 53 | 54 | }// namespace fc 55 | #endif /* SRC_PORTS_CONNECTION_UTIL_HPP_ */ 56 | -------------------------------------------------------------------------------- /flexcore/core/detail/connection_utils.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SRC_CORE_DETAIL_CONNECTION_UTILS_HPP_ 2 | #define SRC_CORE_DETAIL_CONNECTION_UTILS_HPP_ 3 | 4 | namespace fc 5 | { 6 | 7 | template 8 | struct connection; 9 | 10 | namespace detail 11 | { 12 | 13 | template 14 | struct apply_helper; 15 | template 16 | void apply(functor, conn_t&); 17 | 18 | template 19 | struct apply_helper> 20 | { 21 | template 22 | void operator()(functor f, connection& conn) const 23 | { 24 | apply(f, conn.source); 25 | apply(f, conn.sink); 26 | } 27 | }; 28 | 29 | template 30 | struct apply_helper 31 | { 32 | template 33 | void operator()(functor f, conn_t& conn) const 34 | { 35 | f(conn); 36 | } 37 | }; 38 | 39 | template 40 | void apply(functor f, conn_t& conn) 41 | { 42 | apply_helper()(f, conn); 43 | } 44 | 45 | } // namespace fc 46 | } // namespace detail 47 | 48 | #endif // SRC_CORE_DETAIL_CONNECTION_HPP_ 49 | -------------------------------------------------------------------------------- /flexcore/core/exceptions.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SRC_CORE_EXCEPTIONS_HPP_ 2 | #define SRC_CORE_EXCEPTIONS_HPP_ 3 | 4 | #include 5 | 6 | namespace fc 7 | { 8 | 9 | /** 10 | * \brief exception which signals errors in the structure of the dataflow graph. 11 | * 12 | * These errors are not meant to be caught during execution of the graph. 13 | * It might be possible to catch them during construction of the graph. 14 | */ 15 | class bad_structure : public std::runtime_error 16 | { 17 | public: 18 | using runtime_error::runtime_error; 19 | }; 20 | 21 | /// Exception which signals that a port is called but not connected. 22 | class not_connected : public bad_structure 23 | { 24 | public: 25 | using bad_structure::bad_structure; 26 | }; 27 | 28 | 29 | 30 | } // namespace fc 31 | 32 | #endif /* SRC_CORE_EXCEPTIONS_HPP_ */ 33 | -------------------------------------------------------------------------------- /flexcore/core/tuple_meta.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * tuple_meta.hpp 3 | * 4 | * Created on: Nov 4, 2015 5 | * Author: ckielwein 6 | */ 7 | 8 | #ifndef SRC_CORE_TUPLE_META_HPP_ 9 | #define SRC_CORE_TUPLE_META_HPP_ 10 | 11 | #include 12 | #include 13 | 14 | namespace fc 15 | { 16 | 17 | /** 18 | * \brief Selection of metafunctions using std::tuple. 19 | * Basically a very minimal subset of boost::fusion 20 | */ 21 | namespace tuple 22 | { 23 | 24 | namespace detail 25 | { 26 | template 27 | constexpr auto make_index_sequence() 28 | { 29 | return std::make_index_sequence>::value>{}; 30 | } 31 | 32 | template 34 | constexpr decltype(auto) binary_invoke_helper(lhs_tuple&& lsh, 35 | rhs_tuple&& rhs, 36 | std::index_sequence, 37 | operation&& op) 38 | { 39 | return std::make_tuple(op( 40 | std::get(std::forward(lsh)), 41 | std::get(std::forward(rhs)))...); 42 | } 43 | 44 | template 46 | 47 | constexpr decltype(auto) unary_invoke_helper(lhs_tuple&& lsh, 48 | std::index_sequence, 49 | operation&& op) 50 | { 51 | return std::make_tuple(op(std::get(std::forward(lsh)))...); 52 | } 53 | 54 | template 55 | constexpr decltype(auto) invoke_function_helper( 56 | operation&& op, tuple&& tup, std::index_sequence) 57 | { 58 | return op(std::get(std::forward(tup))...); 59 | } 60 | } //namespace detail 61 | 62 | ///applies function to every element in tuple 63 | template 64 | constexpr void for_each(tuple&& tup, operation&& op) 65 | { 66 | unary_invoke_helper(std::forward(tup), detail::make_index_sequence(), op); 67 | } 68 | 69 | ///transform, returns tuple of transformed elements 70 | template 71 | constexpr decltype(auto) transform(tuple&& tup, const operation& op) 72 | { 73 | return detail::unary_invoke_helper(std::forward(tup), 74 | detail::make_index_sequence(), op); 75 | } 76 | ///binary_transform, returns tuple of results of bin_op on elements of first and second tuple 77 | template 78 | constexpr decltype(auto) transform(first_tuple&& first, second_tuple&& second, const operation& op) 79 | { 80 | static_assert(std::tuple_size>::value == 81 | std::tuple_size>::value, 82 | "Binary tuple transform needs tuples to have same nr of elements."); 83 | 84 | return detail::binary_invoke_helper(std::forward(first), 85 | std::forward(second), 86 | detail::make_index_sequence(), op); 87 | } 88 | 89 | ///Helper function to call variadic functions with tuple 90 | template 91 | constexpr decltype(auto) invoke_function(operation&& op, tuple&& tup) 92 | { 93 | return detail::invoke_function_helper( 94 | std::forward(op), 95 | std::forward(tup), 96 | detail::make_index_sequence()); 97 | } 98 | 99 | } // namespace tuple 100 | } // namespace fc 101 | 102 | #endif /* SRC_CORE_TUPLE_META_HPP_ */ 103 | -------------------------------------------------------------------------------- /flexcore/extended/base_node.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | namespace fc 9 | { 10 | 11 | static forest_t::iterator find_self(forest_t& forest, const tree_node& node) 12 | { 13 | auto node_id = node.graph_info().get_id(); 14 | auto self = std::find_if(forest.begin(), forest.end(), 15 | [=](auto& other_uniq_ptr) { return node_id == other_uniq_ptr->graph_info().get_id(); }); 16 | assert(self != forest.end()); 17 | return adobe::trailing_of(self); 18 | } 19 | 20 | std::string full_name(forest_t& forest, const tree_node& node) 21 | { 22 | auto position = find_self(forest, node); 23 | // push names of parent / grandparent ... to stack to later reverse order. 24 | std::stack name_stack; 25 | for (auto parent = adobe::find_parent(position); parent != forest.end(); 26 | parent = adobe::find_parent(parent)) 27 | { 28 | name_stack.emplace((*parent)->name()); 29 | } 30 | std::string full_name; 31 | while (!name_stack.empty()) 32 | { 33 | full_name += (name_stack.top() + name_seperator); 34 | name_stack.pop(); 35 | } 36 | full_name += (*position)->name(); 37 | return full_name; 38 | } 39 | 40 | tree_base_node::tree_base_node(const node_args& args) 41 | : fg_(args.fg), region_(args.r), graph_info_(args.graph_info) 42 | { 43 | assert(region_); 44 | } 45 | 46 | std::string tree_base_node::name() const 47 | { 48 | return graph_info_.name(); 49 | } 50 | 51 | graph::graph_node_properties tree_base_node::graph_info() const 52 | { 53 | return graph_info_; 54 | } 55 | 56 | graph::connection_graph& tree_base_node::get_graph() 57 | { 58 | return fg_.graph; 59 | } 60 | 61 | forest_t::iterator owning_base_node::self() const 62 | { 63 | return self_; 64 | } 65 | 66 | forest_t::iterator owning_base_node::add_child(std::unique_ptr child) 67 | { 68 | assert(child); 69 | auto& forest = fg_.forest; 70 | auto child_it = adobe::trailing_of(forest.insert(self(), std::move(child))); 71 | assert(adobe::find_parent(child_it) == self()); 72 | assert(adobe::find_parent(child_it) != forest.end()); 73 | return child_it; 74 | } 75 | 76 | node_args owning_base_node::new_node(node_args args) 77 | { 78 | const auto proxy_iter = add_child(std::make_unique(args)); 79 | args.self = proxy_iter; 80 | return args; 81 | } 82 | 83 | forest_owner::forest_owner( 84 | graph::connection_graph& graph, std::string n, std::shared_ptr r) 85 | : fg_(std::make_unique(graph)) 86 | , tree_root(nullptr) 87 | , viz_(std::make_unique(fg_->graph, fg_->forest)) 88 | { 89 | assert(r); 90 | assert(fg_); 91 | auto& forest = fg_->forest; 92 | auto args = node_args{*fg_, std::move(r), std::move(n)}; 93 | //first place a proxy node in the forest to create tree_node 94 | const auto iter = adobe::trailing_of( 95 | forest.insert(forest.begin(), std::make_unique(args))); 96 | args.self = iter; 97 | 98 | // replace proxy with actual node 99 | *iter = std::make_unique(args); 100 | 101 | tree_root = dynamic_cast(iter->get()); 102 | assert(tree_root); 103 | assert(fg_); 104 | assert(viz_); 105 | } 106 | 107 | forest_owner::~forest_owner() 108 | { 109 | } 110 | 111 | void forest_owner::visualize(std::ostream& out) const 112 | { 113 | assert(viz_); 114 | viz_->visualize(out); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /flexcore/extended/graph/traits.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SRC_GRAPH_TRAITS_HPP_ 2 | #define SRC_GRAPH_TRAITS_HPP_ 3 | 4 | namespace fc 5 | { 6 | namespace graph 7 | { 8 | 9 | ///checks if a type has a member called graph_info 10 | template 11 | constexpr auto has_graph_info(int) -> decltype(std::declval().graph_info, bool()) 12 | { 13 | return true; 14 | } 15 | 16 | template 17 | constexpr bool has_graph_info(...) 18 | { 19 | return false; 20 | } 21 | 22 | 23 | } // namespace graph 24 | } // namespace fc 25 | 26 | #endif /* SRC_GRAPH_TRAITS_HPP_ */ 27 | -------------------------------------------------------------------------------- /flexcore/extended/node_fwd.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SRC_EXTENDED_NODE_FWD_HPP_ 2 | #define SRC_EXTENDED_NODE_FWD_HPP_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace fc 10 | { 11 | /** 12 | * \brief Interface for all nodes (whether part of forest+graph or only graph) 13 | * 14 | * This interface is required to construct node_aware_mixin ports. 15 | */ 16 | class node 17 | { 18 | public: 19 | virtual ~node() = default; 20 | virtual graph::graph_node_properties graph_info() const = 0; 21 | virtual graph::connection_graph& get_graph() = 0; 22 | virtual std::shared_ptr region() = 0; 23 | virtual std::string name() const = 0; 24 | }; 25 | } // namespace fc 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /flexcore/extended/nodes/region_worker_node.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * triggered_node.hpp 3 | * 4 | * Created on: Feb 24, 2016 5 | * Author: jschwan 6 | */ 7 | 8 | #ifndef SRC_NODES_REGION_WORKER_NODE_HPP_ 9 | #define SRC_NODES_REGION_WORKER_NODE_HPP_ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | namespace fc 16 | { 17 | 18 | /** 19 | * \brief node which executes action on work tick of given region 20 | * 21 | * region_worker_node is a convenient way to have nodes automatically work on work tick. 22 | * Extend this class for your own worker nodes. 23 | * \ingroup nodes 24 | */ 25 | class region_worker_node : public tree_base_node 26 | { 27 | public: 28 | template 29 | region_worker_node(action_t&& action, const node_args& node) 30 | : tree_base_node(node) 31 | { 32 | region()->work_tick() >> std::forward(action); 33 | } 34 | 35 | }; 36 | 37 | 38 | } //namespace fc 39 | #endif /* SRC_NODES_REGION_WORKER_NODE_HPP_ */ 40 | -------------------------------------------------------------------------------- /flexcore/extended/nodes/terminal.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * terminal.hpp 3 | * 4 | * Created on: Apr 7, 2016 5 | * Author: ckielwein 6 | */ 7 | 8 | #ifndef FLEXCORE_EXTENDED_NODES_TERMINAL_HPP_ 9 | #define FLEXCORE_EXTENDED_NODES_TERMINAL_HPP_ 10 | 11 | #include 12 | 13 | namespace fc 14 | { 15 | 16 | /** 17 | * \brief Node that relays state from input port to output port. 18 | * 19 | * A state_terminal is useful to form the border of external connections 20 | * and internal connections in a compound node which nests other nodes. 21 | * 22 | * \tparam T type of data transmitted through terminal node. 23 | * \tparam base_node base_node to either include or exclude this node from forest. 24 | * Instantiate terminal with either pure_node or tree_base_node. 25 | * \ingroup nodes 26 | */ 27 | template 28 | class state_terminal : public base_node 29 | { 30 | public: 31 | /** 32 | * \brief Constructor which forwards arguments to base_node 33 | * \param args Arguments for Base node. In the default case this is: 34 | * \code {const node_args&} \endcode 35 | */ 36 | template 37 | explicit state_terminal(base_args&&... args) 38 | : base_node(std::forward(args)...) 39 | , in_state(this) 40 | , out_state(this, [this](){ return in_state.get(); }) 41 | { 42 | } 43 | 44 | /// State sink of type T 45 | auto& in() { return in_state; } 46 | /// State source of type T 47 | auto& out() { return out_state; } 48 | 49 | private: 50 | typename base_node::template state_sink in_state; 51 | typename base_node::template state_source out_state; 52 | }; 53 | 54 | /** 55 | * \brief Node that relays events from input port to output port. 56 | * 57 | * An event_terminal is useful to form the border of external connections 58 | * and internal connections in a compound node which nests other nodes. 59 | * 60 | * \tparam T type of data transmitted through terminal node. 61 | * \tparam base_node base_node to either include or exclude this node from forest. 62 | * Instantiate terminal with either pure_node or tree_base_node. 63 | * \ingroup nodes 64 | */ 65 | template 66 | class event_terminal : public base_node 67 | { 68 | public: 69 | /** 70 | * \brief Constructor which forwards arguments to base_node 71 | * \param args Arguments for Base node. In the default case this is: 72 | * \code {const node_args&} \endcode 73 | */ 74 | template 75 | explicit event_terminal(base_args&&... args) 76 | : base_node(std::forward(args)...) 77 | , in_event(this, 78 | //variadic lambda to also handle void events 79 | [this](auto&&... in){ out_event.fire(std::forward(in)...);}) 80 | , out_event(this) 81 | { 82 | } 83 | 84 | /// Event sink of type T 85 | auto& in() { return in_event; } 86 | /// Event source of type T 87 | auto& out() { return out_event; } 88 | 89 | private: 90 | typename base_node::template event_sink in_event; 91 | typename base_node::template event_source out_event; 92 | }; 93 | 94 | } // namespace fc 95 | 96 | #endif /* FLEXCORE_EXTENDED_NODES_TERMINAL_HPP_ */ 97 | -------------------------------------------------------------------------------- /flexcore/extended/ports/token_tags.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SRC_PORTS_TOKEN_TAGS_HPP_ 2 | #define SRC_PORTS_TOKEN_TAGS_HPP_ 3 | 4 | #include 5 | 6 | namespace fc 7 | { 8 | 9 | /// tag to specify that template uses events 10 | struct event_tag {}; 11 | /// tag to specify that template uses states 12 | struct state_tag {}; 13 | 14 | namespace pure 15 | { 16 | 17 | /// template input port, tag object creates either event_sink or state_sink 18 | template 19 | struct in_port; 20 | 21 | ///in_port for events is event_sink 22 | template 23 | struct in_port 24 | { 25 | using type = pure::event_sink; 26 | }; 27 | 28 | /// in_port for state is state_sink 29 | template 30 | struct in_port 31 | { 32 | using type = pure::state_sink; 33 | }; 34 | 35 | /// template output port, tag object creates either event_source or state_source 36 | template 37 | struct out_port; 38 | 39 | /// out_port for events is event_source 40 | template 41 | struct out_port 42 | { 43 | using type = pure::event_source; 44 | }; 45 | 46 | /// out_port for state is state_source 47 | template 48 | struct out_port 49 | { 50 | using type = pure::state_source; 51 | }; 52 | 53 | } // namespace pure 54 | } // namespace fc 55 | 56 | #endif /* SRC_PORTS_TOKEN_TAGS_HPP_ */ 57 | -------------------------------------------------------------------------------- /flexcore/extended/visualization/visualization.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SRC_VISUALIZATION_HPP_ 2 | #define SRC_VISUALIZATION_HPP_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | namespace fc 10 | { 11 | 12 | namespace graph{ class connection_graph; } 13 | 14 | /** 15 | * \brief Prints the flexcore graph in graphviz format to a stream. 16 | * 17 | * The printed graph consists of the connection_graph and the ownership forest. 18 | * nodes are colored by the region they belong to and ports are modeled as graphviz ports. 19 | */ 20 | class visualization 21 | { 22 | public: 23 | ///Constructs the visualizer with referenzes to graph and forest 24 | visualization(const graph::connection_graph& graph, const forest_t& forest); 25 | ~visualization(); 26 | 27 | ///Prints graphviz format to a given stream 28 | void visualize(std::ostream& stream); 29 | private: 30 | struct impl; 31 | std::unique_ptr pimpl; 32 | }; 33 | } 34 | 35 | #endif /* SRC_VISUALIZATION_HPP_ */ 36 | -------------------------------------------------------------------------------- /flexcore/flexcore-config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | INCLUDE( CMakeFindDependencyMacro ) 4 | SET(THREADS_PREFER_PTHREAD_FLAG @THREADS_PREFER_PTHREAD_FLAG@) 5 | FIND_DEPENDENCY( Threads ) 6 | SET(CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) 7 | FIND_PACKAGE( Boost 1.55 REQUIRED COMPONENTS log ) 8 | 9 | INCLUDE( ${CMAKE_CURRENT_LIST_DIR}/flexcore-targets.cmake ) 10 | 11 | SET_AND_CHECK( flexcore_INCLUDE_DIR "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@" ) 12 | SET_AND_CHECK( flexcore_LIBRARY_DIR "@PACKAGE_CMAKE_INSTALL_LIBDIR@" ) 13 | SET( flexcore_LIBRARY flexcore ) 14 | GET_PROPERTY( flexcore_COMPILE_OPTIONS TARGET flexcore PROPERTY INTERFACE_COMPILE_OPTIONS ) 15 | 16 | -------------------------------------------------------------------------------- /flexcore/infrastructure.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | namespace fc 7 | { 8 | namespace detail { 9 | class scheduled_region : public fc::parallel_region 10 | { 11 | public: 12 | scheduled_region(std::string name, virtual_clock::steady::duration tick_rate, 13 | std::weak_ptr region_maker) 14 | : parallel_region(std::move(name), tick_rate), region_maker(std::move(region_maker)) 15 | { 16 | } 17 | std::shared_ptr 18 | new_region(std::string name, virtual_clock::steady::duration tick_rate) const override; 19 | 20 | private: 21 | std::weak_ptr region_maker; 22 | }; 23 | 24 | /** \brief Factory for creating regions and connecting them to the scheduler 25 | * 26 | * \pre region_factory needs to be held as shared_ptr. 27 | */ 28 | class region_factory : public std::enable_shared_from_this 29 | { 30 | public: 31 | explicit region_factory(thread::cycle_control& scheduler) : scheduler(scheduler) {} 32 | 33 | /// Creates a new region and connects it to the scheduler with a periodic task. 34 | std::shared_ptr new_region(const std::string& name, 35 | const virtual_clock::steady::duration& tick_rate); 36 | 37 | private: 38 | thread::cycle_control& scheduler; 39 | }; 40 | 41 | std::shared_ptr 42 | scheduled_region::new_region(std::string name, virtual_clock::steady::duration tick_rate) const 43 | { 44 | if (auto factory = region_maker.lock()) 45 | return factory->new_region(std::move(name), tick_rate); 46 | else 47 | throw std::runtime_error{"Region factory has been destroyed already"}; 48 | } 49 | 50 | std::shared_ptr 51 | region_factory::new_region(const std::string& name, 52 | const virtual_clock::steady::duration& tick_rate) 53 | { 54 | auto region = std::make_shared(name, tick_rate, shared_from_this()); 55 | auto tick_cycle = fc::thread::periodic_task(region); 56 | scheduler.add_task(std::move(tick_cycle),tick_rate); 57 | return region; 58 | } 59 | } // namespace detail 60 | 61 | std::shared_ptr 62 | infrastructure::add_region(const std::string& name, 63 | const virtual_clock::steady::duration& tick_rate) 64 | { 65 | return region_maker->new_region(name, tick_rate); 66 | } 67 | 68 | infrastructure::infrastructure() 69 | : scheduler(std::make_unique()) 70 | , region_maker(std::make_shared(scheduler)) 71 | , graph() 72 | , forest_root(graph, "root", add_region("root_region", thread::cycle_control::medium_tick)) 73 | { 74 | } 75 | 76 | infrastructure::~infrastructure() 77 | { 78 | stop_scheduler(); 79 | } 80 | 81 | void infrastructure::iterate_main_loop() 82 | { 83 | using namespace std::chrono_literals; 84 | std::this_thread::sleep_for(0.5s); 85 | if (auto ex = scheduler.last_exception()) 86 | { 87 | std::rethrow_exception(ex); 88 | } 89 | } 90 | 91 | void infrastructure::infinite_main_loop() 92 | { 93 | while( true ) 94 | { 95 | iterate_main_loop(); 96 | } 97 | } 98 | 99 | } /* namespace fc */ 100 | -------------------------------------------------------------------------------- /flexcore/infrastructure.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SRC_INFRASTRUCTURE_HPP_ 2 | #define SRC_INFRASTRUCTURE_HPP_ 3 | 4 | #include 5 | #include 6 | 7 | namespace fc 8 | { 9 | namespace detail { 10 | class region_factory; 11 | } 12 | 13 | class infrastructure 14 | { 15 | public: 16 | infrastructure(); 17 | ~infrastructure(); 18 | 19 | std::shared_ptr add_region(const std::string& name, 20 | const virtual_clock::steady::duration& tick_rate); 21 | 22 | owning_base_node& node_owner() { return forest_root.nodes(); } 23 | graph::connection_graph& get_graph() { return graph; } 24 | void visualize(std::ostream& out) { forest_root.visualize(out); } 25 | void infinite_main_loop(); 26 | void start_scheduler() { scheduler.start(); } 27 | void stop_scheduler() { scheduler.stop(); } 28 | void iterate_main_loop(); 29 | 30 | private: 31 | thread::cycle_control scheduler; 32 | std::shared_ptr region_maker; 33 | graph::connection_graph graph; 34 | forest_owner forest_root; 35 | }; 36 | 37 | } /* namespace fc */ 38 | 39 | #endif /* SRC_INFRASTRUCTURE_HPP_ */ 40 | -------------------------------------------------------------------------------- /flexcore/ports.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SRC_PORTS_PORTS_HPP_ 2 | #define SRC_PORTS_PORTS_HPP_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace fc 11 | { 12 | 13 | /** 14 | * \brief mixin for ports, which makes them aware of parent node and available in graph. 15 | * 16 | * Use these ports together with tree_base_node and owning_base_node. 17 | * \ingroup ports 18 | */ 19 | template 20 | struct node_aware_mixin : graph::graph_connectable> 21 | { 22 | using base = graph::graph_connectable>; 23 | 24 | /** 25 | * \brief Constructs port with node_aware mixin. 26 | * \param node_ptr pointer to node which owns this port 27 | * \pre node_ptr != nullptr 28 | * \param base_constructor_args constructor arguments to underlying port. 29 | * These are forwarded to base 30 | */ 31 | template 32 | explicit node_aware_mixin(node* node_ptr, args&&... base_constructor_args) 33 | : base(node_ptr->get_graph(), node_ptr->graph_info(), 34 | *(node_ptr->region().get()), 35 | std::forward(base_constructor_args)...) 36 | { 37 | assert(node_ptr); 38 | } 39 | }; 40 | 41 | template struct is_active_sink> : is_active_sink {}; 42 | template struct is_active_source> : is_active_source {}; 43 | 44 | template 45 | using default_mixin = node_aware_mixin; 46 | 47 | /** 48 | * \brief Default event_sink port 49 | * \ingroup ports 50 | */ 51 | template 52 | using event_sink = default_mixin>; 53 | 54 | /** 55 | * \brief Default event_source port 56 | * \ingroup ports 57 | */ 58 | template 59 | using event_source = default_mixin>; 60 | 61 | /** 62 | * \brief Default state_sink port 63 | * \ingroup ports 64 | */ 65 | template 66 | using state_sink = default_mixin>; 67 | 68 | /** 69 | * \brief Default state_source port 70 | * \ingroup ports 71 | */ 72 | template 73 | using state_source = default_mixin>; 74 | 75 | // -- dispatch -- 76 | 77 | /// template input port, tag object creates either event_sink or state_sink 78 | template 79 | struct in_port 80 | { 81 | using type = default_mixin::type>; 82 | }; 83 | 84 | /// template output port, tag object creates either event_source or state_source 85 | template 86 | struct out_port 87 | { 88 | using type = default_mixin::type>; 89 | }; 90 | 91 | } // namespace fc 92 | 93 | #endif /* SRC_PORTS_PORTS_HPP_ */ 94 | -------------------------------------------------------------------------------- /flexcore/pure/detail/port_traits.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * port_traits.hpp 3 | * 4 | * Created on: Sep 30, 2015 5 | * Author: ckielwein 6 | */ 7 | 8 | #ifndef SRC_PORTS_PORT_TRAITS_HPP_ 9 | #define SRC_PORTS_PORT_TRAITS_HPP_ 10 | 11 | #include 12 | 13 | // A collection of port specific meta functions and traits. 14 | 15 | namespace fc 16 | { 17 | namespace detail 18 | { 19 | 20 | template 21 | struct handle_type 22 | { 23 | using type = std::function; // need rvalue ref here? 24 | }; 25 | 26 | template<> 27 | struct handle_type 28 | { 29 | using type = std::function; 30 | }; 31 | 32 | template