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:

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(&parallelGrp, {}, "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 in src/GraphCtrl/GraphElement/GGroup/GCondition/GCondition.h.
  • Single-branch selection requires deriving from GCondition and returning the target child index from choose(), 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 in src/GraphCtrl/GraphElement/GGroup/GCondition/GMultiCondition.h.
  • Registration follows the standard CGraph pattern using createGGroup() and registerGGroup(), 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:

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 →