How to Implement Conditional Execution Flows in CGraph Pipelines
CGraph implements conditional execution flows through the GCondition abstraction, which uses a virtual choose() method to select specific child elements at runtime, while GMultiCondition provides serial and parallel execution modes for running multiple branches.
CGraph is a high-performance C++ task graph framework that enables dynamic pipeline control through condition groups. The primary mechanism for implementing conditional execution flows in CGraph pipelines involves inheriting from GCondition and overriding the selection logic to determine which child nodes execute. This approach supports data-driven branching, feature toggles, and error handling within complex computational graphs.
Understanding the GCondition Abstraction
Core Architecture and the choose() Method
At the heart of conditional execution lies GCondition, an abstract class defined in src/GraphCtrl/GraphElement/GGroup/GCondition/GCondition.h. This class inherits from GGroup and requires implementations to override the pure-virtual method:
virtual CIndex choose() = 0;
The choose() method returns the zero-based index of the child element to execute. Returning -1 signals the framework to execute the last element in the group. When the pipeline runs, GCondition::run() queries choose() and dispatches execution to the selected child only.
Source File Locations
The condition group implementation spans several key files in the CGraph repository:
src/GraphCtrl/GraphElement/GGroup/GCondition/GCondition.h— Defines the abstract base class andchoose()contractsrc/GraphCtrl/GraphElement/GGroup/GCondition/GMultiCondition.h— Template implementation for serial and parallel multi-condition groupstutorial/MyGCondition/MyCondition.h— Example user-defined condition selecting a specific child indextutorial/T21-MultiCondition.cpp— Demonstrates both serial and parallel execution patternstutorial/T06-Condition.cpp— Basic condition usage without multi-condition wrappers
Implementing Single-Branch Selection
Creating a Custom Condition Class
To implement conditional execution flows that select a single branch, derive from GCondition and implement the selection logic. The tutorial file tutorial/MyGCondition/MyCondition.h provides a minimal example:
// MyCondition.h
#include "CGraph.h"
class MyCondition : public CGraph::GCondition {
public:
// Always select the second child (index 1)
CIndex choose() override { return 1; }
};
For dynamic branching based on runtime state, inspect internal flags or pipeline parameters:
class SwitchCondition : public CGraph::GCondition {
public:
CIndex choose() override {
// Return 0 for success path, 1 for failure path
return validationPassed ? 0 : 1;
}
private:
bool validationPassed = false; // Set during previous node execution
};
Registering Conditions in the Pipeline
Condition groups are created and registered similarly to standard nodes. Use createGGroup to instantiate the condition and registerGGroup to add it to the pipeline:
// pipeline_example.cpp
#include "MyCondition.h"
void example_simple_condition() {
auto pipeline = CGraph::GPipelineFactory::create();
// Create a condition group with two alternative nodes
auto condGroup = pipeline->createGGroup<CGraph::GCondition>({
pipeline->createGNode<NodeA>(CGraph::GNodeInfo("nodeA")),
pipeline->createGNode<NodeB>(CGraph::GNodeInfo("nodeB"))
});
// Register with no predecessors
CGraph::CStatus status = pipeline->registerGGroup(&condGroup, {}, "myCond");
if (!status.isOK()) return;
pipeline->process();
CGraph::GPipelineFactory::remove(pipeline);
}
Multi-Condition Execution Patterns
Serial Execution with GMultiCondition
When you need to execute multiple child elements in a specific order, use GMultiCondition with the SERIAL template parameter. This class is defined in src/GraphCtrl/GraphElement/GGroup/GCondition/GMultiCondition.h and overrides choose() to always return -1, ensuring all children run.
The serialRun() method iterates through children sequentially:
// T21-MultiCondition.cpp excerpt
void tutorial_serial_condition() {
auto pipeline = CGraph::GPipelineFactory::create();
// SERIAL group: children execute one after another
auto serialGrp = pipeline->createGGroup<
CGraph::GMultiCondition<CGraph::GMultiConditionType::SERIAL>
>({
pipeline->createGNode<PreprocessNode>(CGraph::GNodeInfo("preprocess")),
pipeline->createGNode<ValidateNode>(CGraph::GNodeInfo("validate")),
pipeline->createGNode<TransformNode>(CGraph::GNodeInfo("transform"))
});
CGraph::GElementPtr prev = nullptr;
pipeline->registerGElement<StartNode>(&prev, {}, "start");
pipeline->registerGGroup(&serialGrp, {prev}, "serialGroup");
pipeline->process();
CGraph::GPipelineFactory::remove(pipeline);
}
Parallel Execution with GMultiCondition
For independent operations that can run simultaneously, use GMultiCondition with the PARALLEL template parameter. The parallelRun() method dispatches each child to the CGraph thread pool:
// Parallel execution example
void tutorial_parallel_condition() {
auto pipeline = CGraph::GPipelineFactory::create();
auto parallelGrp = pipeline->createGGroup<
CGraph::GMultiCondition<CGraph::GMultiConditionType::PARALLEL>
>({
pipeline->createGNode<IOReadNode>(CGraph::GNodeInfo("ioRead")),
pipeline->createGNode<NetworkFetchNode>(CGraph::GNodeInfo("networkFetch")),
pipeline->createGNode<CacheLoadNode>(CGraph::GNodeInfo("cacheLoad"))
});
// All three nodes start simultaneously
pipeline->registerGGroup(¶llelGrp, {}, "parallelGroup");
pipeline->process();
CGraph::GPipelineFactory::remove(pipeline);
}
Execution continues only after all parallel children complete. This pattern maximizes throughput for I/O-bound or computationally independent tasks.
Practical Use Cases for Conditional Flows
Dynamic Branching Based on Runtime Data
Conditional execution flows enable data-driven pipeline routing. Implement choose() to inspect parameters set by previous nodes:
class DataDrivenCondition : public CGraph::GCondition {
public:
CStatus init() override {
// Retrieve parameter from previous execution
auto* param = CGraph::GParamManager::get()->get<PipelineParam>("config");
threshold = param->confidenceThreshold;
return CStatus();
}
CIndex choose() override {
// Route to high-precision path (index 0) or fast path (index 1)
return (threshold > 0.9) ? 0 : 1;
}
private:
float threshold = 0.0f;
};
This pattern supports A/B testing, quality-of-service routing, and adaptive algorithms where the execution path depends on intermediate results.
Error Handling and Fallback Paths
Use GCondition to implement circuit-breaker patterns or fallback chains. Structure the condition with multiple children representing primary, secondary, and error-handling nodes:
class ResilientCondition : public CGraph::GCondition {
public:
CIndex choose() override {
if (primarySuccess) return 0; // Primary service
if (secondaryAvailable) return 1; // Backup service
return 2; // Error handler (last child)
}
// Set by previous nodes via shared state
bool primarySuccess = false;
bool secondaryAvailable = false;
};
Register the children in order: primary node, fallback node, and error handler. The choose() method acts as a decision tree, ensuring the pipeline degrades gracefully rather than failing entirely.
Summary
- GCondition provides the foundation for conditional execution flows in CGraph pipelines through the pure-virtual
choose()method defined insrc/GraphCtrl/GraphElement/GGroup/GCondition/GCondition.h. - Single-branch selection requires deriving from
GConditionand returning the target child index fromchoose(), enabling dynamic routing based on runtime state. - GMultiCondition extends this model to execute all children either serially (
GMultiConditionType::SERIAL) or in parallel (GMultiConditionType::PARALLEL), with implementation details insrc/GraphCtrl/GraphElement/GGroup/GCondition/GMultiCondition.h. - Registration follows the standard CGraph pattern using
createGGroup()andregisterGGroup(), treating condition groups similarly to standard nodes while maintaining dependency relationships. - Practical applications include data-driven branching, A/B testing, circuit-breaker patterns, and resilient fallback chains.
Frequently Asked Questions
How does GCondition decide which child node to execute?
The GCondition class delegates this decision to the pure-virtual choose() method, which must be implemented by derived classes. This method returns a CIndex (integer) representing the zero-based position of the child to execute, or -1 to select the last child. When the pipeline runs, GCondition::run() queries this method and dispatches execution only to the selected child, skipping all others.
What is the difference between GCondition and GMultiCondition?
GCondition is an abstract base class designed for selective execution—it runs exactly one child determined by the choose() implementation. GMultiCondition is a concrete template class derived from GCondition that overrides choose() to always return -1, causing it to execute all children. The template parameter GMultiConditionType controls whether children run sequentially (SERIAL) or concurrently (PARALLEL), making GMultiCondition suitable for grouped execution rather than branching logic.
Can condition groups be nested within other groups or connected to standard nodes?
Yes, condition groups integrate seamlessly with standard CGraph pipelines. You register them using registerGGroup(), specifying predecessor elements (which can be standard nodes or other groups) in the dependency list. The condition group itself can also serve as a dependency for subsequent nodes. This allows complex hierarchies where a GCondition selects between sub-pipelines, or where a GMultiCondition encapsulates a sequence of operations treated as a single logical unit within the larger graph.
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 →