Skip to content

Albert-Coder/cpp.react

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

289 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Introduction

Cpp.React is an experimental Reactive Programming library for C++11.

It provides abstractions to simplify the implementation of reactive behaviour. As an alternative to raw callbacks, it offers the following benefits:

  • Less boilerplate code;
  • consistent updating without redundant calculations or glitches;
  • implicit parallelism.

Compiling

So far, the build has only been tested in Visual Studio 2013 as it's the development environment I'm using. The Intel C++ Compiler 14.0 with Visual Studio 2012/13 is theoretically supported as well, but last I checked, it did not compile anymore due to some bugs with C++11 support.

You are welcome to try compiling it with other C++11 compilers/on other platforms and report any issues you encounter.

Dependencies

Status

This library is still under development and should not be considered release quality yet. That being said, I've been working on it for about 6 months and it's in a usable state.

Documentation

The documentation is still incomplete, but it already contains plenty of useful information and examples. It can be found in the wiki.

Features by example

Signals

Signals are self-updating reactive variables. They can be combined to expressions to create new signals, which are automatically re-calculated whenever one of their data dependencies changes.

#include "react/Domain.h"
#include "react/Signal.h"
///...
using namespace react;

REACTIVE_DOMAIN(D);

// Two variable that can be manipulated imperatively
D::VarSignalT<int> width  = D::MakeVar(1);
D::VarSignalT<int> height = D::MakeVar(2);

// Arithmetic operators are overloaded to lift expressions
// with signal operands and build new signals from them
D::SignalT<int>    area   = width * height;

cout << "area: " << area.Value() << endl; // => area: 2

// Width changed, so area is re-calculated automatically
width <<= 10;

cout << "area: " << area.Value() << endl; // => area: 20

Events

Event streams represent flows of discrete values. They are first-class objects and can be merged, filtered, transformed or composed to more complex types.

#include "react/Domain.h"
#include "react/Event.h"
#include "react/Observer.h"
//...
using namespace std;
using namespace react;

REACTIVE_DOMAIN(D);

// Two event sources
D::EventSource<> leftClicked  = D::MakeEventSource();
D::EventSource<> rightClicked = D::MakeEventSource();

// Merge both event streams and register an observer
auto clickObserver = (leftClicked | rightClicked)
    .Observe([] { cout << "button clicked!" << endl; });

Implicit parallelism

The change propagation is handled implicitly by a propagation engine. Depending on the selected engine, independent propagation paths are automatically parallelized.

#include "react/Domain.h"
#include "react/Signal.h"
#include "react/engine/ToposortEngine.h"
//...
using namespace react;


// Sequential
{
    REACTIVE_DOMAIN(D, ToposortEngine<sequential>);

    auto a = D::MakeVar(1);
    auto b = D::MakeVar(2);
    auto c = D::MakeVar(3);

    auto x = (a + b) * c;

    b <<= 20;
}

// Parallel
{
    REACTIVE_DOMAIN(D, ToposortEngine<parallel>);

    auto in = D::MakeVar(0);

    // The ->* operator is overloaded for a DSL

    auto op1 = in ->* [] (int in)
    {
        int result = in /* Costly operation #1 */;
        return result;
    };

    auto op2 = in ->* [] (int in)
    {
        int result = in /* Costly operation #2 */;
        return result;
    };

    auto out = op1 + op2;

    // op1 and op2 can be re-calculated in parallel
    in <<= 123456789;
}

// Queued input
{
    REACTIVE_DOMAIN(D, ToposortEngine<sequential_queue>);

    auto a = D::MakeVar(1);
    auto b = D::MakeVar(2);
    auto c = D::MakeVar(3);

    auto x = (a + b) * c;

    // Thread-safe input (Note: unspecified order)
    std::thread t1{ [&] { a <<= 10; } };
    std::thread t2{ [&] { b <<= 100; } };
    std::thread t3{ [&] { a <<= 1000; } };
    std::thread t4{ [&] { b <<= 10000; } };

    t1.join(); t2.join(); t3.join(); t4.join();
}

Reactive loops

(Note: Experimental)

#include "react/Domain.h"
#include "react/Event.h"
#include "react/Reactor.h"
//...
using namespace std;
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)

Reactive objects and dynamic reactives

#include "react/Domain.h"
#include "react/Signal.h"
#include "react/ReactiveObject.h"
//...
using namespace std;
using namespace react;

REACTIVE_DOMAIN(D);

class Company : public ReactiveObject<D>
{
public:
    VarSignalT<string>    Name;

    Company(const char* name) :
        Name{ MakeVar(string(name)) }
    {}

    bool operator==(const Company&) const;
};

class Manager : public ReactiveObject<D>
{
public:
    VarSignalT<Company&>    CurrentCompany;
    ObserverT               NameObserver;

    Manager(initialCompany& company) :
        CurrentCompany{ MakeVar(ref(company)) },
        NameObserver
        {
            // Reactive reference to inner event stream of signal
            REACTIVE_REF(CurrentCompany, Name)
                .Observe([] (const string& name) {
                    cout << "Manager: Now managing " << name << endl;
                });
        }
    {}
};

Company company1{ "Cellnet" };
Company company2{ "Borland" };

Manager manager{ company1 };

company1.Name <<= string("BT Cellnet");
company2.Name <<= string("Inprise");

// Observer moves from company1.Name to company2.Name
manager.CurrentCompany <<= ref(company2);

company1.Name <<= string("O2");
company2.Name <<= string("Borland");

More examples

About

C++React: A reactive programming library for C++11.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages

  • C++ 99.4%
  • CMake 0.6%