# Methods for Parameter Passing Between CGraph Nodes Using GParam

> Explore CGraph's GParam subsystem for type-safe and thread-safe parameter passing between nodes. Efficiently share data across your pipeline stages.

- Repository: [Chunel/cgraph](https://github.com/chunelfeng/cgraph)
- Tags: how-to-guide
- Published: 2026-02-27

---

**CGraph implements parameter passing between nodes through the GParam subsystem, which provides type-safe creation, retrieval, and thread-safe access macros for sharing data across pipeline stages.**

The CGraph repository (`chunelfeng/cgraph`) is a high-performance C++ task scheduling framework that enables complex pipeline construction. When nodes need to exchange data without tight coupling, the framework offers the **GParam** mechanism—a lightweight, key-based registry system that maintains type safety while allowing any node to access shared state through the `GParamManager`.

## Understanding the GParam Architecture

The parameter passing system rests on three core components that manage the lifecycle and accessibility of shared data objects.

### The GParam Base Class

All shared parameters must inherit from `GParam`, defined in [`src/GraphCtrl/GraphParam/GParam.h`](https://github.com/chunelfeng/cgraph/blob/main/src/GraphCtrl/GraphParam/GParam.h). This base class stores the parameter's string key, an optional back-trace for debugging, and a `_param_shared_lock_` member that provides thread-safety. Depending on compiler capabilities, this lock is either a `std::shared_mutex` (C++17) or a `std::recursive_mutex` for older environments.

### The GParamManager Registry

The `GParamManager` class, located in [`src/GraphCtrl/GraphParam/GParamManager.h`](https://github.com/chunelfeng/cgraph/blob/main/src/GraphCtrl/GraphParam/GParamManager.h), maintains a centralized `std::unordered_map<std::string, GParamPtr>` called `params_map_`. This registry acts as the single source of truth for all parameter instances, handling creation, lookup, and deletion operations. Every `GParam` object is stored under a unique string key, ensuring that nodes can reference shared data without direct pointers to each other.

### The GParamManagerWrapper Mixin

To expose parameter operations to individual nodes, CGraph provides the `GParamManagerWrapper` class in [`src/GraphCtrl/GraphParam/GParamManagerWrapper.h`](https://github.com/chunelfeng/cgraph/blob/main/src/GraphCtrl/GraphParam/GParamManagerWrapper.h). This mixin is incorporated into `GNode` through the macro `CGRAPH_DECLARE_GPARAM_MANAGER_WRAPPER_WITH_MEMBER`, supplying methods like `createGParam`, `getGParam`, and `removeGParam`. The wrapper also tracks "concerned" parameters—those actually accessed by the node—enabling pipeline introspection tools to report which keys were utilized during execution.

## Three Stages of Parameter Passing

The GParam workflow follows a distinct three-stage pattern: creation, retrieval, and optional locking for concurrent access.

### Stage 1: Creating Parameters with CGRAPH_CREATE_GPARAM

Nodes register new parameter types using the `CGRAPH_CREATE_GPARAM(Type, key)` macro, defined in [`src/GraphCtrl/GraphParam/GParamUtils.h`](https://github.com/chunelfeng/cgraph/blob/main/src/GraphCtrl/GraphParam/GParamUtils.h). This macro expands to a call to `createGParam<Type>(key)`, which instantiates the concrete type and registers it in the `GParamManager` under the specified key. The macro returns a `CStatus` object indicating success or failure.

```cpp
// In MyWriteParamNode.h
CStatus init() override {
    // Register a GParam of type MyParam under the key "param1"
    return CGRAPH_CREATE_GPARAM(MyParam, "param1");
}

```

### Stage 2: Retrieving Parameters with CGRAPH_GET_GPARAM_WITH_NO_EMPTY

Subsequent nodes (or the same node later in the lifecycle) fetch the shared instance using `CGRAPH_GET_GPARAM_WITH_NO_EMPTY(Type, key)`. This macro invokes `getGParamWithNoEmpty<Type>(key)`, which performs a type-safe lookup and throws an exception if the key does not exist, preventing null pointer dereferences.

```cpp
// In MyReadParamNode.h
CStatus run() override {
    // Fetch the same MyParam instance; throws if missing
    auto* myParam = CGRAPH_GET_GPARAM_WITH_NO_EMPTY(MyParam, "param1");
    CGraph::CGRAPH_ECHO("Read iValue = %d, iCount = %d",
                        myParam->iValue, myParam->iCount);
    return CStatus();
}

```

### Stage 3: Protecting Concurrent Access with Scoped Locks

When nodes execute in parallel and mutate shared parameters, CGraph provides scoped lock macros to ensure thread safety. The `CGRAPH_PARAM_WRITE_CODE_BLOCK(param)` and `CGRAPH_PARAM_READ_CODE_BLOCK(param)` macros, defined in [`src/GraphCtrl/GraphParam/GParamUtils.h`](https://github.com/chunelfeng/cgraph/blob/main/src/GraphCtrl/GraphParam/GParamUtils.h), create RAII-style locks on the parameter's internal mutex.

```cpp
CStatus run() override {
    auto* myParam = CGRAPH_GET_GPARAM_WITH_NO_EMPTY(MyParam, "param1");
    int val = 0, cnt = 0;
    {
        // Scoped write lock – only this block holds the exclusive lock
        CGRAPH_PARAM_WRITE_CODE_BLOCK(myParam);
        val = ++myParam->iValue;   // modify
        cnt = ++myParam->iCount;   // modify
    }
    CGraph::CGRAPH_ECHO("[%s], iValue = %d, iCount = %d",
                        this->getName().c_str(), val, cnt);
    return CStatus();
}

```

## Practical Implementation Example

To implement parameter passing in a real pipeline, you must first define a custom parameter type, then create nodes that write to and read from the shared instance.

### Defining a Custom Parameter

User-defined parameters inherit from `CGraph::GParam` and include the data fields to be shared. The tutorial example in [`tutorial/MyParams/MyParam.h`](https://github.com/chunelfeng/cgraph/blob/main/tutorial/MyParams/MyParam.h) demonstrates this pattern:

```cpp
#include "GraphCtrl/GraphParam/GParam.h"

struct MyParam : public CGraph::GParam {
    int iValue = 0;
    int iCount = 0;
};

```

### Write Node Implementation

The `MyWriteParamNode` in [`tutorial/MyGNode/MyWriteParamNode.h`](https://github.com/chunelfeng/cgraph/blob/main/tutorial/MyGNode/MyWriteParamNode.h) demonstrates initialization and locked mutation:

```cpp
class MyWriteParamNode : public CGraph::GNode {
public:
    CStatus init() override {
        return CGRAPH_CREATE_GPARAM(MyParam, "param1");
    }

    CStatus run() override {
        auto* myParam = CGRAPH_GET_GPARAM_WITH_NO_EMPTY(MyParam, "param1");
        int val = 0, cnt = 0;
        {
            CGRAPH_PARAM_WRITE_CODE_BLOCK(myParam);
            val = ++myParam->iValue;
            cnt = ++myParam->iCount;
        }
        CGraph::CGRAPH_ECHO("[%s], iValue = %d, iCount = %d",
                            this->getName().c_str(), val, cnt);
        return CStatus();
    }
};

```

### Read Node Implementation

The `MyReadParamNode` in [`tutorial/MyGNode/MyReadParamNode.h`](https://github.com/chunelfeng/cgraph/blob/main/tutorial/MyGNode/MyReadParamNode.h) shows read-only access:

```cpp
class MyReadParamNode : public CGraph::GNode {
public:
    CStatus run() override {
        auto* myParam = CGRAPH_GET_GPARAM_WITH_NO_EMPTY(MyParam, "param1");
        CGraph::CGRAPH_ECHO("Read iValue = %d, iCount = %d",
                            myParam->iValue, myParam->iCount);
        return CStatus();
    }
};

```

## Debugging and Back-Trace Support

To assist with pipeline debugging, CGraph supports back-trace recording when parameters are created. By using the `CGRAPH_CREATE_GPARAM_WITH_BACKTRACE` macro, the system calls `GParam::addBacktrace` (implemented in [`src/GraphCtrl/GraphParam/GParam.h`](https://github.com/chunelfeng/cgraph/blob/main/src/GraphCtrl/GraphParam/GParam.h) lines 38-44) to store the name of the node that instantiated the parameter. This allows developers to trace the origin of shared data when inspecting pipeline state or diagnosing issues with missing keys.

## Summary

- **GParam** is the base class for all shared parameters in CGraph, providing built-in thread-safety mechanisms via `std::shared_mutex` or `std::recursive_mutex`.
- **GParamManager** maintains a centralized `params_map_` registry in [`src/GraphCtrl/GraphParam/GParamManager.h`](https://github.com/chunelfeng/cgraph/blob/main/src/GraphCtrl/GraphParam/GParamManager.h), ensuring a single source of truth for each parameter key.
- **Creation** uses the `CGRAPH_CREATE_GPARAM(Type, key)` macro to register type-safe instances during node initialization.
- **Retrieval** employs `CGRAPH_GET_GPARAM_WITH_NO_EMPTY(Type, key)` to fetch shared instances with null-checking guarantees.
- **Concurrency** is handled through `CGRAPH_PARAM_WRITE_CODE_BLOCK` and `CGRAPH_PARAM_READ_CODE_BLOCK` macros, which provide RAII-style scoped locks on the parameter's internal mutex.
- **Debugging** features include back-trace recording via `CGRAPH_CREATE_GPARAM_WITH_BACKTRACE` to track which node created each parameter.

## Frequently Asked Questions

### How do I create a custom parameter type for sharing data between nodes?

Define a C++ struct or class that inherits from `CGraph::GParam` and include the data fields you need to share. For example, in [`tutorial/MyParams/MyParam.h`](https://github.com/chunelfeng/cgraph/blob/main/tutorial/MyParams/MyParam.h), a `MyParam` struct extends `GParam` and contains `int iValue` and `int iCount` members. Once defined, you can use this type with the `CGRAPH_CREATE_GPARAM` and `CGRAPH_GET_GPARAM_WITH_NO_EMPTY` macros.

### What happens if a node tries to retrieve a parameter that does not exist?

When using the `CGRAPH_GET_GPARAM_WITH_NO_EMPTY(Type, key)` macro, the underlying `getGParamWithNoEmpty` method will throw an exception if the specified key is not found in the `GParamManager`'s registry. This prevents null pointer dereferences and forces developers to ensure parameters are created (typically in an `init()` method) before they are accessed in `run()`.

### Are GParam operations thread-safe when nodes run in parallel?

Yes, but thread safety requires explicit locking. The `GParam` base class contains a `_param_shared_lock_` member (either `std::shared_mutex` or `std::recursive_mutex`). To protect concurrent access, use the `CGRAPH_PARAM_WRITE_CODE_BLOCK(param)` macro for exclusive write access or `CGRAPH_PARAM_READ_CODE_BLOCK(param)` for shared read access. These macros create RAII-style locks that automatically release when the scope exits.

### Can I trace which node created a specific parameter for debugging purposes?

Yes, CGraph supports back-trace functionality for parameters. When creating a parameter, use the `CGRAPH_CREATE_GPARAM_WITH_BACKTRACE` macro instead of the standard creation macro. This records the creating node's name inside the `GParam` object via the `addBacktrace` method (found in [`src/GraphCtrl/GraphParam/GParam.h`](https://github.com/chunelfeng/cgraph/blob/main/src/GraphCtrl/GraphParam/GParam.h)), allowing you to trace the origin of shared data during pipeline debugging or inspection.