C++ single-header graph computation library with:
- a compile-time static DAG builder:
dangle::start(...) - a mutable reactive graph API:
dangle::val<T>+dangle::graph
Header: src/dangle.hpp
Directed acyclic computation graph; nodes are pure functions.
- input types are inferred from the first function signature
- intermediate and output values are referenced by typed
node_handle<T, idx>values - slot indices are compile-time constants
auto [bid, ask, mid, b0] = dangle::start(
[](double b, double a) { return (b + a) / 2.0; }
);
auto [spread, b1] = b0.compute(
[](double b, double a) { return a - b; }, bid, ask
);
auto [norm, b2] = b1.compute(
[](double s, double m) { return s / m; }, spread, mid
);
auto spec = b2.build(mid, spread, norm);start(fn)returns input handles, the first output handle, and abuilderbuilder.compute(fn, handles...)appends a node and returns(new_handle, new_builder)builder.build(handles...)selects outputs and returns aspec
auto [m, s, n] = spec.evaluate(99.5, 100.5);spec.evaluate(...)is stateless and re-entrant- input types must exactly match the declared input types
- execution order is registration order
dangle::val<T> and dangle::graph provide a persistent,
lazy-evaluated reactive graph. Updating an input marks the graph dirty;
reading a value reruns the graph only when needed.
dangle::val<double> x = 3.0;
dangle::graph g;
auto& sq = g.bind([](double v) { return v * v; }, x);
auto& cube = g.bind(
[](double s, double v) { return s * v; }, sq, x
);
*cube; // 27.0
x = 4.0;
*cube; // 64.0You can also bind from an existing graph-owned value:
auto& fourth = sq.bind([](double v) { return v * v; }, sq);graph::bind(fn, val...) -> val<R>®isters a node and stores the output inside the graphval<T>::bind(fn, val...) -> val<R>&forwards to the owning graphval<T>::operator=marks the graph dirtyval<T>::operator*triggersrun_if_dirty()and returns the current value
examples/bsm.cpp
- Black-Scholes pricing and greeks using
dangle::graph - updates
dangle::val<double>inputs between scenarios - prints scenario comparisons and an options chain
examples/loan.cpp
- amortization schedule using the same reactive graph
- reuses the graph across multiple loan scenarios by reassigning inputs
make # build tests and examples into ./bld
make test # build and run doctest suite
make lint # run clang-tidy checks
./bld/bsm # run Black-Scholes example
./bld/loan # run loan schedule example