Inherits from Task
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.
// 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;
};
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) |
// 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;
}
// 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
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;
}
The ForParameters
structure provides a convenient way to specify loop parameters:
// 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.
The For class provides two methods for executing the loop:
loop->start(); // Blocks until loop completes
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.
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.
The For class performs basic validation of loop parameters:
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).
The For class can be integrated with other components in the task framework:
// 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();
}
// 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();
}
// 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();
}
for (m_current = m_start; m_current != m_stop; m_current += m_step)
!=
) rather than comparison
(<
or >
)