diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..3b6477a8 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,36 @@ +### Configuration +cmake_minimum_required (VERSION 2.6) + +project (CppReact) + +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wpedantic") + +include_directories ("${PROJECT_SOURCE_DIR}/include") + +### CppReact +add_library(CppReact + src/detail/graph_impl.cpp) + +target_link_libraries(CppReact tbb) + +### examples/ +option(build_examples "Build examples?" ON) +if(build_examples) + add_subdirectory(examples) +endif() + +### benchmarks/ +option(build_benchmarks "Build benchmarks?" OFF) +if(build_benchmarks) + add_subdirectory(benchmarks) +endif() + +### tests/ +option(build_tests "Build unit tests?" OFF) +if(build_tests) + add_subdirectory(tests) +endif() diff --git a/README.md b/README.md index fe6e8266..39bf846c 100644 --- a/README.md +++ b/README.md @@ -1,185 +1,40 @@ -## Introduction +# ![C++React](http://snakster.github.io/cpp.react//media/logo_banner3.png) -Cpp.React is an experimental [Reactive Programming](http://en.wikipedia.org/wiki/Reactive_programming) framework for C++11. Its purpose is to provide abstractions that simplify the implementation of reactive behaviour. +C++React is reactive programming library for C++14. It enables the declarative definition of data dependencies between state and event flows. +Based on these definitions, propagation of changes is handled automatically. -The general idea is that dependency relations between data/actions are expressed declarively, while the actual propagation of changes is handled automatically. The benefits: -* Reduction of boilerplate code. -* Updating is always consistent and glitch-free. -* Support for implicit parallelization of updates. +Here's a simple example: -#### Compiling - -I mainly tested the build on Windows with Visual Studio 2013. -The Intel C++ Compiler 14.0 with Visual Studio 2012/13 is theoretically supported as well, but it doesn't compile the current codebase anymore due to [some bugs]() with C++11 support. - -Cpp.React uses standard C++11 and the dependencies are portable, so other compilers/platforms should work, too. - -###### Dependencies -* [Intel TBB 4.2](https://www.threadingbuildingblocks.org/) (required) -* [Google test framework](https://code.google.com/p/googletest/) (optional, to compile the tests) -* [Boost C++ Libraries](http://www.boost.org/) (optional, to use ReactiveLoop, which requires boost::coroutine) - -## Features by example - -#### Signals - -Signals are time-varying reactive values, that can be combined to create reactive expressions. -These expressions are automatically recalculated whenever one of their dependent values changes. - -```C++ -#include "react/Signal.h" -///... -using namespace react; - -REACTIVE_DOMAIN(MyDomain); - -auto width = MyDomain::MakeVar(1); -auto height = MyDomain::MakeVar(2); - -auto area = width * height; - -cout << "area: " << area() << endl; // => area: 2 -width <<= 10; -cout << "area: " << area() << endl; // => area: 20 -``` - -For more information, see the [Signal guide](SignalGuide) - -#### Event streams - -Event streams represent flows of discrete values as first-class objects, based on ideas found in [Deprecating the Observer Pattern](http://infoscience.epfl.ch/record/176887/files/DeprecatingObservers2012.pdf). - -```C++ -#include "react/EventStream.h" -//... -using namespace react; - -REACTIVE_DOMAIN(MyDomain); - -auto leftClicked = MyDomain::MakeEventSource(); -auto rightClicked = MyDomain::MakeEventSource(); - -auto clicked = leftClicked | rightClicked; - -Observe(clicked, [] { cout << "button clicked!" << endl; }); -``` - -#### Implicit parallelism - -The change propagation is handled implicitly by a so called propagation engine. -Depending on the selected engine, independent propagation paths are automatically parallelized. -For more details, see Propagation Engines. - -```C++ -#include "react/propagation/TopoSortEngine.h" -//... -using namespace react; - -// Single-threaded updating -REACTIVE_DOMAIN(MyDomain, TopoSortEngine); - -// Parallel updating -REACTIVE_DOMAIN(MyDomain, TopoSortEngine); - -// Input from multiple threads -REACTIVE_DOMAIN(MyDomain, TopoSortEngine); -REACTIVE_DOMAIN(MyDomain, TopoSortEngine); - -// Parallel updating + input from multiple threads + pipelining -REACTIVE_DOMAIN(MyDomain, TopoSortEngine); ``` - -#### Reactive loops - -```C++ -#include "react/Reactor.h" -//... using namespace react; -REACTIVE_DOMAIN(D); - -using PointT = pair; -using PathT = vector; - -vector paths; - -auto mouseDown = D::MakeEventSource(); -auto mouseUp = D::MakeEventSource(); -auto mouseMove = D::MakeEventSource(); - -D::ReactiveLoopT loop -{ - [&] (D::ReactiveLoopT::Context& ctx) - { - PathT points; - - points.emplace_back(ctx.Await(mouseDown)); +void AddNumbers(int a, int b) { return a + b; } - ctx.RepeatUntil(mouseUp, [&] { - points.emplace_back(ctx.Await(mouseMove)); - }); +// Two state variable objects. You can change their values manually. +auto a = StateVar::Create(0); +auto b = StateVar::Create(0); - points.emplace_back(ctx.Await(mouseUp)); +// Another state object. Its value is calculated automatically based on the given function and arguments. +// If the arguments change, the value is re-calculated. +auto sum = State::Create(AddNumbers, a, b); - paths.push_back(points); - } -}; - -mouseDown << PointT(1,1); -mouseMove << PointT(2,2) << PointT(3,3) << PointT(4,4); -mouseUp << PointT(5,5); - -mouseMove << PointT(999,999); - -mouseDown << PointT(10,10); -mouseMove << PointT(20,20); -mouseUp << PointT(30,30); - -// => paths[0]: (1,1) (2,2) (3,3) (4,4) (5,5) -// => paths[1]: (10,10) (20,20) (30,30) +// sum == 0 +a.Set(21); +// sum == 21 +b.Set(21); +// sum == 42 ``` -#### Reactive objects and dynamic reactives - -```C++ -#include "react/ReactiveObject.h" -//... -REACTIVE_DOMAIN(D); - -using namespace react; - -class Company : public ReactiveObject -{ -public: - VarSignalT Name; - - Company(const char* name) : - Name{ MakeVar(string(name)) } - { - } +The underlying system constructs a dependency graph to collect which values are affected by a change, and in which order they have to be re-calculated. +This guarantees several properties: +- _correctness_ - no updates are forgotten; +- _consistency_ - no value is updated before all its incoming dependencies have been updated; +- _efficiency_ - no value is re-calculated more than once per update cycle, and changes are only propagated along paths where a new value is different from the old one. - inline bool operator==(const Company& other) const { /* ... */ } -}; +The system also knows when it's safe to update values in parallel from multiple threads, and it can do live profiling to decide when that's worthwhile. -class Manager : public ReactiveObject -{ - ObserverT nameObs; - -public: - VarRefSignalT CurrentCompany; - - Manager(initialCompany& company) : - CurrentCompany{ MakeVar(std::ref(company)) } - { - nameObs = REACTIVE_REF(CurrentCompany, Name).Observe([] (string name) { - cout << "Manager: Now managing " << name << endl; - }); - } -}; -``` +## Development status -### More examples +I'm currently in the process of rewriting the library more or less from scratch and many things are still broken or outdated. -* [Examples](https://github.com/schlangster/cpp.react/blob/master/src/sandbox/Main.cpp) -* [Benchmark](https://github.com/schlangster/cpp.react/blob/master/src/benchmark/BenchmarkLifeSim.h) -* [Test cases](https://github.com/schlangster/cpp.react/tree/master/src/test) +[The old, but stable and documented version is still available in this branch.](https://github.com/snakster/cpp.react/tree/legacy1) diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt new file mode 100644 index 00000000..b74640e9 --- /dev/null +++ b/benchmarks/CMakeLists.txt @@ -0,0 +1,4 @@ +### CppReactBenchmark + +add_executable(CppReactBenchmark src/Main.cpp) +target_link_libraries(CppReactBenchmark CppReact tbbmalloc_proxy) diff --git a/src/benchmark/BenchmarkBase.h b/benchmarks/src/BenchmarkBase.h similarity index 75% rename from src/benchmark/BenchmarkBase.h rename to benchmarks/src/BenchmarkBase.h index c8ed25f1..6684c062 100644 --- a/src/benchmark/BenchmarkBase.h +++ b/benchmarks/src/BenchmarkBase.h @@ -1,17 +1,20 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2017. // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once +#include #include +#include #include #include #include +#include -#include "react/common/Util.h" +#include "react/common/utility.h" /////////////////////////////////////////////////////////////////////////////////////////////////// // Get unique random numbers from range. @@ -52,18 +55,6 @@ inline const std::string CurrentDateTime() return buf; } -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// BenchmarkBase -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class BenchmarkBase -{ -public: - typedef D Domain; - - double Run(); -}; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// RunBenchmark /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -73,11 +64,8 @@ template typename TBenchmark, typename TParams > -void RunBenchmark(std::ostream& logfile, TBenchmark& b, TParams params) +void RunBenchmark(std::ostream& logfile, TBenchmark b, const TParams& params) { - std::cout << "Engine: " << typeid(TBenchmark::Domain::Policy::Engine).name() << std::endl << std::endl; - logfile << "Engine: " << typeid(TBenchmark::Domain::Policy::Engine).name() << std::endl << std::endl; - double sum = 0; double min = DBL_MAX; double max = DBL_MIN; @@ -86,7 +74,7 @@ void RunBenchmark(std::ostream& logfile, TBenchmark& b, TParams params) { double r = b.Run(params); std::cout << "\tRun " << i << ": " << r << std::endl; - logfile << "\tRun " << i << ": " << r << std::endl; + logfile << "\tRun " << i << ": " << r << std::endl; sum += r; @@ -114,9 +102,8 @@ void RunBenchmark(std::ostream& logfile, TBenchmark& b, TParams params) template < int RUN_COUNT, - template class TBenchmark, - typename TParams, - typename ... Ds + typename TBenchmark, + typename TParams > void RunBenchmarkClass(const char* name, std::ostream& out, const TParams& params) { @@ -128,8 +115,8 @@ void RunBenchmarkClass(const char* name, std::ostream& out, const TParams& param params.Print(out); out << ") =====" << std::endl << std::endl; - REACT_EXPAND_PACK(RunBenchmark(out, TBenchmark(), params)); + RunBenchmark(out, TBenchmark(), params); } -#define RUN_BENCHMARK(out, runCount, benchmarkClass, params, ...) \ - RunBenchmarkClass(#benchmarkClass, out, params) +#define RUN_BENCHMARK(out, runCount, benchmarkClass, params) \ + RunBenchmarkClass(#benchmarkClass, out, params) diff --git a/src/benchmark/BenchmarkFanout.h b/benchmarks/src/BenchmarkFanout.h similarity index 82% rename from src/benchmark/BenchmarkFanout.h rename to benchmarks/src/BenchmarkFanout.h index 5f379ea9..099f26d2 100644 --- a/src/benchmark/BenchmarkFanout.h +++ b/benchmarks/src/BenchmarkFanout.h @@ -1,21 +1,26 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2017. // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once +#if 0 + #ifndef CPP_REACT_BENCHMARK_FANOUT_H #define CPP_REACT_BENCHMARK_FANOUT_H -#include #include #include #include #include "BenchmarkBase.h" +#include "react/state.h" +/* +using namespace react; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Benchmark_Fanout /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -25,12 +30,7 @@ struct BenchmarkParams_Fanout N(n), K(k), Delay(delay) - { - } - - const int N; - const int K; - const int Delay; + {} void Print(std::ostream& out) const { @@ -38,21 +38,23 @@ struct BenchmarkParams_Fanout << ", K = " << K << ", Delay = " << Delay; } + + const int N; + const int K; + const int Delay; }; -template -struct Benchmark_Fanout : public BenchmarkBase +struct Benchmark_Fanout { double Run(const BenchmarkParams_Fanout& params) { - using MyDomain = D; - using MyHandle = MyDomain::SignalT; + using MySignal = Signal; bool initializing = true; - auto in = MyDomain::MakeVar(1); + auto in = MakeVar(1); - std::vector nodes; + std::vector nodes; auto f = [&initializing,¶ms] (int a) { if (params.Delay > 0 && !initializing) @@ -65,7 +67,7 @@ struct Benchmark_Fanout : public BenchmarkBase for (int i=0; i* f; + auto t = MakeSignal(in, f); nodes.push_back(t); } @@ -82,4 +84,8 @@ struct Benchmark_Fanout : public BenchmarkBase } }; -#endif // CPP_REACT_BENCHMARK_FANOUT_H \ No newline at end of file +*/ + +#endif // CPP_REACT_BENCHMARK_FANOUT_H + +#endif \ No newline at end of file diff --git a/src/benchmark/BenchmarkGrid.h b/benchmarks/src/BenchmarkGrid.h similarity index 50% rename from src/benchmark/BenchmarkGrid.h rename to benchmarks/src/BenchmarkGrid.h index 80ffa53d..ad2d90af 100644 --- a/src/benchmark/BenchmarkGrid.h +++ b/benchmarks/src/BenchmarkGrid.h @@ -1,62 +1,67 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2017. // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once +#if 0 + +#ifndef REACT_BENCHMARK_GRID_H +#define REACT_BENCHMARK_GRID_H + #include #include -#include #include +#include "tbb/tick_count.h" + #include "BenchmarkBase.h" +#include "react/state.h" + +using namespace react; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// GridGraphGenerator /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename TValue -> +template class GridGraphGenerator { public: - typedef typename D::template SignalT MyHandle; + using SignalType = Signal; - typedef std::function Func1T; - typedef std::function Func2T; + using Func1T = std::function; + using Func2T = std::function; - typedef std::vector HandleVect; - typedef std::vector WidthVect; + using SignalVectType = std::vector; - HandleVect InputSignals; - HandleVect OutputSignals; + SignalVectType inputSignals; + SignalVectType outputSignals; - Func1T Function1; - Func2T Function2; + Func1T function1; + Func2T function2; - WidthVect Widths; + std::vector widths; - void Generate() + void Generate(const GroupBase& group) { - assert(InputSignals.size() >= 1); - assert(Widths.size() >= 1); + assert(inputSignals.size() >= 1); + assert(widths.size() >= 1); - HandleVect buf1 = InputSignals; - HandleVect buf2; + SignalVectType buf1 = std::move(inputSignals); + SignalVectType buf2; - HandleVect* curBuf = &buf1; - HandleVect* nextBuf = &buf2; + SignalVectType* curBuf = &buf1; + SignalVectType* nextBuf = &buf2; - int curWidth = InputSignals.size(); + size_t curWidth = inputSignals.size(); - int nodeCount = 1; + size_t nodeCount = 1; nodeCount += curWidth; - for (auto targetWidth : Widths) + for (auto targetWidth : widths) { while (curWidth != targetWidth) { @@ -70,43 +75,43 @@ class GridGraphGenerator if (shouldGrow) { - auto s = (*l) ->* Function1; - nextBuf->push_back(s); + auto s = SignalType{ group, function1, *l }; + nextBuf->push_back(std::move(s)); } while (r != curBuf->end()) { - auto s = (*l,*r) ->* Function2; - nextBuf->push_back(s); - nodeCount++; + auto s = SignalType{ group, function2, *l, *r }; + nextBuf->push_back(std::move(s)); + ++nodeCount; ++l; ++r; } if (shouldGrow) { - auto s = (*l) ->* Function1; - nextBuf->push_back(s); - nodeCount++; + auto s = SignalType{ group, function1, *l }; + nextBuf->push_back(std::move(s)); + ++nodeCount; } curBuf->clear(); // Swap buffer pointers - HandleVect* t = curBuf; + SignalVectType* t = curBuf; curBuf = nextBuf; nextBuf = t; if (shouldGrow) - curWidth++; + ++curWidth; else - curWidth--; + --curWidth; } } //printf ("NODE COUNT %d\n", nodeCount); - OutputSignals.clear(); - OutputSignals.insert(OutputSignals.begin(), curBuf->begin(), curBuf->end()); + outputSignals.clear(); + outputSignals.insert(outputSignals.begin(), curBuf->begin(), curBuf->end()); } }; @@ -118,39 +123,36 @@ struct BenchmarkParams_Grid BenchmarkParams_Grid(int n, int k) : N(n), K(k) - { - } - - const int N; - const int K; + {} void Print(std::ostream& out) const { out << "N = " << N << ", K = " << K; } + + const int N; + const int K; }; -template -struct Benchmark_Grid : public BenchmarkBase +struct Benchmark_Grid { - double Run(const BenchmarkParams_Grid& params) + double Run(const BenchmarkParams_Grid& params, const GroupBase& group) { - using MyDomain = D; + VarSignal in{ group, 1 }; + Signal in2 = in; - auto in = MyDomain::MakeVar(1); + GridGraphGenerator generator; - GridGraphGenerator generator; + generator.inputSignals.push_back(in2); - generator.InputSignals.push_back(in); + generator.widths.push_back(params.N); + generator.widths.push_back(1); - generator.Widths.push_back(params.N); - generator.Widths.push_back(1); + generator.function1 = [] (int a) { return a; }; + generator.function2 = [] (int a, int b) { return a + b; }; - generator.Function1 = [] (int a) { return a; }; - generator.Function2 = [] (int a, int b) { return a + b; }; - - generator.Generate(); + generator.Generate(group); auto t0 = tbb::tick_count::now(); for (int i=0; i return d; } -}; \ No newline at end of file +}; + +#endif // REACT_BENCHMARK_GRID_H + +#endif \ No newline at end of file diff --git a/benchmarks/src/BenchmarkLifeSim.h b/benchmarks/src/BenchmarkLifeSim.h new file mode 100644 index 00000000..2511882d --- /dev/null +++ b/benchmarks/src/BenchmarkLifeSim.h @@ -0,0 +1,327 @@ + +// Copyright Sebastian Jeckel 2017. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#pragma once +/* +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "BenchmarkBase.h" + +#include "react/Domain.h" +#include "react/Signal.h" +#include "react/Event.h" +#include "react/logging/EventLog.h" + +using namespace react; + +using std::cout; +using std::string; +using std::unique_ptr; +using std::vector; + +// FIXME: GCC doesn't like enum class in MakeEventSource +enum { summer, winter }; +enum { enter, leave }; + +template +struct Incrementer { T operator()(Token,T v) const { return v+1; } }; + +using PositionT = std::pair; +using BoundsT = std::tuple; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Time +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class Time +{ +public: + USING_REACTIVE_DOMAIN(D) + + EventSourceT<> NewDay = MakeEventSource(); + + SignalT TotalDays = Iterate(NewDay, 0, Incrementer()); + SignalT DayOfYear = TotalDays % 365; + + SignalT Season = MakeSignal( + DayOfYear, + [] (int day) -> int { + return day < 180 ? winter : summer; + }); +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Region +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class Region +{ + Time& theTime; + +public: + USING_REACTIVE_DOMAIN(D) + + BoundsT Bounds; + + EventSourceT EnterOrLeave = MakeEventSource(); + + SignalT AnimalCount = Iterate( + EnterOrLeave, + 0, + [] (int m, int count) { + return m == enter ? count + 1 : count - 1; + }); + + SignalT FoodPerDay = MakeSignal( + theTime.Season, + calculateFoodPerDay); + + SignalT FoodOutputPerDay = MakeSignal( + With(FoodPerDay,AnimalCount), + calculateFoodOutputPerDay); + + EventsT FoodOutput = Pulse(theTime.NewDay, FoodOutputPerDay); + + Region(Time& time, int x, int y) : + theTime( time ), + Bounds( x*10, x*10+9, y*10, y*10+9 ) + {} + + PositionT Center() const + { + using std::get; + + return PositionT(get<0>(Bounds) + 5, get<2>(Bounds) + 5); + } + + PositionT Clamp(PositionT pos) const + { + using std::get; + + pos.first = get<0>(Bounds) + (abs(pos.first) % 10); + pos.second = get<2>(Bounds) + (abs(pos.second) % 10); + + return pos; + } + + bool IsInRegion(PositionT pos) const + { + using std::get; + + return get<0>(Bounds) <= pos.first && pos.first <= get<1>(Bounds) && + get<2>(Bounds) <= pos.second && pos.second <= get<3>(Bounds); + } + +private: + static int calculateFoodPerDay(int season) + { + return season == summer ? 20 : 10; + } + + static int calculateFoodOutputPerDay(int food, int count) + { + return count > 0 ? food/count : 0; + } + +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// World +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class World +{ +public: + USING_REACTIVE_DOMAIN(D) + + vector>> Regions; + + World(Time& time, int w) : + w_( w ) + { + for (int x=0; x>(new Region(time, x, y))); + } + + Region* GetRegion(PositionT pos) + { + for (auto& r : Regions) + { + if (r->IsInRegion(pos)) + return r.get(); + } + + //printf("Out of bounds %d %d\n", pos.first, pos.second); + assert(false); + return nullptr; + } + + PositionT Clamp(PositionT pos) const + { + pos.first = abs(pos.first) % 10*w_; + pos.second = abs(pos.second) % 10*w_; + + return pos; + } + +private: + int w_; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Animal +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class Animal +{ + Time& theTime; + World& theWorld; + + std::mt19937 generator; + +public: + USING_REACTIVE_DOMAIN(D) + + VarSignalT*> CurrentRegion; + + EventsT FoodReceived = REACTIVE_PTR(CurrentRegion, FoodOutput); + SignalT ShouldMigrate = Hold(FoodReceived, 0) < 10; + EventsT Moving = Pulse(theTime.NewDay, ShouldMigrate); + + SignalT Position; + SignalT*> NewRegion; + + EventsT*> RegionChanged = Monitor(NewRegion); + + SignalT Age = Iterate(theTime.NewDay, 0, Incrementer()); + SignalT Health = Iterate(FoodReceived, 100, calculateHealth); + + Animal(Time& time, World& world, Region* initRegion, unsigned seed) : + theTime( time ), + theWorld( world ), + generator( seed ), + CurrentRegion( MakeVar(initRegion) ), + Position + ( + Iterate(Moving, initRegion->Center(), With(CurrentRegion), + [this] (bool moving, PositionT position, Region* region) { + std::uniform_int_distribution dist(-1,1); + + // Wander randomly + for (int i=0; i<100; i++) + { + position.first += dist(generator); + position.second += dist(generator); + } + + // Should migrate? + if (moving) + return theWorld.Clamp(position); + else + return region->Clamp(position); + }) + ), + NewRegion + ( + MakeSignal(Position, + [this] (PositionT pos) { + return theWorld.GetRegion(pos); + }) + ), + regionChangeContinuation_ + ( + MakeContinuation(RegionChanged, With(CurrentRegion), + [this] (Region* newRegion, Region* oldRegion) { + oldRegion->EnterOrLeave(leave); + newRegion->EnterOrLeave(enter); + + // Change region in continuation + CurrentRegion <<= newRegion; + }) + ) + { + initRegion->EnterOrLeave(enter); + } + +private: + Continuation regionChangeContinuation_; + + static int calculateHealth(int food, int health) + { + auto newHealth = health + food - 10; + return newHealth < 0 ? 0 : newHealth > 10000 ? 10000 : newHealth; + } +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Run simulation +/////////////////////////////////////////////////////////////////////////////////////////////////// +struct BenchmarkParams_LifeSim +{ + BenchmarkParams_LifeSim(int n, int w, int k) : + N( n ), W( w ), K( k ) + {} + + void Print(std::ostream& out) const + { + out << "N = " << N + << ", K = " << K + << ", W = " << W; + } + + const int N; + const int W; + const int K; +}; + +template +struct Benchmark_LifeSim : public BenchmarkBase +{ + double Run(const BenchmarkParams_LifeSim& params) + { + Time theTime; + World theWorld( theTime, params.W ); + + vector>> animals; + + std::mt19937 gen( 2015 ); + std::uniform_int_distribution dist( 0u, theWorld.Regions.size()-1 ); + + for (int i=0; i>(new Animal(theTime, theWorld, r, i+1))); + } + + // WHEEL IN THE SKY KEEPS ON TURNING + auto t0 = tbb::tick_count::now(); + + for (int i=0; i #include #include #include #include "BenchmarkBase.h" -#include "react/common/Types.h" +#include "react/group.h" +#include "react/state.h" + +using namespace react; +/* /////////////////////////////////////////////////////////////////////////////////////////////////// /// DiamondGraphGenerator /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -85,7 +91,7 @@ class RandomGraphGenerator auto edgeNodeIt = edgeNodes.begin(); auto slowNodeIt = slowNodes.begin(); - uint cur = 0; + auto cur = 0; HandleVect nodes(nodeCount); for (int w=0; w); -REACTIVE_DOMAIN(TopoSortDomain, TopoSortEngine); -REACTIVE_DOMAIN(PulseCountDomain, PulseCountEngine); -REACTIVE_DOMAIN(SourceSetDomain, SourceSetEngine); -REACTIVE_DOMAIN(ELMDomain, ELMEngine); -REACTIVE_DOMAIN(SubtreeDomain, SubtreeEngine); +//using namespace react; -void runBenchmarkGrid(std::ostream& out) +/*void runBenchmarkGrid(std::ostream& out) { RUN_BENCHMARK(out, 5, Benchmark_Grid, BenchmarkParams_Grid(20, 10000), - TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + ToposortSTDomain, ToposortDomain, PulsecountDomain); RUN_BENCHMARK(out, 5, Benchmark_Grid, BenchmarkParams_Grid(30, 10000), - TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + ToposortSTDomain, ToposortDomain, PulsecountDomain); RUN_BENCHMARK(out, 5, Benchmark_Grid, BenchmarkParams_Grid(40, 10000), - TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + ToposortSTDomain, ToposortDomain, PulsecountDomain); RUN_BENCHMARK(out, 5, Benchmark_Grid, BenchmarkParams_Grid(50, 10000), - TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); -} + ToposortSTDomain, ToposortDomain, PulsecountDomain); +}*/ -void runBenchmarkRandom(std::ostream& out) +/*void runBenchmarkRandom(std::ostream& out) { const auto w = 20; const auto h = 11; @@ -71,57 +57,57 @@ void runBenchmarkRandom(std::ostream& out) { int x = (slowPercent * (w*(h-1))) / 100; //RUN_BENCHMARK(out, 5, Benchmark_Random, BenchmarkParams_Random(w, h, 20, 0, 10, 40, x, true, seed1, seed2), - // TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + // ToposortDomain, ELMDomain, PulsecountDomain, SourceSetDomain); RUN_BENCHMARK(out, 5, Benchmark_Random, BenchmarkParams_Random(w, h, 20, 0, 10, 40, x, true, seed1, seed2), - TopoSortSTDomain); + ToposortSTDomain); } seed1 *= 2; seed2 *= 2; } -} +}*/ void runBenchmarkFanout(std::ostream& out) { //RUN_BENCHMARK(out, 5, Benchmark_Fanout, BenchmarkParams_Fanout(10, 10000, 0), - // TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + // ToposortSTDomain, ToposortDomain, ELMDomain, PulsecountDomain, SourceSetDomain); //RUN_BENCHMARK(out, 5, Benchmark_Fanout, BenchmarkParams_Fanout(100, 10000, 0), - // TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + // ToposortSTDomain, ToposortDomain, ELMDomain, PulsecountDomain, SourceSetDomain); //RUN_BENCHMARK(out, 5, Benchmark_Fanout, BenchmarkParams_Fanout(1000, 10000, 0), - // TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + // ToposortSTDomain, ToposortDomain, ELMDomain, PulsecountDomain, SourceSetDomain); - RUN_BENCHMARK(out, 3, Benchmark_Fanout, BenchmarkParams_Fanout(10, 10, 10), - TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + /* RUN_BENCHMARK(out, 3, Benchmark_Fanout, BenchmarkParams_Fanout(10, 10, 10), + ToposortSTDomain, ToposortDomain, PulsecountDomain); RUN_BENCHMARK(out, 3, Benchmark_Fanout, BenchmarkParams_Fanout(100, 10, 10), - TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + ToposortSTDomain, ToposortDomain, PulsecountDomain); RUN_BENCHMARK(out, 3, Benchmark_Fanout, BenchmarkParams_Fanout(1000, 10, 10), - TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + ToposortSTDomain, ToposortDomain, PulsecountDomain);*/ } void runBenchmarkSequence(std::ostream& out) { //RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(10, 10000, 0), - // TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + // ToposortSTDomain, ToposortDomain, ELMDomain, PulsecountDomain, SourceSetDomain); //RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(100, 10000, 0), - // TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + // ToposortSTDomain, ToposortDomain, ELMDomain, PulsecountDomain, SourceSetDomain); //RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(1000, 10000, 0), - // TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + // ToposortSTDomain, ToposortDomain, ELMDomain, PulsecountDomain, SourceSetDomain); - RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(10, 10, 10), - TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + //RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(10, 10, 10), + // ToposortSTDomain, ToposortDomain, PulsecountDomain); - RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(100, 10, 10), - TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + //RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(100, 10, 10), + // ToposortSTDomain, ToposortDomain, PulsecountDomain); - RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(1000, 10, 10), - TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + //RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(1000, 10, 10), + // ToposortSTDomain, ToposortDomain, PulsecountDomain); } void runBenchmarkLifeSim(std::ostream& out) @@ -130,21 +116,21 @@ void runBenchmarkLifeSim(std::ostream& out) // ELMDomain); //RUN_BENCHMARK(std::cout, 1, Benchmark_LifeSim, BenchmarkParams_LifeSim(250, 30, 10000), - // SourceSetDomain, PulseCountDomain); + // SourceSetDomain, PulsecountDomain); - RUN_BENCHMARK(out, 1, Benchmark_LifeSim, BenchmarkParams_LifeSim(100, 15, 10000), - TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + //RUN_BENCHMARK(out, 1, Benchmark_LifeSim, BenchmarkParams_LifeSim(100, 15, 10000), + // ToposortSTDomainConc, ToposortDomainConc, PulsecountDomainConc); //RUN_BENCHMARK(out, 3, Benchmark_LifeSim, BenchmarkParams_LifeSim(100, 50, 100), - // PulseCountDomain, PulseCountDomain); + // PulsecountDomain, PulsecountDomain); } void runBenchmarks() { std::ofstream logfile; - std::string path = "Benchmark Results/" + CurrentDateTime() + ".txt"; - logfile.open(path.c_str()); + //std::string path = "Benchmark Results/" + CurrentDateTime() + ".txt"; + //logfile.open(path.c_str()); // === GRID //runBenchmarkGrid(logfile); @@ -161,17 +147,17 @@ void runBenchmarks() //runBenchmarkSequence(logfile); // === LIFESIM - runBenchmarkLifeSim(logfile); + //runBenchmarkLifeSim(logfile); logfile.close(); } void debugBenchmarks() { - using TestDomain = PulseCountDomain; + //using TestDomain = PulsecountDomain; - RUN_BENCHMARK(std::cout, 3, Benchmark_Grid, BenchmarkParams_Grid(30, 1), - PulseCountDomain); + //RUN_BENCHMARK(std::cout, 1, Benchmark_Grid, BenchmarkParams_Grid(30, 1), + // TestDomain); //RUN_BENCHMARK(std::cout, 1, Benchmark_Fanout, BenchmarkParams_Fanout(1000, 5, 0), // TestDomain); @@ -183,7 +169,10 @@ void debugBenchmarks() // TestDomain); //RUN_BENCHMARK(std::cout, 1, Benchmark_Random, BenchmarkParams_Random(10, 25, 10, 0, 10, 25, 25, false), - // TopoSortDomain); + // ToposortDomain); + + //RUN_BENCHMARK(std::cout, 1, Benchmark_Random, BenchmarkParams_Random(40, 11, 2, 0, 1, 40, 40, false, 41556, 21624), + // TestDomain); //const auto w = 10; //const auto h = 11; @@ -197,38 +186,28 @@ void debugBenchmarks() // { // int x = (slowPercent * (w*(h-1))) / 100; // RUN_BENCHMARK(std::cout, 1, Benchmark_Random, BenchmarkParams_Random(w, h, 100, 0, 5, 25, x, false, seed1 / j, seed2 / j), - // TopoSortDomain); + // ToposortDomain); // } //} //RunBenchmark<1>(Benchmark2()); //RunBenchmark<1>(Benchmark2()); - //RunBenchmark<3>(Benchmark2()); - -#ifdef REACT_ENABLE_LOGGING - std::ofstream logfile; - logfile.open("log.txt"); - - TestDomain::Log().Write(logfile); - logfile.close(); -#endif + //RunBenchmark<3>(Benchmark2()); } void profileBenchmark() { - RUN_BENCHMARK(std::cout, 3, Benchmark_Grid, BenchmarkParams_Grid(30, 10000), - TopoSortSTDomain); - //TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SubtreeDomain, SourceSetDomain); + //RUN_BENCHMARK(std::cout, 3, Benchmark_Grid, BenchmarkParams_Grid(100, 10000)); //RUN_BENCHMARK(std::cout, 1, Benchmark_Grid, BenchmarkParams_Grid(30, 10000), - // SourceSetDomain); + //SourceSetDomain); - //RUN_BENCHMARK(std::cout, 1, Benchmark_Random, BenchmarkParams_Random(20, 11, 100, 0, 5, 80, 20, false, 41556, 21624), + //RUN_BENCHMARK(std::cout, 3, Benchmark_Random, BenchmarkParams_Random(20, 11, 100, 0, 1, 40, 40, false, 41556, 21624), //SubtreeDomain); - //TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SubtreeDomain, SourceSetDomain); + //ToposortSTDomain, ToposortDomain, PulsecountDomain, SubtreeDomain); //RUN_BENCHMARK(std::cout, 1, Benchmark_LifeSim, BenchmarkParams_LifeSim(100, 15, 10000), - // TopoSortDomain); + //ToposortSTDomainConc); } } // ~anonymous namespace @@ -237,5 +216,12 @@ int main() { //runBenchmarks(); //debugBenchmarks(); - profileBenchmark(); + //profileBenchmark(); +} + +#endif + +int main() +{ + return 0; } \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 00000000..417819a0 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,27 @@ +### Example_BasicAlgorithms +add_executable(Example_BasicAlgorithms src/BasicAlgorithms.cpp) +target_link_libraries(Example_BasicAlgorithms CppReact) + +### Example_BasicComposition +add_executable(Example_BasicComposition src/BasicComposition.cpp) +target_link_libraries(Example_BasicComposition CppReact) + +### Example_BasicEvents +add_executable(Example_BasicEvents src/BasicEvents.cpp) +target_link_libraries(Example_BasicEvents CppReact) + +### Example_BasicObservers +add_executable(Example_BasicObservers src/BasicObservers.cpp) +target_link_libraries(Example_BasicObservers CppReact) + +### Example_BasicSignals +add_executable(Example_BasicSignals src/BasicSignals.cpp) +target_link_libraries(Example_BasicSignals CppReact) + +### Example_BasicSynchronization +add_executable(Example_BasicSynchronization src/BasicSynchronization.cpp) +target_link_libraries(Example_BasicSynchronization CppReact) + +### Example_Sandbox +add_executable(Example_Sandbox src/Main.cpp) +target_link_libraries(Example_Sandbox CppReact) diff --git a/examples/src/BasicAlgorithms.cpp b/examples/src/BasicAlgorithms.cpp new file mode 100644 index 00000000..7829049c --- /dev/null +++ b/examples/src/BasicAlgorithms.cpp @@ -0,0 +1,545 @@ + +// Copyright Sebastian Jeckel 2017. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include + +#include "react/state.h" +#include "react/event.h" +#include "react/observer.h" +#include "react/algorithm.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 1 - Converting events to signals +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example1 +{ + using namespace std; + using namespace react; + + Group g; + + struct Sensor + { + EventSource samples = EventSource::Create(g); + State lastSample = Hold(0, samples); + + auto GetReactiveMembers() const -> decltype(auto) + { return std::tie(lastSample); } + }; + + void Run() + { + cout << "Example 1 - Converting events to signals" << endl; + + Sensor sensor; + + auto obs = Observer::Create(g, [] (int v) + { + cout << v << endl; + }, sensor.lastSample); + + sensor.samples << 20 << 21 << 21 << 22; // output: 20, 21, 22 + + g.DoTransaction([&] + { + sensor.samples << 30 << 31 << 31 << 32; + }); // output: 32 + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 2 - Converting signals to events +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example2 +{ + using namespace std; + using namespace react; + + Group g; + + struct Employee + { + StateVar name = StateVar::Create(g, string( "Bob" )); + StateVar salary = StateVar::Create(g, 66666); + }; + + void Run() + { + cout << "Example 2 - Converting signals to events" << endl; + + Employee bob; + + auto obs = Observer::Create([] (const auto& events, const string& name) + { + for (int newSalary : events) + cout << name << " now earns " << newSalary << endl; + }, Monitor(bob.salary), bob.name); + + bob.salary.Set(66667); + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 3 - Folding event streams into signals (1) +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example3 +{ + using namespace std; + using namespace react; + + Group g; + + struct Counter + { + EventSource<> increment = EventSource<>::Create(g); + + State count = Iterate(0, [] (const auto& events, int count) + { + for (auto e : events) + ++count; + return count; + }, increment); + }; + + void Run() + { + cout << "Example 3 - Folding event streams into signals (1)" << endl; + + Counter myCounter; + + myCounter.increment.Emit(); + myCounter.increment.Emit(); + myCounter.increment.Emit(); + + auto obs = Observer::Create([] (int v) + { + cout << v << endl; // output: 3 + }, myCounter.count); + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 4 - Folding event streams into signals (2) +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example4 +{ + using namespace std; + using namespace react; + + Group g; + + struct Sensor + { + EventSource input = EventSource::Create(g); + + State count = Iterate(0, [] (const auto& events, int count) + { + for (auto e : events) + ++count; + return count; + }, input); + + State sum = Iterate(0.0f, [] (const auto& events, float sum) + { + for (auto e : events) + sum += e; + return sum; + }, input); + + State average = State::Create([] (int c, float s) + { + if (c != 0) + return s / c; + else + return 0.0f; + + }, count, sum); + }; + + void Run() + { + cout << "Example 4 - Folding event streams into signals (2)" << endl; + + Sensor mySensor; + + mySensor.input << 10.0f << 5.0f << 10.0f << 8.0f; + + auto obs = Observer::Create([] (float v) + { + cout << "Average: " << v << endl; // output: 8.25 + }, mySensor.average); + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 5 - Folding event streams into signals (3) +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example5 +{ + using namespace std; + using namespace react; + + Group g; + + enum ECmd { increment, decrement, reset }; + + class Counter + { + private: + static int DoCounterLoop(const EventValueList& cmds, int count, int delta, int start) + { + for (int cmd : cmds) + { + if (cmd == increment) + count += delta; + else if (cmd == decrement) + count -= delta; + else + count = start; + } + + return count; + } + + public: + EventSource update = EventSource::Create(g); + + StateVar delta = StateVar::Create(g, 1); + StateVar start = StateVar::Create(g, 0); + + State count = Iterate(0, DoCounterLoop, update, delta, start); + }; + + void Run() + { + cout << "Example 5 - Folding event streams into signals (3)" << endl; + + Counter myCounter; + + { + auto obs = Observer::Create([] (int v) + { + cout << "Start: " << v << endl; // output: 0 + }, myCounter.count); + } + + + myCounter.update.Emit(increment); + myCounter.update.Emit(increment); + myCounter.update.Emit(increment); + + { + auto obs = Observer::Create([] (int v) + { + cout << "3x increment by 1: " << v << endl; // output: 3 + }, myCounter.count); + } + + myCounter.delta.Set(5); + myCounter.update.Emit(decrement); + + { + auto obs = Observer::Create([] (int v) + { + cout << "1x decrement by 5: " << v << endl; // output: -2 + }, myCounter.count); + } + + myCounter.start.Set(100); + myCounter.update.Emit(reset); + + { + auto obs = Observer::Create([] (int v) + { + cout << "reset to 100: " << v << endl; // output: 100 + }, myCounter.count); + } + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 6 - Avoiding expensive copies +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example6 +{ + using namespace std; + using namespace react; + + Group g; + + class Sensor + { + private: + static void DoIterateAllSamples(const EventValueList& events, vector& all) + { + for (int input : events) + all.push_back(input); + } + + static void DoIterateCritSamples(const EventValueList events, vector& critical, int threshold) + { + for (int input : events) + if (input > threshold) + critical.push_back(input); + } + + public: + EventSource input = EventSource::Create(g); + + StateVar threshold = StateVar::Create(g, 42); + + State> allSamples = IterateByRef>(vector{ }, DoIterateAllSamples, input); + + State> criticalSamples = IterateByRef>(vector{ }, DoIterateCritSamples, input, threshold); + }; + + void Run() + { + cout << "Example 6 - Avoiding expensive copies" << endl; + + Sensor mySensor; + + mySensor.input << 40 << 29 << 43 << 50; + + cout << "All samples: "; + { + auto obs = Observer::Create([] (const auto& allSamples) + { + for (auto const& v : allSamples) + cout << v << " "; + }, mySensor.allSamples); + } + cout << endl; + + cout << "Critical samples: "; + { + auto obs = Observer::Create([] (const auto& criticalSamples) + { + for (auto const& v : criticalSamples) + cout << v << " "; + }, mySensor.criticalSamples); + } + cout << endl; + } +} + + +using namespace react; + +Group g; + +struct MyClass +{ + StateVar a = StateVar::Create(g, 10); + StateVar b = StateVar::Create(g, 20); + + bool operator==(const MyClass& other) + { return a == other.a; } + + struct Flat; +}; + +struct MyClass::Flat : public Flattened +{ + using Flattened::Flattened; + + Ref a = this->Flatten(MyClass::a); + Ref b = this->Flatten(MyClass::b); +}; + +using namespace react; +using namespace std; + +void test1() +{ + + + /*StateVar x; + State y; + State z; + + State>> list; + State>> map; + + State> flatlist = FlattenList(list); + State> flatmap = FlattenMap(map); + + MyClass cls1; + MyClass::Flat cls2(cls1); + + StateVar> sig; + + Flatten(g, sig);*/ + + auto w1 = StateVar::Create(g, "Widget1"); + auto w2 = StateVar::Create(g, "Widget2"); + auto w3 = StateVar::Create(g, "Widget3"); + + auto allWidgets = { w1, w2, w3 }; + + auto d1 = StateVar::Create(g, "Data1"); + auto d2 = StateVar::Create(g, "Data2"); + auto d3 = StateVar::Create(g, "Data3"); + + auto allData = { d1, d2, d3 }; + + auto objects = StateVar>>::Create(g); + + auto obs = Observer::Create([] (const auto& flatList) + { + cout << "Objects: "; + for (const string& s : flatList) + cout << s << " "; + cout << endl; + }, FlattenList(objects)); + // Objects: + + objects.Modify([&] (auto& w) + { + w.push_back(w1); + }); + // Objects: Widget1 + + w1.Set("Widget1 (x)"); + // Objects: Widget1 (x) + + objects.Set(allWidgets); + // Objects: Widget1 (x) Widget2 Widget3 + + objects.Set(allData); + // Objects: Data1 Data2 Data3 + + w2.Set("Widget2 (x)"); + + g.DoTransaction([&] + { + w3.Set("Widget3 (x)"); + + d1.Set("Data1 (x)"); + d2.Set("Data2 (x)"); + + objects.Set(allWidgets); + }); + // Objects: Widget1 (x) Widget2 (x) Widget3 (x) + + objects.Modify([&] (auto& w) + { + w.clear(); + w.insert(end(w), allWidgets); + w.insert(end(w), allData); + }); + // Objects: Widget1 (x) Widget2 (x) Widget3 (x) Data1 (x) Data2 (x) Data3 + +} + +class Office; +class Company; +class Employee; + +//---- +class Office +{ +public: + StateVar location; + + StateVar>> employees; + + State employeeCount = State::Create(CalcEmployeeCount, FlattenList(employees)); + + struct Flat; + +private: + static int CalcEmployeeCount(const vector& offices) + { return offices.size(); } +}; + +struct Office::Flat : public Flattened +{ + using Flattened::Flattened; + + int employeeCount = this->Flatten(Office::employeeCount); +}; + +//---- +class Employee +{ +public: + StateVar name; + + StateRef office; + + struct Flat; + +private: +}; + +struct Employee::Flat : public Flattened +{ + using Flattened::Flattened; + + Ref name = this->Flatten(Employee::name); + Ref office = this->Flatten(Employee::office); +}; + +//---- +class Company +{ +public: + StateVar>> offices; + + State employeeCount; + + struct Flat; + +private: + static int CalcEmployeeCount(const vector& offices) + { + int count = 0; + + for (const auto& office : offices) + count += office.employeeCount; + + return count; + } +}; + +struct Company::Flat : public Flattened +{ + using Flattened::Flattened; + + Ref>> offices = this->Flatten(Company::offices); + int employeeCount = this->Flatten(Company::employeeCount); +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Run examples +/////////////////////////////////////////////////////////////////////////////////////////////////// +int main() +{ + /*example1::Run(); + example2::Run(); + example3::Run(); + example4::Run(); + example5::Run(); + example6::Run();*/ + + test1(); + + return 0; +} \ No newline at end of file diff --git a/examples/src/BasicComposition.cpp b/examples/src/BasicComposition.cpp new file mode 100644 index 00000000..acec053e --- /dev/null +++ b/examples/src/BasicComposition.cpp @@ -0,0 +1,130 @@ + +// Copyright Sebastian Jeckel 2017. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +#include "react/state.h" +#include "react/event.h" +#include "react/observer.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 1 - Reactive class members +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example1 +{ + using namespace std; + using namespace react; + + Group g; + + class Shape + { + public: + StateVar width = StateVar::Create(g, 0); + StateVar height = StateVar::Create(g, 0); + + State size = State::Create(g, CalcSize, width, height); + + auto GetReactiveMembers() const -> decltype(auto) + { return std::tie(width, height, size); } + + private: + static int CalcSize(int w, int h) + { return w * h; } + }; + + void Run() + { + cout << "Example 1 - Reactive class members" << endl; + + auto myShape = ObjectState::Create(g, Shape()); + + auto obs = Observer::Create([] (const auto& ctx) + { + const Shape& shape = ctx.GetObject(); + cout << "Size is " << ctx.Get(shape.size) << endl; + }, myShape); + + g.DoTransaction([&] + { + myShape->width.Set(4); + myShape->height.Set(4); + }); // output: Size changed to 16 + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 2 - Slots +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example2 +{ + using namespace std; + using namespace react; + + Group g; + + class Company + { + public: + StateVar name; + + Company(const char* name) : + name( StateVar::Create(g, name) ) + { } + + bool operator==(const Company& other) const + { return name == other.name; } + }; + + class Employee + { + public: + StateSlot myCompanyName; + + Employee(const Company& company) : + myCompanyName( StateSlot::Create(company.name) ) + { } + + void SetCompany(const Company& company) + { myCompanyName.Set(company.name); } + }; + + void Run() + { + cout << "Example 2 - Slots" << endl; + + Company company1( "MetroTec" ); + Company company2( "ACME" ); + + Employee alice( company1 ); + + auto obs = Observer::Create([] (const string& name) + { + cout << "Alice now works for " << name << endl; + }, alice.myCompanyName); + + company1.name.Set(string( "ModernTec" )); // output: Alice now works for ModernTec + alice.SetCompany(company2); // output: Alice now works for ACME + company2.name.Set(string( "A.C.M.E." )); // output: Alice now works for A.C.M.E. + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Run examples +/////////////////////////////////////////////////////////////////////////////////////////////////// +int main() +{ + example1::Run(); + example2::Run(); + + return 0; +} \ No newline at end of file diff --git a/examples/src/BasicEvents.cpp b/examples/src/BasicEvents.cpp new file mode 100644 index 00000000..98fe1ef3 --- /dev/null +++ b/examples/src/BasicEvents.cpp @@ -0,0 +1,235 @@ + +// Copyright Sebastian Jeckel 2017. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +#include "react/Event.h" +#include "react/Observer.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 1 - Hello world +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example1 +{ + using namespace std; + using namespace react; + + // Defines a group. + // Each group represents a separate dependency graph. + // Reactives from different groups can not be mixed. + Group group; + + // An event source that emits values of type string + namespace v1 + { + EventSource mySource = EventSource::Create(group); + + void Run() + { + cout << "Example 1 - Hello world (string source)" << endl; + + auto obs = Observer::Create([] (const auto& events) + { + for (const auto& e : events) + cout << e << std::endl; + }, mySource); + + mySource << string("Hello world #1"); + + // Or without the operator: + mySource.Emit(string("Hello world #2")); + + cout << endl; + } + } + + // An event source without an explicit value type + namespace v2 + { + EventSource<> helloWorldTrigger = EventSource<>::Create(group); + + void Run() + { + cout << "Example 1 - Hello world (token source)" << endl; + + int count = 0; + + auto obs = Observer::Create([&] (const auto& events) + { + for (auto t : events) + cout << "Hello world #" << ++count << endl; + }, helloWorldTrigger); + + helloWorldTrigger.Emit(); + + // Or without the stream operator: + helloWorldTrigger << Token::value; + + cout << endl; + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 2 - Merging event streams +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example2 +{ + using namespace std; + using namespace react; + + Group group; + + // An event stream that merges both sources + EventSource<> leftClick = EventSource<>::Create(group); + EventSource<> rightClick = EventSource<>::Create(group); + + Event<> anyClick = Merge(leftClick, rightClick); + + void Run() + { + cout << "Example 2 - Merging event streams (Merge)" << endl; + + int count = 0; + + auto obs = Observer::Create([&] (const auto& events) + { + for (auto t : events) + cout << "clicked #" << ++count << endl; + }, anyClick); + + leftClick.Emit(); // output: clicked #1 + rightClick.Emit(); // output: clicked #2 + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 3 - Filtering events +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example3 +{ + using namespace std; + using namespace react; + + Group group; + + EventSource numbers = EventSource::Create(group); + + Event greater10 = Filter([] (int n) { return n > 10; }, numbers); + + void Run() + { + cout << "Example 3 - Filtering events" << endl; + + auto obs = Observer::Create([&] (const auto& events) + { + for (auto n : events) + cout << n << endl; + }, greater10); + + numbers << 5 << 11 << 7 << 100; // output: 11, 100 + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 4 - Filtering events +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example4 +{ + using namespace std; + using namespace react; + + Group group; + + // Data types + enum class Tag { normal, critical }; + using TaggedNum = pair; + + EventSource numbers = EventSource::Create(group); + + Event tagged = Transform([] (int n) + { + if (n > 10) + return TaggedNum( Tag::critical, n ); + else + return TaggedNum( Tag::normal, n ); + }, numbers); + + void Run() + { + cout << "Example 4 - Transforming events" << endl; + + auto obs = Observer::Create([] (const auto& events) + { + for (TaggedNum e : events) + { + if (e.first == Tag::critical) + cout << "(critical) " << e.second << endl; + else + cout << "(normal) " << e.second << endl; + } + }, tagged); + + numbers << 5; // output: (normal) 5 + numbers << 20; // output: (critical) 20 + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 5 - Queuing multiple inputs +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example5 +{ + using namespace std; + using namespace react; + + Group group; + + EventSource src = EventSource::Create(group); + + void Run() + { + cout << "Example 5 - Queuing multiple inputs" << endl; + + auto obs = Observer::Create([] (const auto& events) + { + for (int e : events) + cout << e << endl; + }, src); + // output: 1, 2, 3, 4 + + group.DoTransaction([] + { + src << 1 << 2 << 3; + src << 4; + }); + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Run examples +/////////////////////////////////////////////////////////////////////////////////////////////////// +int main() +{ + example1::v1::Run(); + example1::v2::Run(); + example2::Run(); + example3::Run(); + example4::Run(); + example5::Run(); + + return 0; +} \ No newline at end of file diff --git a/examples/src/BasicObservers.cpp b/examples/src/BasicObservers.cpp new file mode 100644 index 00000000..54b24857 --- /dev/null +++ b/examples/src/BasicObservers.cpp @@ -0,0 +1,53 @@ + +// Copyright Sebastian Jeckel 2017. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +#include "react/state.h" +#include "react/event.h" +#include "react/observer.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 1 - Creating subject-bound observers +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example1 +{ + using namespace std; + using namespace react; + + Group group; + + StateVar x = StateVar::Create(group, 1); + + void Run() + { + cout << "Example 1 - Creating observers" << endl; + + { + auto st = State::Create([] (int x) { return x; }, x); + + auto obs = Observer::Create([] (int v) { cout << v << endl; }, st); + + x.Set(2); // output: 2 + } + + x.Set(3); // no ouput + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Run examples +/////////////////////////////////////////////////////////////////////////////////////////////////// +int main() +{ + example1::Run(); + + return 0; +} \ No newline at end of file diff --git a/examples/src/BasicSignals.cpp b/examples/src/BasicSignals.cpp new file mode 100644 index 00000000..c743de83 --- /dev/null +++ b/examples/src/BasicSignals.cpp @@ -0,0 +1,228 @@ + +// Copyright Sebastian Jeckel 2017. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include +#include + +#include "react/state.h" +#include "react/observer.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 1 - Hello world +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example1 +{ + using namespace std; + using namespace react; + + // The concat function + string ConcatFunc(string first, string second) + { return first + string(" ") + second; } + + void PrintFunc(const string& s) + { cout << s << endl; } + + // Defines a group. + // Each group represents a separate dependency graph. + // Reactives from different groups can not be mixed. + Group group; + + // The two words + StateVar firstWord = StateVar::Create(group, string("Change")); + StateVar secondWord = StateVar::Create(group, string("me!")); + + // A signal that concatenates both words + State bothWords = State::Create(ConcatFunc, firstWord, secondWord); + + void Run() + { + auto obs = Observer::Create(PrintFunc, bothWords); + + cout << "Example 1 - Hello world" << endl; + + firstWord.Set(string("Hello")); + secondWord.Set(string("World")); + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 2 - Reacting to value changes +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example2 +{ + using namespace std; + using namespace react; + + Group group; + + StateVar x = StateVar::Create(group, 1); + + State xAbs = State::Create([] (int v) { return abs(v); }, x); + + void Run() + { + cout << "Example 2 - Reacting to value changes" << endl; + + auto obs = Observer::Create([] (int newValue) + { cout << "xAbs changed to " << newValue << endl; }, xAbs); + + // initially x is 1 + x.Set(2); // output: xAbs changed to 2 + x.Set(-3); // output: xAbs changed to 3 + x.Set(3); // no output, xAbs is still 3 + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 3 - Changing multiple inputs +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example3 +{ + using namespace std; + using namespace react; + + int sumFunc(int a, int b) + { return a + b; } + + Group group; + + StateVar a = StateVar::Create(group, 1); + StateVar b = StateVar::Create(group, 1); + + State x = State::Create(sumFunc, a, b); + State y = State::Create(sumFunc, a, b); + State z = State::Create(sumFunc, x, y); + + void Run() + { + cout << "Example 3 - Changing multiple inputs" << endl; + + auto obs = Observer::Create([] (int newValue) + { cout << "z changed to " << newValue << endl; }, z); + + a.Set(2); // output: z changed to 6 + b.Set(2); // output: z changed to 8 + + group.DoTransaction([&] + { + a.Set(4); + b.Set(4); + }); // output: z changed to 16 + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 4 - Modifying signal values in place +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example4 +{ + using namespace std; + using namespace react; + + Group group; + + StateVar> data = StateVar>::Create(group); + + void Run() + { + cout << "Example 4 - Modifying signal values in place" << endl; + + data.Modify([] (vector& data) + { data.push_back("Hello"); }); + + data.Modify([] (vector& data) + { data.push_back("World"); }); + + auto obs = Observer::Create([] (const vector& data) + { + for (const auto& s : data) + cout << s << " "; + }, data); + + + cout << endl; + // output: Hello World + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 5 - Complex signals +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example5 +{ + using namespace std; + using namespace react; + + Group group; + + // Helpers + using ExprPairType = pair; + using ExprVectType = vector; + + string MakeExprStr(int a, int b, const char* op) + { + return to_string(a) + string(op) + to_string(b); + } + + void PrintExpressions(const ExprVectType& expressions) + { + cout << "Expressions: " << endl; + for (const auto& p : expressions) + cout << "\t" << p.first << " is " << p.second << endl; + } + + // Input operands + StateVar a = StateVar::Create(group, 1); + StateVar b = StateVar::Create(group, 2); + + // The expression vector + State expressions = State::Create([] (int a, int b) + { + ExprVectType result; + result.push_back(make_pair(MakeExprStr(a, b, "+"), a + b)); + result.push_back(make_pair(MakeExprStr(a, b, "-"), a - b)); + result.push_back(make_pair(MakeExprStr(a, b, "*"), a * b)); + return result; + }, a, b ); + + void Run() + { + cout << "Example 5 - Complex signals (v3)" << endl; + + auto obs = Observer::Create(PrintExpressions, expressions); + + a.Set(50); + b.Set(60); + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Run examples +/////////////////////////////////////////////////////////////////////////////////////////////////// +int main() +{ + example1::Run(); + example2::Run(); + example3::Run(); + example4::Run(); + example5::Run(); + + return 0; +} \ No newline at end of file diff --git a/examples/src/BasicSynchronization.cpp b/examples/src/BasicSynchronization.cpp new file mode 100644 index 00000000..d5b29de7 --- /dev/null +++ b/examples/src/BasicSynchronization.cpp @@ -0,0 +1,177 @@ + +// Copyright Sebastian Jeckel 2017. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include "tbb/tick_count.h" + +#include "react/state.h" +#include "react/event.h" +#include "react/observer.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 1 - Asynchronous transactions +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example1 +{ + using namespace react; + using namespace std; + + Group group; + + class Sensor + { + public: + EventSource samples = EventSource::Create(group); + }; + + void Run() + { + cout << "Example 1 - Asynchronous transactions" << endl; + + Sensor mySensor; + + auto obs = Observer::Create([&] (const auto& events) + { + for (auto t : events) + cout << t << endl; + }, mySensor.samples); + + SyncPoint sp; + + group.EnqueueTransaction([&] + { + mySensor.samples << 30 << 31 << 31 << 32; + }, sp); + + group.EnqueueTransaction([&] + { + mySensor.samples << 40 << 41 << 51 << 62; + }, sp); + + // Waits until both transactions are completed. + // This does not mean that both transactions are interleaved. + sp.Wait(); + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 2 - Transaction merging +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example2 +{ + using namespace react; + using namespace std; + + Group group; + + class Sensor + { + public: + EventSource samples = EventSource::Create(group); + }; + + const int K = 10000; + + namespace v1 + { + void Run() + { + cout << "Example 2 - Transaction merging (no merging)" << endl; + + Sensor mySensor; + int sum = 0; + + auto obs = Observer::Create([&] (const auto& events) + { + for (int e : events) + sum += e; + }, mySensor.samples); + + SyncPoint sp; + + cout << "Executing " << K << " async transactions..."; + + auto t0 = tbb::tick_count::now(); + + for (int i=0; i < K; i++) + { + group.EnqueueTransaction([&] + { + mySensor.samples << 3 << 4 << 2 << 1; + }, sp); + } + + sp.Wait(); + + double d = (tbb::tick_count::now() - t0).seconds(); + + cout << " done." << endl; + + cout << " Sum: " << sum << endl; + cout << " Time: " << d << endl; + + cout << endl; + } + } + + namespace v2 + { + void Run() + { + cout << "Example 2 - Transaction merging (allow merging)" << endl; + + Sensor mySensor; + int sum = 0; + + auto obs = Observer::Create([&] (const auto& events) + { + for (int e : events) + sum += e; + }, mySensor.samples); + + SyncPoint sp; + + cout << "Executing " << K << " async transactions..."; + + auto t0 = tbb::tick_count::now(); + + for (int i=0; i < K; i++) + { + group.EnqueueTransaction([&] + { + mySensor.samples << 3 << 4 << 2 << 1; + }, sp, TransactionFlags::allow_merging); + } + + sp.Wait(); + + double d = (tbb::tick_count::now() - t0).seconds(); + + cout << " done." << endl; + + cout << " Sum: " << sum << endl; + cout << " Time: " << d << endl; + + cout << endl; + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Run examples +/////////////////////////////////////////////////////////////////////////////////////////////////// +int main() +{ + example1::Run(); + + example2::v1::Run(); + example2::v2::Run(); + + return 0; +} \ No newline at end of file diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h deleted file mode 100644 index d4396e8f..00000000 --- a/include/react/Algorithm.h +++ /dev/null @@ -1,196 +0,0 @@ - -// Copyright Sebastian Jeckel 2014. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#pragma once - -#include "react/detail/Defs.h" - -#include -#include -#include -#include -#include - -#include "Signal.h" -#include "EventStream.h" -#include "react/detail/graph/ConversionNodes.h" - -/*****************************************/ REACT_BEGIN /*****************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Fold -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename V, - typename E, - typename FIn, - typename S = std::decay::type, - typename F = std::decay::type -> -auto Fold(V&& init, const Events& events, FIn&& func) - -> Signal -{ - return Signal( - std::make_shared>( - std::forward(init), events.GetPtr(), std::forward(func))); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Iterate -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename V, - typename E, - typename FIn, - typename S = std::decay::type, - typename F = std::decay::type -> -auto Iterate(V&& init, const Events& events, FIn&& func) - -> Signal -{ - return Signal( - std::make_shared>( - std::forward(init), events.GetPtr(), std::forward(func))); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Hold -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename V, - typename T = std::decay::type -> -auto Hold(V&& init, const Events& events) - -> Signal -{ - return Signal( - std::make_shared>( - std::forward(init), events.GetPtr())); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Snapshot -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S, - typename E -> -auto Snapshot(const Signal& target, const Events& trigger) - -> Signal -{ - return Signal( - std::make_shared>( - target.GetPtr(), trigger.GetPtr())); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Monitor -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S -> -auto Monitor(const Signal& target) - -> Events -{ - return Events( - std::make_shared>( - target.GetPtr())); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Changed -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S -> -auto Changed(const Signal& target) - -> Events -{ - return Transform(Monitor(target), [] (const S& v) { return true; }); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ChangedTo -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename V, - typename S = std::decay::type -> -auto ChangedTo(const Signal& target, V&& value) - -> Events -{ - auto transformFunc = [=] (const S& v) { return v == value; }; - auto filterFunc = [=] (bool v) { return v == true; }; - - return Filter(Transform(Monitor(target), transformFunc), filterFunc); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Pulse -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S, - typename E -> -auto Pulse(const Signal& target, const Events& trigger) - -> Events -{ - return Events( - std::make_shared>( - target.GetPtr(), trigger.GetPtr())); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Flatten -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename TInnerValue -> -auto Flatten(const Signal>& node) - -> Events -{ - return Events( - std::make_shared, TInnerValue>>( - node.GetPtr(), node().GetPtr())); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Incrementer -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct Incrementer : public std::unary_function -{ - T operator() (T v) const { return v+1; } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Decrementer -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct Decrementer : public std::unary_function -{ - T operator() (T v) const { return v-1; } -}; - -/******************************************/ REACT_END /******************************************/ diff --git a/include/react/EventStream.h b/include/react/EventStream.h deleted file mode 100644 index 5f72a195..00000000 --- a/include/react/EventStream.h +++ /dev/null @@ -1,389 +0,0 @@ - -// Copyright Sebastian Jeckel 2014. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#pragma once - -#include "react/detail/Defs.h" - -#include -#include -#include -#include - -#include "Observer.h" -#include "react/detail/ReactiveBase.h" -#include "react/detail/ReactiveDomain.h" -#include "react/detail/graph/EventStreamNodes.h" - -/*****************************************/ REACT_BEGIN /*****************************************/ - -enum class EventToken { token }; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Events -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E = EventToken -> -class Events : public Reactive> -{ -protected: - using NodeT = REACT_IMPL::EventStreamNode; - -public: - using ValueT = E; - - Events() : - Reactive() - { - } - - explicit Events(const std::shared_ptr& ptr) : - Reactive(ptr) - { - } - - template - Events Filter(F&& f) - { - return react::Filter(*this, std::forward(f)); - } - - template - Events Transform(F&& f) - { - return react::Transform(*this, std::forward(f)); - } - - template - Observer Observe(F&& f) - { - return react::Observe(*this, std::forward(f)); - } -}; - -/******************************************/ REACT_END /******************************************/ - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -template -bool Equals(const Events& lhs, const Events& rhs) -{ - return lhs.Equals(rhs); -} - -/****************************************/ REACT_IMPL_END /***************************************/ - -/*****************************************/ REACT_BEGIN /*****************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Eventsource -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E = EventToken -> -class EventSource : public Events -{ -private: - using NodeT = REACT_IMPL::EventSourceNode; - -public: - EventSource() : - Events() - { - } - - explicit EventSource(const std::shared_ptr& ptr) : - Events(ptr) - { - } - - template - void Emit(V&& v) const - { - D::AddInput(*std::static_pointer_cast(ptr_), std::forward(v)); - } - - template ::value>::type> - void Emit() const - { - Emit(EventToken::token); - } - - const EventSource& operator<<(const E& e) const - { - Emit(e); - return *this; - } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// TempEvents -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E, - typename TOp -> -class TempEvents : public Events -{ -protected: - using NodeT = REACT_IMPL::EventOpNode; - -public: - TempEvents() : - Events() - {} - - explicit TempEvents(const std::shared_ptr& ptr) : - Events(ptr) - {} - - explicit TempEvents(std::shared_ptr&& ptr) : - Events(std::move(ptr)) - {} - - TOp StealOp() - { - return std::move(std::static_pointer_cast(ptr_)->StealOp()); - } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// MakeEventSource -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -auto MakeEventSource() - -> EventSource -{ - return EventSource( - std::make_shared>()); -} - -template -auto MakeEventSource() - -> EventSource -{ - return EventSource( - std::make_shared>()); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Merge -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename TArg1, - typename ... TArgs, - typename E = TArg1, - typename TOp = REACT_IMPL::EventMergeOp, - REACT_IMPL::EventStreamNodePtr ...> -> -auto Merge(const Events& arg1, const Events& ... args) - -> TempEvents -{ - static_assert(sizeof...(TArgs) > 0, - "react::Merge requires at least 2 arguments."); - - return TempEvents( - std::make_shared>( - arg1.GetPtr(), args.GetPtr() ...)); -} - -template -< - typename TLeftEvents, - typename TRightEvents, - typename D = TLeftEvents::DomainT, - typename TLeftVal = TLeftEvents::ValueT, - typename TRightVal = TRightEvents::ValueT, - typename E = TLeftVal, - typename TOp = REACT_IMPL::EventMergeOp, - REACT_IMPL::EventStreamNodePtr>, - class = std::enable_if< - IsEvent::value>::type, - class = std::enable_if< - IsEvent::value>::type -> -auto operator|(const TLeftEvents& lhs, const TRightEvents& rhs) - -> TempEvents -{ - return TempEvents( - std::make_shared>( - lhs.GetPtr(), rhs.GetPtr())); -} - -template -< - typename D, - typename TLeftVal, - typename TLeftOp, - typename TRightVal, - typename TRightOp, - typename E = TLeftVal, - typename TOp = REACT_IMPL::EventMergeOp -> -auto operator|(TempEvents&& lhs, TempEvents&& rhs) - -> TempEvents -{ - return TempEvents( - std::make_shared>( - lhs.StealOp(), rhs.StealOp())); -} - -template -< - typename D, - typename TLeftVal, - typename TLeftOp, - typename TRightEvents, - typename TRightVal = TRightEvents::ValueT, - typename E = TLeftVal, - typename TOp = REACT_IMPL::EventMergeOp>, - class = std::enable_if< - IsEvent::value>::type -> -auto operator|(TempEvents&& lhs, const TRightEvents& rhs) - -> TempEvents -{ - return TempEvents( - std::make_shared>( - lhs.StealOp(), rhs.GetPtr())); -} - -template -< - typename TLeftEvents, - typename D, - typename TRightVal, - typename TRightOp, - typename TLeftVal = TLeftEvents::ValueT, - typename E = TLeftVal, - typename TOp = REACT_IMPL::EventMergeOp, - TRightOp>, - class = std::enable_if< - IsEvent::value>::type -> -auto operator|(const TLeftEvents& lhs, TempEvents&& rhs) - -> TempEvents -{ - return TempEvents( - std::make_shared>( - lhs.GetPtr(), rhs.StealOp())); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Filter -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E, - typename FIn, - typename F = std::decay::type, - typename TOp = REACT_IMPL::EventFilterOp> -> -auto Filter(const Events& src, FIn&& filter) - -> TempEvents -{ - return TempEvents( - std::make_shared>( - std::forward(filter), src.GetPtr())); -} - -template -< - typename D, - typename E, - typename TOpIn, - typename FIn, - typename F = std::decay::type, - typename TOpOut = REACT_IMPL::EventFilterOp -> -auto Filter(TempEvents&& src, FIn&& filter) - -> TempEvents -{ - return TempEvents( - std::make_shared>( - std::forward(filter), src.StealOp())); -} - -template -< - typename TEvents, - typename F, - class = std::enable_if< - IsEvent::value>::type -> -auto operator&(TEvents&& src, F&& filter) - -> decltype(Filter(std::forward(src), std::forward(filter))) -{ - return Filter(std::forward(src), std::forward(filter)); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Transform -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E, - typename FIn, - typename F = std::decay::type, - typename TOp = REACT_IMPL::EventTransformOp> -> -auto Transform(const Events& src, FIn&& func) - -> TempEvents -{ - return TempEvents( - std::make_shared>( - std::forward(func), src.GetPtr())); -} - -template -< - typename D, - typename E, - typename TOpIn, - typename FIn, - typename F = std::decay::type, - typename TOpOut = REACT_IMPL::EventTransformOp -> -auto Transform(TempEvents&& src, FIn&& func) - -> TempEvents -{ - return TempEvents( - std::make_shared>( - std::forward(func), src.StealOp())); -} - -template -< - typename TEvents, - typename F, - class = std::enable_if< - IsEvent::value>::type -> -auto operator->*(TEvents&& src, F&& func) - -> decltype(Transform(std::forward(src), std::forward(func))) -{ - return Transform(std::forward(src), std::forward(func)); -} - -/******************************************/ REACT_END /******************************************/ diff --git a/include/react/Observer.h b/include/react/Observer.h deleted file mode 100644 index 2095d9de..00000000 --- a/include/react/Observer.h +++ /dev/null @@ -1,236 +0,0 @@ - -// Copyright Sebastian Jeckel 2014. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#pragma once - -#include "react/detail/Defs.h" - -#include -#include - -#include "react/detail/IReactiveNode.h" - -#include "react/detail/graph/ObserverNodes.h" - -/*****************************************/ REACT_BEGIN /*****************************************/ - -template -class Reactive; - -template -class Signal; - -template -class Events; - -enum class EventToken; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Observer -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class Observer -{ -public: - using SubjectT = REACT_IMPL::NodeBase; - using ObserverNodeT = REACT_IMPL::ObserverNode; - - Observer() : - ptr_{ nullptr }, - subject_{ nullptr } - { - } - - Observer(ObserverNodeT* ptr, const std::shared_ptr& subject) : - ptr_{ ptr }, - subject_{ subject } - { - } - - const ObserverNodeT* GetPtr() const - { - return ptr_; - } - - bool Detach() - { - if (ptr_ == nullptr) - return false; - - D::Observers().Unregister(ptr_); - return true; - } - -private: - // Ownership managed by registry - ObserverNodeT* ptr_; - - // While the observer handle exists, the subject is not destroyed - std::shared_ptr subject_; -}; - -/******************************************/ REACT_END /******************************************/ - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ObserverRegistry -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class ObserverRegistry -{ -public: - using SubjectT = NodeBase; - -private: - class Entry_ - { - public: - Entry_(std::unique_ptr&& obs, SubjectT* aSubject) : - obs_{ std::move(obs) }, - Subject{ aSubject } - { - } - - Entry_(Entry_&& other) : - obs_(std::move(other.obs_)), - Subject(other.Subject) - { - } - - SubjectT* Subject; - - private: - // Manage lifetime - std::unique_ptr obs_; - }; - -public: - void Register(std::unique_ptr&& obs, SubjectT* subject) - { - auto* raw = obs.get(); - observerMap_.emplace(raw, Entry_(std::move(obs),subject)); - } - - void Unregister(IObserverNode* obs) - { - obs->Detach(); - observerMap_.erase(obs); - } - - void UnregisterFrom(SubjectT* subject) - { - auto it = observerMap_.begin(); - while (it != observerMap_.end()) - { - if (it->second.Subject == subject) - { - it->first->Detach(); - it = observerMap_.erase(it); - } - else - { - ++it; - } - } - } - -private: - std::unordered_map observerMap_; -}; - -/****************************************/ REACT_IMPL_END /***************************************/ - -/*****************************************/ REACT_BEGIN /*****************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Observe -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename F, - typename TArg -> -auto Observe(const Signal& subject, F&& func) - -> Observer -{ - std::unique_ptr< REACT_IMPL::ObserverNode> pUnique( - new REACT_IMPL::SignalObserverNode( - subject.GetPtr(), std::forward(func))); - - auto* raw = pUnique.get(); - - D::Observers().Register(std::move(pUnique), subject.GetPtr().get()); - - return Observer(raw, subject.GetPtr()); -} - -template -< - typename D, - typename F, - typename TArg, - typename = std::enable_if< - ! std::is_same::value>::type -> -auto Observe(const Events& subject, F&& func) - -> Observer -{ - std::unique_ptr< REACT_IMPL::ObserverNode> pUnique( - new REACT_IMPL::EventObserverNode( - subject.GetPtr(), std::forward(func))); - - auto* raw = pUnique.get(); - - D::Observers().Register(std::move(pUnique), subject.GetPtr().get()); - - return Observer(raw, subject.GetPtr()); -} - -template -< - typename D, - typename F -> -auto Observe(const Events& subject, F&& func) - -> Observer -{ - auto f = [func] (EventToken _) { func(); }; - std::unique_ptr< REACT_IMPL::ObserverNode> pUnique( - new REACT_IMPL::EventObserverNode( - subject.GetPtr(), std::move(f))); - - auto* raw = pUnique.get(); - - D::Observers().Register(std::move(pUnique), subject.GetPtr().get()); - - return Observer(raw, subject.GetPtr()); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// DetachAllObservers -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - template class TNode, - typename TArg -> -void DetachAllObservers(const Reactive>& subject) -{ - D::Observers().UnregisterFrom(subject.GetPtr().get()); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// DetachThisObserver -/////////////////////////////////////////////////////////////////////////////////////////////////// -inline void DetachThisObserver() -{ - REACT_IMPL::current_observer_state_::shouldDetach = true; -} - -/******************************************/ REACT_END /******************************************/ diff --git a/include/react/ReactiveObject.h b/include/react/ReactiveObject.h deleted file mode 100644 index 639dd11c..00000000 --- a/include/react/ReactiveObject.h +++ /dev/null @@ -1,134 +0,0 @@ - -// Copyright Sebastian Jeckel 2014. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#pragma once - -#include "react/detail/Defs.h" - -#include "react/detail/ReactiveDomain.h" - -/*****************************************/ REACT_BEGIN /*****************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ReactiveObject -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class ReactiveObject -{ -public: - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// Aliases - /////////////////////////////////////////////////////////////////////////////////////////////////// - using DomainT = D; - - template - using SignalT = Signal; - - template - using VarSignalT = VarSignal; - - template - using RefSignalT = RefSignal; - - template - using VarRefSignalT = VarRefSignal; - - template - using EventsT = Events; - - template - using EventSourceT = EventSource; - - using ObserverT = Observer; - - using ReactiveLoopT = ReactiveLoop; - - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// MakeVar - /////////////////////////////////////////////////////////////////////////////////////////////////// - template - < - typename V, - typename S = std::decay::type, - class = std::enable_if< - !IsSignal::value>::type - > - static auto MakeVar(V&& value) - -> VarSignalT - { - return REACT::MakeVar(std::forward(value)); - } - - ////////////////////////////////////////////////////////////////////////////////////////////////////////// - // MakeVar (higher order) - ////////////////////////////////////////////////////////////////////////////////////////////////////////// - template - < - typename V, - typename S = std::decay::type, - typename TInner = S::ValueT, - class = std::enable_if< - IsSignal::value>::type - > - static auto MakeVar(V&& value) - -> VarSignalT> - { - return REACT::MakeVar(std::forward(value)); - } - - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// MakeSignal - /////////////////////////////////////////////////////////////////////////////////////////////////// - template - < - typename F, - typename ... TArgs - > - static auto MakeSignal(F&& func, const SignalT& ... args) - -> SignalT::type> - { - using S = typename std::result_of::type; - - return REACT::MakeSignal(std::forward(func), args ...); - } - - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// MakeEventSource - /////////////////////////////////////////////////////////////////////////////////////////////////// - template - static auto MakeEventSource() - -> EventSourceT - { - return REACT::MakeEventSource(); - } - - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// Flatten macros - /////////////////////////////////////////////////////////////////////////////////////////////////// - // Todo: Add safety wrapper + static assert to check for this for ReactiveObject - // Note: Using static_cast rather than -> return type, because when using lambda for inline class - // initialization, decltype did not recognize the parameter r - #define REACTIVE_REF(obj, name) \ - Flatten( \ - MakeSignal( \ - [] (REACT_IMPL::Identity::Type::ValueT::type r) \ - { \ - return static_cast::Type>(r.name); \ - }, \ - obj)) - - #define REACTIVE_PTR(obj, name) \ - Flatten( \ - MakeSignal( \ - [] (REACT_IMPL::Identity::Type::ValueT r) \ - { \ - REACT_ASSERT(r != nullptr); \ - return static_castname)>::Type>(r->name); \ - }, \ - obj)) -}; - -/******************************************/ REACT_END /******************************************/ diff --git a/include/react/Reactor.h b/include/react/Reactor.h deleted file mode 100644 index c756f715..00000000 --- a/include/react/Reactor.h +++ /dev/null @@ -1,70 +0,0 @@ - -// Copyright Sebastian Jeckel 2014. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#pragma once - -#ifndef REACT_DISABLE_REACTORS - -#include "react/detail/Defs.h" - -#include -#include - -#include "react/common/Util.h" - -#include "react/EventStream.h" -#include "react/detail/ReactiveBase.h" -#include "react/detail/ReactiveDomain.h" -#include "react/detail/graph/ReactorNodes.h" - -/*****************************************/ REACT_BEGIN /*****************************************/ - -template -class ReactiveLoop -{ -public: - class Context; - - using NodeT = REACT_IMPL::ReactorNode; - - class Context - { - public: - Context(NodeT& node) : - node_{ node } - { - } - - template - E& Await(const Events& evn) - { - return node_.Await(evn.GetPtr()); - } - - template - void RepeatUntil(const Events& evn, F func) - { - node_.RepeatUntil(evn.GetPtr(), func); - } - - private: - NodeT& node_; - }; - - template - ReactiveLoop(F&& func) : - nodePtr_{ new REACT_IMPL::ReactorNode(std::forward(func)) } - { - nodePtr_->StartLoop(); - } - -private: - std::unique_ptr nodePtr_; -}; - -/******************************************/ REACT_END /******************************************/ - -#endif //REACT_DISABLE_REACTORS \ No newline at end of file diff --git a/include/react/Signal.h b/include/react/Signal.h deleted file mode 100644 index a7f77987..00000000 --- a/include/react/Signal.h +++ /dev/null @@ -1,665 +0,0 @@ - -// Copyright Sebastian Jeckel 2014. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#pragma once - -#include "react/detail/Defs.h" - -#include -#include -#include -#include -#include - -#include "react/common/Util.h" -#include "react/detail/ReactiveBase.h" -#include "react/detail/ReactiveDomain.h" -#include "react/detail/graph/SignalNodes.h" - -/*****************************************/ REACT_BEGIN /*****************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Signal -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S -> -class Signal : public Reactive> -{ -protected: - using NodeT = REACT_IMPL::SignalNode; - -public: - using ValueT = S; - - Signal() : - Reactive() - {} - - explicit Signal(const std::shared_ptr& ptr) : - Reactive(ptr) - {} - - explicit Signal(std::shared_ptr&& ptr) : - Reactive(std::move(ptr)) - {} - - const S& Value() const - { - return ptr_->ValueRef(); - } - - const S& operator()(void) const - { - return Value(); - } - - template - Observer Observe(F&& f) - { - return react::Observe(*this, std::forward(f)); - } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// VarSignal -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S -> -class VarSignal : public Signal -{ -protected: - using NodeT = REACT_IMPL::VarNode; - -public: - VarSignal() : - Signal() - {} - - explicit VarSignal(const std::shared_ptr& ptr) : - Signal(ptr) - {} - - explicit VarSignal(std::shared_ptr&& ptr) : - Signal(std::move(ptr)) - {} - - template - void Set(V&& newValue) const - { - D::AddInput(*std::static_pointer_cast(ptr_), std::forward(newValue)); - } - - template - const VarSignal& operator<<=(V&& newValue) const - { - Set(std::forward(newValue)); - return *this; - } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// TempSignal -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S, - typename TOp -> -class TempSignal : public Signal -{ -protected: - using NodeT = REACT_IMPL::SignalOpNode; - -public: - TempSignal() : - Signal() - {} - - explicit TempSignal(const std::shared_ptr& ptr) : - Signal(ptr) - {} - - explicit TempSignal(std::shared_ptr&& ptr) : - Signal(std::move(ptr)) - {} - - TOp StealOp() - { - return std::move(std::static_pointer_cast(ptr_)->StealOp()); - } -}; - -/******************************************/ REACT_END /******************************************/ - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -template -bool Equals(const Signal& lhs, const Signal& rhs) -{ - return lhs.Equals(rhs); -} - -template -bool Equals(const VarSignal& lhs, const VarSignal& rhs) -{ - return lhs.Equals(rhs); -} - -/****************************************/ REACT_IMPL_END /***************************************/ - -/*****************************************/ REACT_BEGIN /*****************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// MakeVar -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename V, - typename S = std::decay::type, - class = std::enable_if< - ! IsReactive::value>::type -> -auto MakeVar(V&& value) - -> VarSignal -{ - return VarSignal( - std::make_shared>( - std::forward(value))); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// MakeVar (higher order reactives) -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename V, - typename S = std::decay::type, - typename TInner = S::ValueT, - class = std::enable_if< - IsSignal::value>::type -> -auto MakeVar(V&& value) - -> VarSignal> -{ - return VarSignal>( - std::make_shared>>( - std::forward(value))); -} - -template -< - typename D, - typename V, - typename S = std::decay::type, - typename TInner = S::ValueT, - class = std::enable_if< - IsEvent::value>::type -> -auto MakeVar(V&& value) - -> VarSignal> -{ - return VarSignal>( - std::make_shared>>( - std::forward(value))); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// MakeSignal -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename FIn, - typename ... TArgs, - typename F, //= std::decay::type, - typename S, //= std::result_of::type, - typename TOp //= REACT_IMPL::FunctionOp ...> -> -auto MakeSignal(FIn&& func, const Signal& ... args) - -> TempSignal -{ - static_assert(sizeof...(TArgs) > 0, - "react::MakeSignal requires at least 1 signal dependency."); - - return TempSignal( - std::make_shared>( - std::forward(func), args.GetPtr() ... )); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Unary operators -/////////////////////////////////////////////////////////////////////////////////////////////////// -#define DECLARE_OP(op,name) \ -template \ -struct name ## OpFunctor \ -{ \ - T operator()(const T& v) const { return op v; } \ -}; \ - \ -template \ -< \ - typename TSignal, \ - typename D = TSignal::DomainT, \ - typename TVal = TSignal::ValueT, \ - typename F = name ## OpFunctor, \ - typename S = std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp>, \ - class = std::enable_if< \ - IsSignal::value>::type \ -> \ -auto operator ## op(const TSignal& arg) \ - -> TempSignal \ -{ \ - return TempSignal( \ - std::make_shared>( \ - F(), arg.GetPtr())); \ -} - -DECLARE_OP(+, UnaryPlus); -DECLARE_OP(-, UnaryMinus); -DECLARE_OP(!, LogicalNegation); -DECLARE_OP(++, Increment); -DECLARE_OP(--, Decrement); - -#undef DECLARE_OP - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Binary operators -/////////////////////////////////////////////////////////////////////////////////////////////////// -#define DECLARE_OP(op,name) \ -template \ -struct name ## OpFunctor \ -{ \ - auto operator()(const L& lhs, const R& rhs) const \ - -> decltype(std::declval() op std::declval()) \ - { \ - return lhs op rhs; \ - } \ -}; \ - \ -template \ -struct name ## OpRFunctor \ -{ \ - name ## OpRFunctor(name ## OpRFunctor&& other) : \ - LeftVal{ std::move(other.LeftVal) } \ - {} \ - \ - template \ - name ## OpRFunctor(T&& val) : \ - LeftVal{ std::forward(val) } \ - {} \ - \ - name ## OpRFunctor(const name ## OpRFunctor& other) = delete; \ - \ - auto operator()(const R& rhs) const \ - -> decltype(std::declval() op std::declval()) \ - { \ - return LeftVal op rhs; \ - } \ - \ - L LeftVal; \ -}; \ - \ -template \ -struct name ## OpLFunctor \ -{ \ - name ## OpLFunctor(name ## OpLFunctor&& other) : \ - RightVal{ std::move(other.RightVal) } \ - {} \ - \ - template \ - name ## OpLFunctor(T&& val) : \ - RightVal{ std::forward(val) } \ - {} \ - \ - name ## OpLFunctor(const name ## OpLFunctor& other) = delete; \ - \ - auto operator()(const L& lhs) const \ - -> decltype(std::declval() op std::declval()) \ - { \ - return lhs op RightVal; \ - } \ - \ - R RightVal; \ -}; \ - \ -template \ -< \ - typename TLeftSignal, \ - typename TRightSignal, \ - typename D = TLeftSignal::DomainT, \ - typename TLeftVal = TLeftSignal::ValueT, \ - typename TRightVal = TRightSignal::ValueT, \ - typename F = name ## OpFunctor, \ - typename S = std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp, \ - REACT_IMPL::SignalNodePtr>, \ - class = std::enable_if< \ - IsSignal::value>::type, \ - class = std::enable_if< \ - IsSignal::value>::type \ -> \ -auto operator ## op(const TLeftSignal& lhs, const TRightSignal& rhs) \ - -> TempSignal \ -{ \ - return TempSignal( \ - std::make_shared>( \ - F(), lhs.GetPtr(), rhs.GetPtr())); \ -} \ - \ -template \ -< \ - typename TLeftSignal, \ - typename TRightValIn, \ - typename D = TLeftSignal::DomainT, \ - typename TLeftVal = TLeftSignal::ValueT, \ - typename TRightVal = std::decay::type, \ - typename F = name ## OpLFunctor, \ - typename S = std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp>, \ - class = std::enable_if< \ - IsSignal::value>::type, \ - class = std::enable_if< \ - ! IsSignal::value>::type \ -> \ -auto operator ## op(const TLeftSignal& lhs, TRightValIn&& rhs) \ - -> TempSignal \ -{ \ - return TempSignal( \ - std::make_shared>( \ - F(std::forward(rhs)), lhs.GetPtr())); \ -} \ - \ -template \ -< \ - typename TLeftValIn, \ - typename TRightSignal, \ - typename D = TRightSignal::DomainT, \ - typename TLeftVal = std::decay::type, \ - typename TRightVal = TRightSignal::ValueT, \ - typename F = name ## OpRFunctor, \ - typename S = std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp>, \ - class = std::enable_if< \ - ! IsSignal::value>::type, \ - class = std::enable_if< \ - IsSignal::value>::type \ -> \ -auto operator ## op(TLeftValIn&& lhs, const TRightSignal& rhs) \ - -> TempSignal \ -{ \ - return TempSignal( \ - std::make_shared>( \ - F(std::forward(lhs)), rhs.GetPtr())); \ -} \ -template \ -< \ - typename D, \ - typename TLeftVal, \ - typename TLeftOp, \ - typename TRightVal, \ - typename TRightOp, \ - typename F = name ## OpFunctor, \ - typename S = std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp \ -> \ -auto operator ## op(TempSignal&& lhs, \ - TempSignal&& rhs) \ - -> TempSignal \ -{ \ - return TempSignal( \ - std::make_shared>( \ - F(), lhs.StealOp(), rhs.StealOp())); \ -} \ - \ -template \ -< \ - typename D, \ - typename TLeftVal, \ - typename TLeftOp, \ - typename TRightSignal, \ - typename TRightVal = TRightSignal::ValueT, \ - typename F = name ## OpFunctor, \ - typename S = std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp>, \ - class = std::enable_if< \ - IsSignal::value>::type \ -> \ - auto operator ## op(TempSignal&& lhs, \ - const TRightSignal& rhs) \ - -> TempSignal \ -{ \ - return TempSignal( \ - std::make_shared>( \ - F(), lhs.StealOp(), rhs.GetPtr())); \ -} \ - \ -template \ -< \ - typename TLeftSignal, \ - typename D, \ - typename TRightVal, \ - typename TRightOp, \ - typename TLeftVal = TLeftSignal::ValueT, \ - typename F = name ## OpFunctor, \ - typename S = std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp>, \ - class = std::enable_if< \ - IsSignal::value>::type \ -> \ -auto operator ## op(const TLeftSignal& lhs, TempSignal&& rhs) \ - -> TempSignal \ -{ \ - return TempSignal( \ - std::make_shared>( \ - F(), lhs.GetPtr(), rhs.StealOp())); \ -} \ - \ -template \ -< \ - typename D, \ - typename TLeftVal, \ - typename TLeftOp, \ - typename TRightValIn, \ - typename TRightVal = std::decay::type, \ - typename F = name ## OpLFunctor, \ - typename S = std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp, \ - class = std::enable_if< \ - ! IsSignal::value>::type \ -> \ -auto operator ## op(TempSignal&& lhs, TRightValIn&& rhs) \ - -> TempSignal \ -{ \ - return TempSignal( \ - std::make_shared>( \ - F(std::forward(rhs)), lhs.StealOp())); \ -} \ - \ -template \ -< \ - typename TLeftValIn, \ - typename D, \ - typename TRightVal, \ - typename TRightOp, \ - typename TLeftVal = std::decay::type, \ - typename F = name ## OpRFunctor, \ - typename S = std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp, \ - class = std::enable_if< \ - ! IsSignal::value>::type \ -> \ -auto operator ## op(TLeftValIn&& lhs, TempSignal&& rhs) \ - -> TempSignal \ -{ \ - return TempSignal( \ - std::make_shared>( \ - F(std::forward(lhs)), rhs.StealOp())); \ -} - -DECLARE_OP(+, Addition); -DECLARE_OP(-, Subtraction); -DECLARE_OP(*, Multiplication); -DECLARE_OP(/, Division); -DECLARE_OP(%, Modulo); - -DECLARE_OP(==, Equal); -DECLARE_OP(!=, NotEqual); -DECLARE_OP(<, Less); -DECLARE_OP(<=, LessEqual); -DECLARE_OP(>, Greater); -DECLARE_OP(>=, GreaterEqual); - -DECLARE_OP(&&, LogicalAnd); -DECLARE_OP(||, LogicalOr); - -#undef DECLARE_OP - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// InputPack - Wraps several nodes in a tuple. Create with comma operator. -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename ... TValues -> -struct InputPack -{ - std::tuple& ...> Data; - - template - InputPack(const Signal& first, const Signal& second) : - Data(std::tie(first, second)) - { - } - - template - InputPack(const InputPack& curArgs, const Signal& newArg) : - Data(std::tuple_cat(curArgs.Data, std::tie(newArg))) - { - } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Comma operator overload to create input pack from 2 nodes. -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename TLeftVal, - typename TRightVal -> -auto operator,(const Signal& a, const Signal& b) - -> InputPack -{ - return InputPack(a, b); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Comma operator overload to append node to existing input pack. -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename ... TCurValues, - typename TAppendValue -> -auto operator,(const InputPack& cur, const Signal& append) - -> InputPack -{ - return InputPack(cur, append); -} - -/******************************************/ REACT_END /******************************************/ - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -template -< - typename D, - typename F, - typename ... TValues -> -struct ApplyHelper -{ - static inline auto MakeSignal(F&& func, const Signal& ... args) - -> Signal() ...))> - { - return D::MakeSignal(std::forward(func), args ...); - } -}; - -/****************************************/ REACT_IMPL_END /***************************************/ - -/*****************************************/ REACT_BEGIN /*****************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// operator->* overload to connect inputs to a function and return the resulting node. -/////////////////////////////////////////////////////////////////////////////////////////////////// -// Single input -template -< - typename D, - typename F, - template class TSignal, - typename TValue, - class = std::enable_if< - IsSignal>::value>::type -> -auto operator->*(const TSignal& inputNode, F&& func) - -> Signal::type> -{ - return D::MakeSignal(std::forward(func), inputNode); -} - -// Multiple inputs -template -< - typename D, - typename F, - typename ... TSignals -> -auto operator->*(const InputPack& inputPack, F&& func) - -> Signal::type> -{ - return apply( - REACT_IMPL::ApplyHelper::MakeSignal, - std::tuple_cat(std::forward_as_tuple(std::forward(func)), inputPack.Data)); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Flatten -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename TInnerValue -> -auto Flatten(const Signal>& node) - -> Signal -{ - return Signal( - std::make_shared, TInnerValue>>( - node.GetPtr(), node.Value().GetPtr())); -} - -/******************************************/ REACT_END /******************************************/ diff --git a/include/react/algorithm.h b/include/react/algorithm.h new file mode 100644 index 00000000..e6c3a307 --- /dev/null +++ b/include/react/algorithm.h @@ -0,0 +1,305 @@ + +// Copyright Sebastian Jeckel 2017. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef REACT_ALGORITHM_H_INCLUDED +#define REACT_ALGORITHM_H_INCLUDED + +#pragma once + +#include "react/detail/defs.h" + +#include +#include +#include + +#include "react/api.h" +#include "react/state.h" +#include "react/event.h" + +#include "react/detail/algorithm_nodes.h" + +/*****************************************/ REACT_BEGIN /*****************************************/ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Holds the most recent event in a state +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +auto Hold(const Group& group, T&& initialValue, const Event& evnt) -> State +{ + using REACT_IMPL::HoldNode; + using REACT_IMPL::SameGroupOrLink; + using REACT_IMPL::CreateWrappedNode; + + return CreateWrappedNode, HoldNode>( + group, std::forward(initialValue), SameGroupOrLink(group, evnt)); +} + +template +auto Hold(T&& initialValue, const Event& evnt) -> State + { return Hold(evnt.GetGroup(), std::forward(initialValue), evnt); } + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Emits value changes of target state. +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +auto Monitor(const Group& group, const State& state) -> Event +{ + using REACT_IMPL::MonitorNode; + using REACT_IMPL::SameGroupOrLink; + using REACT_IMPL::CreateWrappedNode; + + return CreateWrappedNode, MonitorNode>( + group, SameGroupOrLink(group, state)); +} + +template +auto Monitor(const State& state) -> Event + { return Monitor(state.GetGroup(), state); } + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Iteratively combines state value with values from event stream (aka Fold) +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +auto Iterate(const Group& group, T&& initialValue, F&& func, const Event& evnt) -> State +{ + using REACT_IMPL::IterateNode; + using REACT_IMPL::SameGroupOrLink; + using REACT_IMPL::CreateWrappedNode; + + using FuncType = typename std::decay::type; + + return CreateWrappedNode, IterateNode>( + group, std::forward(initialValue), std::forward(func), SameGroupOrLink(group, evnt)); +} + +template +auto IterateByRef(const Group& group, T&& initialValue, F&& func, const Event& evnt) -> State +{ + using REACT_IMPL::IterateByRefNode; + using REACT_IMPL::SameGroupOrLink; + using REACT_IMPL::CreateWrappedNode; + + using FuncType = typename std::decay::type; + + return CreateWrappedNode, IterateByRefNode>( + group, std::forward(initialValue), std::forward(func), SameGroupOrLink(group, evnt)); +} + +template +auto Iterate(T&& initialValue, F&& func, const Event& evnt) -> State + { return Iterate(evnt.GetGroup(), std::forward(initialValue), std::forward(func), evnt); } + +template +auto IterateByRef(T&& initialValue, F&& func, const Event& evnt) -> State + { return IterateByRef(evnt.GetGroup(), std::forward(initialValue), std::forward(func), evnt); } + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Iterate - Synced +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +auto Iterate(const Group& group, T&& initialValue, F&& func, const Event& evnt, const State& ... states) -> State +{ + using REACT_IMPL::SyncedIterateNode; + using REACT_IMPL::SameGroupOrLink; + using REACT_IMPL::CreateWrappedNode; + + using FuncType = typename std::decay::type; + + return CreateWrappedNode, SyncedIterateNode>( + group, std::forward(initialValue), std::forward(func), SameGroupOrLink(group, evnt), SameGroupOrLink(group, states) ...); +} + +template +auto IterateByRef(const Group& group, T&& initialValue, F&& func, const Event& evnt, const State& ... states) -> State +{ + using REACT_IMPL::SyncedIterateByRefNode; + using REACT_IMPL::SameGroupOrLink; + using REACT_IMPL::CreateWrappedNode; + + using FuncType = typename std::decay::type; + + return CreateWrappedNode, SyncedIterateByRefNode>( + group, std::forward(initialValue), std::forward(func), SameGroupOrLink(group, evnt), SameGroupOrLink(group, states) ...); +} + +template +auto Iterate(T&& initialValue, F&& func, const Event& evnt, const State& ... states) -> State + { return Iterate(evnt.GetGroup(), std::forward(initialValue), std::forward(func), evnt, states ...); } + +template +auto IterateByRef(T&& initialValue, F&& func, const Event& evnt, const State& ... states) -> State + { return IterateByRef(evnt.GetGroup(), std::forward(initialValue), std::forward(func), evnt, states ...); } + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Snapshot - Sets state value to value of other state when event is received +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +auto Snapshot(const Group& group, const State& state, const Event& evnt) -> State +{ + using REACT_IMPL::SnapshotNode; + using REACT_IMPL::SameGroupOrLink; + using REACT_IMPL::CreateWrappedNode; + + return CreateWrappedNode, SnapshotNode>( + group, SameGroupOrLink(group, state), SameGroupOrLink(group, evnt)); +} + +template +auto Snapshot(const State& state, const Event& evnt) -> State + { return Snapshot(state.GetGroup(), state, evnt); } + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Pulse - Emits value of target state when event is received +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +auto Pulse(const Group& group, const State& state, const Event& evnt) -> Event +{ + using REACT_IMPL::PulseNode; + using REACT_IMPL::SameGroupOrLink; + using REACT_IMPL::CreateWrappedNode; + + return CreateWrappedNode, PulseNode>( + group, SameGroupOrLink(group, state), SameGroupOrLink(group, evnt)); +} + +template +auto Pulse(const State& state, const Event& evnt) -> Event + { return Pulse(state.GetGroup(), state, evnt); } + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Flatten +/////////////////////////////////////////////////////////////////////////////////////////////////// +template class TState, + typename = std::enable_if_t, TState>>> +auto Flatten(const Group& group, const State>& state) -> State +{ + using REACT_IMPL::FlattenStateNode; + using REACT_IMPL::SameGroupOrLink; + using REACT_IMPL::CreateWrappedNode; + + return CreateWrappedNode, FlattenStateNode>(group, SameGroupOrLink(group, state)); +} + +template class TState, + typename = std::enable_if_t, TState>>> +auto Flatten(const State>& state) -> State + { return Flatten(state.GetGroup(), state); } + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// FlattenList +/////////////////////////////////////////////////////////////////////////////////////////////////// +template