Reaction
A lightweight, header-only, high-performance reactive programming framework for C++20.
Install / Use
/learn @lumia431/ReactionREADME
Reaction: Modern C++ Reactive Programming Framework
Reaction is a blazing-fast, modern C++20 header-only reactive framework that brings React/Vue-style dataflow to native C++ – perfect for UI Dataflow, Game logic, Financial Services, Real-time calculation and more.
🚀 Performance Optimized
- Zero-cost abstractions through compile-time calculation
- Minimal runtime overhead with smart change propagation
- Propagation efficiency at the level of millions per second
🔗 Intelligent Dependency Management
- Automatic DAG detection and cycle prevention
- Fine-grained change propagation control
- Optimize multiple calls due to duplicate dependencies
🛡️ Safety Guarantees
- Compile-time type checking with C++20 concepts
- Safe value semantics throughout the framework
- Framework manages node lifetime internally
🔄 Multi-threading Support
Reaction supports multi-threading with automatic thread safety detection and zero-overhead in single-threaded mode.
🔍 Comparison: QProperty vs RxCpp vs Reaction
| Feature / Metric | 🟩 QProperty (Qt6) | 🟨 RxCpp | 🟥 Reaction |
| ------------------------------ | -------------------------------------------- | ------------------------------------------------ | ------------------------------------------------------------ |
| Expression Support | ✅ setBinding(), but only single-layer | ✅ Supports chained map, combine_latest, etc. | ✅✅ Fully supports deep nested expressions |
| Expression Nesting Depth | ❌ Limited to one layer | ⚠️ Supports nesting, but verbose | ✅ Unlimited depth with automatic dependency tracking |
| Update Propagation | Manual propagation per layer | Reactive push chain per layer | Automatic DAG-based propagation with pruning |
| Dependency Tracking | ❌ Manual | ⚠️ Manual via operator chaining | ✅ Automatic via lazy evaluation capturing dependencies |
| Performance (Update Delay) | ✅ Fast (O(1) propagation) | ❌ Slow (heap allocations and nested chaining) | ✅✅ Fast (pruned update, lazy eval, diffing) |
| Memory Usage | ✅ Very low (stack + signals) | ❌ High (many heap-allocated observables) | ⚠️ Moderate (DAG storage, optimized with small object opt.) |
| Syntax Simplicity | ✅ Simple (setBinding, value()) | ❌ Verbose template syntax | ✅ Clean expression templates, close to natural syntax |
| Type Support | ✅ Built-ins and registered custom types | ✅ Template-based, supports any type | ✅ Type-erased or templated support for any combination |
| Container Support | ✅ Can be used in containers | ✅ Can compose multiple observables | ✅ Supports container expressions (e.g. map/filter outputs) |
| Threading Model | UI-thread default, manual safety for signals | ✅ Multi-threaded pipelines | ✅ Main thread default, pluggable lock strategies |
| Error Handling | ❌ None | ✅ Robust error flow (on_error_resume_next) | ✅ Error node propagation, pluggable failure strategy |
| Debuggability | ⚠️ Lambdas harder to trace | ❌ Difficult due to complex types | ✅ Trackable dependencies, observable IDs, chain tracing |
| Template Instance Size | ✅ Small | ❌ Huge (template explosion) | ✅ Optimized with type-erasure or instance deduplication |
| Build Time | ✅ Fast | ❌ Very slow for large expressions | ✅ Separated headers, controllable instantiation |
| Learning Curve | ✅ Low (Qt-style usage) | ❌ Steep (functional style) | ⚠️ Medium (understanding type deduction + expression design) |
| Use Case Fit | UI property binding, light state syncing | Asynchronous pipelines, stream logic | UI + state modeling + expression trees with complex logic |
📊 Performance Benchmarks
Comparative performance results against rxcpp and folly (tested on 2025-06-14):
Deep Dependency Test (Tree Structure, Depth=13)
| Framework | Avg. Time (μs) | Iterations | Relative Speed | |--------------------|---------------:|-------------:|---------------:| | reaction | 765 | 901 | (baseline) | | rxcpp | 1664 | 412 | 2.17x slower | | folly | 8760 | 603 | 11.45x slower |
Wide Dependency Test (10,000 Nodes)
| Framework | Avg. Time (μs) | Iterations | Relative Speed | |--------------------|---------------:|-------------:|---------------:| | reaction | 261 | 2,626 | (baseline) | | rxcpp | 721 | 960 | 2.76x slower | | folly | 3769 | 523 | 14.45x slower |
Key Findings:
- Deep dependency scenarios: ~2.17x faster than rxcpp, ~11.45x faster than folly
- Wide dependency scenarios: ~2.76x faster than rxcpp, ~14.45x faster than folly
- Outstanding performance: Reaction demonstrates superior performance across different dependency patterns
- Test Environment:
- 8-core CPU @ 2.8GHz
- 32KB L1 Data Cache
- 4MB L2 Unified Cache
- Linux 5.15.0-78-generic
📦 Requirements
- Compiler: C++20 compatible (GCC 10+, Clang 12+, MSVC 19.30+)
- Build System: CMake 3.15+
🛠 Installation
1. From source (manual installation)
To build and install the reaction reactive framework manually:
git clone https://github.com/lumia431/reaction.git && cd reaction
cmake -S . -B build
cmake --install build/ --prefix /your/install/path
After installation, you can include and link against reaction in your own CMake-based project:
find_package(reaction REQUIRED)
target_link_libraries(your_target PRIVATE reaction)
2. Using vcpkg (recommended)
You can also install reaction via vcpkg, which handles dependencies and CMake integration automatically.
- Install reaction via vcpkg:
cd /path/to/vcpkg
./vcpkg install reaction
- Integrate vcpkg with your CMake project:
When configuring your project, specify the vcpkg toolchain file:
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake
cmake --build build
- Use reaction in your CMakeLists.txt:
find_package(reaction CONFIG REQUIRED)
add_executable(my_app main.cpp)
# Link against the vcpkg-provided target
target_link_libraries(my_app PRIVATE reaction::reaction)
Uninstall
To uninstall the framework:
cmake --build build/ --target uninstall
If you want to run example or test units:
cmake -S . -DBUILD_EXAMPLES=TRUE -DBUILD_TESTS=TRUE -B build
cmake --build build/
🚀 Quick Start
#include <reaction/reaction.h>
#include <iostream>
#include <iomanip>
#include <cmath>
int main() {
using namespace reaction;
// 1. Reactive variables for stock prices
auto buyPrice = var(100.0).setName("buyPrice"); // Price at which stock was bought
auto currentPrice = var(105.0); // Current market price
// 2. Use 'calc' to compute profit or loss amount
auto profit = calc([&]() {
return currentPrice() - buyPrice();
});
// 3. Use 'expr' to compute percentage gain/loss
auto profitPercent = expr(std::abs(currentPrice - buyPrice) / buyPrice * 100);
// 4. Use 'action' to print the log whenever values change
auto logger = action([&]() {
std::cout << std::fixed << std::setprecision(2);
std::cout << "[Stock Update] Current Price: $" << currentPrice()
<< ", Profit: $" << profit()
<< " (" << profitPercent() << "%)\n";
});
// Simulate price changes
currentPrice.value(110.0).value(95.0); // Stock price increases
buyPrice.value(90.0); // Buy price adjusted
return 0;
}
📖 Basic Usage
1. Reactive Variables: var
Define reactive state variables with var<T>.
auto a = reaction::var(1); // int variable
auto b = reaction::var(3.14); // double variable
- get value:
auto val = a.get();
- assignment:
a.value(2);
2. Derived Computation: calc
Use calc to create reactive computations based on one or more var instances.
- Lambda Capture Style:
auto a = reaction::var(1);
auto b = reaction::var(3.14);
auto sum = reaction::calc([=]() {
return a() + b(); // Retrieve current values using a() and b()
});
