Using CGraph TemplateNode for Parameterized and Reusable Nodes

CGraph's GTemplateNode class enables compile-time parameterized nodes that maintain full GNode compatibility, allowing developers to create type-safe, reusable pipeline components with zero runtime overhead.

The chunelfeng/cgraph library provides a powerful templating mechanism for building parameterized and reusable nodes in CGraph pipelines. Unlike standard GNode instances, template nodes accept variadic compile-time arguments that enable generic programming patterns while integrating seamlessly with the engine's scheduling and dependency analysis systems.

What Is GTemplateNode in CGraph?

GTemplateNode is a thin variadic-template wrapper defined in src/GraphCtrl/GraphElement/GNode/GTemplateNode.h. It inherits directly from GNode and adds no new runtime behavior. Instead, it serves as a type-safe carrier for compile-time parameters (types, constants, or sizes) that are forwarded to derived classes during construction.

The template parameters are used strictly for static type checking and constructor argument forwarding. Once instantiated, the node behaves exactly like any other GNode in the pipeline.

Core Architecture Components

Component Role Source Location
GElement Abstract base class for all pipeline elements. src/GraphCtrl/GraphElement/GElement/GElement.h
GNode Concrete element implementing the run() method. src/GraphCtrl/GraphElement/GNode/GNode.h
GTemplateNode<Args…> Variadic template wrapper enabling compile-time parameterization. src/GraphCtrl/GraphElement/GNode/GTemplateNode.h
GPipeline Orchestrator providing registerGElement and registerGNode overloads for template nodes. src/GraphCtrl/GraphPipeline/GPipeline.h (lines 70-75)
GPipeline.inl Implementation of template-node registration logic using perfect forwarding. src/GraphCtrl/GraphPipeline/GPipeline.inl (lines 77-91)

Benefits of Using TemplateNode for Parameterized Nodes

  • Parameterized Reuse: A single class definition can be instantiated with different compile-time arguments (e.g., MyTemplateNode<int,float> vs. MyTemplateNode<int>) without code duplication.
  • Zero Runtime Overhead: All template parameters are resolved at compile time. The engine treats the node as a standard GNode with no special-case handling or virtual dispatch penalties.
  • Clean API: Constructor arguments are passed directly in registerGElement or registerGNode calls, eliminating the need for factory functions or void* user data patterns.
  • Strong Type Safety: The registration overloads use c_enable_if_t<std::is_base_of<GTemplateNode<Args…>, TNode>::value> guards (defined in GPipeline.h) to prevent accidental registration of non-template nodes with template-specific overloads.

How to Create and Register a TemplateNode

Step 1: Define Your Templated Node

Create a class that inherits from CGraph::GTemplateNode<Args…> and implement custom constructors to receive runtime data. The template parameters enable compile-time specialization while the constructors handle dynamic initialization.

// tutorial/MyGNode/MyTemplateNode.h
#ifndef CGRAPH_MYTEMPLATENODE_H
#define CGRAPH_MYTEMPLATENODE_H

#include "CGraph.h"

template <typename ...Args>
class MyTemplateNode : public CGraph::GTemplateNode<Args...> {
public:
    // Constructor for (int, float) signature
    explicit MyTemplateNode(int num, float score) {
        num_   = num;
        score_ = score;
    }

    // Constructor for single int argument
    explicit MyTemplateNode(int num) {
        num_   = num;
        score_ = 7.0f;   // default value
    }

    CStatus run() override {
        CGraph::CGRAPH_ECHO("[MyTemplateNode] num = %d, score = %f", num_, score_);
        return CStatus();
    }

private:
    int   num_;
    float score_;
};

#endif // CGRAPH_MYTEMPLATENODE_H

Step 2: Register Nodes with the Pipeline

Use registerGElement with explicit template arguments to instantiate your node. The pipeline forwards constructor arguments via perfect forwarding implemented in GPipeline.inl (lines 77-91).

// tutorial/T08-Template.cpp
#include "MyGNode/MyTemplateNode.h"
#include "MyGNode/MyTemplateV2Node.h"

using namespace CGraph;

void tutorial_template() {
    GPipelinePtr pipeline = GPipelineFactory::create();

    // Pointers to receive created nodes
    GTemplateNodePtr<int,float> a = nullptr;
    GTemplateNodePtr<int,float> b = nullptr;
    GTemplateNodePtr<int>       c = nullptr;
    GElementPtr                d = nullptr;

    // Register with different constructor signatures
    pipeline->registerGElement<MyTemplateNode<int,float>>(&a, {}, 3, 3.5f);
    pipeline->registerGElement<MyTemplateNode<int,float>>(&b, {a}, 5, 3.75f);
    pipeline->registerGElement<MyTemplateNode<int>>(&c, {b}, 8);

    // Register node with non-type template parameter
    pipeline->registerGElement<MyTemplateV2Node<4>>(&d, {c});

    pipeline->process();
    GPipelineFactory::remove(pipeline);
}

Step 3: Using the registerGNode Shortcut

If you do not need the raw pointer for later dependency references, use registerGNode (defined in GPipeline.inl, lines 67-74) to receive the concrete node directly:

auto *nodeA = pipeline->registerGNode<MyTemplateNode<int,float>>({},
                                                            3, 3.5f);
auto *nodeB = pipeline->registerGNode<MyTemplateNode<int,float>>({nodeA},
                                                            5, 3.75f);

This shortcut forwards to registerGElement internally but returns the pointer rather than requiring an output parameter.

Key Implementation Files

File Purpose Location
GTemplateNode.h Defines the variadic template base class GTemplateNode<Args…> and the GTemplateNodePtr alias. src/GraphCtrl/GraphElement/GNode/GTemplateNode.h
GPipeline.h Declares template overloads for registerGElement and registerGNode with type safety guards. src/GraphCtrl/GraphPipeline/GPipeline.h (lines 70-75)
GPipeline.inl Implements perfect forwarding of constructor arguments during template node registration. src/GraphCtrl/GraphPipeline/GPipeline.inl (lines 67-91)
MyTemplateNode.h Tutorial example showing custom constructors and runtime parameter handling. tutorial/MyGNode/MyTemplateNode.h
T08-Template.cpp End-to-end demonstration of multiple template instantiations and dependency chaining. tutorial/T08-Template.cpp

Summary

  • GTemplateNode is a zero-overhead wrapper in GTemplateNode.h that enables compile-time parameterization while maintaining full GNode compatibility.
  • Type safety is enforced at compile time through std::is_base_of checks in GPipeline.h, preventing mismatched registrations.
  • Constructor forwarding in GPipeline.inl (lines 77-91) allows runtime arguments to be passed directly during registration without factory boilerplate.
  • Reusability is achieved by instantiating the same class with different template arguments (e.g., MyTemplateNode<int,float> vs. MyTemplateNode<int>).
  • Integration with existing pipelines is seamless because template nodes are stored as GTemplateNodePtr but treated as standard GElement instances during execution.

Frequently Asked Questions

What is the difference between GNode and GTemplateNode?

GNode is the concrete base class for all executable nodes in CGraph, providing the virtual run() method and dependency management. GTemplateNode<Args…> is a thin variadic-template wrapper defined in src/GraphCtrl/GraphElement/GNode/GTemplateNode.h that inherits from GNode and adds compile-time parameterization without introducing new runtime behavior or virtual functions.

Can I use non-type template parameters with GTemplateNode?

Yes. The variadic template design supports any compile-time constant, including non-type parameters. For example, MyTemplateV2Node<4> (as shown in tutorial/T08-Template.cpp) passes the integer 4 as a template argument, allowing the node to use that value in constexpr contexts or as a fixed array size.

Does using GTemplateNode add runtime overhead?

No. GTemplateNode is a zero-cost abstraction. All template parameters are resolved at compile time, and the GPipeline engine stores and schedules these nodes exactly like standard GNode instances. The registration logic in src/GraphCtrl/GraphPipeline/GPipeline.inl uses perfect forwarding to construct the object in place without additional indirection or heap allocation overhead.

How do I pass dependencies when registering a TemplateNode?

Dependencies are passed as the second argument to registerGElement or registerGNode using an initializer list or vector of GElementPtr. For example: pipeline->registerGElement<MyTemplateNode<int,float>>(&b, {a}, 5, 3.75f); registers node b with a as its dependency, while the remaining arguments (5, 3.75f) are forwarded to the constructor defined in your derived class.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →