Back to Index

For

Inherits from Task

Contents

The For class implements a loop controller that represents an iteration process as a Task in the signaling framework. It provides iteration functionality similar to a standard for-loop (for(start; stop; step)) but with the ability to emit signals on each iteration and execute asynchronously. This allows other components to react to loop iterations and monitor the loop's progress.

Features

Class Interface

// Helper structure for loop parameters
struct ForParameters {
    std::optional<int> start;
    std::optional<int> stop;
    std::optional<int> step;
    
    ForParameters() = default;
    ForParameters(int start_, int stop_, int step_ = 1);
};

class For : public Task {
public:
    // Constructor
    For(const ForParameters ¶ms = ForParameters());
    
    // Loop configuration
    void set(const ForParameters ¶ms = ForParameters());
    
    // Getters and setters for loop parameters
    int startValue() const;
    void setStartValue(int value);
    
    int stopValue() const;
    void setStopValue(int value);
    
    int stepValue() const;
    void setStepValue(int value);
    
    // Current iteration value
    int getCurrentValue() const;
    
    // Loop execution
    void start();
    std::future<void> startAsync();
    
private:
    int m_start;
    int m_stop;
    int m_step;
    int m_current;
};

Signals

The For class emits the following signals:

Signal Type Description Arguments
tick Data Emitted on each iteration of the loop start (int), stop (int), current (int), step (int)

Usage Examples

Basic Loop with Signal Handlers

Create and Use a For Loop with Signal Handlers
// Create and use a For loop with signal handlers
void runLoop() {
    // Create a counter task
    auto counter = std::make_shared<Counter>();
    
    // Create a loop that iterates from 0 to 10 with step 2
    auto loop = std::make_shared<For>(ForParameters(0, 10, 2));
    
    // Connect the loop's tick signal to update the counter
    loop->connectData("tick", [counter](const ArgumentPack& args) {
        int current = args.get<int>(2); // Get current value (index 2)
        
        std::cout << "Loop iteration: " << current << std::endl;
        
        // Update counter based on current loop value
        counter->setValue(current);
    });
    
    // Connect counter's valueChanged signal
    counter->connectData("valueChanged", [](const ArgumentPack& args) {
        int oldValue = args.get<int>(0);
        int newValue = args.get<int>(1);
        
        std::cout << "Counter changed from " << oldValue 
                  << " to " << newValue << std::endl;
    });
    
    // Start the loop (synchronously)
    loop->start();
    
    // Or start asynchronously
    auto future = loop->startAsync();
    
    // Do other work while loop executes
    
    // Wait for completion if needed
    if (future.valid()) {
        future.wait();
    }
    
    std::cout << "Loop completed" << std::endl;
}

Direct Parameter Configuration

Configure Loop Parameters Directly
// Example with direct parameter configuration
void configureLoop() {
    auto loop = std::make_shared<For>();
    
    // Configure loop parameters directly
    loop->setStartValue(5);
    loop->setStopValue(25);
    loop->setStepValue(5);
    
    // Or use the set method with ForParameters
    loop->set(ForParameters(5, 25, 5));
    
    // Check current configuration
    std::cout << "Loop configured: " 
              << loop->startValue() << " to " 
              << loop->stopValue() << " by " 
              << loop->stepValue() << std::endl;
    
    // Start the loop
    loop->start();
}

Asynchronous Loop Execution

Run Loop in Background
// Asynchronous loop execution
void backgroundLoop() {
    auto loop = std::make_shared<For>(ForParameters(0, 5, 1));
    
    // Connect tick signal
    loop->connectData("tick", [](const ArgumentPack& args) {
        int current = args.get<int>(2);
        std::cout << "Background iteration: " << current << std::endl;
        
        // Simulate work
        std::this_thread::sleep_for(std::chrono::milliseconds(200));
    });
    
    // Start asynchronously
    std::cout << "Starting loop in background..." << std::endl;
    auto future = loop->startAsync();
    
    // Do other work while loop runs
    std::cout << "Main thread continuing with other work" << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    std::cout << "Main thread still working..." << std::endl;
    
    // Wait for loop to complete
    future.wait();
    std::cout << "Background loop completed" << std::endl;
}

ForParameters Structure

The ForParameters structure provides a convenient way to specify loop parameters:

Parameter Specification Options
// Create with explicit values
ForParameters params(0, 100, 2);

// Create with default values and set only specific parameters
ForParameters params;
params.start = 10;
params.stop = 50;
// step remains default (not set)

// Use optional parameters (only set what's needed)
ForParameters params;
params.stop = 100; // Only setting the stop value

The std::optional type allows specifying only the parameters that need to change, leaving others at their default values.

Loop Execution

The For class provides two methods for executing the loop:

1. Synchronous Execution

loop->start(); // Blocks until loop completes

2. Asynchronous Execution

std::future<void> future = loop->startAsync(); // Returns immediately

// Later, wait for completion
future.wait();

During loop execution, the tick signal is emitted on each iteration with all loop state information.

Tick Signal Arguments

The tick signal provides comprehensive information about the loop state:

Index Type Description
0 int Start value of the loop
1 int Stop value of the loop
2 int Current iteration value
3 int Step value of the loop

This allows signal handlers to access all aspects of the loop state.

Parameter Validation

The For class performs basic validation of loop parameters:

Parameter Validation
if (m_start > m_stop && m_step > 0) {
    emitString("warn", "Bad configuration of the ForLoop");
}

This warns users when the loop is misconfigured (e.g., infinite loops or loops that will never execute).

Warning: The current implementation doesn't guarantee protection against infinite loops. It is the developer's responsibility to ensure valid loop parameters.

Integration with Other Components

The For class can be integrated with other components in the task framework:

With Counter

Loop with Counter Integration
// Use For with Counter for tracking iterations
void loopWithCounter() {
    auto counter = std::make_shared<Counter>(0, 0, 100);
    auto loop = std::make_shared<For>(ForParameters(1, 10, 1));
    
    // Connect loop ticks to counter
    loop->connectData("tick", [counter](const ArgumentPack& args) {
        int current = args.get<int>(2);
        
        // Update counter on each iteration
        counter->setValue(current * 10);
    });
    
    // Connect to counter signals
    counter->connectData("valueChanged", [](const ArgumentPack& args) {
        int newValue = args.get<int>(1);
        std::cout << "Progress: " << newValue << "%" << std::endl;
    });
    
    // Start the loop
    loop->start();
}

With Algorithm

Trigger Algorithm on Specific Iterations
// Execute algorithms at specific iterations
void iteratedAlgorithm(std::shared_ptr<Algorithm> algorithm) {
    auto loop = std::make_shared<For>(ForParameters(1, 5, 1));
    
    // Connect loop ticks to algorithm execution
    loop->connectData("tick", [algorithm](const ArgumentPack& args) {
        int current = args.get<int>(2);
        
        // Change algorithm parameters based on iteration
        ArgumentPack algorithmArgs;
        algorithmArgs.add<int>(current);
        algorithmArgs.add<std::string>("Iteration " + std::to_string(current));
        
        // Execute the algorithm with the current arguments
        algorithm->exec(algorithmArgs);
    });
    
    // Start the loop
    loop->start();
}

With ThreadPool

Execute Different Tasks in ThreadPool
// Distribute different work across a thread pool
void distributedTasks(std::shared_ptr<ThreadPool> pool) {
    auto loop = std::make_shared<For>(ForParameters(0, 5, 1));
    
    // Connect loop ticks to thread pool task creation
    loop->connectData("tick", [pool](const ArgumentPack& args) {
        int current = args.get<int>(2);
        
        // Create a task based on current iteration
        auto task = pool->createAndAdd<MyTask>(
            "Task " + std::to_string(current),
            current * 100  // Different work amount per task
        );
    });
    
    // Start the loop to create tasks
    loop->start();
    
    // Execute all tasks in the pool
    pool->exec();
}

Best Practices

Implementation Details

Note: The For loop implementation is optimized for signaling on each iteration rather than for performance. For high-performance loops with many iterations where signals aren't needed, standard C++ loops may be more efficient.