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 d5d7eda7..39bf846c 100644 --- a/README.md +++ b/README.md @@ -1,262 +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) library for C++11. +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. -It provides abstractions to simplify the implementation of reactive behaviour. -As an alternative to raw callbacks, it offers the following benefits: -* Less boilerplate code; -* consistent updating without redundant calculations or glitches; -* implicit parallelization. +Here's a simple example: -#### Compiling - -So far, the build has only been tested in Visual Studio 2013 as it's the development environment I'm using. -The Intel C++ Compiler 14.0 with Visual Studio 2012/13 is theoretically supported as well, but last I checked, it did not compile anymore due to [some bugs]() with C++11 support. - -You are encouraged to try compiling it with other C++11 compilers and tell me why it doesn't work :) - -###### 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 self-updating reactive variables. -They can be combined to expressions to create new signals, which are automatically recalculated whenever one of their data dependencies changes. - -```C++ -#include "react/Domain.h" -#include "react/Signal.h" -///... -using namespace react; - -REACTIVE_DOMAIN(D); - -// Two variable that can be manipulated imperatively -auto width = D::MakeVar(1); -auto height = D::MakeVar(2); - -auto area = width * height; - -cout << "area: " << area() << endl; // => area: 2 - -// Width changed, so area is re-calculated automatically -width <<= 10; - -cout << "area: " << area() << endl; // => area: 20 -``` - -#### Events - -Event streams represent flows of discrete values. -They are first-class objects and can be merged, filtered, transformed or composed to more complex types. - -```C++ -#include "react/Domain.h" -#include "react/Event.h" -#include "react/Observer.h" -//... -using namespace std; -using namespace react; - -REACTIVE_DOMAIN(D); - -// Two event sources -auto leftClicked = D::MakeEventSource(); -auto rightClicked = D::MakeEventSource(); - -// Merge both event streams and register an observer -auto clickObserver = (leftClicked | rightClicked) - .Observe([] { cout << "button clicked!" << endl; }); -``` - -#### Implicit parallelism - -The change propagation is handled implicitly by a _propagation engine_. -Depending on the selected engine, independent propagation paths are automatically parallelized. - -```C++ -#include "react/Domain.h" -#include "react/Signal.h" -#include "react/engine/ToposortEngine.h" -//... -using namespace react; - - -// Sequential -{ - REACTIVE_DOMAIN(D, ToposortEngine); - - auto a = D::MakeVar(1); - auto b = D::MakeVar(2); - auto c = D::MakeVar(3); - - auto x = (a + b) * c; - - b <<= 20; -} - -// Parallel -{ - REACTIVE_DOMAIN(D, ToposortEngine); - - auto in = D::MakeVar(0); - - auto op1 = in ->* [] (int in) - { - int result = in /* Costly operation #1 */; - return result; - }; - - auto op2 = in ->* [] (int in) - { - int result = in /* Costly operation #2 */; - return result; - }; - - auto out = op1 + op2; - - // op1 and op2 can be re-calculated in parallel - in <<= 123456789; -} - -// Queued input -{ - REACTIVE_DOMAIN(D, ToposortEngine); - - auto a = D::MakeVar(1); - auto b = D::MakeVar(2); - auto c = D::MakeVar(3); - - auto x = (a + b) * c; - - // Thread-safe input (Note: unspecified order) - std::thread t1{ [&] { a <<= 10; } }; - std::thread t2{ [&] { b <<= 100; } }; - std::thread t3{ [&] { a <<= 1000; } }; - std::thread t4{ [&] { b <<= 10000; } }; - - t1.join(); t2.join(); t3.join(); t4.join(); -} ``` - -#### Reactive loops -(Note: Experimental) - -```C++ -#include "react/Domain.h" -#include "react/Event.h" -#include "react/Reactor.h" -//... -using namespace std; 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/Domain.h" -#include "react/Signal.h" -#include "react/ReactiveObject.h" -//... -using namespace std; -using namespace react; - -REACTIVE_DOMAIN(D); - -class Company : public ReactiveObject -{ -public: - VarSignalT 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. - Company(const char* name) : - Name{ MakeVar(string(name)) } - {} +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. - bool operator==(const Company&) const; -}; - -class Manager : public ReactiveObject -{ -public: - VarSignalT CurrentCompany; - ObserverT NameObserver; - - Manager(initialCompany& company) : - CurrentCompany{ MakeVar(ref(company)) }, - NameObserver - { - // Reactive reference to inner event stream of signal - REACTIVE_REF(CurrentCompany, Name) - .Observe([] (const string& name) { - cout << "Manager: Now managing " << name << endl; - }); - } - {} -}; - -Company company1{ "Cellnet" }; -Company company2{ "Borland" }; - -Manager manager{ company1 }; - -company1.Name <<= string("BT Cellnet"); -company2.Name <<= string("Inprise"); - -// Observer moves from company1.Name to company2.Name -manager.CurrentCompany <<= ref(company2); - -company1.Name <<= string("O2"); -company2.Name <<= string("Borland"); -``` +## 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 51% rename from src/benchmark/BenchmarkGrid.h rename to benchmarks/src/BenchmarkGrid.h index b64d6f27..ad2d90af 100644 --- a/src/benchmark/BenchmarkGrid.h +++ b/benchmarks/src/BenchmarkGrid.h @@ -1,11 +1,16 @@ -// 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 @@ -14,50 +19,49 @@ #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) { @@ -71,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()); } }; @@ -119,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(SubtreeDomain, SubtreeEngine); - -void runBenchmarkGrid(std::ostream& out) +/*void runBenchmarkGrid(std::ostream& out) { RUN_BENCHMARK(out, 5, Benchmark_Grid, BenchmarkParams_Grid(20, 10000), ToposortSTDomain, ToposortDomain, PulsecountDomain); @@ -50,9 +39,9 @@ void runBenchmarkGrid(std::ostream& out) RUN_BENCHMARK(out, 5, Benchmark_Grid, BenchmarkParams_Grid(50, 10000), ToposortSTDomain, ToposortDomain, PulsecountDomain); -} +}*/ -void runBenchmarkRandom(std::ostream& out) +/*void runBenchmarkRandom(std::ostream& out) { const auto w = 20; const auto h = 11; @@ -77,7 +66,7 @@ void runBenchmarkRandom(std::ostream& out) seed1 *= 2; seed2 *= 2; } -} +}*/ void runBenchmarkFanout(std::ostream& out) { @@ -90,14 +79,14 @@ void runBenchmarkFanout(std::ostream& out) //RUN_BENCHMARK(out, 5, Benchmark_Fanout, BenchmarkParams_Fanout(1000, 10000, 0), // ToposortSTDomain, ToposortDomain, ELMDomain, PulsecountDomain, SourceSetDomain); - RUN_BENCHMARK(out, 3, Benchmark_Fanout, BenchmarkParams_Fanout(10, 10, 10), + /* 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, PulsecountDomain); RUN_BENCHMARK(out, 3, Benchmark_Fanout, BenchmarkParams_Fanout(1000, 10, 10), - ToposortSTDomain, ToposortDomain, PulsecountDomain); + ToposortSTDomain, ToposortDomain, PulsecountDomain);*/ } void runBenchmarkSequence(std::ostream& out) @@ -111,14 +100,14 @@ void runBenchmarkSequence(std::ostream& out) //RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(1000, 10000, 0), // 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(10, 10, 10), + // ToposortSTDomain, ToposortDomain, PulsecountDomain); - RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(100, 10, 10), - ToposortSTDomain, ToposortDomain, PulsecountDomain); + //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, PulsecountDomain); + //RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(1000, 10, 10), + // ToposortSTDomain, ToposortDomain, PulsecountDomain); } void runBenchmarkLifeSim(std::ostream& out) @@ -129,8 +118,8 @@ void runBenchmarkLifeSim(std::ostream& out) //RUN_BENCHMARK(std::cout, 1, Benchmark_LifeSim, BenchmarkParams_LifeSim(250, 30, 10000), // SourceSetDomain, PulsecountDomain); - RUN_BENCHMARK(out, 1, Benchmark_LifeSim, BenchmarkParams_LifeSim(100, 15, 10000), - ToposortSTDomain, ToposortDomain, PulsecountDomain); + //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); @@ -140,8 +129,8 @@ 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); @@ -158,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); @@ -182,6 +171,9 @@ void debugBenchmarks() //RUN_BENCHMARK(std::cout, 1, Benchmark_Random, BenchmarkParams_Random(10, 25, 10, 0, 10, 25, 25, false), // 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; @@ -201,31 +193,21 @@ void debugBenchmarks() //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 } 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, PulsecountDomain, SubtreeDomain); //RUN_BENCHMARK(std::cout, 1, Benchmark_LifeSim, BenchmarkParams_LifeSim(100, 15, 10000), - // ToposortDomain); + //ToposortSTDomainConc); } } // ~anonymous namespace @@ -234,5 +216,12 @@ int main() { //runBenchmarks(); //debugBenchmarks(); - profileBenchmark(); + //profileBenchmark(); +} + +#endif + +int main() +{ + return 0; } \ No newline at end of file diff --git a/doc/media/reactives1.graphml b/doc/media/reactives1.graphml deleted file mode 100644 index aa855cad..00000000 --- a/doc/media/reactives1.graphml +++ /dev/null @@ -1,151 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - a - - - - - - - - - - - - - - - - - b - - - - - - - - - - - - - - - - - c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - x - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/reactives1.png b/doc/media/reactives1.png deleted file mode 100644 index 705e0557..00000000 Binary files a/doc/media/reactives1.png and /dev/null differ diff --git a/doc/media/reactives2.graphml b/doc/media/reactives2.graphml deleted file mode 100644 index 1c902632..00000000 --- a/doc/media/reactives2.graphml +++ /dev/null @@ -1,308 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - 2 - - - - - - - - - - - - - - - - - - 2 - - - - - - - - - - - - - - - - - - 2 - - - - - - - - - - - - - - - - - - 1 - - - - - - - - - - - - - - - - - - 1 - - - - - - - - - - - - - - - - - - a - - - - - - - - - - - - - - - - - - b - - - - - - - - - - - - - - - - - - c - - - - - - - - - - - - - - - - - - x - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/reactives2.png b/doc/media/reactives2.png deleted file mode 100644 index f8fab071..00000000 Binary files a/doc/media/reactives2.png and /dev/null differ diff --git a/doc/media/toposort1.graphml b/doc/media/toposort1.graphml deleted file mode 100644 index 1cf13a90..00000000 --- a/doc/media/toposort1.graphml +++ /dev/null @@ -1,579 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - a - - - - - - - - - - - - - - - - - b - - - - - - - - - - - - - - - - - c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - x - - - - - - - - - - - - - - - - - a - - - - - - - - - - - - - - - - - b - - - - - - - - - - - - - - - - - c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - x - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - a - - - - - - - - - - - - - - - - - b - - - - - - - - - - - - - - - - - c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - x - - - - - - - - - - - - - - - - - b <<= K - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - level 0 - - - - - - - - - - - - - - - - - level 1 - - - - - - - - - - - - - - - - - level 2 - - - - - - - - - - - - - - - - - x=(a+b)*c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/toposort1.png b/doc/media/toposort1.png deleted file mode 100644 index bf7270ee..00000000 Binary files a/doc/media/toposort1.png and /dev/null differ diff --git a/doc/media/toposort2.graphml b/doc/media/toposort2.graphml deleted file mode 100644 index c49c92b0..00000000 --- a/doc/media/toposort2.graphml +++ /dev/null @@ -1,570 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - task 1 - - - - - - - - - - - - - - - - - P= { - - - - - - - - - - - - - - - - - } - - - - - - - - - - - - - - - - - task 2 - - - - - - - - - - - - - - - - - task 3 - - - - - - - - - - - - - - - - - task 4 - - - - - - - - - - - - - - - - - - heavy - - - - - - - - - - - - - - - - - - light - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/toposort2.png b/doc/media/toposort2.png deleted file mode 100644 index 87f36465..00000000 Binary files a/doc/media/toposort2.png and /dev/null differ diff --git a/doc/media/toposort3.graphml b/doc/media/toposort3.graphml deleted file mode 100644 index 69529a72..00000000 --- a/doc/media/toposort3.graphml +++ /dev/null @@ -1,156 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - op2 - - - - - - - - - - - - - - - - - op1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - delayed by op2 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/toposort3.png b/doc/media/toposort3.png deleted file mode 100644 index d60b848a..00000000 Binary files a/doc/media/toposort3.png and /dev/null differ 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 f8d18fa3..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 "react/detail/graph/AlgorithmNodes.h" - -/*****************************************/ REACT_BEGIN /*****************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Forward declarations -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class Signal; - -template -class VarSignal; - -template -class Events; - -template -class EventSource; - -enum class EventToken; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Fold -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename V, - typename E, - typename FIn, - typename S = std::decay::type -> -auto Fold(V&& init, const Events& events, FIn&& func) - -> Signal -{ - using F = std::decay::type; - - return Signal( - std::make_shared>( - std::forward(init), events.NodePtr(), std::forward(func))); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Iterate -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename V, - typename E, - typename FIn, - typename S = std::decay::type -> -auto Iterate(V&& init, const Events& events, FIn&& func) - -> Signal -{ - using F = std::decay::type; - - return Signal( - std::make_shared>( - std::forward(init), events.NodePtr(), 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.NodePtr())); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Snapshot -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S, - typename E -> -auto Snapshot(const Signal& target, const Events& trigger) - -> Signal -{ - return Signal( - std::make_shared>( - target.NodePtr(), trigger.NodePtr())); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Monitor -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S -> -auto Monitor(const Signal& target) - -> Events -{ - return Events( - std::make_shared>( - target.NodePtr())); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Pulse -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S, - typename E -> -auto Pulse(const Signal& target, const Events& trigger) - -> Events -{ - return Events( - std::make_shared>( - target.NodePtr(), trigger.NodePtr())); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Changed -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S -> -auto Changed(const Signal& target) - -> Events -{ - return Monitor(target) - .Transform([] (const S& v) { return EventToken::token; }); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ChangedTo -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename V, - typename S = std::decay::type -> -auto ChangedTo(const Signal& target, V&& value) - -> Events -{ - return Monitor(target) - .Transform([=] (const S& v) { return v == value; }) - .Filter([] (bool v) { return v == true; }) - .Transform([=] (const S& v) { return EventToken::token; }) -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Incrementer -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct Incrementer -{ - T operator()(T v) const { return v+1; } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Decrementer -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct Decrementer -{ - T operator()(T v) const { return v-1; } -}; - -/******************************************/ REACT_END /******************************************/ diff --git a/include/react/Domain.h b/include/react/Domain.h deleted file mode 100644 index 836541c9..00000000 --- a/include/react/Domain.h +++ /dev/null @@ -1,227 +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/TypeTraits.h" -#include "react/detail/ReactiveInput.h" -#include "react/detail/Options.h" - -#ifdef REACT_ENABLE_LOGGING - #include "react/detail/logging/EventLog.h" - #include "react/detail/logging/EventRecords.h" -#endif //REACT_ENABLE_LOGGING - -#include "react/detail/IReactiveEngine.h" -#include "react/engine/ToposortEngine.h" - -/*****************************************/ REACT_BEGIN /*****************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Forward declarations -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class ReactiveLoop; - -template -class Observer; - -template -class ScopedObserver; - -template -class Signal; - -template -class VarSignal; - -template -class TempSignal; - -template -class Events; - -template -class EventSource; - -template -class TempEvents; - -enum class EventToken; - -using REACT_IMPL::TurnFlagsT; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// DomainBase -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class DomainBase -{ -public: - using TurnT = typename TPolicy::Engine::TurnT; - - DomainBase() = delete; - - using Policy = TPolicy; - using Engine = REACT_IMPL::EngineInterface; - - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// Aliases for reactives of current domain - /////////////////////////////////////////////////////////////////////////////////////////////////// - template - using SignalT = Signal; - - template - using VarSignalT = VarSignal; - - template - using EventsT = Events; - - template - using EventSourceT = EventSource; - - using ObserverT = Observer; - - using ScopedObserverT = ScopedObserver; - - 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 FIn, - typename ... TArgs, - typename F = std::decay::type, - typename S = std::result_of::type, - typename TOp = REACT_IMPL::FunctionOp ...> - > - static auto MakeSignal(FIn&& func, const SignalT& ... args) - -> TempSignal - { - return REACT::MakeSignal(std::forward(func), args ...); - } - - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// MakeEventSource - /////////////////////////////////////////////////////////////////////////////////////////////////// - template - static auto MakeEventSource() - -> EventSourceT - { - return REACT::MakeEventSource(); - } - - static auto MakeEventSource() - -> EventSourceT - { - return REACT::MakeEventSource(); - } - - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// DoTransaction - /////////////////////////////////////////////////////////////////////////////////////////////////// - template - static void DoTransaction(F&& func) - { - REACT_IMPL::InputManager::DoTransaction(0, std::forward(func)); - } - - template - static void DoTransaction(TurnFlagsT flags, F&& func) - { - REACT_IMPL::InputManager::DoTransaction(flags, std::forward(func)); - } - -#ifdef REACT_ENABLE_LOGGING - /////////////////////////////////////////////////////////////////////////////////////////////// - /// Log - /////////////////////////////////////////////////////////////////////////////////////////////// - static EventLog& Log() - { - static ObserverRegistry instance; - return instance; - } -#endif //REACT_ENABLE_LOGGING -}; - -/******************************************/ REACT_END /******************************************/ - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Policy -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename TEngine = ToposortEngine -> -struct DomainPolicy -{ - using Engine = TEngine; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Ensure singletons are created immediately after domain declaration (TODO hax) -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class DomainInitializer -{ -public: - DomainInitializer() - { -#ifdef REACT_ENABLE_LOGGING - DomainSpecificData::Log(); -#endif //REACT_ENABLE_LOGGING - - typename D::Engine::Engine(); - } -}; - -/****************************************/ REACT_IMPL_END /***************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Domain definition macro -/////////////////////////////////////////////////////////////////////////////////////////////////// -#define REACTIVE_DOMAIN(name, ...) \ - struct name : public REACT::DomainBase> {}; \ - REACT_IMPL::DomainInitializer< name > name ## _initializer_; \ No newline at end of file diff --git a/include/react/Event.h b/include/react/Event.h deleted file mode 100644 index d2e8b5b6..00000000 --- a/include/react/Event.h +++ /dev/null @@ -1,636 +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 "react/Observer.h" -#include "react/TypeTraits.h" -#include "react/detail/EventBase.h" - -/*****************************************/ REACT_BEGIN /*****************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventToken -/////////////////////////////////////////////////////////////////////////////////////////////////// -enum class EventToken { token }; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Events -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E = EventToken -> -class Events : public REACT_IMPL::EventStreamBase -{ -protected: - using BaseT = REACT_IMPL::EventStreamBase; - -private: - using NodeT = REACT_IMPL::EventStreamNode; - using NodePtrT = REACT_IMPL::SharedPtrT; - -public: - using ValueT = E; - - Events() = default; - Events(const Events&) = default; - - Events(Events&& other) : - EventStreamBase{ std::move(other) } - {} - - explicit Events(NodePtrT&& nodePtr) : - EventStreamBase{ std::move(nodePtr) } - {} - - bool Equals(const Events& other) const - { - return BaseT::Equals(other); - } - - bool IsValid() const - { - return BaseT::IsValid(); - } - - auto Forward() const - -> decltype(REACT::Forward(std::declval())) - { - return REACT::Forward(*this); - } - - template - auto Merge(const Events& ... args) const - -> decltype(REACT::Merge(std::declval(), std::forward(args) ...)) - { - return REACT::Merge(*this, std::forward(args) ...); - } - - template - auto Filter(F&& f) const - -> decltype(REACT::Filter(std::declval(), std::forward(f))) - { - return REACT::Filter(*this, std::forward(f)); - } - - template - auto Transform(F&& f) const - -> decltype(REACT::Transform(std::declval(), std::forward(f))) - { - return REACT::Transform(*this, std::forward(f)); - } - - template - Observer Observe(F&& f) const - { - return REACT::Observe(*this, std::forward(f)); - } -}; - -// Specialize for references -template -< - typename D, - typename E -> -class Events : public REACT_IMPL::EventStreamBase> -{ -protected: - using BaseT = REACT_IMPL::EventStreamBase>; - -private: - using NodeT = REACT_IMPL::EventStreamNode>; - using NodePtrT = REACT_IMPL::SharedPtrT; - -public: - using ValueT = E; - - Events() = default; - Events(const Events&) = default; - - Events(Events&& other) : - EventStreamBase{ std::move(other) } - {} - - explicit Events(NodePtrT&& nodePtr) : - EventStreamBase{ std::move(nodePtr) } - {} - - bool Equals(const Events& other) const - { - return BaseT::Equals(other); - } - - bool IsValid() const - { - return BaseT::IsValid(); - } - - auto Forward() const - -> decltype(REACT::Forward(std::declval())) - { - return REACT::Forward(*this); - } - - template - auto Merge(const Events& ... args) - -> decltype(REACT::Merge(std::declval(), std::forward(args) ...)) - { - return REACT::Merge(*this, std::forward(args) ...); - } - - template - auto Filter(F&& f) const - -> decltype(REACT::Filter(std::declval(), std::forward(f))) - { - return REACT::Filter(*this, std::forward(f)); - } - - template - auto Transform(F&& f) const - -> decltype(REACT::Transform(std::declval(), std::forward(f))) - { - return REACT::Transform(*this, std::forward(f)); - } - - template - Observer Observe(F&& f) const - { - return REACT::Observe(*this, std::forward(f)); - } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventSource -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E = EventToken -> -class EventSource : public Events -{ -private: - using NodeT = REACT_IMPL::EventSourceNode; - using NodePtrT = REACT_IMPL::SharedPtrT; - -public: - EventSource() = default; - EventSource(const EventSource&) = default; - - EventSource(EventSource&& other) : - Events{ std::move(other) } - {} - - explicit EventSource(NodePtrT&& nodePtr) : - Events{ std::move(nodePtr) } - {} - - void Emit(const E& e) const - { - BaseT::emit(e); - } - - void Emit(E&& e) const - { - BaseT::emit(std::move(e)); - } - - template ::value>::type> - void Emit() const - { - BaseT::emit(EventToken::token); - } - - const EventSource& operator<<(const E& e) const - { - BaseT::emit(e); - return *this; - } - - const EventSource& operator<<(E&& e) const - { - BaseT::emit(std::move(e)); - return *this; - } -}; - -// Specialize for references -template -< - typename D, - typename E -> -class EventSource : public Events> -{ -private: - using NodeT = REACT_IMPL::EventSourceNode>; - using NodePtrT = REACT_IMPL::SharedPtrT; - -public: - EventSource() = default; - EventSource(const EventSource&) = default; - - EventSource(EventSource&& other) : - Events{ std::move(other) } - {} - - explicit EventSource(NodePtrT&& nodePtr) : - Events{ std::move(nodePtr) } - {} - - void Emit(std::reference_wrapper e) const - { - BaseT::emit(e); - } - - const EventSource& operator<<(std::reference_wrapper e) const - { - BaseT::emit(e); - return *this; - } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// TempEvents -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E, - typename TOp -> -class TempEvents : public Events -{ -protected: - using NodeT = REACT_IMPL::EventOpNode; - using NodePtrT = REACT_IMPL::SharedPtrT; - -public: - TempEvents() = default; - TempEvents(const TempEvents&) = default; - - TempEvents(TempEvents&& other) : - Events{ std::move(other) } - {} - - explicit TempEvents(NodePtrT&& nodePtr) : - Events{ std::move(nodePtr) } - {} - - TOp StealOp() - { - return std::move(reinterpret_cast(ptr_.get())->StealOp()); - } - - template - auto Merge(const Events& ... args) - -> decltype(REACT::Merge(std::declval(), std::forward(args) ...)) - { - return REACT::Merge(*this, std::forward(args) ...); - } - - template - auto Filter(F&& f) const - -> decltype(REACT::Filter(std::declval(), std::forward(f))) - { - return REACT::Filter(*this, std::forward(f)); - } - - template - auto Transform(F&& f) const - -> decltype(REACT::Transform(std::declval(), std::forward(f))) - { - return REACT::Transform(*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 /*****************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// MakeEventSource -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -auto MakeEventSource() - -> EventSource -{ - return EventSource( - std::make_shared>()); -} - -template -auto MakeEventSource() - -> EventSource -{ - return EventSource( - std::make_shared>()); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Forward -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E -> -auto Forward(const Events& other) - -> Events -{ - return Events( - std::make_shared>(other.NodePtr())); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Merge -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename TArg1, - typename ... TArgs, - typename E = TArg1, - typename TOp = REACT_IMPL::EventMergeOp, - REACT_IMPL::EventStreamNodePtrT ...> -> -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.NodePtr(), args.NodePtr() ...)); -} - -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::EventStreamNodePtrT>, - 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.NodePtr(), rhs.NodePtr())); -} - -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.NodePtr())); -} - -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.NodePtr(), 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.NodePtr())); -} - -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 EIn, - typename FIn, - typename F = std::decay::type, - typename EOut = std::result_of::type, - typename TOp = REACT_IMPL::EventTransformOp> -> -auto Transform(const Events& src, FIn&& func) - -> TempEvents -{ - return TempEvents( - std::make_shared>( - std::forward(func), src.NodePtr())); -} - -template -< - typename D, - typename EIn, - typename TOpIn, - typename FIn, - typename F = std::decay::type, - typename EOut = std::result_of::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)); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Flatten -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename TInnerValue -> -auto Flatten(const Signal>& node) - -> Events -{ - return Events( - std::make_shared, TInnerValue>>( - node.NodePtr(), node().NodePtr())); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Observe -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename FIn, - typename E, - class = std::enable_if< - ! std::is_same::value>::type -> -auto Observe(const Events& subject, FIn&& func) - -> Observer -{ - using F = std::decay::type; - using TNode = REACT_IMPL::EventObserverNode; - - auto* raw = REACT_IMPL::DomainSpecificObserverRegistry::Instance(). - template Register(subject, std::forward(func)); - - return Observer(raw, subject.NodePtr()); -} - -template -< - typename D, - typename FIn -> -auto Observe(const Events& subject, FIn&& func) - -> Observer -{ - auto wrapper = [func] (EventToken) { func(); }; - - using TNode = REACT_IMPL::EventObserverNode; - - auto* raw = REACT_IMPL::DomainSpecificObserverRegistry::Instance(). - template Register(subject, std::move(wrapper)); - - return Observer(raw, subject.NodePtr()); -} - -/******************************************/ REACT_END /******************************************/ diff --git a/include/react/Observer.h b/include/react/Observer.h deleted file mode 100644 index 811a0b51..00000000 --- a/include/react/Observer.h +++ /dev/null @@ -1,104 +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/IReactiveNode.h" -#include "react/detail/ObserverBase.h" -#include "react/detail/graph/ObserverNodes.h" - -/*****************************************/ REACT_BEGIN /*****************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Observer -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class Observer -{ -private: - using SubjectT = REACT_IMPL::NodeBasePtrT; - using NodeT = REACT_IMPL::IObserver; - -public: - Observer() : - nodePtr_{ nullptr } - {} - - Observer(const Observer&) = delete; - - Observer(Observer&& other) : - nodePtr_{ std::move(other.nodePtr_) }, - subject_{ std::move(other.subject_) } - {} - - Observer(NodeT* nodePtr, const SubjectT& subject) : - nodePtr_{ nodePtr }, - subject_{ subject } - {} - - bool IsValid() const - { - return nodePtr_ != nullptr; - } - - void Detach() - { - REACT_ASSERT(IsValid(), "Detach on invalid Observer."); - REACT_IMPL::DomainSpecificObserverRegistry::Instance().Unregister(nodePtr_); - } - -private: - // Ownership managed by registry - NodeT* nodePtr_; - - // While the observer handle exists, the subject is not destroyed - SubjectT subject_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ScopedObserver -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class ScopedObserver -{ -public: - ScopedObserver(Observer&& obs) : - obs_{ std::move(obs) } - {} - - ~ScopedObserver() - { - if (obs_.IsValid()) - obs_.Detach(); - } - -private: - Observer obs_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// DetachAllObservers -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -void DetachAllObservers(const TSubject& subject) -{ - using D = typename TSubject::DomainT; - - REACT_IMPL::DomainSpecificObserverRegistry::Instance().UnregisterFrom( - subject.NodePtr().get()); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// DetachThisObserver -/////////////////////////////////////////////////////////////////////////////////////////////////// -inline void DetachThisObserver() -{ - REACT_IMPL::current_observer_state_::shouldDetach = true; -} - -/******************************************/ REACT_END /******************************************/ \ No newline at end of file diff --git a/include/react/ReactiveObject.h b/include/react/ReactiveObject.h deleted file mode 100644 index 0f0f03a3..00000000 --- a/include/react/ReactiveObject.h +++ /dev/null @@ -1,177 +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/TypeTraits.h" -#include "react/common/Util.h" - - -/*****************************************/ REACT_BEGIN /*****************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Forward declarations -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class Signal; - -template -class VarSignal; - -template -class TempSignal; - -template -class Events; - -template -class EventSource; - -template -class TempEvents; - -template -class ReactiveLoop; - -template -class Observer; - -template -class ScopedObserver; - -enum class EventToken; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ReactiveObject -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class ReactiveObject -{ -public: - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// Aliases - /////////////////////////////////////////////////////////////////////////////////////////////////// - using DomainT = D; - - template - using SignalT = Signal; - - template - using VarSignalT = VarSignal; - - template - using EventsT = Events; - - template - using EventSourceT = EventSource; - - using ObserverT = Observer; - - using ScopedObserverT = ScopedObserver; - - 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)); - } - - template - < - typename S - > - static auto MakeVar(std::reference_wrapper value) - -> VarSignalT - { - return REACT::MakeVar(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 FIn, - typename ... TArgs, - typename F = std::decay::type, - typename S = std::result_of::type - > - static auto MakeSignal(FIn&& func, const SignalT& ... args) - -> SignalT - { - 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( \ - [] (const REACT_IMPL::Identity::Type::ValueT& 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 ec5ae9b5..00000000 --- a/include/react/Reactor.h +++ /dev/null @@ -1,68 +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 - -#ifdef REACT_ENABLE_REACTORS - -#include "react/detail/Defs.h" - -#include -#include - -#include "react/common/Util.h" - -#include "react/Event.h" -#include "react/detail/ReactiveBase.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.NodePtr()); - } - - template - void RepeatUntil(const Events& evn, F func) - { - node_.RepeatUntil(evn.NodePtr(), 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_ENABLE_REACTORS \ No newline at end of file diff --git a/include/react/Signal.h b/include/react/Signal.h deleted file mode 100644 index 6c404d75..00000000 --- a/include/react/Signal.h +++ /dev/null @@ -1,820 +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 "react/Observer.h" -#include "react/TypeTraits.h" -#include "react/detail/SignalBase.h" - -/*****************************************/ REACT_BEGIN /*****************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Signal -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S -> -class Signal : public REACT_IMPL::SignalBase -{ -protected: - using BaseT = REACT_IMPL::SignalBase; - -private: - using NodeT = REACT_IMPL::SignalNode; - using NodePtrT = REACT_IMPL::SharedPtrT; - -public: - using ValueT = S; - - Signal() = default; - Signal(const Signal&) = default; - - Signal(Signal&& other) : - SignalBase{ std::move(other) } - {} - - explicit Signal(NodePtrT&& nodePtr) : - SignalBase{ std::move(nodePtr) } - {} - - const S& Value() const { return BaseT::getValue(); } - const S& operator()() const { return BaseT::getValue(); } - - bool Equals(const Signal& other) const - { - return BaseT::Equals(other); - } - - bool IsValid() const - { - return BaseT::IsValid(); - } - - template ::value>::type> - S Flatten() const - { - return REACT::Flatten(*this); - } - - template - Observer Observe(F&& f) const - { - return REACT::Observe(*this, std::forward(f)); - } -}; - -// Specialize for references -template -< - typename D, - typename S -> -class Signal : public REACT_IMPL::SignalBase> -{ -protected: - using BaseT = REACT_IMPL::SignalBase>; - -private: - using NodeT = REACT_IMPL::SignalNode>; - using NodePtrT = REACT_IMPL::SharedPtrT; - -public: - using ValueT = S; - - Signal() = default; - Signal(const Signal&) = default; - - Signal(Signal&& other) : - SignalBase{ std::move(other) } - {} - - explicit Signal(NodePtrT&& nodePtr) : - SignalBase{ std::move(nodePtr) } - {} - - const S& Value() const { return BaseT::getValue(); } - const S& operator()() const { return BaseT::getValue(); } - - bool Equals(const Signal& other) const - { - return BaseT::Equals(other); - } - - bool IsValid() const - { - return BaseT::IsValid(); - } - - template - Observer Observe(F&& f) const - { - return REACT::Observe(*this, std::forward(f)); - } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// VarSignal -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S -> -class VarSignal : public Signal -{ -private: - using NodeT = REACT_IMPL::VarNode; - using NodePtrT = REACT_IMPL::SharedPtrT; - -public: - VarSignal() = default; - VarSignal(const VarSignal&) = default; - - VarSignal(VarSignal&& other) : - Signal{ std::move(other) } - {} - - explicit VarSignal(NodePtrT&& nodePtr) : - Signal{ std::move(nodePtr) } - {} - - void Set(const S& newValue) const - { - BaseT::setValue(newValue); - } - - void Set(S&& newValue) const - { - BaseT::setValue(std::move(newValue)); - } - - const VarSignal& operator<<=(const S& newValue) const - { - BaseT::setValue(newValue); - return *this; - } - - const VarSignal& operator<<=(S&& newValue) const - { - BaseT::setValue(std::move(newValue)); - return *this; - } -}; - -// Specialize for references -template -< - typename D, - typename S -> -class VarSignal : public Signal> -{ -private: - using NodeT = REACT_IMPL::VarNode>; - using NodePtrT = REACT_IMPL::SharedPtrT; - -public: - using ValueT = S; - - VarSignal() = default; - VarSignal(const VarSignal&) = default; - - VarSignal(VarSignal&& other) : - Signal{ std::move(other) } - {} - - explicit VarSignal(NodePtrT&& nodePtr) : - Signal{ std::move(nodePtr) } - {} - - void Set(std::reference_wrapper newValue) const - { - BaseT::setValue(newValue); - } - - const VarSignal& operator<<=(std::reference_wrapper newValue) const - { - BaseT::setValue(newValue); - return *this; - } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// TempSignal -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S, - typename TOp -> -class TempSignal : public Signal -{ -private: - using NodeT = REACT_IMPL::SignalOpNode; - using NodePtrT = REACT_IMPL::SharedPtrT; - -public: - TempSignal() = default; - TempSignal(const TempSignal&) = default; - - TempSignal(TempSignal&& other) : - Signal{ std::move(other) } - {} - - explicit TempSignal(NodePtrT&& ptr) : - Signal{ std::move(ptr) } - {} - - TOp StealOp() - { - return std::move(reinterpret_cast(ptr_.get())->StealOp()); - } -}; - -/******************************************/ REACT_END /******************************************/ - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -template -bool Equals(const Signal& lhs, const Signal& 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))); -} - -template -< - typename D, - typename S -> -auto MakeVar(std::reference_wrapper value) - -> VarSignal -{ - return VarSignal( - std::make_shared>>(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 ... TValues, - 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...(TValues) > 0, - "react::MakeSignal requires at least 1 signal dependency."); - - return TempSignal( - std::make_shared>( - std::forward(func), args.NodePtr() ... )); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// 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.NodePtr())); \ -} \ - \ -template \ -< \ - typename D, \ - typename TVal, \ - typename TOpIn, \ - typename F = name ## OpFunctor, \ - typename S = std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp \ -> \ -auto operator ## op(TempSignal&& arg) \ - -> TempSignal \ -{ \ - return TempSignal( \ - std::make_shared>( \ - F(), arg.StealOp())); \ -} - -DECLARE_OP(+, UnaryPlus); -DECLARE_OP(-, UnaryMinus); -DECLARE_OP(!, LogicalNegation); -DECLARE_OP(~, BitwiseComplement); -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::SignalNodePtrT>, \ - 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.NodePtr(), rhs.NodePtr())); \ -} \ - \ -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.NodePtr())); \ -} \ - \ -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.NodePtr())); \ -} \ -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.NodePtr())); \ -} \ - \ -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.NodePtr(), 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); - -//DECLARE_OP(&, BitwiseAnd); -//DECLARE_OP(|, BitwiseOr); -//DECLARE_OP(^, BitwiseXor); -//DECLARE_OP(<<, BitwiseLeftShift); // MSVC: Internal compiler error -//DECLARE_OP(>>, BitwiseRightShift); - -#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 signals. -/////////////////////////////////////////////////////////////////////////////////////////////////// -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::type> - { - 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.NodePtr(), node.Value().NodePtr())); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Observe -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename FIn, - typename S -> -auto Observe(const Signal& subject, FIn&& func) - -> Observer -{ - using F = std::decay::type; - using TNode = REACT_IMPL::SignalObserverNode; - - auto* obs = REACT_IMPL::DomainSpecificObserverRegistry::Instance(). - template Register(subject, std::forward(func)); - - return Observer{ obs, subject.NodePtr() }; -} - -/******************************************/ REACT_END /******************************************/ diff --git a/include/react/TypeTraits.h b/include/react/TypeTraits.h deleted file mode 100644 index 09f84092..00000000 --- a/include/react/TypeTraits.h +++ /dev/null @@ -1,100 +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" - -/*****************************************/ REACT_BEGIN /*****************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Forward declarations -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class Signal; - -template -class VarSignal; - -template -class TempSignal; - -template -class Events; - -template -class EventSource; - -template -class TempEvents; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// IsSignal -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct IsSignal { static const bool value = false; }; - -template -struct IsSignal> { static const bool value = true; }; - -template -struct IsSignal> { static const bool value = true; }; - -template -struct IsSignal> { static const bool value = true; }; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// IsEvent -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct IsEvent { static const bool value = false; }; - -template -struct IsEvent> { static const bool value = true; }; - -template -struct IsEvent> { static const bool value = true; }; - -template -struct IsEvent> { static const bool value = true; }; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// IsReactive -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct IsReactive { static const bool value = false; }; - -template -struct IsReactive> { static const bool value = true; }; - -template -struct IsReactive> { static const bool value = true; }; - -template -struct IsReactive> { static const bool value = true; }; - -template -struct IsReactive> { static const bool value = true; }; - -template -struct IsReactive> { static const bool value = true; }; - -template -struct IsReactive> { static const bool value = true; }; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// RemoveInput -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct RemoveInput { using Type = T; }; - -template -struct RemoveInput> { using Type = Signal; }; - -template -struct RemoveInput> { using Type = Events; }; - -/******************************************/ REACT_END /******************************************/ \ No newline at end of file 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