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
GNodewith no special-case handling or virtual dispatch penalties. - Clean API: Constructor arguments are passed directly in
registerGElementorregisterGNodecalls, eliminating the need for factory functions orvoid*user data patterns. - Strong Type Safety: The registration overloads use
c_enable_if_t<std::is_base_of<GTemplateNode<Args…>, TNode>::value>guards (defined inGPipeline.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.hthat enables compile-time parameterization while maintaining fullGNodecompatibility. - Type safety is enforced at compile time through
std::is_base_ofchecks inGPipeline.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
GTemplateNodePtrbut treated as standardGElementinstances 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →