Back to Index

If

Inherits from: Task

Contents

The If class provides a conditional control flow task in the task framework. It evaluates a condition and then executes either a "then" task or an "else" task based on the result. This enables building complex task workflows with conditional branching while maintaining the signal-based communication patterns of the framework.

Features

Class Interface

class If : public Task {
public:
    // Type definition for condition functions
    using ConditionFunction = std::function;
    
    // Constructor
    explicit If(ConditionFunction condition);
    
    // Configuration methods (fluent interface)
    If& then(Task* task);
    If& else_(Task* task);
    
    // Execution methods
    void execute(const ArgumentPack& args = {});
    std::future executeAsync(const ArgumentPack& args = {});
    
private:
    ConditionFunction m_condition;
    std::shared_ptr m_thenTask;
    std::shared_ptr m_elseTask;
};

Signals

Signal Type Description Arguments
started Simple Emitted when execution begins None
finished Simple Emitted when execution completes None
conditionEvaluated Simple Emitted after condition evaluation None
branchSelected Data Reports which branch was selected bool (result), string (branch name)
thenExecuted Simple Emitted when the then branch completes None
elseExecuted Simple Emitted when the else branch completes None
noBranchExecuted Simple Emitted when no branch task was available None
log, warn, error Data Standard logging signals string (message)

Usage Example

Creating and Using an If Task
// Creating a conditional task
If checkValue([](const ArgumentPack& args) {
    // Condition that evaluates whether the first argument > 10
    if (args.empty()) return false;
    return args.get(0) > 10;
});

// Configure the branches
MyTask highValueTask("Process high value");
MyTask lowValueTask("Process low value");

checkValue->then(&highValueTask)
          ->else_(&lowValueTask);

// Connect to signals
checkValue.connectData("branchSelected", [](const ArgumentPack& args) {
    bool result = args.get(0);
    std::string branch = args.get(1);
    std::cout << "Selected branch: " << branch 
              << " (condition: " << (result ? "true" : "false") << ")" << std::endl;
});

// Create arguments
ArgumentPack args;
args.add(15);  // This will cause the then branch to execute

// Execute the conditional task
checkValue->execute(args);

Condition Functions

The condition function determines which branch is executed. Here are several examples:

Condition Function Examples
// Simple condition based on arguments
auto condition1 = [](const ArgumentPack& args) {
    return args.get(0) > 10;
};

// Condition that captures external state
int threshold = 5;
auto condition2 = [threshold](const ArgumentPack& args) {
    return args.get(0) > threshold;
};

// Condition that doesn't use arguments
auto condition3 = [](const ArgumentPack&) {
    return std::time(nullptr) % 2 == 0;  // Condition based on current time
};

// Condition that uses a component's state
auto counter = std::make_shared(0, 0, 100);
auto condition4 = [counter](const ArgumentPack&) {
    return counter->getValue() > 50;
};

Asynchronous Execution

The If task supports asynchronous execution for non-blocking operations:

Asynchronous Execution
// Create arguments
ArgumentPack args;
args.add(5);  // This will cause the else branch to execute

// Execute asynchronously
std::future future = conditionalTask->executeAsync(args);

// Do other work while the conditional task executes
// ...

// Wait for completion when needed
future.wait();

Nesting Conditional Tasks

Conditional tasks can be nested to create complex decision trees:

Nested Conditions
// Create outer condition
auto outerCondition = std::make_shared([](const ArgumentPack& args) {
    return args.get(0) > 0;
});

// Create inner condition (for positive numbers)
auto innerCondition = std::make_shared([](const ArgumentPack& args) {
    return args.get(0) > 10;
});

// Configure inner condition
auto highValueTask = std::make_shared("High value");
auto mediumValueTask = std::make_shared("Medium value");
innerCondition->then(highValueTask)
              ->else_(mediumValueTask);

// Configure outer condition
auto negativeValueTask = std::make_shared("Negative value");
outerCondition->then(innerCondition)  // Use inner condition as the then branch
              ->else_(negativeValueTask);

// Execute the nested conditions
ArgumentPack args;
args.add(15);  // Will execute highValueTask
outerCondition->execute(args);

Integration with ThreadPool

The If task integrates seamlessly with the ThreadPool for parallel conditional execution:

Using If with ThreadPool
// Create a thread pool
auto pool = std::make_shared();

// Create multiple conditional tasks
for (int i = 0; i < 5; i++) {
    // Create condition
    auto task = std::make_shared([](const ArgumentPack& args) {
        return args.get(0) % 2 == 0;  // Check if value is even
    });
    
    // Configure branches
    auto thenTask = std::make_shared("EvenProcessor-" + std::to_string(i));
    auto elseTask = std::make_shared("OddProcessor-" + std::to_string(i));
    
    task->then(thenTask)
        ->else_(elseTask);
    
    // Add to thread pool with arguments
    pool->add(std::make_shared([task, i]() {
        ArgumentPack args;
        args.add(i);
        task->execute(args);
    }));
}

// Execute all tasks in parallel
pool->exec();

Best Practices

Note: The If task is particularly useful for implementing dynamic workflows where execution paths depend on runtime conditions. It can be combined with other task components like For, Counter, and ThreadPool to create sophisticated control flows.