Cpp.React is an experimental Reactive Programming framework for C++11. It provides abstractions to simplify the implementation of reactive behaviour. This is accomplished by enabling the declarative expression of dataflows and handling the propagation of changes automatically. Implicit parallelism for this process is supported as well.
So far, I've only tested building the framework on Windows, with:
- Visual Studio 2013
- Intel C++ Compiler 14.0 in Visual Studio 2012/13
Cpp.React uses standard C++11 and the dependencies are portable, so other compilers/platforms should work, too.
- Intel TBB 4.2 (required)
- Google test framework (optional, to compile the tests)
- Boost C++ Libraries (optional, to use ReactiveLoop, which requires boost::coroutine)
Signals are time-varying reactive values, that can be combined to create reactive expressions. These expressions are automatically recalculated whenever one of their dependent values changes. Example:
#include "react/Signal.h"
///...
using namespace react;
REACTIVE_DOMAIN(MyDomain);
auto width = MyDomain::MakeVar(1);
auto height = MyDomain::MakeVar(2);
auto area = width * height;
cout << "area: " << area() << endl; // => area: 2
width <<= 10;
cout << "area: " << area() << endl; // => area: 20Event streams represent flows of discrete values as first-class objects, based on ideas found in Deprecating the Observer Pattern. Example:
#include "react/EventStream.h"
//...
using namespace react;
REACTIVE_DOMAIN(MyDomain);
auto leftClicked = MyDomain::MakeEventSource();
auto rightClicked = MyDomain::MakeEventSource();
auto clicked = leftClicked | rightClicked;
Observe(clicked, [] { cout << "button clicked!" << endl; });The change propagation is handled implicitly by a so called propagation engine. Depending on the selected engine, independent propagation paths are automatically parallelized. For more details, see Propagation Engines.
#include "react/propagation/TopoSortEngine.h"
//...
using namespace react;
// Single-threaded updating
REACTIVE_DOMAIN(MyDomain, TopoSortEngine<sequential>);
// Parallel updating
REACTIVE_DOMAIN(MyDomain, TopoSortEngine<parallel>);
// Input from multiple threads
REACTIVE_DOMAIN(MyDomain, TopoSortEngine<sequential_queuing>);
REACTIVE_DOMAIN(MyDomain, TopoSortEngine<parallel_queuing>);
// Parallel updating + input from multiple threads + pipelining
REACTIVE_DOMAIN(MyDomain, TopoSortEngine<parallel_pipelining>);#include "react/Reactor.h"
//...
using namespace react;
REACTIVE_DOMAIN(D);
using PointT = pair<int,int>;
using PathT = vector<PointT>;
vector<PathT> paths;
auto mouseDown = D::MakeEventSource<PointT>();
auto mouseUp = D::MakeEventSource<PointT>();
auto mouseMove = D::MakeEventSource<PointT>();
D::ReactiveLoopT loop
{
[&] (D::ReactiveLoopT::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);
}
};
mouseDown << PointT(1,1);
mouseMove << PointT(2,2) << PointT(3,3) << PointT(4,4);
mouseUp << PointT(5,5);
mouseMove << PointT(999,999);
mouseDown << PointT(10,10);
mouseMove << PointT(20,20);
mouseUp << PointT(30,30);
// => paths[0]: (1,1) (2,2) (3,3) (4,4) (5,5)
// => paths[1]: (10,10) (20,20) (30,30)#include "react/ReactiveObject.h"
//...
REACTIVE_DOMAIN(D);
using namespace react;
class Company : public ReactiveObject<D>
{
public:
VarSignalT<string> Name;
Company(const char* name) :
Name{ MakeVar(string(name)) }
{
}
inline bool operator==(const Company& other) const { /* ... */ }
};
class Manager : public ReactiveObject<D>
{
ObserverT nameObs;
public:
VarRefSignalT<Company> CurrentCompany;
Manager(initialCompany& company) :
CurrentCompany{ MakeVar(std::ref(company)) }
{
nameObs = REACTIVE_REF(CurrentCompany, Name).Observe([] (string name) {
cout << "Manager: Now managing " << name << endl;
});
}
};