Overview#

This page gives the big picture: what the parts of the library are, how they depend on each other, and in which order a program uses them. Each part has its own detailed page.

The parts and their dependencies#

types.h ─────────── identifiers (qualifier_t, node_id_t, socket_id_t, socket_uuid_t, link_t),
   │                 socket types (socket_t), ng::Status, callback and validator signatures
   │
registry.h ──────── global key -> value stores used for self-registration
small_map.h ─────── sorted vector map, used for small hot structures
   │
socket.h ────────── InputSocket / OutputSocket: name, ID, types, value, allowed values, callbacks
node.h ──────────── Node: an instance in a graph, only knows the IDs of its sockets
node_definition.h ─ NodeDefinition: blueprint of a node type, registered in the NodesRegistry
   │
graph.h ─────────── Graph: owns nodes, sockets, links and interface (parameters/results)
   │
serialization.h ─── Graph -> bytes
deserialization.h ─ bytes -> Graph
   │
graph_manager.h ─── graphs in memory + folders on the disk ("heads"); needed by proxies
   │
data_bank.h ─────── storage of the runtime values, one pool per type
operator.h ──────── base class of the objects doing the computation of a node
graph_runtime.h ─── Graph -> flat list of operators bound to the data bank, and run
   │
io.h ────────────── human-readable printing (ng::io::operator<<)

Everything lives in the ng namespace. Only the last three runtime headers are templates (parameterized by the data bank type), everything else is compiled in src/.

What the library provides, and what the user provides#

nodes-graph is a mechanism: it knows how to store, link, check, save, load and execute nodes, but it doesn’t know any concrete node. A program using it provides:

What

How

Example in the tests

Socket types

One bit of ng::socket_t per type

tests/elements/socket_types.h

Validators (optional)

ng::ValidatorsRegistry, one per socket type

tests/elements/validators.cc

Node types

ng::NodeDefinition in ng::NodesRegistry

tests/elements/nodes.cc

Callbacks (optional)

Functions in the ng::*CallbacksRegistry, attached to sockets by qualifier

tests/elements/callbacks.cc

Data types

A ng::Parser<T> specialization per type, grouped in a ng::DataBank<Ts...>

tests/elements/data_bank.h

Operators

Classes derived from ng::Operator<DB>, in ng::OperatorInstantiatorsRegistry<DB>

tests/elements/operators.h

Life cycle of a graph#

1. static initialization   node types, operators, validators, callbacks are registered
2. edition                 Graph::add_node / add_link / set_socket_value / expose_parameter ...
                           every change is checked immediately (types, cycles, validators, ...)
3. persistence (optional)  GraphManager::save_graph  ->  <head>/a/b/c.graph
4. reuse (optional)        another graph adds it as a proxy node: Graph::add_proxy_node("a.b.c")
5. execution               GraphRuntime<DB> rt(graph);  rt.run();  rt.get_result<T>("alias")

The graph is always valid: there is no “validate” step before saving or running. Invalid operations are refused with a ng::Status and leave the graph unchanged.

Where things are stored#

To avoid pointer-chasing, nothing points to anything: objects reference each other by ID.

  • A Graph stores four flat containers: nodes by node_id_t, input sockets and output sockets by socket_uuid_t (= node ID + socket ID), and links in a std::set<link_t>.

  • A Node only stores the IDs of its sockets.

  • A link is a pair of socket UUIDs, stored as (to, from) (see Identifiers).

  • At runtime, operators don’t point to each other either: each socket is bound to a data_id_t, an index in the data bank. Two linked sockets are bound to the same index.

Reading order to get back into the code#

  1. Identifiers — the vocabulary used everywhere.

  2. Registries and self-registration — how node types and callbacks become available.

  3. The Graph then Socket values, enumerations, validators — the core data structure and its rules.

  4. Serialization format and The GraphManager — the disk.

  5. Proxies: graphs inside graphs — graphs inside graphs.

  6. The runtime — execution.

  7. Tests, examples and sandbox — how to check that a change didn’t break anything.

  8. Known limitations — what is known to be missing or fragile.