diff --git a/CMakeLists.txt b/CMakeLists.txt index ae21be06..3b6477a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,11 +13,7 @@ include_directories ("${PROJECT_SOURCE_DIR}/include") ### CppReact add_library(CppReact - src/engine/PulsecountEngine.cpp - src/engine/SubtreeEngine.cpp - src/engine/ToposortEngine.cpp - src/logging/EventLog.cpp - src/logging/EventRecords.cpp) + src/detail/graph_impl.cpp) target_link_libraries(CppReact tbb) diff --git a/README.md b/README.md index a10f27b3..39bf846c 100644 --- a/README.md +++ b/README.md @@ -1,195 +1,40 @@ -# ![C++React](http://schlangster.github.io/cpp.react//media/logo_banner3.png) +# ![C++React](http://snakster.github.io/cpp.react//media/logo_banner3.png) -C++React is 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. -Generally speaking, it provides abstractions to handle change propagation and data processing for a push-based event model. -A more practical description is that it enables coordinated, multi-layered - and potentially parallel - execution of callbacks. -All this happens implicitly, based on declarative definitions, with guarantees regarding +Here's a simple example: -- _update minimality_ - nothing is re-calculated or processed unnecessarily; -- _glitch freedom_ - no transiently inconsistent data sets; -- _thread safety_ - no data races for parallel execution. - -The core abstractions of the library are - -- _signals_, reactive variables that are automatically re-calculated when their dependencies change, and -- _event streams_ as composable first class objects. - -Signals specifically deal with aspects of time-varying state, whereas event streams facilitate event processing in general. - -Additional features include - -- a publish/subscribe mechanism for callbacks with side effects; -- a set of operations and algorithms to combine signals and events; -- a domain model to encapsulate multiple reactive systems; -- transactions to group related events, supporting both synchronous and asynchrounous execution. - - -## Documentation - -[If you're interested in learning about C++React, have a look at its documentation.](http://schlangster.github.io/cpp.react/) - - -## Development - -This library is a work-in-progress and should not be considered release quality yet. -It is, however, in a perfectly usable state and has already received a fair amount of testing and tuning. - - -### Dependencies - -* [Intel TBB 4.2](https://www.threadingbuildingblocks.org/) (required) -* [Google test framework](https://code.google.com/p/googletest/) (optional, to compile the unit tests) -* [Boost 1.55.0 C++ Libraries](http://www.boost.org/) (optional, to include Reactor.h, which requires `boost::coroutine`) - - -### Compiling - -C++React has been tested with the following compilers: - -* Visual Studio 2013.2 -* GCC 4.8.2 -* Clang 3.4 - -To build with Visual Studio, use the pre-made solution found in `project/msvc/`. - -To build with GCC or Clang, use [CMake](http://www.cmake.org/): -``` -mkdir build -cd build -cmake .. -make ``` - -For more details, refer to the [Build instructions](https://github.com/schlangster/cpp.react/wiki/Build-instructions). - - -## Features by example - -### Signals - -Signals are self-updating reactive variables. -They can be combined as expressions to create new signals, which are automatically re-calculated whenever one of their dependencies changes. - -```C++ -using namespace std; using namespace react; -// Define a reactive domain that uses single-threaded, sequential updating -REACTIVE_DOMAIN(D, sequential) - -// Define aliases for types of the given domain, -// e.g. using VarSignalT = VarSignal -USING_REACTIVE_DOMAIN(D) - -// Two reactive variables that can be manipulated imperatively -VarSignalT width = MakeVar(1); -VarSignalT height = MakeVar(2); - -// A signal that depends on width and height and multiplies their values -SignalT area = MakeSignal( - With(width, height), - [] (int w, int h) { - return w * h; - }); -``` -Signal values can be accessed imperatively: -```C++ -cout << "area: " << area.Value() << endl; // => area: 2 - -// Width changed, so area is re-calculated automatically -width.Set(10); - -cout << "area: " << area.Value() << endl; // => area: 20 -``` - -Or, instead of using `Value()` to pull the new value, callback functions can be registered to receive notifications on a change: -```C++ -Observe(area, [] (int newValue) { - cout << "area changed: " << newValue << endl; -}); -``` - -Overloaded operators for signal types allow to omit `MakeSignal` in this case for a more concise syntax: -```C++ -// Lift as reactive expression - equivalent to previous example -SignalT area = width * height; -``` - -### Event streams - -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++ -using namespace std; -using namespace react; - -REACTIVE_DOMAIN(D, sequential) -USING_REACTIVE_DOMAIN(D) - -// Two event sources -EventSourceT leftClicked = MakeEventSource(); -EventSourceT rightClicked = MakeEventSource(); - -// Merge both event streams -EventsT merged = leftClicked | rightClicked; - -// React to events -Observe(merged, [] (Token) { - cout << "clicked!" << endl; -}); -``` -``` -rightClicked.Emit(); // => clicked! -``` - -### Parallelism and concurrency - -The change propagation is handled implicitly. -Depending on the selected concurrency policy, updates can be parallelized: - -```C++ -// Sequential updating -REACTIVE_DOMAIN(D, sequential) - -VarSignalT a = MakeVar(1); -VarSignalT b = MakeVar(2); -VarSignalT c = MakeVar(3); - -// Using overloaded arithmetic operators instead of MakeSignal -SignalT x = (a + b) * c; -``` - -```C++ -// Parallel updating -REACTIVE_DOMAIN(D, parallel) - -VarSignalT in = MakeVar(0); +void AddNumbers(int a, int b) { return a + b; } -SignalT op1 = MakeSignal(in, [] (int in) -{ - int result = doCostlyOperation1(in); - return result; -}; +// Two state variable objects. You can change their values manually. +auto a = StateVar::Create(0); +auto b = StateVar::Create(0); -SignalT op2 = MakeSignal(in, [] (int in) -{ - int result = doCostlyOperation2(in); - return result; -}; +// 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); -// op1 and op2 can be re-calculated in parallel -SignalT out = op1 + op2; +// sum == 0 +a.Set(21); +// sum == 21 +b.Set(21); +// sum == 42 ``` -### More examples +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. -* [Examples](https://github.com/schlangster/cpp.react/tree/master/examples/src) -* [Test cases](https://github.com/schlangster/cpp.react/tree/master/tests/src) +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. -## Acknowledgements +## Development status -The API of C++React has been inspired by the following two research papers: +I'm currently in the process of rewriting the library more or less from scratch and many things are still broken or outdated. -* [Deprecating the Observer Pattern with Scala.React](http://infoscience.epfl.ch/record/176887/files/DeprecatingObservers2012.pdf) -* [REScala: Bridging Between Object-oriented and Functional Style in Reactive Applications](http://www.stg.tu-darmstadt.de/media/st/research/rescala_folder/REScala-Bridging-The-Gap-Between-Object-Oriented-And-Functional-Style-In-Reactive-Applications.pdf) +[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/src/BenchmarkBase.h b/benchmarks/src/BenchmarkBase.h index deaaf931..6684c062 100644 --- a/benchmarks/src/BenchmarkBase.h +++ b/benchmarks/src/BenchmarkBase.h @@ -1,5 +1,5 @@ -// 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) @@ -14,7 +14,7 @@ #include #include -#include "react/common/Util.h" +#include "react/common/utility.h" /////////////////////////////////////////////////////////////////////////////////////////////////// // Get unique random numbers from range. @@ -55,18 +55,6 @@ inline const std::string CurrentDateTime() return buf; } -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// BenchmarkBase -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class BenchmarkBase -{ -public: - typedef D Domain; - - double Run(); -}; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// RunBenchmark /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -78,9 +66,6 @@ template > void RunBenchmark(std::ostream& logfile, TBenchmark b, const TParams& params) { - std::cout << "Engine: " << typeid(typename TBenchmark::Domain::Policy::Engine).name() << std::endl << std::endl; - logfile << "Engine: " << typeid(typename TBenchmark::Domain::Policy::Engine).name() << std::endl << std::endl; - double sum = 0; double min = DBL_MAX; double max = DBL_MIN; @@ -89,7 +74,7 @@ void RunBenchmark(std::ostream& logfile, TBenchmark b, const 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; @@ -117,9 +102,8 @@ void RunBenchmark(std::ostream& logfile, TBenchmark b, const 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) { @@ -131,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/benchmarks/src/BenchmarkFanout.h b/benchmarks/src/BenchmarkFanout.h index bc2c8b3e..099f26d2 100644 --- a/benchmarks/src/BenchmarkFanout.h +++ b/benchmarks/src/BenchmarkFanout.h @@ -1,11 +1,13 @@ -// 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 @@ -15,8 +17,8 @@ #include "BenchmarkBase.h" -#include "react/Signal.h" - +#include "react/state.h" +/* using namespace react; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -42,8 +44,7 @@ struct BenchmarkParams_Fanout const int Delay; }; -template -struct Benchmark_Fanout : public BenchmarkBase +struct Benchmark_Fanout { double Run(const BenchmarkParams_Fanout& params) { @@ -83,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/benchmarks/src/BenchmarkGrid.h b/benchmarks/src/BenchmarkGrid.h index 551f3db8..ad2d90af 100644 --- a/benchmarks/src/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,54 +19,49 @@ #include "BenchmarkBase.h" -#include "react/Signal.h" +#include "react/state.h" using namespace react; /////////////////////////////////////////////////////////////////////////////////////////////////// /// GridGraphGenerator /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename TValue -> +template class GridGraphGenerator { public: - using MySignal = Signal; + using SignalType = Signal; - using Func1T = std::function; - using Func2T = std::function; + using Func1T = std::function; + using Func2T = std::function; - using SignalVectT = std::vector; - using WidthVectT = std::vector; + using SignalVectType = std::vector; - SignalVectT InputSignals; - SignalVectT OutputSignals; + SignalVectType inputSignals; + SignalVectType outputSignals; - Func1T Function1; - Func2T Function2; + Func1T function1; + Func2T function2; - WidthVectT 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); - SignalVectT buf1 = InputSignals; - SignalVectT buf2; + SignalVectType buf1 = std::move(inputSignals); + SignalVectType buf2; - SignalVectT* curBuf = &buf1; - SignalVectT* nextBuf = &buf2; + SignalVectType* curBuf = &buf1; + SignalVectType* nextBuf = &buf2; - size_t curWidth = InputSignals.size(); + size_t curWidth = inputSignals.size(); size_t nodeCount = 1; nodeCount += curWidth; - for (auto targetWidth : Widths) + for (auto targetWidth : widths) { while (curWidth != targetWidth) { @@ -75,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 - SignalVectT* 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()); } }; @@ -135,24 +135,24 @@ struct BenchmarkParams_Grid 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) { - auto in = MakeVar(1); + VarSignal in{ group, 1 }; + Signal in2 = in; - GridGraphGenerator generator; + GridGraphGenerator generator; - generator.InputSignals.push_back(in); + generator.inputSignals.push_back(in2); - 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 index b53856dd..2511882d 100644 --- a/benchmarks/src/BenchmarkLifeSim.h +++ b/benchmarks/src/BenchmarkLifeSim.h @@ -1,11 +1,11 @@ -// 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 @@ -67,6 +67,8 @@ class Time template class Region { + Time& theTime; + public: USING_REACTIVE_DOMAIN(D) @@ -83,21 +85,17 @@ class Region SignalT FoodPerDay = MakeSignal( theTime.Season, - [] (int season) { - return season == summer ? 20 : 10; - }); + calculateFoodPerDay); SignalT FoodOutputPerDay = MakeSignal( With(FoodPerDay,AnimalCount), - [] (int food, int count) { - return count > 0 ? food/count : 0; - }); + calculateFoodOutputPerDay); EventsT FoodOutput = Pulse(theTime.NewDay, FoodOutputPerDay); Region(Time& time, int x, int y) : - Bounds( x*10, x*10+9, y*10, y*10+9 ), - theTime( time ) + theTime( time ), + Bounds( x*10, x*10+9, y*10, y*10+9 ) {} PositionT Center() const @@ -126,7 +124,16 @@ class Region } private: - Time& theTime; + static int calculateFoodPerDay(int season) + { + return season == summer ? 20 : 10; + } + + static int calculateFoodOutputPerDay(int food, int count) + { + return count > 0 ? food/count : 0; + } + }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -138,7 +145,7 @@ class World public: USING_REACTIVE_DOMAIN(D) - vector>> Regions; + vector>> Regions; World(Time& time, int w) : w_( w ) @@ -199,14 +206,7 @@ class Animal EventsT*> RegionChanged = Monitor(NewRegion); SignalT Age = Iterate(theTime.NewDay, 0, Incrementer()); - - SignalT Health = Iterate( - FoodReceived, - 100, - [] (int food, int health) { - auto newHealth = health + food - 10; - return newHealth < 0 ? 0 : newHealth > 10000 ? 10000 : newHealth; - }); + SignalT Health = Iterate(FoodReceived, 100, calculateHealth); Animal(Time& time, World& world, Region* initRegion, unsigned seed) : theTime( time ), @@ -239,20 +239,29 @@ class Animal [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); + } - Observe( - RegionChanged, - With(CurrentRegion), - [this] (Region* newRegion, Region* oldRegion) { - oldRegion->EnterOrLeave(leave); - newRegion->EnterOrLeave(enter); +private: + Continuation regionChangeContinuation_; - // Change region in continuation - CurrentRegion <<= newRegion; - }); + static int calculateHealth(int food, int health) + { + auto newHealth = health + food - 10; + return newHealth < 0 ? 0 : newHealth > 10000 ? 10000 : newHealth; } }; @@ -309,8 +318,10 @@ struct Benchmark_LifeSim : public BenchmarkBase logfile.open("log.txt"); D::Log().Write(logfile); - logfile.close()*/; + logfile.close()*//*; - return d; + //return d; } -}; \ No newline at end of file +}; + +*/ \ No newline at end of file diff --git a/benchmarks/src/BenchmarkRandom.h b/benchmarks/src/BenchmarkRandom.h index 63cbd996..80257410 100644 --- a/benchmarks/src/BenchmarkRandom.h +++ b/benchmarks/src/BenchmarkRandom.h @@ -1,24 +1,25 @@ -// 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 + #include #include #include #include #include "BenchmarkBase.h" -#include "react/common/Types.h" -#include "react/Domain.h" -#include "react/Signal.h" +#include "react/group.h" +#include "react/state.h" using namespace react; - +/* /////////////////////////////////////////////////////////////////////////////////////////////////// /// DiamondGraphGenerator /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -215,8 +216,7 @@ struct BenchmarkParams_Random const int SlowSeed; }; -template -struct Benchmark_Random : public BenchmarkBase +struct Benchmark_Random { double Run(const BenchmarkParams_Random& params) { @@ -321,4 +321,6 @@ struct Benchmark_Random : public BenchmarkBase return d; } -}; \ No newline at end of file +};*/ + +#endif \ No newline at end of file diff --git a/benchmarks/src/BenchmarkSequence.h b/benchmarks/src/BenchmarkSequence.h index f30d3064..f02c4890 100644 --- a/benchmarks/src/BenchmarkSequence.h +++ b/benchmarks/src/BenchmarkSequence.h @@ -1,18 +1,22 @@ -// 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 + #include #include #include #include "BenchmarkBase.h" -#include "react/Signal.h" +#include "react/state.h" + +/* using namespace react; @@ -39,8 +43,7 @@ struct BenchmarkParams_Sequence const int Delay; }; -template -struct Benchmark_Sequence : public BenchmarkBase +struct Benchmark_Sequence { double Run(const BenchmarkParams_Sequence& params) { @@ -76,4 +79,6 @@ struct Benchmark_Sequence : public BenchmarkBase return d; } -}; \ No newline at end of file +};*/ + +#endif \ No newline at end of file diff --git a/benchmarks/src/Main.cpp b/benchmarks/src/Main.cpp index eb47f33f..d8bc4d76 100644 --- a/benchmarks/src/Main.cpp +++ b/benchmarks/src/Main.cpp @@ -1,43 +1,32 @@ -// 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) -//#define REACT_ENABLE_LOGGING - -#include "tbb/tick_count.h" -#include "tbb/tbbmalloc_proxy.h" +#if 0 +//#include "tbb/tick_count.h" +//#include "tbb/tbbmalloc_proxy.h" //#include "ittnotify.h" -#include "BenchmarkGrid.h" +/*#include "BenchmarkGrid.h" #include "BenchmarkRandom.h" #include "BenchmarkFanout.h" #include "BenchmarkSequence.h" #include "BenchmarkLifeSim.h" -#include "react/Domain.h" +#include "react/Group.h" #include "react/Signal.h" #include "react/Algorithm.h" -#include "react/common/Util.h" -#include "react/logging/EventLog.h" - -#include "react/engine/ToposortEngine.h" -#include "react/engine/PulsecountEngine.h" -#include "react/engine/SubtreeEngine.h" +#include "react/common/Util.h"*/ /////////////////////////////////////////////////////////////////////////////////////////////////// namespace { -using namespace react; - -REACTIVE_DOMAIN(ToposortSTDomain, sequential, ToposortEngine) -REACTIVE_DOMAIN(ToposortDomain, parallel, ToposortEngine) -REACTIVE_DOMAIN(PulsecountDomain, parallel, PulsecountEngine) -REACTIVE_DOMAIN(SubtreeDomain, parallel, SubtreeEngine) +//using namespace react; -void runBenchmarkGrid(std::ostream& out) +/*void runBenchmarkGrid(std::ostream& out) { RUN_BENCHMARK(out, 5, Benchmark_Grid, BenchmarkParams_Grid(20, 10000), ToposortSTDomain, ToposortDomain, 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,14 +147,14 @@ void runBenchmarks() //runBenchmarkSequence(logfile); // === LIFESIM - runBenchmarkLifeSim(logfile); + //runBenchmarkLifeSim(logfile); logfile.close(); } void debugBenchmarks() { - using TestDomain = PulsecountDomain; + //using TestDomain = PulsecountDomain; //RUN_BENCHMARK(std::cout, 1, Benchmark_Grid, BenchmarkParams_Grid(30, 1), // TestDomain); @@ -182,8 +171,8 @@ 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); + //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; @@ -204,21 +193,11 @@ 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, 1, Benchmark_Grid, BenchmarkParams_Grid(30, 10000), - ToposortSTDomain); - //ToposortSTDomain, ToposortDomain, PulsecountDomain, SubtreeDomain); + //RUN_BENCHMARK(std::cout, 3, Benchmark_Grid, BenchmarkParams_Grid(100, 10000)); //RUN_BENCHMARK(std::cout, 1, Benchmark_Grid, BenchmarkParams_Grid(30, 10000), //SourceSetDomain); @@ -228,7 +207,7 @@ void profileBenchmark() //ToposortSTDomain, ToposortDomain, PulsecountDomain, SubtreeDomain); //RUN_BENCHMARK(std::cout, 1, Benchmark_LifeSim, BenchmarkParams_LifeSim(100, 15, 10000), - //ToposortSTDomain); + //ToposortSTDomainConc); } } // ~anonymous namespace @@ -237,5 +216,12 @@ int main() { //runBenchmarks(); //debugBenchmarks(); - profileBenchmark(); + //profileBenchmark(); +} + +#endif + +int main() +{ + return 0; } \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 940479ae..417819a0 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -14,18 +14,6 @@ target_link_libraries(Example_BasicEvents CppReact) add_executable(Example_BasicObservers src/BasicObservers.cpp) target_link_libraries(Example_BasicObservers CppReact) -### Example_BasicReactors -find_package(Boost 1.55 COMPONENTS coroutine context system) -if(Boost_FOUND) - include_directories(${Boost_INCLUDE_DIRS}) - add_executable(Example_BasicReactors src/BasicReactors.cpp) - target_link_libraries(Example_BasicReactors CppReact ${Boost_LIBRARIES}) -else() - message("boost::coroutine not found. Skipping build of Example_BasicReactors.") -endif() -#add_exyecutable(Example_BasicReactors src/BasicReactors.cpp) -#target_link_libraries(Example_BasicReactors CppReact) - ### Example_BasicSignals add_executable(Example_BasicSignals src/BasicSignals.cpp) target_link_libraries(Example_BasicSignals CppReact) diff --git a/examples/src/BasicAlgorithms.cpp b/examples/src/BasicAlgorithms.cpp index 07907c76..7829049c 100644 --- a/examples/src/BasicAlgorithms.cpp +++ b/examples/src/BasicAlgorithms.cpp @@ -1,5 +1,5 @@ -// 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) @@ -9,11 +9,10 @@ #include #include -#include "react/Domain.h" -#include "react/Signal.h" -#include "react/Event.h" -#include "react/Observer.h" -#include "react/Algorithm.h" +#include "react/state.h" +#include "react/event.h" +#include "react/observer.h" +#include "react/algorithm.h" /////////////////////////////////////////////////////////////////////////////////////////////////// /// Example 1 - Converting events to signals @@ -23,32 +22,34 @@ namespace example1 using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) + Group g; - class Sensor + struct Sensor { - public: - USING_REACTIVE_DOMAIN(D) + EventSource samples = EventSource::Create(g); + State lastSample = Hold(0, samples); - EventSourceT Samples = MakeEventSource(); - SignalT LastSample = Hold(Samples, 0); + auto GetReactiveMembers() const -> decltype(auto) + { return std::tie(lastSample); } }; void Run() { cout << "Example 1 - Converting events to signals" << endl; - Sensor mySensor; + Sensor sensor; - Observe(mySensor.LastSample, [] (int v) { - std::cout << v << std::endl; - }); + auto obs = Observer::Create(g, [] (int v) + { + cout << v << endl; + }, sensor.lastSample); - mySensor.Samples << 20 << 21 << 21 << 22; // output: 20, 21, 22 + sensor.samples << 20 << 21 << 21 << 22; // output: 20, 21, 22 - DoTransaction([&] { - mySensor.Samples << 30 << 31 << 31 << 32; - }); // output: 32 + g.DoTransaction([&] + { + sensor.samples << 30 << 31 << 31 << 32; + }); // output: 32 cout << endl; } @@ -62,17 +63,12 @@ namespace example2 using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) + Group g; - class Employee + struct Employee { - public: - USING_REACTIVE_DOMAIN(D) - - VarSignalT Name = MakeVar(string( "Bob" )); - VarSignalT Salary = MakeVar(3000); - - EventsT SalaryChanged = Monitor(Salary); + StateVar name = StateVar::Create(g, string( "Bob" )); + StateVar salary = StateVar::Create(g, 66666); }; void Run() @@ -81,156 +77,197 @@ namespace example2 Employee bob; - Observe( - bob.SalaryChanged, - With(bob.Name), - [] (int newSalary, const string& name) { - cout << name << " now earns " << newSalary << endl; - }); + 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 - Creating stateful signals (1) +/// Example 3 - Folding event streams into signals (1) /////////////////////////////////////////////////////////////////////////////////////////////////// namespace example3 { using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) + Group g; - class Counter + struct Counter { - public: - USING_REACTIVE_DOMAIN(D) - - EventSourceT<> Increment = MakeEventSource(); - - SignalT Count = Iterate( - Increment, - 0, - [] (Token, int oldCount) { - return oldCount + 1; - }); + 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 - Creating stateful signals (1)" << endl; + cout << "Example 3 - Folding event streams into signals (1)" << endl; Counter myCounter; - // Note: Using function-style operator() instead of .Emit() and .Value() - myCounter.Increment(); - myCounter.Increment(); - myCounter.Increment(); + myCounter.increment.Emit(); + myCounter.increment.Emit(); + myCounter.increment.Emit(); - cout << myCounter.Count() << endl; // output: 3 + auto obs = Observer::Create([] (int v) + { + cout << v << endl; // output: 3 + }, myCounter.count); cout << endl; } } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Example 4 - Creating stateful signals (2) +/// Example 4 - Folding event streams into signals (2) /////////////////////////////////////////////////////////////////////////////////////////////////// namespace example4 { using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) + Group g; - class Sensor + struct Sensor { - public: - USING_REACTIVE_DOMAIN(D) - - EventSourceT Input = MakeEventSource(); - - SignalT Average = Iterate( - Input, - 0.0f, - [] (int sample, float oldAvg) { - return (oldAvg + sample) / 2.0f; - }); + 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 - Creating stateful signals (2)" << endl; + cout << "Example 4 - Folding event streams into signals (2)" << endl; Sensor mySensor; - mySensor.Input << 10 << 5 << 10 << 8; + mySensor.input << 10.0f << 5.0f << 10.0f << 8.0f; - cout << "Average: " << mySensor.Average() << endl; // output: 3 + auto obs = Observer::Create([] (float v) + { + cout << "Average: " << v << endl; // output: 8.25 + }, mySensor.average); cout << endl; } } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Example 5 - Creating stateful signals (3) +/// Example 5 - Folding event streams into signals (3) /////////////////////////////////////////////////////////////////////////////////////////////////// namespace example5 { using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) + Group g; enum ECmd { increment, decrement, reset }; class Counter { - public: - USING_REACTIVE_DOMAIN(D) - - EventSourceT Update = MakeEventSource(); - VarSignalT Delta = MakeVar(1); - VarSignalT Start = MakeVar(0); - - SignalT Count = Iterate( - Update, - Start.Value(), - With(Delta, Start), - [] (int cmd, int oldCount, int delta, int start) { + private: + static int DoCounterLoop(const EventValueList& cmds, int count, int delta, int start) + { + for (int cmd : cmds) + { if (cmd == increment) - return oldCount + delta; + count += delta; else if (cmd == decrement) - return oldCount - delta; + count -= delta; else - return start; - }); - }; + count = start; + } - void Run() - { - cout << "Example 5 - Creating stateful signals (3)" << endl; - - Counter myCounter; + return count; + } - cout << "Start: " << myCounter.Count() << endl; // output: 0 - - myCounter.Update(increment); - myCounter.Update(increment); - myCounter.Update(increment); + public: + EventSource update = EventSource::Create(g); - cout << "3x increment by 1: " << myCounter.Count() << endl; // output: 3 + StateVar delta = StateVar::Create(g, 1); + StateVar start = StateVar::Create(g, 0); - myCounter.Delta <<= 5; - myCounter.Update(decrement); + State count = Iterate(0, DoCounterLoop, update, delta, start); + }; - cout << "1x decrement by 5: " << myCounter.Count() << endl; // output: -2 + void Run() + { + cout << "Example 5 - Folding event streams into signals (3)" << endl; - myCounter.Start <<= 100; - myCounter.Update(reset); + Counter myCounter; - cout << "reset to 100: " << myCounter.Count() << endl; // output: 100 + { + 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; } @@ -244,32 +281,32 @@ namespace example6 using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) + Group g; class Sensor { - public: - USING_REACTIVE_DOMAIN(D) - - EventSourceT Input = MakeEventSource(); - - VarSignalT Threshold = MakeVar(42); - - SignalT> AllSamples = Iterate( - Input, - vector{}, - [] (int input, vector& all) { + private: + static void DoIterateAllSamples(const EventValueList& events, vector& all) + { + for (int input : events) all.push_back(input); - }); + } - SignalT> CriticalSamples = Iterate( - Input, - vector{}, - With(Threshold), - [] (int input, vector& critical, int threshold) { + 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() @@ -278,33 +315,231 @@ namespace example6 Sensor mySensor; - mySensor.Input << 40 << 29 << 43 << 50; + mySensor.input << 40 << 29 << 43 << 50; cout << "All samples: "; - for (auto const& v : mySensor.AllSamples()) - cout << v << " "; + { + auto obs = Observer::Create([] (const auto& allSamples) + { + for (auto const& v : allSamples) + cout << v << " "; + }, mySensor.allSamples); + } cout << endl; cout << "Critical samples: "; - for (auto const& v : mySensor.CriticalSamples()) - cout << v << " "; - cout << endl; - + { + 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(); + /*example1::Run(); example2::Run(); example3::Run(); example4::Run(); example5::Run(); - example6::Run(); + example6::Run();*/ + + test1(); return 0; } \ No newline at end of file diff --git a/examples/src/BasicComposition.cpp b/examples/src/BasicComposition.cpp index da6075f5..acec053e 100644 --- a/examples/src/BasicComposition.cpp +++ b/examples/src/BasicComposition.cpp @@ -1,5 +1,5 @@ -// 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) @@ -8,10 +8,9 @@ #include #include -#include "react/Domain.h" -#include "react/Signal.h" -#include "react/Event.h" -#include "react/Observer.h" +#include "react/state.h" +#include "react/event.h" +#include "react/observer.h" /////////////////////////////////////////////////////////////////////////////////////////////////// /// Example 1 - Reactive class members @@ -21,155 +20,99 @@ namespace example1 using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) + Group g; class Shape { public: - USING_REACTIVE_DOMAIN(D) + StateVar width = StateVar::Create(g, 0); + StateVar height = StateVar::Create(g, 0); - VarSignalT Width = MakeVar(0); - VarSignalT Height = MakeVar(0); + State size = State::Create(g, CalcSize, width, height); - SignalT Size = Width * Height; + auto GetReactiveMembers() const -> decltype(auto) + { return std::tie(width, height, size); } - EventSourceT<> HasMoved = MakeEventSource(); + private: + static int CalcSize(int w, int h) + { return w * h; } }; void Run() { cout << "Example 1 - Reactive class members" << endl; - Shape myShape; + auto myShape = ObjectState::Create(g, Shape()); - Observe(myShape.Size, [] (int newValue) { - cout << "Size changed to " << newValue << endl; - }); + auto obs = Observer::Create([] (const auto& ctx) + { + const Shape& shape = ctx.GetObject(); + cout << "Size is " << ctx.Get(shape.size) << endl; + }, myShape); - DoTransaction([&] { - myShape.Width <<= 4; - myShape.Height <<= 4; - }); // output: Size changed to 16 + g.DoTransaction([&] + { + myShape->width.Set(4); + myShape->height.Set(4); + }); // output: Size changed to 16 cout << endl; } } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Example 2 - Signals of references +/// Example 2 - Slots /////////////////////////////////////////////////////////////////////////////////////////////////// namespace example2 { using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) + Group g; class Company { public: - const char* Name; + StateVar name; Company(const char* name) : - Name( name ) - {} + name( StateVar::Create(g, name) ) + { } - // Note: To be used as a signal value type, - // values of the type must be comparable bool operator==(const Company& other) const - { - return this == &other; - } + { return name == other.name; } }; class Employee { public: - USING_REACTIVE_DOMAIN(D) + StateSlot myCompanyName; - VarSignalT MyCompany; + Employee(const Company& company) : + myCompanyName( StateSlot::Create(company.name) ) + { } - Employee(Company& company) : - MyCompany( MakeVar(ref(company)) ) - {} + void SetCompany(const Company& company) + { myCompanyName.Set(company.name); } }; void Run() { - cout << "Example 2 - Signals of references" << endl; + cout << "Example 2 - Slots" << endl; - Company company1( "MetroTec" ); - Company company2( "ACME" ); - - Employee bob( company1 ); - - Observe(bob.MyCompany, [] (const Company& company) { - cout << "Bob works for " << company.Name << endl; - }); - - bob.MyCompany <<= ref(company2); // output: Bob now works for ACME - - cout << endl; - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Example 3 - Dynamic signal references -/////////////////////////////////////////////////////////////////////////////////////////////////// -namespace example3 -{ - using namespace std; - using namespace react; - - REACTIVE_DOMAIN(D, sequential) - - class Company - { - public: - USING_REACTIVE_DOMAIN(D) - - VarSignalT Name; - - Company(const char* name) : - Name( MakeVar(string( name )) ) - {} - - bool operator==(const Company& other) const - { - return this == &other; - } - }; - - class Employee - { - public: - USING_REACTIVE_DOMAIN(D) - - VarSignalT MyCompany; - - Employee(Company& company) : - MyCompany( MakeVar(ref(company)) ) - {} - }; - - void Run() - { - cout << "Example 3 - Dynamic signal references" << endl; - - Company company1( "MetroTec" ); - Company company2( "ACME" ); + Company company1( "MetroTec" ); + Company company2( "ACME" ); Employee alice( company1 ); - auto obs = Observe( - REACTIVE_REF(alice.MyCompany, Name), - [] (const string& name) { + auto obs = Observer::Create([] (const string& name) + { cout << "Alice now works for " << name << endl; - }); + }, alice.myCompanyName); - company1.Name <<= string( "ModernTec" ); // output: Alice now works for ModernTec - alice.MyCompany <<= ref(company2); // output: Alice now works for ACME - company2.Name <<= string( "A.C.M.E." ); // output: Alice now works for A.C.M.E. + 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; } @@ -181,10 +124,7 @@ namespace example3 int main() { example1::Run(); - example2::Run(); - example3::Run(); - return 0; } \ No newline at end of file diff --git a/examples/src/BasicEvents.cpp b/examples/src/BasicEvents.cpp index 656a141b..98fe1ef3 100644 --- a/examples/src/BasicEvents.cpp +++ b/examples/src/BasicEvents.cpp @@ -1,5 +1,5 @@ -// 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) @@ -8,7 +8,6 @@ #include #include -#include "react/Domain.h" #include "react/Event.h" #include "react/Observer.h" @@ -20,36 +19,31 @@ namespace example1 using namespace std; using namespace react; - // Defines a domain. - // Each domain represents a separate dependency graph, managed by a dedicated propagation engine. - // Reactives of different domains can not be combined. - REACTIVE_DOMAIN(D, sequential) - - // Define type aliases for the given domain in this namespace. - // Now we can use EventSourceT instead of D::EventSourceT. - USING_REACTIVE_DOMAIN(D) + // 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 { - EventSourceT mySource = MakeEventSource(); + EventSource mySource = EventSource::Create(group); void Run() { cout << "Example 1 - Hello world (string source)" << endl; - Observe(mySource, [] (const string& s) { - std::cout << s << std::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")); - // Or as a function call: - mySource(string("Hello world #3")); - cout << endl; } } @@ -57,7 +51,7 @@ namespace example1 // An event source without an explicit value type namespace v2 { - EventSourceT<> helloWorldTrigger = MakeEventSource(); + EventSource<> helloWorldTrigger = EventSource<>::Create(group); void Run() { @@ -65,18 +59,17 @@ namespace example1 int count = 0; - Observe(helloWorldTrigger, [&] (Token) { - cout << "Hello world #" << ++count << endl; - }); + 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; - // Or as a function call: - helloWorldTrigger(); - cout << endl; } } @@ -90,57 +83,30 @@ namespace example2 using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) - USING_REACTIVE_DOMAIN(D) + Group group; // An event stream that merges both sources - namespace v1 - { - EventSourceT<> leftClick = MakeEventSource(); - EventSourceT<> rightClick = MakeEventSource(); - - EventsT<> anyClick = Merge(leftClick, rightClick); - - void Run() - { - cout << "Example 2 - Merging event streams (Merge)" << endl; - - int count = 0; - - Observe(anyClick, [&] (Token) { - cout << "clicked #" << ++count << endl; - }); + EventSource<> leftClick = EventSource<>::Create(group); + EventSource<> rightClick = EventSource<>::Create(group); - leftClick.Emit(); // output: clicked #1 - rightClick.Emit(); // output: clicked #2 + Event<> anyClick = Merge(leftClick, rightClick); - cout << endl; - } - } - - // Using overloaded operator | instead of explicit Merge - namespace v2 + void Run() { - EventSourceT<> leftClick = MakeEventSource(); - EventSourceT<> rightClick = MakeEventSource(); - - EventsT<> anyClick = leftClick | rightClick; - - void Run() - { - cout << "Example 2 - Merging event streams (operator)" << endl; + cout << "Example 2 - Merging event streams (Merge)" << endl; - int count = 0; + int count = 0; - Observe(anyClick, [&] (Token) { - cout << "clicked #" << ++count << endl; - }); + 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 + leftClick.Emit(); // output: clicked #1 + rightClick.Emit(); // output: clicked #2 - cout << endl; - } + cout << endl; } } @@ -152,22 +118,21 @@ namespace example3 using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) - USING_REACTIVE_DOMAIN(D) + Group group; - EventSourceT numbers = MakeEventSource(); + EventSource numbers = EventSource::Create(group); - EventsT greater10 = Filter(numbers, [] (int n) { - return n > 10; - }); + Event greater10 = Filter([] (int n) { return n > 10; }, numbers); void Run() { cout << "Example 3 - Filtering events" << endl; - Observe(greater10, [] (int n) { - cout << n << endl; - }); + auto obs = Observer::Create([&] (const auto& events) + { + for (auto n : events) + cout << n << endl; + }, greater10); numbers << 5 << 11 << 7 << 100; // output: 11, 100 @@ -183,32 +148,36 @@ namespace example4 using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) - USING_REACTIVE_DOMAIN(D) + Group group; // Data types - enum class ETag { normal, critical }; - using TaggedNum = pair; + enum class Tag { normal, critical }; + using TaggedNum = pair; - EventSourceT numbers = MakeEventSource(); + EventSource numbers = EventSource::Create(group); - EventsT tagged = Transform(numbers, [] (int n) { - if (n > 10) - return TaggedNum( ETag::critical, n ); - else - return TaggedNum( ETag::normal, n ); - }); + 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; - Observe(tagged, [] (const TaggedNum& t) { - if (t.first == ETag::critical) - cout << "(critical) " << t.second << endl; - else - cout << "(normal) " << t.second << 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 @@ -225,23 +194,26 @@ namespace example5 using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) - USING_REACTIVE_DOMAIN(D) + Group group; - EventSourceT src = MakeEventSource(); + EventSource src = EventSource::Create(group); void Run() { cout << "Example 5 - Queuing multiple inputs" << endl; - Observe(src, [] (int v) { - cout << v << endl; - }); // output: 1, 2, 3, 4 - - DoTransaction([] { - src << 1 << 2 << 3; - src << 4; - }); + 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; } @@ -254,14 +226,9 @@ int main() { example1::v1::Run(); example1::v2::Run(); - - example2::v1::Run(); - example2::v2::Run(); - + example2::Run(); example3::Run(); - example4::Run(); - example5::Run(); return 0; diff --git a/examples/src/BasicObservers.cpp b/examples/src/BasicObservers.cpp index ba5b3420..54b24857 100644 --- a/examples/src/BasicObservers.cpp +++ b/examples/src/BasicObservers.cpp @@ -1,5 +1,5 @@ -// 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) @@ -8,10 +8,9 @@ #include #include -#include "react/Domain.h" -#include "react/Signal.h" -#include "react/Event.h" -#include "react/Observer.h" +#include "react/state.h" +#include "react/event.h" +#include "react/observer.h" /////////////////////////////////////////////////////////////////////////////////////////////////// /// Example 1 - Creating subject-bound observers @@ -21,152 +20,23 @@ namespace example1 using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) - USING_REACTIVE_DOMAIN(D) + Group group; - auto x = MakeVar(1); - - namespace v1 - { - void Run() - { - cout << "Example 1 - Creating subject-bound observers (v1)" << endl; - - { - // Create a signal in the function scope - auto mySignal = MakeSignal(x, [] (int x) { return x; } ); - - // The lifetime of the observer is bound to mySignal. - // After Run() returns, mySignal is destroyed, and so is the observer - Observe(mySignal, [] (int mySignal) { - cout << mySignal << endl; - }); - - x <<= 2; // output: 2 - } - - x <<= 3; // no ouput - - cout << endl; - } - } - - namespace v2 - { - void Run() - { - cout << "Example 1 - Creating subject-bound observers (v2)" << endl; - - // Outer scope - { - // Unbound observer - ObserverT obs; - - // Inner scope - { - auto mySignal = MakeSignal(x, [] (int x) { return x; } ); - - // Move-assign to obs - obs = Observe(mySignal, [] (int mySignal) { - cout << mySignal << endl; - }); - - // The node linked to mySignal is now also owned by obs - - x <<= 2; // output: 2 - } - // ~Inner scope - - // mySignal was destroyed, but as long as obs exists and is still - // attached to the signal node, this signal node won't be destroyed - - x <<= 3; // output: 3 - } - // ~Outer scope - - // obs was destroyed - // -> the signal node is no longer owned by anything and is destroyed - // -> the observer node is destroyed as it was bound to the subject - - x <<= 4; // no ouput - - cout << endl; - } - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Example 2 - Detaching observers manually -/////////////////////////////////////////////////////////////////////////////////////////////////// -namespace example2 -{ - using namespace std; - using namespace react; - - REACTIVE_DOMAIN(D, sequential) - USING_REACTIVE_DOMAIN(D) - - EventSourceT<> trigger = MakeEventSource(); - - void Run() - { - cout << "Example 2 - Detaching observers manually" << endl; - - ObserverT obs = Observe(trigger, [] (Token) { - cout << "Triggered!" << endl; - }); - - trigger.Emit(); // output: Triggered! - - obs.Detach(); // Remove the observer - - trigger.Emit(); // no output - - cout << endl; - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Example 3 - Using scoped observers -/////////////////////////////////////////////////////////////////////////////////////////////////// -namespace example3 -{ - using namespace std; - using namespace react; - - REACTIVE_DOMAIN(D, sequential) - USING_REACTIVE_DOMAIN(D) - - EventSourceT<> trigger = MakeEventSource(); + StateVar x = StateVar::Create(group, 1); void Run() { - cout << "Example 3 - Using scoped observers" << endl; + cout << "Example 1 - Creating observers" << endl; - // Inner scope { - ScopedObserverT scopedObs - ( - Observe(trigger, [] (Token) { - cout << "Triggered!" << endl; - }) - ); + auto st = State::Create([] (int x) { return x; }, x); - trigger.Emit(); // output: Triggered! - } - // ~Inner scope + auto obs = Observer::Create([] (int v) { cout << v << endl; }, st); - trigger.Emit(); // no output + x.Set(2); // output: 2 + } - // Note the semantic difference between ScopedObserverT and ObserverT. - // - // During its lifetime, the ObserverT handle of an observer guarantees that the - // observed subject will not be destroyed and allows explicit detach. - // But even after the ObserverT handle is destroyed, the subject may continue to exist - // and so will the observer. - // - // ScopedObserverT has similar semantics to a scoped lock. - // When it's destroyed, it detaches and destroys the observer. + x.Set(3); // no ouput cout << endl; } @@ -177,12 +47,7 @@ namespace example3 /////////////////////////////////////////////////////////////////////////////////////////////////// int main() { - example1::v1::Run(); - example1::v2::Run(); - - example2::Run(); - - example3::Run(); + example1::Run(); return 0; } \ No newline at end of file diff --git a/examples/src/BasicReactors.cpp b/examples/src/BasicReactors.cpp deleted file mode 100644 index 1e30cc1c..00000000 --- a/examples/src/BasicReactors.cpp +++ /dev/null @@ -1,161 +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) - -#include -#include -#include - -#include "react/Domain.h" -#include "react/Signal.h" -#include "react/Event.h" -#include "react/Reactor.h" - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Example 1 - Creating reactive loops -/////////////////////////////////////////////////////////////////////////////////////////////////// -namespace example1 -{ - using namespace std; - using namespace react; - - REACTIVE_DOMAIN(D, sequential) - USING_REACTIVE_DOMAIN(D) - - using PointT = pair; - using PathT = vector; - - vector paths; - - EventSourceT mouseDown = MakeEventSource(); - EventSourceT mouseUp = MakeEventSource(); - EventSourceT mouseMove = MakeEventSource(); - - ReactorT loop - { - [&] (ReactorT::Context ctx) - { - PathT points; - - points.emplace_back(ctx.Await(mouseDown)); - - ctx.RepeatUntil(mouseUp, [&] { - points.emplace_back(ctx.Await(mouseMove)); - }); - - points.emplace_back(ctx.Await(mouseUp)); - - paths.push_back(points); - } - }; - - void Run() - { - cout << "Example 1 - Creating reactive loops" << endl; - - 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 ); - - for (const auto& path : paths) - { - cout << "Path: "; - for (const auto& point : path) - cout << "(" << point.first << "," << point.second << ") "; - cout << endl; - } - - cout << endl; - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Example 2 - Creating reactive loops -/////////////////////////////////////////////////////////////////////////////////////////////////// -namespace example2 -{ - using namespace std; - using namespace react; - - REACTIVE_DOMAIN(D, sequential) - USING_REACTIVE_DOMAIN(D) - - using PointT = pair; - using PathT = vector; - - vector paths; - - EventSourceT mouseDown = MakeEventSource(); - EventSourceT mouseUp = MakeEventSource(); - EventSourceT mouseMove = MakeEventSource(); - - VarSignalT counter = MakeVar(103); - - ReactorT loop - { - [&] (ReactorT::Context ctx) - { - PathT points; - - points.emplace_back(ctx.Await(mouseDown)); - - auto count = ctx.Get(counter); - - ctx.RepeatUntil(mouseUp, [&] { - points.emplace_back(ctx.Await(mouseMove)); - }); - - points.emplace_back(ctx.Await(mouseUp)); - - paths.push_back(points); - } - }; - - void Run() - { - cout << "Example 2 - Creating reactive loops" << endl; - - mouseDown << PointT( 1,1 ); - mouseMove << PointT( 2,2 ) << PointT( 3,3 ) << PointT( 4,4 ); - mouseUp << PointT( 5,5 ); - - counter <<= 42; - - mouseMove << PointT( 999,999 ); - - counter <<= 80; - - mouseDown << PointT( 10,10 ); - mouseMove << PointT( 20,20 ); - mouseUp << PointT( 30,30 ); - - for (const auto& path : paths) - { - cout << "Path: "; - for (const auto& point : path) - cout << "(" << point.first << "," << point.second << ") "; - cout << endl; - } - - cout << endl; - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Run examples -/////////////////////////////////////////////////////////////////////////////////////////////////// -int main() -{ - example1::Run(); - example2::Run(); - - return 0; -} \ No newline at end of file diff --git a/examples/src/BasicSignals.cpp b/examples/src/BasicSignals.cpp index 03707949..c743de83 100644 --- a/examples/src/BasicSignals.cpp +++ b/examples/src/BasicSignals.cpp @@ -1,5 +1,5 @@ -// 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) @@ -11,9 +11,8 @@ #include #include -#include "react/Domain.h" -#include "react/Signal.h" -#include "react/Observer.h" +#include "react/state.h" +#include "react/observer.h" /////////////////////////////////////////////////////////////////////////////////////////////////// /// Example 1 - Hello world @@ -23,74 +22,35 @@ namespace example1 using namespace std; using namespace react; - // Defines a domain. - // Each domain represents a separate dependency graph, managed by a dedicated propagation engine. - // Reactives of different domains can not be combined. - REACTIVE_DOMAIN(D, sequential) - - // Define type aliases for the given domain in this namespace. - // Now we can use VarSignalT instead of D::VarSignalT. - USING_REACTIVE_DOMAIN(D) - // The concat function - string concatFunc(string first, string second) { - return first + string(" ") + second; - } - - // A signal that concatenates both words - namespace v1 - { - // The two words - VarSignalT firstWord = MakeVar(string("Change")); - VarSignalT secondWord = MakeVar(string("me!")); - - SignalT bothWords = MakeSignal(With(firstWord,secondWord), concatFunc); + string ConcatFunc(string first, string second) + { return first + string(" ") + second; } - void Run() - { - cout << "Example 1 - Hello world (MakeSignal)" << endl; - - // Imperative imperative value access - cout << bothWords.Value() << endl; - - // Imperative imperative change - firstWord <<= string("Hello"); - - cout << bothWords.Value() << endl; + void PrintFunc(const string& s) + { cout << s << endl; } - secondWord <<= string("World"); + // 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!")); - cout << bothWords.Value() << endl; - - cout << endl; - } - } + // A signal that concatenates both words + State bothWords = State::Create(ConcatFunc, firstWord, secondWord); - // Using overloaded operator + instead of explicit MakeSignal - namespace v2 + void Run() { - // The two words - VarSignalT firstWord = MakeVar(string("Change")); - VarSignalT secondWord = MakeVar(string("me!")); - - SignalT bothWords = firstWord + string(" ") + secondWord; - - void Run() - { - cout << "Example 1 - Hello world (operators)" << endl; - - cout << bothWords.Value() << endl; - - firstWord <<= string("Hello"); - - cout << bothWords.Value() << endl; + auto obs = Observer::Create(PrintFunc, bothWords); - secondWord <<= string("World"); + cout << "Example 1 - Hello world" << endl; - cout << bothWords.Value() << endl; + firstWord.Set(string("Hello")); + secondWord.Set(string("World")); - cout << endl; - } + cout << endl; } } @@ -102,24 +62,23 @@ namespace example2 using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) - USING_REACTIVE_DOMAIN(D) + Group group; + + StateVar x = StateVar::Create(group, 1); - VarSignalT x = MakeVar(1); - SignalT xAbs = MakeSignal(x, [] (int x) { return abs(x); }); + State xAbs = State::Create([] (int v) { return abs(v); }, x); void Run() { cout << "Example 2 - Reacting to value changes" << endl; - Observe(xAbs, [] (int newValue) { - cout << "xAbs changed to " << newValue << endl; - }); + auto obs = Observer::Create([] (int newValue) + { cout << "xAbs changed to " << newValue << endl; }, xAbs); // initially x is 1 - x <<= 2; // output: xAbs changed to 2 - x <<= -3; // output: xAbs changed to 3 - x <<= 3; // no output, xAbs is still 3 + 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; } @@ -133,31 +92,33 @@ namespace example3 using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) - USING_REACTIVE_DOMAIN(D) + int sumFunc(int a, int b) + { return a + b; } - VarSignalT a = MakeVar(1); - VarSignalT b = MakeVar(1); + Group group; - SignalT x = a + b; - SignalT y = a + b; - SignalT z = x + y; + 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; - Observe(z, [] (int newValue) { - std::cout << "z changed to " << newValue << std::endl; - }); + auto obs = Observer::Create([] (int newValue) + { cout << "z changed to " << newValue << endl; }, z); - a <<= 2; // output: z changed to 6 - b <<= 2; // output: z changed to 8 + a.Set(2); // output: z changed to 6 + b.Set(2); // output: z changed to 8 - DoTransaction([] { - a <<= 4; - b <<= 4; - }); // output: z changed to 16 + group.DoTransaction([&] + { + a.Set(4); + b.Set(4); + }); // output: z changed to 16 cout << endl; } @@ -171,27 +132,29 @@ namespace example4 using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) - USING_REACTIVE_DOMAIN(D) + Group group; - VarSignalT> data = MakeVar(vector{ }); + 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("Hello"); }); + + data.Modify([] (vector& data) + { data.push_back("World"); }); + + auto obs = Observer::Create([] (const vector& data) + { + for (const auto& s : data) + cout << s << " "; + }, data); - data.Modify([] (vector& data) { - data.push_back("World"); - }); - for (const auto& s : data.Value()) - cout << s << " "; cout << endl; - // output: Hell World + // output: Hello World cout << endl; } @@ -205,168 +168,48 @@ namespace example5 using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) - USING_REACTIVE_DOMAIN(D) + Group group; // Helpers - using ExprPairT = pair; - using ExprVectT = vector; + using ExprPairType = pair; + using ExprVectType = vector; - string makeExprStr(int a, int b, const char* op) + string MakeExprStr(int a, int b, const char* op) { return to_string(a) + string(op) + to_string(b); } - ExprPairT makeExprPair(const string& s, int v) - { - return make_pair(s, v); - } - - void printExpressions(const ExprVectT& expressions) + void PrintExpressions(const ExprVectType& expressions) { cout << "Expressions: " << endl; for (const auto& p : expressions) cout << "\t" << p.first << " is " << p.second << endl; } - // Version 1 - Intermediate signals - namespace v1 - { - // Input operands - VarSignalT a = MakeVar(1); - VarSignalT b = MakeVar(2); - - // Calculations - SignalT sum = a + b; - SignalT diff = a - b; - SignalT prod = a * b; - - using std::placeholders::_1; - using std::placeholders::_2; - - // Stringified expressions - SignalT sumExpr = - MakeSignal(With(a,b), bind(makeExprStr, _1, _2, "+")); - - SignalT diffExpr = - MakeSignal(With(a,b), bind(makeExprStr, _1, _2, "-")); - - SignalT prodExpr = - MakeSignal(With(a,b), bind(makeExprStr, _1, _2, "*")); - - // The expression vector - SignalT expressions = MakeSignal( - With( - MakeSignal(With(sumExpr, sum), &makeExprPair), - MakeSignal(With(diffExpr, diff), &makeExprPair), - MakeSignal(With(prodExpr, prod), &makeExprPair) - ), - [] (const ExprPairT& sumP, const ExprPairT& diffP, const ExprPairT& prodP) { - return ExprVectT{ sumP, diffP, prodP}; - }); - - void Run() - { - cout << "Example 5 - Complex signals (v1)" << endl; - - Observe(expressions, printExpressions); - - a <<= 10; - b <<= 20; - - cout << endl; - } - } + // Input operands + StateVar a = StateVar::Create(group, 1); + StateVar b = StateVar::Create(group, 2); - // Version 2 - Intermediate signals in a function - namespace v2 - { - SignalT createExpressionSignal(const SignalT& a, const SignalT& b) - { - using std::placeholders::_1; - using std::placeholders::_2; - - // Inside a function, we can use auto - auto sumExpr = - MakeSignal(With(a,b), bind(makeExprStr, _1, _2, "+")); - - auto diffExpr = - MakeSignal(With(a,b), bind(makeExprStr, _1, _2, "-")); - - auto prodExpr = - MakeSignal(With(a,b), bind(makeExprStr, _1, _2, "*")); - - return MakeSignal( - With( - MakeSignal(With(sumExpr, a + b), &makeExprPair), - MakeSignal(With(diffExpr, a - b), &makeExprPair), - MakeSignal(With(prodExpr, a * b), &makeExprPair) - ), - [] (const ExprPairT& sumP, const ExprPairT& diffP, const ExprPairT& prodP) { - return ExprVectT{ sumP, diffP, prodP }; - }); - } - - // Input operands - VarSignalT a = MakeVar(1); - VarSignalT b = MakeVar(2); - - // The expression vector - SignalT expressions = createExpressionSignal(a, b); - - void Run() + // The expression vector + State expressions = State::Create([] (int a, int b) { - cout << "Example 5 - Complex signals (v2)" << endl; - - Observe(expressions, printExpressions); - - a <<= 30; - b <<= 40; - - cout << endl; - } - } - - // Version 3 - Imperative function - namespace v3 - { - // Input operands - VarSignalT a = MakeVar(1); - VarSignalT b = MakeVar(2); - - // The expression vector - SignalT expressions = MakeSignal(With(a,b), [] (int a, int b) { - ExprVectT 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)); - + 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; + void Run() + { + cout << "Example 5 - Complex signals (v3)" << endl; - Observe(expressions, printExpressions); + auto obs = Observer::Create(PrintExpressions, expressions); - a <<= 50; - b <<= 60; + a.Set(50); + b.Set(60); - cout << endl; - } + cout << endl; } } @@ -375,18 +218,11 @@ namespace example5 /////////////////////////////////////////////////////////////////////////////////////////////////// int main() { - example1::v1::Run(); - example1::v2::Run(); - + example1::Run(); example2::Run(); - example3::Run(); - example4::Run(); - - example5::v1::Run(); - example5::v2::Run(); - example5::v3::Run(); + example5::Run(); return 0; } \ No newline at end of file diff --git a/examples/src/BasicSynchronization.cpp b/examples/src/BasicSynchronization.cpp index 38f05f3e..d5b29de7 100644 --- a/examples/src/BasicSynchronization.cpp +++ b/examples/src/BasicSynchronization.cpp @@ -1,5 +1,5 @@ -// 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) @@ -8,9 +8,9 @@ #include "tbb/tick_count.h" -#include "react/Domain.h" -#include "react/Event.h" -#include "react/Observer.h" +#include "react/state.h" +#include "react/event.h" +#include "react/observer.h" /////////////////////////////////////////////////////////////////////////////////////////////////// /// Example 1 - Asynchronous transactions @@ -20,14 +20,12 @@ namespace example1 using namespace react; using namespace std; - REACTIVE_DOMAIN(D, sequential_concurrent) + Group group; class Sensor { public: - USING_REACTIVE_DOMAIN(D) - - EventSourceT Samples = MakeEventSource(); + EventSource samples = EventSource::Create(group); }; void Run() @@ -36,23 +34,27 @@ namespace example1 Sensor mySensor; - Observe(mySensor.Samples, [] (int v) { - cout << v << std::endl; - }); + auto obs = Observer::Create([&] (const auto& events) + { + for (auto t : events) + cout << t << endl; + }, mySensor.samples); - TransactionStatus status; + SyncPoint sp; - AsyncTransaction(status, [&] { - mySensor.Samples << 30 << 31 << 31 << 32; - }); + group.EnqueueTransaction([&] + { + mySensor.samples << 30 << 31 << 31 << 32; + }, sp); - AsyncTransaction(status, [&] { - mySensor.Samples << 40 << 41 << 51 << 62; - }); + group.EnqueueTransaction([&] + { + mySensor.samples << 40 << 41 << 51 << 62; + }, sp); // Waits until both transactions are completed. // This does not mean that both transactions are interleaved. - status.Wait(); + sp.Wait(); cout << endl; } @@ -66,17 +68,15 @@ namespace example2 using namespace react; using namespace std; - REACTIVE_DOMAIN(D, sequential_concurrent) + Group group; class Sensor { public: - USING_REACTIVE_DOMAIN(D) - - EventSourceT Samples = MakeEventSource(); + EventSource samples = EventSource::Create(group); }; - const int K = 100000; + const int K = 10000; namespace v1 { @@ -87,11 +87,13 @@ namespace example2 Sensor mySensor; int sum = 0; - Observe(mySensor.Samples, [&] (int v) { - sum += v; - }); + auto obs = Observer::Create([&] (const auto& events) + { + for (int e : events) + sum += e; + }, mySensor.samples); - TransactionStatus status; + SyncPoint sp; cout << "Executing " << K << " async transactions..."; @@ -99,12 +101,13 @@ namespace example2 for (int i=0; i < K; i++) { - AsyncTransaction(status, [&] { - mySensor.Samples << 3 << 4 << 2 << 1; - }); + group.EnqueueTransaction([&] + { + mySensor.samples << 3 << 4 << 2 << 1; + }, sp); } - status.Wait(); + sp.Wait(); double d = (tbb::tick_count::now() - t0).seconds(); @@ -126,11 +129,13 @@ namespace example2 Sensor mySensor; int sum = 0; - Observe(mySensor.Samples, [&] (int v) { - sum += v; - }); + auto obs = Observer::Create([&] (const auto& events) + { + for (int e : events) + sum += e; + }, mySensor.samples); - TransactionStatus status; + SyncPoint sp; cout << "Executing " << K << " async transactions..."; @@ -138,12 +143,13 @@ namespace example2 for (int i=0; i < K; i++) { - AsyncTransaction(allow_merging, status, [&] { - mySensor.Samples << 3 << 4 << 2 << 1; - }); + group.EnqueueTransaction([&] + { + mySensor.samples << 3 << 4 << 2 << 1; + }, sp, TransactionFlags::allow_merging); } - status.Wait(); + sp.Wait(); double d = (tbb::tick_count::now() - t0).seconds(); @@ -163,6 +169,7 @@ namespace example2 int main() { example1::Run(); + example2::v1::Run(); example2::v2::Run(); diff --git a/examples/src/Main.cpp b/examples/src/Main.cpp deleted file mode 100644 index 1a8f1ee3..00000000 --- a/examples/src/Main.cpp +++ /dev/null @@ -1,231 +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) - -//#define REACT_ENABLE_LOGGING - -#include "react/Domain.h" -#include "react/Signal.h" -#include "react/Event.h" -#include "react/Algorithm.h" - -#include "tbb/tick_count.h" - -#include - -using namespace std; -using namespace react; - -// Defines a domain. -// Each domain represents a separate dependency graph, managed by a dedicated propagation engine. -// Reactives of different domains can not be combined. - - -void SignalExample3() -{ - REACTIVE_DOMAIN(D, sequential_concurrent) - - cout << "Signal Example 3" << endl; - - auto src = MakeVar(0); - - // Input values can be manipulated imperatively in observers. - // Inputs are implicitly thread-safe, buffered and executed in a continuation turn. - // This continuation turn is queued just like a regular turn. - // If other turns are already queued, they are executed before the continuation. - auto cont = MakeContinuation(src, [&] (int v) { - cout << "V: " << v << endl; - if (v < 10) - src <<= v+1; - }); - - src <<= 1; - - cout << endl; -} - -void SignalExample4() -{ - REACTIVE_DOMAIN(L, sequential_concurrent, ToposortEngine) - REACTIVE_DOMAIN(R, sequential_concurrent, ToposortEngine) - - cout << "Signal Example 4" << endl; - - auto srcL = MakeVar(0); - auto srcR = MakeVar(0); - - auto contL = MakeContinuation(srcL, [&] (int v) { - cout << "L->R: " << v << endl; - if (v < 10) - srcR <<= v+1; - }); - - auto contR = MakeContinuation(srcR, [&] (int v) { - cout << "R->L: " << v << endl; - if (v < 10) - srcL <<= v+1; - }); - - srcL <<= 1; - printf("End\n"); - - cout << endl; -} - -void SignalExample5() -{ - REACTIVE_DOMAIN(L, sequential_concurrent, ToposortEngine) - REACTIVE_DOMAIN(R, sequential_concurrent, ToposortEngine) - - cout << "Signal Example 5" << endl; - - auto srcL = MakeVar(0); - auto depL1 = MakeVar(0); - auto depL2 = MakeVar(0); - auto srcR = MakeVar(0); - - auto contL = MakeContinuation( - Monitor(srcL), - With(depL1, depL2), - [&] (int v, int depL1, int depL2) { - cout << "L->R: " << v << endl; - if (v < 10) - srcR <<= v+1; - }); - - auto contR = MakeContinuation( - Monitor(srcR), - [&] (int v) { - cout << "R->L: " << v << endl; - if (v < 10) - srcL <<= v+1; - }); - - srcL <<= 1; - printf("End\n"); - - cout << endl; -} - -void testme() -{ - REACTIVE_DOMAIN(D, sequential_concurrent) - - std::vector results; - - auto f_0 = [] (int a) -> int - { - int k = 0; - for (int i = 0; i<10000; i++) - k += i; - return a + k; - }; - - auto f_n = [] (int a, int b) -> int - { - int k = 0; - for (int i=0; i<10000; i++) - k += i; - return a + b + k; - }; - - auto n1 = MakeVar(0); - auto n2 = n1 ->* f_0; - auto n3 = ((n2, n1) ->* f_n) ->* f_0; - auto n4 = n3 ->* f_0; - auto n5 = ((((n4, n3) ->* f_n), n1) ->* f_n) ->* f_0; - auto n6 = n5 ->* f_0; - auto n7 = ((n6, n5) ->* f_n) ->* f_0; - auto n8 = n7 ->* f_0; - auto n9 = ((((((n8, n7) ->* f_n), n5) ->* f_n), n1) ->* f_n) ->* f_0; - auto n10 = n9 ->* f_0; - auto n11 = ((n10, n9) ->* f_n) ->* f_0; - auto n12 = n11 ->* f_0; - auto n13 = ((((n12, n11) ->* f_n), n9) ->* f_n) ->* f_0; - auto n14 = n13 ->* f_0; - auto n15 = ((n14, n13) ->* f_n) ->* f_0; - auto n16 = n15 ->* f_0; - auto n17 = ((((((n16, n15) ->* f_n), n13) ->* f_n), n9) ->* f_n) ->* f_0; - - auto src = MakeEventSource(); - - atomic c( 0 ); - - Observe(src, [&] (int v){ - c++; - }); - - auto x0 = tbb::tick_count::now(); - - TransactionStatus st; - - for (int i=0; i<10000; i++) - { - AsyncTransaction(st, [&,i] { - n1 <<= 1+i; - }); - } - - for (int i=0; i<10000; i++) - { - AsyncTransaction(st, [&,i] { - n1 <<= 20000+i; - }); - } - - for (int i=0; i<10000; i++) - { - AsyncTransaction(st, [&,i] { - n1 <<= 100000+i; - }); - } - - st.Wait(); - - //std::thread t3([&] { - // for (int i=0; i<10000; i++) - // n1 <<= 1+i; - //}); - - //std::thread t2([&] { - // for (int i=0; i<10000; i++) - // n1 <<= 20000+i; - //}); - - //std::thread t1([&] { - // for (int i=0; i<10000; i++) - // n1 <<= 100000+i; - //}); - - //t3.join(); - //t2.join(); - //t1.join(); - //std::chrono::milliseconds dura( 10000 ); - //std::this_thread::sleep_for( dura ); - - auto x1 = tbb::tick_count::now(); - - double d = (x1 - x0).seconds(); - printf("Time %g\n", d); - printf("Updates %d\n", c.load()); - printf("n1 %d\n", n1()); -} - -int main() -{ - SignalExample3(); - SignalExample4(); - testme(); - -#ifdef REACT_ENABLE_LOGGING - std::ofstream logfile; - logfile.open("log.txt"); - - D::Log().Write(logfile); - logfile.close(); -#endif - - 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 2f0e7730..00000000 --- a/include/react/Algorithm.h +++ /dev/null @@ -1,236 +0,0 @@ - -// Copyright Sebastian Jeckel 2014. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#ifndef REACT_ALGORITHM_H_INCLUDED -#define REACT_ALGORITHM_H_INCLUDED - -#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 Token; - -template -class SignalPack; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Hold - Hold the most recent event in a signal -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename V, - typename T = typename std::decay::type -> -auto Hold(const Events& events, V&& init) - -> Signal -{ - using REACT_IMPL::HoldNode; - - return Signal( - std::make_shared>( - std::forward(init), GetNodePtr(events))); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Monitor - Emits value changes of target signal -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S -> -auto Monitor(const Signal& target) - -> Events -{ - using REACT_IMPL::MonitorNode; - - return Events( - std::make_shared>( - GetNodePtr(target))); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Iterate - Iteratively combines signal value with values from event stream -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E, - typename V, - typename FIn, - typename S = typename std::decay::type -> -auto Iterate(const Events& events, V&& init, FIn&& func) - -> Signal -{ - using REACT_IMPL::IterateNode; - using REACT_IMPL::IterateByRefNode; - - using F = typename std::decay::type; - using R = typename std::result_of::type; - using TNode = typename std::conditional< - std::is_same::value, - IterateByRefNode, - IterateNode - >::type; - - return Signal( - std::make_shared( - std::forward(init), GetNodePtr(events), std::forward(func))); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Iterate - Synced -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E, - typename V, - typename FIn, - typename ... TDepValues, - typename S = typename std::decay::type -> -auto Iterate(const Events& events, V&& init, - const SignalPack& depPack, FIn&& func) - -> Signal -{ - using REACT_IMPL::SyncedIterateNode; - using REACT_IMPL::SyncedIterateByRefNode; - - using F = typename std::decay::type; - using R = typename std::result_of::type; - using TNode = typename std::conditional< - std::is_same::value, - SyncedIterateByRefNode, - SyncedIterateNode - >::type; - - struct NodeBuilder_ - { - NodeBuilder_(const Events& source, V&& init, FIn&& func) : - MySource( source ), - MyInit( std::forward(init) ), - MyFunc( std::forward(func) ) - {} - - auto operator()(const Signal& ... deps) - -> Signal - { - return Signal( - std::make_shared( - std::forward(MyInit), GetNodePtr(MySource), - std::forward(MyFunc), GetNodePtr(deps) ...)); - } - - const Events& MySource; - V MyInit; - FIn MyFunc; - }; - - return REACT_IMPL::apply( - NodeBuilder_( events, std::forward(init), std::forward(func) ), - depPack.Data); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Snapshot - Sets signal value to value of other signal when event is received -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S, - typename E -> -auto Snapshot(const Events& trigger, const Signal& target) - -> Signal -{ - using REACT_IMPL::SnapshotNode; - - return Signal( - std::make_shared>( - GetNodePtr(target), GetNodePtr(trigger))); -} - - - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Pulse - Emits value of target signal when event is received -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S, - typename E -> -auto Pulse(const Events& trigger, const Signal& target) - -> Events -{ - using REACT_IMPL::PulseNode; - - return Events( - std::make_shared>( - GetNodePtr(target), GetNodePtr(trigger))); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Changed - Emits token when target signal was changed -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S -> -auto Changed(const Signal& target) - -> Events -{ - return Monitor(target).Tokenize(); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ChangedTo - Emits token when target signal was changed to value -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename V, - typename S = typename std::decay::type -> -auto ChangedTo(const Signal& target, V&& value) - -> Events -{ - return Monitor(target) - .Filter([=] (const S& v) { return v == value; }) - .Tokenize(); -} - -/******************************************/ REACT_END /******************************************/ - -#endif // REACT_ALGORITHM_H_INCLUDED \ No newline at end of file diff --git a/include/react/Domain.h b/include/react/Domain.h deleted file mode 100644 index 1279c3f9..00000000 --- a/include/react/Domain.h +++ /dev/null @@ -1,415 +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) - -#ifndef REACT_DOMAIN_H_INCLUDED -#define REACT_DOMAIN_H_INCLUDED - -#pragma once - -#include "react/detail/Defs.h" - -#include -#include - -#include "react/detail/DomainBase.h" -#include "react/detail/ReactiveInput.h" - -#include "react/detail/graph/ContinuationNodes.h" - -#ifdef REACT_ENABLE_LOGGING - #include "react/logging/EventLog.h" - #include "react/logging/EventRecords.h" -#endif //REACT_ENABLE_LOGGING - -/*****************************************/ REACT_BEGIN /*****************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Forward declarations -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class Signal; - -template -class VarSignal; - -template -class TempSignal; - -template -class Events; - -template -class EventSource; - -template -class TempEvents; - -enum class Token; - -template -class Observer; - -template -class ScopedObserver; - -template -class Reactor; - -template -class SignalPack; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Common types & constants -/////////////////////////////////////////////////////////////////////////////////////////////////// -using REACT_IMPL::TransactionFlagsT; - -// ETransactionFlags -using REACT_IMPL::ETransactionFlags; -using REACT_IMPL::allow_merging; - -#ifdef REACT_ENABLE_LOGGING - using REACT_IMPL::EventLog; -#endif //REACT_ENABLE_LOGGING - -// Domain modes -using REACT_IMPL::EDomainMode; -using REACT_IMPL::sequential; -using REACT_IMPL::sequential_concurrent; -using REACT_IMPL::parallel; -using REACT_IMPL::parallel_concurrent; - -// Expose enum type so aliases for engines can be declared, but don't -// expose the actual enum values as they are reserved for internal use. -using REACT_IMPL::EPropagationMode; - -using REACT_IMPL::WeightHint; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// TransactionStatus -/////////////////////////////////////////////////////////////////////////////////////////////////// -class TransactionStatus -{ - using StateT = REACT_IMPL::SharedWaitingState; - using PtrT = REACT_IMPL::WaitingStatePtrT; - -public: - // Default ctor - inline TransactionStatus() : - statePtr_( StateT::Create() ) - {} - - // Move ctor - inline TransactionStatus(TransactionStatus&& other) : - statePtr_( std::move(other.statePtr_) ) - { - other.statePtr_ = StateT::Create(); - } - - // Move assignment - inline TransactionStatus& operator=(TransactionStatus&& other) - { - if (this != &other) - { - statePtr_ = std::move(other.statePtr_); - other.statePtr_ = StateT::Create(); - } - return *this; - } - - // Deleted copy ctor & assignment - TransactionStatus(const TransactionStatus&) = delete; - TransactionStatus& operator=(const TransactionStatus&) = delete; - - inline void Wait() - { - assert(statePtr_.Get() != nullptr); - statePtr_->Wait(); - } - -private: - PtrT statePtr_; - - template - friend void AsyncTransaction(TransactionStatus& status, F&& func); - - template - friend void AsyncTransaction(TransactionFlagsT flags, TransactionStatus& status, F&& func); -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Continuation -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename D2 -> -class Continuation : public REACT_IMPL::ContinuationBase -{ -private: - using NodePtrT = REACT_IMPL::NodeBasePtrT; - -public: - using SourceDomainT = D; - using TargetDomainT = D2; - - // Default ctor - Continuation() = default; - - // Move ctor - Continuation(Continuation&& other) : - Continuation::ContinuationBase( std::move(other) ) - {} - - // Node ctor - explicit Continuation(NodePtrT&& nodePtr) : - Continuation::ContinuationBase( std::move(nodePtr) ) - {} - - // Move assignment - Continuation& operator=(Continuation&& other) - { - Continuation::ContinuationBase::operator=( std::move(other) ); - return *this; - } - - // Deleted copy ctor & assignment - Continuation(const Continuation&) = delete; - Continuation& operator=(const Continuation&) = delete; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// MakeContinuation - Signals -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename DOut = D, - typename S, - typename FIn -> -auto MakeContinuation(TransactionFlagsT flags, const Signal& trigger, FIn&& func) - -> Continuation -{ - static_assert(DOut::is_concurrent, - "MakeContinuation requires support for concurrent input to target domain."); - - using REACT_IMPL::SignalContinuationNode; - using F = typename std::decay::type; - - return Continuation( - std::make_shared>( - flags, GetNodePtr(trigger), std::forward(func))); -} - -template -< - typename D, - typename DOut = D, - typename S, - typename FIn -> -auto MakeContinuation(const Signal& trigger, FIn&& func) - -> Continuation -{ - return MakeContinuation(0, trigger, std::forward(func)); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// MakeContinuation - Events -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename DOut = D, - typename E, - typename FIn -> -auto MakeContinuation(TransactionFlagsT flags, const Events& trigger, FIn&& func) - -> Continuation -{ - static_assert(DOut::is_concurrent, - "MakeContinuation requires support for concurrent input to target domain."); - - using REACT_IMPL::EventContinuationNode; - using F = typename std::decay::type; - - return Continuation( - std::make_shared>( - flags, GetNodePtr(trigger), std::forward(func))); -} - -template -< - typename D, - typename DOut = D, - typename E, - typename FIn -> -auto MakeContinuation(const Events& trigger, FIn&& func) - -> Continuation -{ - return MakeContinuation(0, trigger, std::forward(func)); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// MakeContinuation - Synced -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename DOut = D, - typename E, - typename FIn, - typename ... TDepValues -> -auto MakeContinuation(TransactionFlagsT flags, const Events& trigger, - const SignalPack& depPack, FIn&& func) - -> Continuation -{ - static_assert(DOut::is_concurrent, - "MakeContinuation requires support for concurrent input to target domain."); - - using REACT_IMPL::SyncedContinuationNode; - using F = typename std::decay::type; - - struct NodeBuilder_ - { - NodeBuilder_(TransactionFlagsT flags, const Events& trigger, FIn&& func) : - MyFlags( flags ), - MyTrigger( trigger ), - MyFunc( std::forward(func) ) - {} - - auto operator()(const Signal& ... deps) - -> Continuation - { - return Continuation( - std::make_shared>( - MyFlags, - GetNodePtr(MyTrigger), - std::forward(MyFunc), GetNodePtr(deps) ...)); - } - - TransactionFlagsT MyFlags; - const Events& MyTrigger; - FIn MyFunc; - }; - - return REACT_IMPL::apply( - NodeBuilder_( flags, trigger, std::forward(func) ), - depPack.Data); -} - -template -< - typename D, - typename DOut = D, - typename E, - typename FIn, - typename ... TDepValues -> -auto MakeContinuation(const Events& trigger, - const SignalPack& depPack, FIn&& func) - -> Continuation -{ - return MakeContinuation(0, trigger, depPack, std::forward(func)); -} - -/////////////////////////////////////////////////////////////////////////////////////////////// -/// DoTransaction -/////////////////////////////////////////////////////////////////////////////////////////////// -template -void DoTransaction(F&& func) -{ - using REACT_IMPL::DomainSpecificInputManager; - DomainSpecificInputManager::Instance().DoTransaction(0, std::forward(func)); -} - -template -void DoTransaction(TransactionFlagsT flags, F&& func) -{ - using REACT_IMPL::DomainSpecificInputManager; - DomainSpecificInputManager::Instance().DoTransaction(flags, std::forward(func)); -} - -/////////////////////////////////////////////////////////////////////////////////////////////// -/// AsyncTransaction -/////////////////////////////////////////////////////////////////////////////////////////////// -template -void AsyncTransaction(F&& func) -{ - static_assert(D::is_concurrent, "AsyncTransaction requires concurrent domain."); - - using REACT_IMPL::DomainSpecificInputManager; - DomainSpecificInputManager::Instance() - .AsyncTransaction(0, nullptr, std::forward(func)); -} - -template -void AsyncTransaction(TransactionFlagsT flags, F&& func) -{ - static_assert(D::is_concurrent, "AsyncTransaction requires concurrent domain."); - - using REACT_IMPL::DomainSpecificInputManager; - DomainSpecificInputManager::Instance() - .AsyncTransaction(flags, nullptr, std::forward(func)); -} - -template -void AsyncTransaction(TransactionStatus& status, F&& func) -{ - static_assert(D::is_concurrent, "AsyncTransaction requires concurrent domain."); - - using REACT_IMPL::DomainSpecificInputManager; - - DomainSpecificInputManager::Instance() - .AsyncTransaction(0, status.statePtr_, std::forward(func)); -} - -template -void AsyncTransaction(TransactionFlagsT flags, TransactionStatus& status, F&& func) -{ - static_assert(D::is_concurrent, "AsyncTransaction requires concurrent domain."); - - using REACT_IMPL::DomainSpecificInputManager; - DomainSpecificInputManager::Instance() - .AsyncTransaction(flags, status.statePtr_, std::forward(func)); -} - -/******************************************/ REACT_END /******************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Domain definition macro -/////////////////////////////////////////////////////////////////////////////////////////////////// -#define REACTIVE_DOMAIN(name, ...) \ - struct name : \ - public REACT_IMPL::DomainBase> {}; \ - REACT_IMPL::DomainInitializer name ## _initializer_; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Define type aliases for given domain -/////////////////////////////////////////////////////////////////////////////////////////////////// -#define USING_REACTIVE_DOMAIN(name) \ - template \ - using SignalT = Signal; \ - \ - template \ - using VarSignalT = VarSignal; \ - \ - template \ - using EventsT = Events; \ - \ - template \ - using EventSourceT = EventSource; \ - \ - using ObserverT = Observer; \ - \ - using ScopedObserverT = ScopedObserver; \ - \ - using ReactorT = Reactor; - -#endif // REACT_DOMAIN_H_INCLUDED \ No newline at end of file diff --git a/include/react/Event.h b/include/react/Event.h deleted file mode 100644 index 550ef5ac..00000000 --- a/include/react/Event.h +++ /dev/null @@ -1,761 +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) - -#ifndef REACT_EVENT_H_INCLUDED -#define REACT_EVENT_H_INCLUDED - -#pragma once - -#include "react/detail/Defs.h" - -#include -#include -#include - -#include "react/Observer.h" -#include "react/TypeTraits.h" -#include "react/common/Util.h" -#include "react/detail/EventBase.h" - -/*****************************************/ REACT_BEGIN /*****************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Forward declarations -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class Events; - -template -class EventSource; - -template -class TempEvents; - -enum class Token; - -template -class Signal; - -template -class SignalPack; - -using REACT_IMPL::WeightHint; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// MakeEventSource -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -auto MakeEventSource() - -> EventSource -{ - using REACT_IMPL::EventSourceNode; - - return EventSource( - std::make_shared>()); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Merge -/////////////////////////////////////////////////////////////////////////////////////////////////// -// Note: Default template arguments are in forward declaration -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 -{ - using REACT_IMPL::EventOpNode; - - static_assert(sizeof...(TArgs) > 0, - "react::Merge requires at least 2 arguments."); - - return TempEvents( - std::make_shared>( - GetNodePtr(arg1), GetNodePtr(args) ...)); -} - -template -< - typename TLeftEvents, - typename TRightEvents, - typename D = typename TLeftEvents::DomainT, - typename TLeftVal = typename TLeftEvents::ValueT, - typename TRightVal = typename TRightEvents::ValueT, - typename E = TLeftVal, - typename TOp = REACT_IMPL::EventMergeOp, - REACT_IMPL::EventStreamNodePtrT>, - class = typename std::enable_if< - IsEvent::value>::type, - class = typename std::enable_if< - IsEvent::value>::type -> -auto operator|(const TLeftEvents& lhs, const TRightEvents& rhs) - -> TempEvents -{ - using REACT_IMPL::EventOpNode; - - return TempEvents( - std::make_shared>( - GetNodePtr(lhs), GetNodePtr(rhs))); -} - -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 -{ - using REACT_IMPL::EventOpNode; - - return TempEvents( - std::make_shared>( - lhs.StealOp(), rhs.StealOp())); -} - -template -< - typename D, - typename TLeftVal, - typename TLeftOp, - typename TRightEvents, - typename TRightVal = typename TRightEvents::ValueT, - typename E = TLeftVal, - typename TOp = REACT_IMPL::EventMergeOp>, - class = typename std::enable_if< - IsEvent::value>::type -> -auto operator|(TempEvents&& lhs, const TRightEvents& rhs) - -> TempEvents -{ - using REACT_IMPL::EventOpNode; - - return TempEvents( - std::make_shared>( - lhs.StealOp(), GetNodePtr(rhs))); -} - -template -< - typename TLeftEvents, - typename D, - typename TRightVal, - typename TRightOp, - typename TLeftVal = typename TLeftEvents::ValueT, - typename E = TLeftVal, - typename TOp = REACT_IMPL::EventMergeOp, - TRightOp>, - class = typename std::enable_if< - IsEvent::value>::type -> -auto operator|(const TLeftEvents& lhs, TempEvents&& rhs) - -> TempEvents -{ - using REACT_IMPL::EventOpNode; - - return TempEvents( - std::make_shared>( - GetNodePtr(lhs), rhs.StealOp())); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Filter -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E, - typename FIn, - typename F = typename std::decay::type, - typename TOp = REACT_IMPL::EventFilterOp> -> -auto Filter(const Events& src, FIn&& filter) - -> TempEvents -{ - using REACT_IMPL::EventOpNode; - - return TempEvents( - std::make_shared>( - std::forward(filter), GetNodePtr(src))); -} - -template -< - typename D, - typename E, - typename TOpIn, - typename FIn, - typename F = typename std::decay::type, - typename TOpOut = REACT_IMPL::EventFilterOp -> -auto Filter(TempEvents&& src, FIn&& filter) - -> TempEvents -{ - using REACT_IMPL::EventOpNode; - - return TempEvents( - std::make_shared>( - std::forward(filter), src.StealOp())); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Filter - Synced -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E, - typename FIn, - typename ... TDepValues -> -auto Filter(const Events& source, const SignalPack& depPack, FIn&& func) - -> Events -{ - using REACT_IMPL::SyncedEventFilterNode; - - using F = typename std::decay::type; - - struct NodeBuilder_ - { - NodeBuilder_(const Events& source, FIn&& func) : - MySource( source ), - MyFunc( std::forward(func) ) - {} - - auto operator()(const Signal& ... deps) - -> Events - { - return Events( - std::make_shared>( - GetNodePtr(MySource), std::forward(MyFunc), GetNodePtr(deps) ...)); - } - - const Events& MySource; - FIn MyFunc; - }; - - return REACT_IMPL::apply( - NodeBuilder_( source, std::forward(func) ), - depPack.Data); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Transform -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename TIn, - typename FIn, - typename F = typename std::decay::type, - typename TOut = typename std::result_of::type, - typename TOp = REACT_IMPL::EventTransformOp> -> -auto Transform(const Events& src, FIn&& func) - -> TempEvents -{ - using REACT_IMPL::EventOpNode; - - return TempEvents( - std::make_shared>( - std::forward(func), GetNodePtr(src))); -} - -template -< - typename D, - typename TIn, - typename TOpIn, - typename FIn, - typename F = typename std::decay::type, - typename TOut = typename std::result_of::type, - typename TOpOut = REACT_IMPL::EventTransformOp -> -auto Transform(TempEvents&& src, FIn&& func) - -> TempEvents -{ - using REACT_IMPL::EventOpNode; - - return TempEvents( - std::make_shared>( - std::forward(func), src.StealOp())); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Transform - Synced -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename TIn, - typename FIn, - typename ... TDepValues, - typename TOut = typename std::result_of::type -> -auto Transform(const Events& source, const SignalPack& depPack, FIn&& func) - -> Events -{ - using REACT_IMPL::SyncedEventTransformNode; - - using F = typename std::decay::type; - - struct NodeBuilder_ - { - NodeBuilder_(const Events& source, FIn&& func) : - MySource( source ), - MyFunc( std::forward(func) ) - {} - - auto operator()(const Signal& ... deps) - -> Events - { - return Events( - std::make_shared>( - GetNodePtr(MySource), std::forward(MyFunc), GetNodePtr(deps) ...)); - } - - const Events& MySource; - FIn MyFunc; - }; - - return REACT_IMPL::apply( - NodeBuilder_( source, std::forward(func) ), - depPack.Data); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Flatten -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename TInnerValue -> -auto Flatten(const Signal>& outer) - -> Events -{ - return Events( - std::make_shared, TInnerValue>>( - GetNodePtr(outer), GetNodePtr(outer.Value()))); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Token -/////////////////////////////////////////////////////////////////////////////////////////////////// -enum class Token { value }; - -struct Tokenizer -{ - template - Token operator()(const T&) const { return Token::value; } -}; - -template -auto Tokenize(TEvents&& source) - -> decltype(Transform(source, Tokenizer{})) -{ - return Transform(source, Tokenizer{}); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Events -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E = Token -> -class Events : public REACT_IMPL::EventStreamBase -{ -private: - using NodeT = REACT_IMPL::EventStreamNode; - using NodePtrT = std::shared_ptr; - -public: - using ValueT = E; - - // Default ctor - Events() = default; - - // Copy ctor - Events(const Events&) = default; - - // Move ctor - Events(Events&& other) : - Events::EventStreamBase( std::move(other) ) - {} - - // Node ctor - explicit Events(NodePtrT&& nodePtr) : - Events::EventStreamBase( std::move(nodePtr) ) - {} - - // Copy assignment - Events& operator=(const Events&) = default; - - // Move assignment - Events& operator=(Events&& other) - { - Events::EventStreamBase::operator=( std::move(other) ); - return *this; - } - - bool Equals(const Events& other) const - { - return Events::EventStreamBase::Equals(other); - } - - bool IsValid() const - { - return Events::EventStreamBase::IsValid(); - } - - void SetWeightHint(WeightHint weight) - { - Events::EventStreamBase::SetWeightHint(weight); - } - - auto Tokenize() const - -> decltype(REACT::Tokenize(std::declval())) - { - return REACT::Tokenize(*this); - } - - template - auto Merge(TArgs&& ... 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)); - } -}; - -// Specialize for references -template -< - typename D, - typename E -> -class Events : public REACT_IMPL::EventStreamBase> -{ -private: - using NodeT = REACT_IMPL::EventStreamNode>; - using NodePtrT = std::shared_ptr; - -public: - using ValueT = E; - - // Default ctor - Events() = default; - - // Copy ctor - Events(const Events&) = default; - - // Move ctor - Events(Events&& other) : - Events::EventStreamBase( std::move(other) ) - {} - - // Node ctor - explicit Events(NodePtrT&& nodePtr) : - Events::EventStreamBase( std::move(nodePtr) ) - {} - - // Copy assignment - Events& operator=(const Events&) = default; - - // Move assignment - Events& operator=(Events&& other) - { - Events::EventStreamBase::operator=( std::move(other) ); - return *this; - } - - bool Equals(const Events& other) const - { - return Events::EventStreamBase::Equals(other); - } - - bool IsValid() const - { - return Events::EventStreamBase::IsValid(); - } - - void SetWeightHint(WeightHint weight) - { - Events::EventStreamBase::SetWeightHint(weight); - } - - auto Tokenize() const - -> decltype(REACT::Tokenize(std::declval())) - { - return REACT::Tokenize(*this); - } - - template - auto Merge(TArgs&& ... 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)); - } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventSource -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E = Token -> -class EventSource : public Events -{ -private: - using NodeT = REACT_IMPL::EventSourceNode; - using NodePtrT = std::shared_ptr; - -public: - // Default ctor - EventSource() = default; - - // Copy ctor - EventSource(const EventSource&) = default; - - // Move ctor - EventSource(EventSource&& other) : - EventSource::Events( std::move(other) ) - {} - - // Node ctor - explicit EventSource(NodePtrT&& nodePtr) : - EventSource::Events( std::move(nodePtr) ) - {} - - // Copy assignemnt - EventSource& operator=(const EventSource&) = default; - - // Move assignment - EventSource& operator=(EventSource&& other) - { - EventSource::Events::operator=( std::move(other) ); - return *this; - } - - // Explicit emit - void Emit(const E& e) const { EventSource::EventStreamBase::emit(e); } - void Emit(E&& e) const { EventSource::EventStreamBase::emit(std::move(e)); } - - void Emit() const - { - static_assert(std::is_same::value, "Can't emit on non token stream."); - EventSource::EventStreamBase::emit(Token::value); - } - - // Function object style - void operator()(const E& e) const { EventSource::EventStreamBase::emit(e); } - void operator()(E&& e) const { EventSource::EventStreamBase::emit(std::move(e)); } - - void operator()() const - { - static_assert(std::is_same::value, "Can't emit on non token stream."); - EventSource::EventStreamBase::emit(Token::value); - } - - // Stream style - const EventSource& operator<<(const E& e) const - { - EventSource::EventStreamBase::emit(e); - return *this; - } - - const EventSource& operator<<(E&& e) const - { - EventSource::EventStreamBase::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 = std::shared_ptr; - -public: - // Default ctor - EventSource() = default; - - // Copy ctor - EventSource(const EventSource&) = default; - - // Move ctor - EventSource(EventSource&& other) : - EventSource::Events( std::move(other) ) - {} - - // Node ctor - explicit EventSource(NodePtrT&& nodePtr) : - EventSource::Events( std::move(nodePtr) ) - {} - - // Copy assignment - EventSource& operator=(const EventSource&) = default; - - // Move assignment - EventSource& operator=(EventSource&& other) - { - EventSource::Events::operator=( std::move(other) ); - return *this; - } - - // Explicit emit - void Emit(std::reference_wrapper e) const { EventSource::EventStreamBase::emit(e); } - - // Function object style - void operator()(std::reference_wrapper e) const { EventSource::EventStreamBase::emit(e); } - - // Stream style - const EventSource& operator<<(std::reference_wrapper e) const - { - EventSource::EventStreamBase::emit(e); - return *this; - } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// TempEvents -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E, - typename TOp -> -class TempEvents : public Events -{ -protected: - using NodeT = REACT_IMPL::EventOpNode; - using NodePtrT = std::shared_ptr; - -public: - // Default ctor - TempEvents() = default; - - // Copy ctor - TempEvents(const TempEvents&) = default; - - // Move ctor - TempEvents(TempEvents&& other) : - TempEvents::Events( std::move(other) ) - {} - - // Node ctor - explicit TempEvents(NodePtrT&& nodePtr) : - TempEvents::Events( std::move(nodePtr) ) - {} - - // Copy assignment - TempEvents& operator=(const TempEvents&) = default; - - // Move assignment - TempEvents& operator=(TempEvents&& other) - { - TempEvents::EventStreamBase::operator=( std::move(other) ); - return *this; - } - - TOp StealOp() - { - return std::move(reinterpret_cast(this->ptr_.get())->StealOp()); - } - - template - auto Merge(TArgs&& ... 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 /***************************************/ - -#endif // REACT_EVENT_H_INCLUDED \ No newline at end of file diff --git a/include/react/Observer.h b/include/react/Observer.h deleted file mode 100644 index 07d76c32..00000000 --- a/include/react/Observer.h +++ /dev/null @@ -1,302 +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) - -#ifndef REACT_OBSERVER_H_INCLUDED -#define REACT_OBSERVER_H_INCLUDED - -#pragma once - -#include "react/detail/Defs.h" - -#include -#include - -#include "react/common/Util.h" -#include "react/detail/IReactiveNode.h" -#include "react/detail/ObserverBase.h" -#include "react/detail/graph/ObserverNodes.h" - -/*****************************************/ REACT_BEGIN /*****************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Forward declarations -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class Signal; - -template -class SignalPack; - -template -class Events; - -using REACT_IMPL::ObserverAction; -using REACT_IMPL::WeightHint; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Observer -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class Observer -{ -private: - using SubjectPtrT = std::shared_ptr>; - using NodeT = REACT_IMPL::ObserverNode; - -public: - // Default ctor - Observer() : - nodePtr_( nullptr ), - subjectPtr_( nullptr ) - {} - - // Move ctor - Observer(Observer&& other) : - nodePtr_( other.nodePtr_ ), - subjectPtr_( std::move(other.subjectPtr_) ) - { - other.nodePtr_ = nullptr; - other.subjectPtr_.reset(); - } - - // Node ctor - Observer(NodeT* nodePtr, const SubjectPtrT& subjectPtr) : - nodePtr_( nodePtr ), - subjectPtr_( subjectPtr ) - {} - - // Move assignment - Observer& operator=(Observer&& other) - { - nodePtr_ = other.nodePtr_; - subjectPtr_ = std::move(other.subjectPtr_); - - other.nodePtr_ = nullptr; - other.subjectPtr_.reset(); - - return *this; - } - - // Deleted copy ctor and assignment - Observer(const Observer&) = delete; - Observer& operator=(const Observer&) = delete; - - void Detach() - { - assert(IsValid()); - subjectPtr_->UnregisterObserver(nodePtr_); - } - - bool IsValid() const - { - return nodePtr_ != nullptr; - } - - void SetWeightHint(WeightHint weight) - { - assert(IsValid()); - nodePtr_->SetWeightHint(weight); - } - -private: - // Owned by subject - NodeT* nodePtr_; - - // While the observer handle exists, the subject is not destroyed - SubjectPtrT subjectPtr_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ScopedObserver -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class ScopedObserver -{ -public: - // Move ctor - ScopedObserver(ScopedObserver&& other) : - obs_( std::move(other.obs_) ) - {} - - // Construct from observer - ScopedObserver(Observer&& obs) : - obs_( std::move(obs) ) - {} - - // Move assignment - ScopedObserver& operator=(ScopedObserver&& other) - { - obs_ = std::move(other.obs_); - } - - // Deleted default ctor, copy ctor and assignment - ScopedObserver() = delete; - ScopedObserver(const ScopedObserver&) = delete; - ScopedObserver& operator=(const ScopedObserver&) = delete; - - ~ScopedObserver() - { - obs_.Detach(); - } - - bool IsValid() const - { - return obs_.IsValid(); - } - - void SetWeightHint(WeightHint weight) - { - obs_.SetWeightHint(weight); - } - -private: - Observer obs_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Observe - Signals -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename FIn, - typename S -> -auto Observe(const Signal& subject, FIn&& func) - -> Observer -{ - using REACT_IMPL::IObserver; - using REACT_IMPL::ObserverNode; - using REACT_IMPL::SignalObserverNode; - using REACT_IMPL::AddDefaultReturnValueWrapper; - - using F = typename std::decay::type; - using R = typename std::result_of::type; - using WrapperT = AddDefaultReturnValueWrapper; - - // If return value of passed function is void, add ObserverAction::next as - // default return value. - using TNode = typename std::conditional< - std::is_same::value, - SignalObserverNode, - SignalObserverNode - >::type; - - - const auto& subjectPtr = GetNodePtr(subject); - - std::unique_ptr> nodePtr( new TNode(subjectPtr, std::forward(func)) ); - ObserverNode* rawNodePtr = nodePtr.get(); - - subjectPtr->RegisterObserver(std::move(nodePtr)); - - return Observer( rawNodePtr, subjectPtr ); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Observe - Events -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename FIn, - typename E -> -auto Observe(const Events& subject, FIn&& func) - -> Observer -{ - using REACT_IMPL::IObserver; - using REACT_IMPL::ObserverNode; - using REACT_IMPL::EventObserverNode; - using REACT_IMPL::AddDefaultReturnValueWrapper; - - using F = typename std::decay::type; - using R = typename std::result_of::type; - using WrapperT = AddDefaultReturnValueWrapper; - - // If return value of passed function is void, add ObserverAction::next as - // default return value. - using TNode = typename std::conditional< - std::is_same::value, - EventObserverNode, - EventObserverNode - >::type; - - const auto& subjectPtr = GetNodePtr(subject); - - std::unique_ptr> nodePtr( new TNode(subjectPtr, std::forward(func)) ); - ObserverNode* rawNodePtr = nodePtr.get(); - - subjectPtr->RegisterObserver(std::move(nodePtr)); - - return Observer( rawNodePtr, subjectPtr ); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Observe - Synced -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename FIn, - typename E, - typename ... TDepValues -> -auto Observe(const Events& subject, - const SignalPack& depPack, FIn&& func) - -> Observer -{ - using REACT_IMPL::IObserver; - using REACT_IMPL::ObserverNode; - using REACT_IMPL::SyncedObserverNode; - using REACT_IMPL::AddDefaultReturnValueWrapper; - - using F = typename std::decay::type; - using R = typename std::result_of::type; - using WrapperT = AddDefaultReturnValueWrapper; - - // If return value of passed function is void, add ObserverAction::next as - // default return value. - using TNode = typename std::conditional< - std::is_same::value, - SyncedObserverNode, - SyncedObserverNode - >::type; - - struct NodeBuilder_ - { - NodeBuilder_(const Events& subject, FIn&& func) : - MySubject( subject ), - MyFunc( std::forward(func) ) - {} - - auto operator()(const Signal& ... deps) - -> ObserverNode* - { - return new TNode( - GetNodePtr(MySubject), std::forward(MyFunc), GetNodePtr(deps) ... ); - } - - const Events& MySubject; - FIn MyFunc; - }; - - const auto& subjectPtr = GetNodePtr(subject); - - std::unique_ptr> nodePtr( REACT_IMPL::apply( - NodeBuilder_( subject, std::forward(func) ), - depPack.Data) ); - - ObserverNode* rawNodePtr = nodePtr.get(); - - subjectPtr->RegisterObserver(std::move(nodePtr)); - - return Observer( rawNodePtr, subjectPtr ); -} - -/******************************************/ REACT_END /******************************************/ - -#endif // REACT_OBSERVER_H_INCLUDED \ No newline at end of file diff --git a/include/react/Reactor.h b/include/react/Reactor.h deleted file mode 100644 index 2149dcac..00000000 --- a/include/react/Reactor.h +++ /dev/null @@ -1,76 +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) - -#ifndef REACT_REACTOR_H_INCLUDED -#define REACT_REACTOR_H_INCLUDED - -#pragma once - -#include "react/detail/Defs.h" - -#include -#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 Reactor -{ -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(GetNodePtr(evn)); - } - - template - void RepeatUntil(const Events& evn, F&& func) - { - node_.RepeatUntil(GetNodePtr(evn), std::forward(func)); - } - - template - const S& Get(const Signal& sig) - { - return node_.Get(GetNodePtr(sig)); - } - - private: - NodeT& node_; - }; - - template - explicit Reactor(F&& func) : - nodePtr_( new REACT_IMPL::ReactorNode(std::forward(func)) ) - { - nodePtr_->StartLoop(); - } - -private: - std::unique_ptr nodePtr_; -}; - -/******************************************/ REACT_END /******************************************/ - -#endif // REACT_REACTOR_H_INCLUDED \ No newline at end of file diff --git a/include/react/Signal.h b/include/react/Signal.h deleted file mode 100644 index 936874b4..00000000 --- a/include/react/Signal.h +++ /dev/null @@ -1,953 +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) - -#ifndef REACT_SIGNAL_H_INCLUDED -#define REACT_SIGNAL_H_INCLUDED - -#pragma once - -#if _MSC_VER && !__INTEL_COMPILER - #pragma warning(disable : 4503) -#endif - -#include "react/detail/Defs.h" - -#include -#include -#include -#include - -#include "react/Observer.h" -#include "react/TypeTraits.h" -#include "react/detail/SignalBase.h" - -/*****************************************/ REACT_BEGIN /*****************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Forward declarations -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class Signal; - -template -class VarSignal; - -template -class TempSignal; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// SignalPack - Wraps several nodes in a tuple. Create with comma operator. -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename ... TValues -> -class SignalPack -{ -public: - SignalPack(const Signal& ... deps) : - Data( std::tie(deps ...) ) - {} - - template - SignalPack(const SignalPack& curArgs, const Signal& newArg) : - Data( std::tuple_cat(curArgs.Data, std::tie(newArg)) ) - {} - - std::tuple& ...> Data; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// With - Utility function to create a SignalPack -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename ... TValues -> -auto With(const Signal& ... deps) - -> SignalPack -{ - return SignalPack(deps ...); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// MakeVar -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename V, - typename S = typename std::decay::type, - class = typename std::enable_if< - ! IsSignal::value>::type, - class = typename std::enable_if< - ! IsEvent::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 = typename std::decay::type, - typename TInner = typename S::ValueT, - class = typename 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 = typename std::decay::type, - typename TInner = typename S::ValueT, - class = typename std::enable_if< - IsEvent::value>::type -> -auto MakeVar(V&& value) - -> VarSignal> -{ - return VarSignal>( - std::make_shared>>( - std::forward(value))); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// MakeSignal -/////////////////////////////////////////////////////////////////////////////////////////////////// -// Single arg -template -< - typename D, - typename TValue, - typename FIn, - typename F = typename std::decay::type, - typename S = typename std::result_of::type, - typename TOp = REACT_IMPL::FunctionOp> -> -auto MakeSignal(const Signal& arg, FIn&& func) - -> TempSignal -{ - return TempSignal( - std::make_shared>( - std::forward(func), GetNodePtr(arg))); -} - -// Multiple args -template -< - typename D, - typename ... TValues, - typename FIn, - typename F = typename std::decay::type, - typename S = typename std::result_of::type, - typename TOp = REACT_IMPL::FunctionOp ...> -> -auto MakeSignal(const SignalPack& argPack, FIn&& func) - -> TempSignal -{ - using REACT_IMPL::SignalOpNode; - - struct NodeBuilder_ - { - NodeBuilder_(FIn&& func) : - MyFunc( std::forward(func) ) - {} - - auto operator()(const Signal& ... args) - -> TempSignal - { - return TempSignal( - std::make_shared>( - std::forward(MyFunc), GetNodePtr(args) ...)); - } - - FIn MyFunc; - }; - - return REACT_IMPL::apply( - NodeBuilder_( std::forward(func) ), - argPack.Data); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Unary operators -/////////////////////////////////////////////////////////////////////////////////////////////////// -#define REACT_DECLARE_OP(op,name) \ -template \ -struct name ## OpFunctor \ -{ \ - T operator()(const T& v) const { return op v; } \ -}; \ - \ -template \ -< \ - typename TSignal, \ - typename D = typename TSignal::DomainT, \ - typename TVal = typename TSignal::ValueT, \ - class = typename std::enable_if< \ - IsSignal::value>::type, \ - typename F = name ## OpFunctor, \ - typename S = typename std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp> \ -> \ -auto operator op(const TSignal& arg) \ - -> TempSignal \ -{ \ - return TempSignal( \ - std::make_shared>( \ - F(), GetNodePtr(arg))); \ -} \ - \ -template \ -< \ - typename D, \ - typename TVal, \ - typename TOpIn, \ - typename F = name ## OpFunctor, \ - typename S = typename std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp \ -> \ -auto operator op(TempSignal&& arg) \ - -> TempSignal \ -{ \ - return TempSignal( \ - std::make_shared>( \ - F(), arg.StealOp())); \ -} - -REACT_DECLARE_OP(+, UnaryPlus) -REACT_DECLARE_OP(-, UnaryMinus) -REACT_DECLARE_OP(!, LogicalNegation) -REACT_DECLARE_OP(~, BitwiseComplement) -REACT_DECLARE_OP(++, Increment) -REACT_DECLARE_OP(--, Decrement) - -#undef REACT_DECLARE_OP - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Binary operators -/////////////////////////////////////////////////////////////////////////////////////////////////// -#define REACT_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 = typename TLeftSignal::DomainT, \ - typename TLeftVal = typename TLeftSignal::ValueT, \ - typename TRightVal = typename TRightSignal::ValueT, \ - class = typename std::enable_if< \ - IsSignal::value>::type, \ - class = typename std::enable_if< \ - IsSignal::value>::type, \ - typename F = name ## OpFunctor, \ - typename S = typename std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp, \ - REACT_IMPL::SignalNodePtrT> \ -> \ -auto operator op(const TLeftSignal& lhs, const TRightSignal& rhs) \ - -> TempSignal \ -{ \ - return TempSignal( \ - std::make_shared>( \ - F(), GetNodePtr(lhs), GetNodePtr(rhs))); \ -} \ - \ -template \ -< \ - typename TLeftSignal, \ - typename TRightValIn, \ - typename D = typename TLeftSignal::DomainT, \ - typename TLeftVal = typename TLeftSignal::ValueT, \ - typename TRightVal = typename std::decay::type, \ - class = typename std::enable_if< \ - IsSignal::value>::type, \ - class = typename std::enable_if< \ - ! IsSignal::value>::type, \ - typename F = name ## OpLFunctor, \ - typename S = typename std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp> \ -> \ -auto operator op(const TLeftSignal& lhs, TRightValIn&& rhs) \ - -> TempSignal \ -{ \ - return TempSignal( \ - std::make_shared>( \ - F( std::forward(rhs) ), GetNodePtr(lhs))); \ -} \ - \ -template \ -< \ - typename TLeftValIn, \ - typename TRightSignal, \ - typename D = typename TRightSignal::DomainT, \ - typename TLeftVal = typename std::decay::type, \ - typename TRightVal = typename TRightSignal::ValueT, \ - class = typename std::enable_if< \ - ! IsSignal::value>::type, \ - class = typename std::enable_if< \ - IsSignal::value>::type, \ - typename F = name ## OpRFunctor, \ - typename S = typename std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp> \ -> \ -auto operator op(TLeftValIn&& lhs, const TRightSignal& rhs) \ - -> TempSignal \ -{ \ - return TempSignal( \ - std::make_shared>( \ - F( std::forward(lhs) ), GetNodePtr(rhs))); \ -} \ -template \ -< \ - typename D, \ - typename TLeftVal, \ - typename TLeftOp, \ - typename TRightVal, \ - typename TRightOp, \ - typename F = name ## OpFunctor, \ - typename S = typename 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 = typename TRightSignal::ValueT, \ - class = typename std::enable_if< \ - IsSignal::value>::type, \ - typename F = name ## OpFunctor, \ - typename S = typename std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp> \ -> \ -auto operator op(TempSignal&& lhs, \ - const TRightSignal& rhs) \ - -> TempSignal \ -{ \ - return TempSignal( \ - std::make_shared>( \ - F(), lhs.StealOp(), GetNodePtr(rhs))); \ -} \ - \ -template \ -< \ - typename TLeftSignal, \ - typename D, \ - typename TRightVal, \ - typename TRightOp, \ - typename TLeftVal = typename TLeftSignal::ValueT, \ - class = typename std::enable_if< \ - IsSignal::value>::type, \ - typename F = name ## OpFunctor, \ - typename S = typename std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp, \ - TRightOp> \ -> \ -auto operator op(const TLeftSignal& lhs, TempSignal&& rhs) \ - -> TempSignal \ -{ \ - return TempSignal( \ - std::make_shared>( \ - F(), GetNodePtr(lhs), rhs.StealOp())); \ -} \ - \ -template \ -< \ - typename D, \ - typename TLeftVal, \ - typename TLeftOp, \ - typename TRightValIn, \ - typename TRightVal = typename std::decay::type, \ - class = typename std::enable_if< \ - ! IsSignal::value>::type, \ - typename F = name ## OpLFunctor, \ - typename S = typename std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp \ -> \ -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 = typename std::decay::type, \ - class = typename std::enable_if< \ - ! IsSignal::value>::type, \ - typename F = name ## OpRFunctor, \ - typename S = typename std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp \ -> \ -auto operator op(TLeftValIn&& lhs, TempSignal&& rhs) \ - -> TempSignal \ -{ \ - return TempSignal( \ - std::make_shared>( \ - F( std::forward(lhs) ), rhs.StealOp())); \ -} - -REACT_DECLARE_OP(+, Addition) -REACT_DECLARE_OP(-, Subtraction) -REACT_DECLARE_OP(*, Multiplication) -REACT_DECLARE_OP(/, Division) -REACT_DECLARE_OP(%, Modulo) - -REACT_DECLARE_OP(==, Equal) -REACT_DECLARE_OP(!=, NotEqual) -REACT_DECLARE_OP(<, Less) -REACT_DECLARE_OP(<=, LessEqual) -REACT_DECLARE_OP(>, Greater) -REACT_DECLARE_OP(>=, GreaterEqual) - -REACT_DECLARE_OP(&&, LogicalAnd) -REACT_DECLARE_OP(||, LogicalOr) - -REACT_DECLARE_OP(&, BitwiseAnd) -REACT_DECLARE_OP(|, BitwiseOr) -REACT_DECLARE_OP(^, BitwiseXor) -//REACT_DECLARE_OP(<<, BitwiseLeftShift); // MSVC: Internal compiler error -//REACT_DECLARE_OP(>>, BitwiseRightShift); - -#undef REACT_DECLARE_OP - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Comma operator overload to create signal pack from 2 signals. -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename TLeftVal, - typename TRightVal -> -auto operator,(const Signal& a, const Signal& b) - -> SignalPack -{ - return SignalPack(a, b); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Comma operator overload to append node to existing signal pack. -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename ... TCurValues, - typename TAppendValue -> -auto operator,(const SignalPack& cur, const Signal& append) - -> SignalPack -{ - return SignalPack(cur, append); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// operator->* overload to connect signals to a function and return the resulting signal. -/////////////////////////////////////////////////////////////////////////////////////////////////// -// Single arg -template -< - typename D, - typename F, - template class TSignal, - typename TValue, - class = typename std::enable_if< - IsSignal>::value>::type -> -auto operator->*(const TSignal& arg, F&& func) - -> Signal::type> -{ - return REACT::MakeSignal(arg, std::forward(func)); -} - -// Multiple args -template -< - typename D, - typename F, - typename ... TValues -> -auto operator->*(const SignalPack& argPack, F&& func) - -> Signal::type> -{ - return REACT::MakeSignal(argPack, std::forward(func)); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Flatten -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename TInnerValue -> -auto Flatten(const Signal>& outer) - -> Signal -{ - return Signal( - std::make_shared, TInnerValue>>( - GetNodePtr(outer), GetNodePtr(outer.Value()))); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Signal -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S -> -class Signal : public REACT_IMPL::SignalBase -{ -private: - using NodeT = REACT_IMPL::SignalNode; - using NodePtrT = std::shared_ptr; - -public: - using ValueT = S; - - // Default ctor - Signal() = default; - - // Copy ctor - Signal(const Signal&) = default; - - // Move ctor - Signal(Signal&& other) : - Signal::SignalBase( std::move(other) ) - {} - - // Node ctor - explicit Signal(NodePtrT&& nodePtr) : - Signal::SignalBase( std::move(nodePtr) ) - {} - - // Copy assignment - Signal& operator=(const Signal&) = default; - - // Move assignment - Signal& operator=(Signal&& other) - { - Signal::SignalBase::operator=( std::move(other) ); - return *this; - } - - const S& Value() const { return Signal::SignalBase::getValue(); } - const S& operator()() const { return Signal::SignalBase::getValue(); } - - bool Equals(const Signal& other) const - { - return Signal::SignalBase::Equals(other); - } - - bool IsValid() const - { - return Signal::SignalBase::IsValid(); - } - - void SetWeightHint(WeightHint weight) - { - Signal::SignalBase::SetWeightHint(weight); - } - - S Flatten() const - { - static_assert(IsSignal::value || IsEvent::value, - "Flatten requires a Signal or Events value type."); - return REACT::Flatten(*this); - } -}; - -// Specialize for references -template -< - typename D, - typename S -> -class Signal : public REACT_IMPL::SignalBase> -{ -private: - using NodeT = REACT_IMPL::SignalNode>; - using NodePtrT = std::shared_ptr; - -public: - using ValueT = S; - - // Default ctor - Signal() = default; - - // Copy ctor - Signal(const Signal&) = default; - - // Move ctor - Signal(Signal&& other) : - Signal::SignalBase( std::move(other) ) - {} - - // Node ctor - explicit Signal(NodePtrT&& nodePtr) : - Signal::SignalBase( std::move(nodePtr) ) - {} - - // Copy assignment - Signal& operator=(const Signal&) = default; - - // Move assignment - Signal& operator=(Signal&& other) - { - Signal::SignalBase::operator=( std::move(other) ); - return *this; - } - - const S& Value() const { return Signal::SignalBase::getValue(); } - const S& operator()() const { return Signal::SignalBase::getValue(); } - - bool Equals(const Signal& other) const - { - return Signal::SignalBase::Equals(other); - } - - bool IsValid() const - { - return Signal::SignalBase::IsValid(); - } - - void SetWeightHint(WeightHint weight) - { - Signal::SignalBase::SetWeightHint(weight); - } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// VarSignal -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S -> -class VarSignal : public Signal -{ -private: - using NodeT = REACT_IMPL::VarNode; - using NodePtrT = std::shared_ptr; - -public: - // Default ctor - VarSignal() = default; - - // Copy ctor - VarSignal(const VarSignal&) = default; - - // Move ctor - VarSignal(VarSignal&& other) : - VarSignal::Signal( std::move(other) ) - {} - - // Node ctor - explicit VarSignal(NodePtrT&& nodePtr) : - VarSignal::Signal( std::move(nodePtr) ) - {} - - // Copy assignment - VarSignal& operator=(const VarSignal&) = default; - - // Move assignment - VarSignal& operator=(VarSignal&& other) - { - VarSignal::SignalBase::operator=( std::move(other) ); - return *this; - } - - void Set(const S& newValue) const - { - VarSignal::SignalBase::setValue(newValue); - } - - void Set(S&& newValue) const - { - VarSignal::SignalBase::setValue(std::move(newValue)); - } - - const VarSignal& operator<<=(const S& newValue) const - { - VarSignal::SignalBase::setValue(newValue); - return *this; - } - - const VarSignal& operator<<=(S&& newValue) const - { - VarSignal::SignalBase::setValue(std::move(newValue)); - return *this; - } - - template - void Modify(const F& func) const - { - VarSignal::SignalBase::modifyValue(func); - } -}; - -// Specialize for references -template -< - typename D, - typename S -> -class VarSignal : public Signal> -{ -private: - using NodeT = REACT_IMPL::VarNode>; - using NodePtrT = std::shared_ptr; - -public: - using ValueT = S; - - // Default ctor - VarSignal() = default; - - // Copy ctor - VarSignal(const VarSignal&) = default; - - // Move ctor - VarSignal(VarSignal&& other) : - VarSignal::Signal( std::move(other) ) - {} - - // Node ctor - explicit VarSignal(NodePtrT&& nodePtr) : - VarSignal::Signal( std::move(nodePtr) ) - {} - - // Copy assignment - VarSignal& operator=(const VarSignal&) = default; - - // Move assignment - VarSignal& operator=(VarSignal&& other) - { - VarSignal::Signal::operator=( std::move(other) ); - return *this; - } - - void Set(std::reference_wrapper newValue) const - { - VarSignal::SignalBase::setValue(newValue); - } - - const VarSignal& operator<<=(std::reference_wrapper newValue) const - { - VarSignal::SignalBase::setValue(newValue); - return *this; - } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// TempSignal -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S, - typename TOp -> -class TempSignal : public Signal -{ -private: - using NodeT = REACT_IMPL::SignalOpNode; - using NodePtrT = std::shared_ptr; - -public: - // Default ctor - TempSignal() = default; - - // Copy ctor - TempSignal(const TempSignal&) = default; - - // Move ctor - TempSignal(TempSignal&& other) : - TempSignal::Signal( std::move(other) ) - {} - - // Node ctor - explicit TempSignal(NodePtrT&& ptr) : - TempSignal::Signal( std::move(ptr) ) - {} - - // Copy assignment - TempSignal& operator=(const TempSignal&) = default; - - // Move assignemnt - TempSignal& operator=(TempSignal&& other) - { - TempSignal::Signal::operator=( std::move(other) ); - return *this; - } - - TOp StealOp() - { - return std::move(reinterpret_cast(this->ptr_.get())->StealOp()); - } -}; - -/******************************************/ REACT_END /******************************************/ - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -template -bool Equals(const Signal& lhs, const Signal& rhs) -{ - return lhs.Equals(rhs); -} - -/****************************************/ REACT_IMPL_END /***************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Flatten macros -/////////////////////////////////////////////////////////////////////////////////////////////////// -// Note: Using static_cast rather than -> return type, because when using lambda for inline -// class initialization, decltype did not recognize the parameter r -// Note2: MSVC doesn't like typename in the lambda -#if _MSC_VER && !__INTEL_COMPILER - #define REACT_MSVC_NO_TYPENAME -#else - #define REACT_MSVC_NO_TYPENAME typename -#endif - -#define REACTIVE_REF(obj, name) \ - Flatten( \ - MakeSignal( \ - obj, \ - [] (const REACT_MSVC_NO_TYPENAME \ - REACT_IMPL::Identity::Type::ValueT& r) \ - { \ - using T = decltype(r.name); \ - using S = REACT_MSVC_NO_TYPENAME REACT::RemoveInput::Type; \ - return static_cast(r.name); \ - })) - -#define REACTIVE_PTR(obj, name) \ - Flatten( \ - MakeSignal( \ - obj, \ - [] (REACT_MSVC_NO_TYPENAME \ - REACT_IMPL::Identity::Type::ValueT r) \ - { \ - assert(r != nullptr); \ - using T = decltype(r->name); \ - using S = REACT_MSVC_NO_TYPENAME REACT::RemoveInput::Type; \ - return static_cast(r->name); \ - })) - -#endif // REACT_SIGNAL_H_INCLUDED \ No newline at end of file diff --git a/include/react/TypeTraits.h b/include/react/TypeTraits.h deleted file mode 100644 index 7141ac14..00000000 --- a/include/react/TypeTraits.h +++ /dev/null @@ -1,144 +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) - -#ifndef REACT_TYPETRAITS_H_INCLUDED -#define REACT_TYPETRAITS_H_INCLUDED - -#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; - -template -class Observer; - -template -class ScopedObserver; - -template -class Continuation; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// 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; }; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// IsObserver -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct IsObserver { static const bool value = false; }; - -template -struct IsObserver> { static const bool value = true; }; - -template -struct IsObserver> { static const bool value = true; }; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// IsContinuation -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct IsContinuation { static const bool value = false; }; - -template -struct IsContinuation> { 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; }; - -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 /******************************************/ - -#endif // REACT_TYPETRAITS_H_INCLUDED \ 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