API Reference

The parametric library API consists of the following parts:

All functions and types provided by the library reside in namespace parametric.

Core

parametric/core.hpp defines everything required to build up compute graphs. The main building blocks are parmetric values (parametric::param), compute nodes (parametric::ComputeNode) and interface parameters(parametric::InterfaceParam). The role of each object is described at the design concept section.

template<typename T, typename S = DefaultSerializer>
class param

This class encapsulates an arbitrary type to be used as a parameter.

A parameter is an object with a value and a valid state. Whenever its value has been changed, it propagates this invalidation to all dependent parameters, requiring their recomputation.

Public Functions

inline param(const std::string &id, const T &v)

Creates a valid parameter from a value and an identifier.

After construction, the parameter is valid (it contains a value)

inline explicit param(const std::string &id)

Creates a invalid parameter with an identifier.

After construction, the parameter is invalid (it does not contain a value)

inline const NodeRef compute_node() const

returns a shared_ptr to the compute node, if this param is a computation result. If it is an independent parameter, the returned pointer will be null.

Returns:

const NodeRef

inline const T &value() const

Returns the current value of the parameter.

inline T &change_value()

Write access to the value.

inline void set_value(const T &value)

Sets the value of the parameter.

The value will only be changed only, if the new value is different than the old one. The parameter will be valid after setting the value.

Note: The check for difference is done using the != operator. If a custom class does not provide this operator, it can be added on global scope e.g.

bool operator(const MyClass& c1, const MyClass& c2)
{
    return ...
}
Parameters:

value – The value to be set.

inline operator const T&() const

Returns the parameter value.

inline param &operator=(const T &other)

Sets the value of the parameter, see param::set_value.

inline std::string id() const

Gets the identifier of the parameter.

inline void set_id(const std::string &id)

Sets the identifier of the parameter.

inline bool is_valid() const

Indicates, whether the parameter contains a value (valid) or not (invalid)

Warning

doxygenfunction: Unable to resolve function “parametric::new_param” with arguments (const T&, const std::string&) in doxygen xml output for project “parametric” from directory: /home/runner/work/parametric/parametric/build/docs/doxygen/xml. Potential matches:

- template<class T, typename S = DefaultSerializer> param<T, S> new_param()
- template<class T, typename S = DefaultSerializer> param<T, S> new_param(const T &v)
- template<class T, typename S = DefaultSerializer> param<T, S> new_param(const std::string &id, const T &v)
template<class T, typename S = DefaultSerializer>
param<T, S> parametric::new_param(const T &v)

Convenience function to create a parameter of type T with value v.

template<class T, typename S = DefaultSerializer>
param<T, S> parametric::new_param()

Convenience function to create an empty parameter of and identifier id.

template<class T, typename S, typename ...Args>
parametric::param<T> parametric::new_parametric_struct(const T &the_struct, const parametric::param<Args, S>&... parametric_members)

This function creates a parametric version from a structure or an object that consists of parameters.

It propagates mutation of the member parameters to the object itself.

The first argument is the structure/object to be wrapped, the following arguments are all parameters, which the object depends on

Here’s an example:

struct AStruct
{
  parametric::param<int> a{10, "a"};
  parametric::param<int> b{100, "a"};
};

auto structSum = [](const AStruct& s) -> int {
  return s.a.value() + s.b.value();
};

AStruct s;
auto parametricS = parametric::new_parametric_struct(s, s.a, s.b);
auto sum = parametric::eval(structSum, parametricS);

The best practice is to create a helper function for each structure that contains parameters by creating a template specialization of parametric::new_param as follows:

template<>
parametric::param<AStruct> parametric::new_param(const AStruct& s)
{
  return parametric::new_parametric_struct(s, s.a, s.b);
}
template<typename Fn, typename S, typename ...Args>
decltype(auto) parametric::eval(Fn wrapped_function, const parametric::param<Args, S>&... parameterArgs)

This function creates a parametric version from a “normal” function.

The first argument is the function to be wrapped It accepts a variable list of input arguments, each must be a parameter

Here’s an example:

double mult(double a, double b)
{
  return a * b;
}

parametric::param<double> a = parametric::new_param(2.0);
parametric::param<double> b = parametric::new_param(10.0);

auto result = parametric::eval(mult, a, b);
template<typename Derived, typename Results = ignore, typename Arguments = ignore>
class ComputeNode : public parametric::ClonableDAGNode<Derived>, public std::enable_shared_from_this<ComputeNode<Derived, ignore, ignore>>

This class is the base class to define arbitrary compute nodes.

A compute node has to

  • inherit from parametric::ComputeNode<YourClass, parametric::Results<ResultType1, ResultType2, …>, parametric::Arguments<ArgType1, ArgType2, …>>

  • be constructed with parametric::compute

A compute node may

  • override the ComputeNode::eval method to perform the computation

  • customize parametric::compute by

    • providing a custom void connect_inputs(args…) function to connect inputs to the compute node

    • providing a custom R initialize_results() function to initialize the output parameters

    • providing a custom void connect_results(R const&) function to connect the outputs to the compute node

    • providing a custom void post_connect() callback to perform some customization after the compute node has been connected to its inputs and outputs, e.g. setting default ids.

A compute node may have any number of inputs and outputs

After a compute node has been connected to its inputs and outputs, argument parameters can be queried with ComputeNode::arg<i>() in the overwritten methods.

Pointers to a result can be queried with ComputeNode::res<i>(). Since it may be possible that a result goes out of scope before the compute node, pointers queried with ComputeNode::res<i>() may be nullptr. This should be checked in the overriden methods before using the pointer.

Example:

// define computing node
class DivComputer : public parametric::ComputeNode<DivComputer, parametric::Results<double>, parametric::Arguments<double, double>>
{
  void eval() const override {
      if (auto result = res<0>(); result) {
          result->set_value(arg<0>().value() / arg<1>().value());
      }
  }

  void post_connect const override {
      if (auto result = res<0>(); result) {
          result->set_id("reosonable_default_id");
      }
  }
};

The DivComputer compute node is then used as follows

auto p1 = parametric::new_param(2.0, "p1");
auto p2 = parametric::new_param(5.0, "p2");
auto res = parametric::compute<DivComputer>(p1, p2);

Public Functions

template<typename ...Args>
inline void connect_inputs(Args&&... args)

default implementation for connecting the inputs to the outputs.

This function may be provided by the derived class to customize the behavior of parametric::compute

Template Parameters:

Args – arguments forwarded from parametric::compute

Parameters:

args – input arguments that shall be connected to the compute node

inline compute_return_value<Results> initialize_results()

default implementation for initializing the results of the compute node

This function may be provided by the derived class to customize the behavior of parametric::compute

Returns:

compute_return_value<Results> a tuple of param<T> values for multi-output functions, a param<T> for single output functions or a shared_ptr to this for void functions

inline void connect_results(compute_return_value<Results> const &res)

default implementation for connecting the results of the compute node to the compute node.

This function may be provided by the derived class to customize the behavior of parametric::compute

Parameters:

res – The results as returned by initialize_results

inline void post_connect() const

override this method as a post connection callback.

This method will be called immediately after the compute node has been connected to its inputs and outputs. It can be used to set a default id for the outputs using arg<i>() and res<i>().

inline virtual void eval() const

override this method to perform the calculation.

Use arg<i>() and res<i>() to query the inputs and outputs

template<typename value_type, typename S = DefaultSerializer>
inline decltype(auto) arg(int i) const

returns the i-th input argument. This function must be called after the compute node has been connected to its inputs via parametric::compute. It ignores the Argument list and expects the argument type to be specified by the user.

Caution: UB if the value_type does not match the actual type of the i-th argument!

Template Parameters:

value_type – the value_type of the i-th argument.

Parameters:

i – the index of the input argument.

Returns:

A reference to an input parameter

template<int i>
inline decltype(auto) arg() const

returns the i-th input argument. This function must be called after the compute node has been connected to its inputs via parametric::compute.

Template Parameters:

i – the index of the input argument.

Returns:

A reference to an input parameter

template<typename value_type, typename S = DefaultSerializer>
inline decltype(auto) res(int i) const

returns a pointer to the i-th output argument. The function must be called after the compute node has been connected to its outputs via parametric::compute.

If the output parameter has already been deleted, the returned pointer will be null. This should be checked before using the pointer. This overload ignores the Result list and requires the user to specify the type of the i-th result

Caution: UB if the value_type does not match the actual type of the i-th result!

Example:

if (auto result = res<double>(0); result) {
    result->set_value(arg<double>(0).value() + arg<double>(1).value());
}

Template Parameters:

i – the index of the output argument.

Returns:

A shared_ptr to the output parameter

template<int i>
inline decltype(auto) res() const

returns a pointer to the i-th output argument. The function must be called after the compute node has been connected to its outputs via parametric::compute.

If the output parameter has already been deleted, the returned pointer will be null. This should be checked before using the pointer.

Example:

if (auto result = res<0>(); result) {
    result->set_value(arg<0>().value() + arg<1>().value());
}

Template Parameters:

i – the index of the output argument.

Returns:

A shared_ptr to the output parameter

inline decltype(auto) args_tuple() const

A convenience function that collects the input arguments in a tuple.

This can be used to forward the input arguments to std::apply for instance.

Returns:

A tuple of the evaluated input arguments.

template<typename T, typename S>
inline void depends_on(param<T, S> const &input)

convenience function to connect this compute node to an input parameter

Template Parameters:

T – the type held by the input parameter

Parameters:

input – the input parameter

template<typename T, typename S>
inline void computes(param<T, S> const &output)

convenience function to connect this compute node to an output parameter

Template Parameters:

T – the type held by the output parameter

Parameters:

output – the output parameter

Warning

doxygenfunction: Unable to resolve function “parametric::compute” with arguments (std::shared_ptr<C> const&, param<Args> const&…) in doxygen xml output for project “parametric” from directory: /home/runner/work/parametric/parametric/build/docs/doxygen/xml. Potential matches:

- template<typename C, typename ...Args> decltype(auto) compute(std::shared_ptr<C> const &ptr, Args&&... args)
- template<typename C, typename S, typename ...Args> decltype(auto) compute(param<Args, S> const&... args)

Warning

doxygenfunction: Unable to resolve function “parametric::compute” with arguments (param<Args> const&…) in doxygen xml output for project “parametric” from directory: /home/runner/work/parametric/parametric/build/docs/doxygen/xml. Potential matches:

- template<typename C, typename ...Args> decltype(auto) compute(std::shared_ptr<C> const &ptr, Args&&... args)
- template<typename C, typename S, typename ...Args> decltype(auto) compute(param<Args, S> const&... args)

DAG

typedef std::shared_ptr<parametric::DAGNode> parametric::NodeRef

A shared pointer to a DAG node.

class DAGNode

This class provides a basis node of a directed acyclic graph (DAG)

It provides all required methods to connect multiple nodes to a larger graph.

Design Considerations: In the current design, child nodes own their parent nodes. This makes it possible to hold a pointer to a child node keeping the whole graph alive.

Subclassed by parametric::ClonableDAGNode< Derived >

Public Types

enum class Direction

Defines the graph travsersal direction.

Values:

enumerator up

Traverse upwards, i.e. in direction of parents

enumerator down

Traverse downwards, i.e. in direction of childs

Public Functions

inline DAGNode(const std::string &id)

DAGNode Constructs the node with an identifier. The id does not have to be unique.

Parameters:

id – The identifier of the node.

inline virtual std::string serialize() const

This virtual function can be overwritten for serializing and deserializing nodes to std::string.

Returns:

std::string

inline std::string id() const

Returns the identifier of the node.

Returns:

The id as a string

inline void set_id(const std::string &id)

Sets the identifier of the node.

Parameters:

id – The identifier string.

inline bool precedes(const DAGNode &node) const

Checks, whether “node” precides the current node in the DAG.

Parameters:

node – The node to be checked

Returns:

True, if node precedes this node.

template<typename Visitor>
inline void accept(Visitor &v, size_t depth = 0, Direction dir = Direction::down) const

This provides an interface for the visitor pattern to walk through the whole DAG down from this node.

Parameters:
  • v – The Visitor that is applied on each node during traversal

  • depth – The current depth of traversal

  • dir – The direction of traversal (either up or down)

template<typename Visitor>
inline void accept(Visitor &v, size_t depth = 0, Direction dir = Direction::down)

This provides an interface for the visitor pattern to walk through the whole DAG down from this node.

Parameters:
  • v – The Visitor that is applied on each node during traversal

  • depth – The current depth of traversal

  • dir – The direction of traversal (either up or down)

inline void remove_parent(const DAGNode &parent)

Removes the node “parent” from the current node.

Parameters:

parent – The node to be removed

inline size_t num_parents() const

returns the number of parents

inline size_t num_children() const

returns the number of children

inline void invalidate()

Invalidates this node and all other childs.

inline virtual void eval() const

This method is internally used to evaluate the state of the node.

inline virtual bool IsValid() const

Returns whether the node is in a valid state.

inline virtual std::shared_ptr<DAGNode> clone(std::shared_ptr<ClonedNodeMap> cloned_nodes = new_cloned_node_map()) const

This function deep-copies a node together with all of its ancestors, while maintaining parent-child-relations.

The optional input cloned_nodes is used to keep track of the already cloned nodes. This way redundant clones can be avoided.

Friends

inline friend void add_parent(const NodeRef &child, const NodeRef &parent)

Adds the node “parent” as parent to the node “child”.

The list of children is unique, but the list of parents may contain duplicates. The rationale behind this is that a compute node could have the same parent for different arguments, e.g. b = a*a, but a parent must only invalidate their children once.

Parameters:
  • child – The node to which “parent” is added as a parent.

  • parent – The parent node to be added.

  • child – The node to which “parent” is added as a parent.

  • parent – The parent node to be added.

Operators

This file contains the parametric variant of arithmetic functions and their operator overloads.

Functions

template<class T1, class T2>
auto operator+(const parametric::param<T1> &a, const parametric::param<T2> &b)
template<class T1, class T2>
auto operator*(const parametric::param<T1> &a, const parametric::param<T2> &b)
namespace parametric

Objects to adapt to legacy code that does not yet use parameters yet

Functions

template<class T1, class T2>
auto p_add(const parametric::param<T1> &a, const parametric::param<T2> &b)
template<class T1, class T2>
auto p_mult(const parametric::param<T1> &a, const parametric::param<T2> &b)