Graph structure#

See The Graph and Socket values, enumerations, validators for how these classes work together.

class InputSocket : public ng::Socket

Public Functions

InputSocket(const std::string &name = "", const socket_id_t &id = null_socket, socket_t type = null_socket_type, bool active = true, const std::string &value = "", bool linkable = true, const std::set<std::string> &allowed_values = {}, const std::set<qualifier_t> &on_added_link_callbacks = {}, const std::set<qualifier_t> &on_broken_link_callbacks = {}, const std::set<qualifier_t> &on_value_changed_callbacks = {})
bool set_value(const std::string &value)

Sets the value if it is allowed (see is_allowed_value). Validators are not checked here (see check_validity).

Returns:

true if the value was accepted.

inline void set_allowed_values(const std::set<std::string> &values)
inline void set_linkable(bool linkable)
inline const std::string &get_value() const
inline const std::set<std::string> &get_allowed_values() const
inline bool is_linkable() const
inline bool is_enum() const
inline const std::set<qualifier_t> &get_on_value_changed_callbacks() const
inline void register_on_value_changed_callback(const qualifier_t &cb)
bool is_allowed_value(const std::string &value) const

A value is allowed if the socket has no whitelist, or if the value is in the whitelist.

bool check_validity(const std::string &value) const

Checks the value against the validators registered (ValidatorsRegistry) for the types of this socket. The value is valid if no validator is registered for any of the socket’s types, or if at least one of the registered validators accepts it.

void trigger_on_value_changed_callbacks(Graph &graph, Node &node, const std::string &old_value)
class OutputSocket : public ng::Socket

Public Functions

OutputSocket(const std::string &name = "", const socket_id_t &id = null_socket, socket_t type = null_socket_type, bool active = true, const std::set<qualifier_t> &on_added_link_callbacks = {}, const std::set<qualifier_t> &on_broken_link_callbacks = {})
class Node

Instance of a node in a graph. The node only knows the IDs of its sockets, the sockets themselves are owned by the Graph.

Public Functions

Node(const std::string &name = "", const node_id_t &id = null_node, const qualifier_t &qualifier = null_qualifier, const std::set<socket_id_t> &input_sockets = {}, const std::set<socket_id_t> &output_sockets = {}, bool proxy = false)
inline void set_name(const std::string &name)
inline void set_proxy(bool proxy)
inline const std::string &get_name() const
inline const node_id_t &get_id() const
inline const qualifier_t &get_qualifier() const
inline const std::set<socket_id_t> &get_input_sockets() const
inline const std::set<socket_id_t> &get_output_sockets() const
inline bool is_proxy() const
inline std::size_t n_input_sockets() const
inline std::size_t n_output_sockets() const
inline bool has_input_socket(const socket_id_t &socket_id) const
inline bool has_output_socket(const socket_id_t &socket_id) const
bool register_input_socket(const socket_id_t &socket_id)
Returns:

true if the socket was not already registered.

bool register_output_socket(const socket_id_t &socket_id)
bool remove_input_socket(const socket_id_t &socket_id)
Returns:

true if the socket was registered.

bool remove_output_socket(const socket_id_t &socket_id)

Public Static Functions

static std::size_t make_random_id()
Returns:

A random number in [1, MAX_NODE_RANDOM_ID].

static void seed_random_ids(uint32_t seed)

Re-seeds the random IDs generator, to make the generated node IDs reproducible.

class NodeDefinition

Blueprint of a node type, registered in the NodesRegistry under its qualifier. Graph::add_node copies its sockets into the graph to create a new Node instance.

Public Functions

NodeDefinition() = default
NodeDefinition(const qualifier_t &qualifier, const std::set<InputSocket> &input_sockets, const std::set<OutputSocket> &output_sockets, const std::string &default_name)
inline const qualifier_t &get_qualifier() const
inline const std::set<InputSocket> &get_input_sockets() const
inline const std::set<OutputSocket> &get_output_sockets() const
inline const std::string &get_default_name() const
class Graph

Public Types

using input_sockets_t = std::map<socket_uuid_t, InputSocket>
using output_sockets_t = std::map<socket_uuid_t, OutputSocket>
using nodes_t = std::map<node_id_t, Node>
using interface_t = std::map<socket_uuid_t, InterfaceProperties>

Public Functions

inline void set_name(const std::string &name)
inline void set_qualifier(const qualifier_t &q)
inline const std::string &get_name() const
inline const std::optional<qualifier_t> &get_qualifier() const
inline const input_sockets_t &get_input_sockets() const
inline const output_sockets_t &get_output_sockets() const
inline const nodes_t &get_nodes() const
inline const links_t &get_links() const
inline const interface_t &get_parameters() const
inline const interface_t &get_results() const
node_id_t add_node(const qualifier_t &node_qualifier)
Returns:

The ID of the new node, or null_node if the node type is not registered.

node_id_t add_node(const char *node_qualifier)
inline bool has_node(const node_id_t &node_id) const
Node &get_node(const node_id_t &node_id)
Throws:

std::out_of_range – if the node doesn’t exist.

const Node &get_node(const node_id_t &node_id) const
bool remove_node(const node_id_t &node_id)

Removes the node, its links, its sockets and their exposure in the interface.

Returns:

false if the node doesn’t exist.

void disconnect_node(const node_id_t &node_id, LinkBreakReason reason = LinkBreakReason::Manual)

Breaks all the links going to or coming from the node.

void rename_node(const node_id_t &node_id, const std::string &name)
Throws:

std::out_of_range – if the node doesn’t exist.

const InputSocket &get_input_socket(const socket_uuid_t &suuid) const
Throws:

std::out_of_range – if the socket doesn’t exist.

const OutputSocket &get_output_socket(const socket_uuid_t &suuid) const
Status set_socket_value(const socket_uuid_t &suuid, const std::string &value)

Sets the value of an input socket, after checking the validators and the allowed values. The callbacks of the socket are triggered only if the value is accepted.

const std::string &get_socket_value(const socket_uuid_t &suuid) const
Throws:

std::out_of_range – if the input socket doesn’t exist.

Status add_link(const socket_uuid_t &from, const socket_uuid_t &to)

Links an output socket to an input socket. If the input socket is already linked, the previous link is replaced. If the input socket is exposed as a parameter, it stops being exposed. Adding a link that already exists is a no-op returning Status::Ok.

Returns:

false if the link doesn’t exist.

Returns:

An iterator to the link targeting this input socket, or get_links().end().

inline bool is_linked(const socket_uuid_t &input) const
links_t get_input_links(const node_id_t &node_id) const
Returns:

All the links whose destination is a socket of this node.

links_t get_output_links(const node_id_t &node_id) const
Returns:

All the links whose source is a socket of this node.

Status expose_parameter(const socket_uuid_t &suuid, const socket_id_t &alias, std::string show_as = "")

Exposes an input socket as a parameter of the graph, so it becomes an input of the proxy nodes. Any link targeting this socket is broken.

Parameters:
  • suuid – The socket to expose.

  • alias – The ID of the socket on the proxy nodes, must be unique among parameters.

  • show_as – The name of the socket on the proxy nodes. Defaults to the socket’s name.

bool remove_parameter(const socket_uuid_t &suuid)
Status expose_result(const socket_uuid_t &suuid, const socket_id_t &alias, std::string show_as = "")

Same as expose_parameter, for output sockets.

bool remove_result(const socket_uuid_t &suuid)
node_id_t add_proxy_node(const qualifier_t &graph_qualifier)

Adds a node representing another graph, loaded from the disk through the GraphManager. Its inputs are the parameters of that graph and its outputs are its results.

Throws:

std::runtime_error – if the graph can’t be found.

Returns:

The ID of the new node, or null_node if the graph tries to contain itself.

node_id_t add_proxy_node(const char *graph_qualifier)
std::set<node_id_t> get_source_nodes() const
Returns:

The nodes that are not the destination of any link.

DepthProperties get_depth_properties() const
struct DepthProperties

Public Members

SmallMap<node_id_t, std::size_t> depth

Length of the longest path from a source node to each node. Only meaningful for nodes that are not in (or downstream of) a cycle.

std::size_t max_depth = 0

Largest value in depth.

bool has_cycle = false

Whether the links form at least one cycle.

struct InterfaceProperties

How a socket is exposed in the interface (parameters or results) of a graph.

Public Members

std::string show_as

Name displayed for the socket of the proxy node.

socket_id_t socket_id

ID of the socket of the proxy node.

class GraphManager

Owns the graphs in memory and the “heads”: root folders on the disk where graphs are saved. A graph with the qualifier “a.b.c” is saved in “<head>/a/b/c.graph”.

Public Types

enum class Owner

Ordered from the most personal to the least personal: when saving, the most personal writable head is used.

Values:

enumerator User
enumerator Group
enumerator Global
enumerator Unknown
using head_id_t = std::size_t
using library_t = std::map<qualifier_t, head_id_t>
using heads_t = SmallMap<head_id_t, HeadProperties>

Public Functions

GraphManager(const GraphManager&) = delete
GraphManager &operator=(const GraphManager&) = delete
~GraphManager() = default
inline const library_t &get_library() const
inline const heads_t &get_heads() const
inline const Graph &get_graph(graph_id_t id) const
Throws:

std::out_of_range – if the graph doesn’t exist.

inline Graph &get_graph(graph_id_t id)
inline bool has_graph(graph_id_t id) const
graph_id_t new_graph(const std::string &name = "")
bool close_graph(graph_id_t id)
Returns:

false if the graph doesn’t exist.

head_id_t add_head(const std::filesystem::path &path, Owner owner = Owner::User)

Registers a new head and discovers the graphs it contains.

void refresh()

Re-discovers the graphs contained in all the readable heads.

bool exists(const qualifier_t &q) const
Returns:

true if a graph with this qualifier exists in one of the heads.

Status save_graph(graph_id_t id)

Saves the graph in the most personal writable head.

Status save_graph(graph_id_t id, head_id_t head_id)
Graph raw_read(const qualifier_t &q) const

Reads a graph from the disk without registering it in the manager.

Throws:

std::runtime_error – if the graph can’t be found or read.

graph_id_t load_graph(const qualifier_t &q, bool force = false)

Loads a graph from the disk and registers it in the manager. If it was already loaded and force is false, the ID of the graph in memory is returned.

Throws:

std::runtime_error – if the graph can’t be found or read.

void reset()

Forgets everything: graphs in memory and heads. Mostly useful for tests.

Public Static Functions

static bool can_write(const std::filesystem::path &path)
static bool can_read(const std::filesystem::path &path)
static GraphManager &instance()
struct HeadProperties

Public Functions

inline HeadProperties(Owner owner = Owner::Unknown, const std::filesystem::path &path = "", bool readable = false, bool writable = false)

Public Members

Owner owner
std::filesystem::path path
bool readable
bool writable
template<typename DB>
class GraphRuntime

Converts a graph into a flat sequence of operators, sorted by depth, and runs them. Proxy nodes are expanded recursively: their operators are inlined at their position. Unlinked inputs are parsed from the value of their socket.

Public Types

using operator_t = Operator<DB>
using operator_ptr_t = std::unique_ptr<operator_t>
using bindings_t = std::map<socket_id_t, data_id_t>

Public Functions

explicit GraphRuntime(const Graph &graph)
Throws:

std::runtime_error – if an operator is missing, a proxy can’t be read or the graph contains a cycle.

explicit GraphRuntime(graph_id_t gid)

Builds the runtime of a graph owned by the GraphManager.

inline const std::vector<operator_ptr_t> &get_operators() const
inline const DB &get_data_bank() const
inline DB &get_data_bank()
inline const bindings_t &get_results() const
inline bool is_executed(std::size_t index) const
template<typename T>
inline std::optional<T> get_result(const socket_id_t &alias) const
Returns:

The value of a result of the graph, if it has been computed with this type.

template<typename T>
inline std::optional<T> get_result(const char *alias) const
bool run()

Runs all the operators in order. Stops at the first operator failing to pull, poll, process or push.

Returns:

true if all the operators were executed.

template<typename DB>
class Operator

Runtime counterpart of a node: the object actually doing the computation. Each socket of the node is bound to an item of the data bank. The runtime calls, in this order: pull (read inputs), poll (ready?), process (compute), push (write outputs).

Public Types

using bindings_t = std::map<socket_id_t, data_id_t>

Public Functions

inline Operator(const std::string &name = "")
inline Operator(const bindings_t &bindings, const std::string &name = "")
virtual ~Operator() = default
inline const std::string &get_name() const
inline const bindings_t &get_bindings() const
inline data_id_t get_binding(const socket_id_t &socket) const
Returns:

The data ID bound to the socket, or null_data.

inline data_id_t get_binding(const char *socket) const
inline void bind(const socket_id_t &socket, data_id_t id)
virtual bool pull(const DB &db) = 0
virtual bool poll() = 0
virtual bool push(DB &db) = 0
inline bool process()
template<typename ...Ts>
class DataBank

Storage of the data exchanged by operators at runtime. There is one pool per type, all the pools share the same IDs sequence so an ID identifies a single item. The ID 0 (null_data) is never attributed.

Public Functions

template<typename T>
inline std::map<data_id_t, T> &get_pool()
template<typename T>
inline const std::map<data_id_t, T> &get_pool() const
template<typename T>
inline data_id_t add(const T &value)
Returns:

The ID of the new item.

template<typename T>
inline std::optional<T> get(data_id_t id) const
inline bool contains(data_id_t id) const
Returns:

true if any pool contains this ID.

inline void erase(data_id_t id)
inline data_id_t reserve_id()

Reserves an ID that can later be filled with store.

inline data_id_t get_next_id() const
template<typename ...Args>
inline bool load(data_id_t id, std::variant<Args...> &var) const

Fills var with the item id, using the first alternative of the variant having an item with this ID.

Returns:

false if none of the alternatives has an item with this ID.

template<typename ...Args>
inline bool store(data_id_t id, const std::variant<Args...> &var)

Stores the content of var under id, replacing any previous item with this ID, whatever its type.

Returns:

false if there is no pool for the type held by the variant.

inline data_id_t add_from_string(const std::string &str)

Parses the string with the Parser of each type, in the order of the template parameters, and adds the first successful result.

Returns:

The ID of the new item, or null_data if no parser accepted the string.

Public Static Attributes

template<typename T>
static constexpr bool has_pool = (std::is_same_v<T, Ts> || ...)