Inherits from Task
The Counter
class is a versatile component in the task framework that provides a
signal-emitting numeric counter with configurable bounds. It extends the base Task
class to
integrate with the signal-slot system, allowing other components to react to counter value changes and
limit events. The Counter maintains an integer value that can be incremented, decremented, or directly
set, with optional minimum and maximum bounds to constrain its range.
class Counter : public Task {
public:
// Constructor
Counter(int initialValue = 0, std::optional<int> minValue = std::nullopt,
std::optional<int> maxValue = std::nullopt);
// Value access and modification
int getValue() const;
bool setValue(int value);
int increment(int amount = 1);
int decrement(int amount = 1);
int reset();
// Range checking
bool isAtMinimum() const;
bool isAtMaximum() const;
// Bound management
std::optional<int> getMinValue() const;
std::optional<int> getMaxValue() const;
bool setMinValue(std::optional<int> min);
bool setMaxValue(std::optional<int> max);
private:
int m_value;
int m_initialValue;
std::optional<int> m_minValue;
std::optional<int> m_maxValue;
bool isInRange(int value) const;
void emitChangeSignals(int oldValue, int newValue);
};
Counter emits the following signals:
Signal | Type | Description | Arguments |
---|---|---|---|
valueChanged |
Data | Emitted when the counter value changes | oldValue (int), newValue (int) |
limitReached |
Data | Emitted when counter hits min or max limit | isMinimum (bool), value (int) |
reset |
Simple | Emitted when counter is reset | None |
log |
Data | Reports counter operations | std::string (log message) |
warn |
Data | Reports warnings (e.g., out-of-range) | std::string (warning message) |
// Create a counter with initial value 0, min 0, max 10
auto counter = std::make_shared<Counter>(0, 0, 10);
// Connect to signals
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;
});
counter->connectData("limitReached", [](const ArgumentPack& args) {
bool isMin = args.get<bool>(0);
int value = args.get<int>(1);
std::cout << "Counter reached " << (isMin ? "minimum" : "maximum")
<< " value: " << value << std::endl;
});
counter->connectSimple("reset", []() {
std::cout << "Counter was reset" << std::endl;
});
// Use the counter
counter->increment(5); // 0 -> 5
counter->increment(10); // 5 -> 10 (max)
std::cout << "Is at max: " << counter->isAtMaximum() << std::endl;
counter->decrement(5); // 10 -> 5
counter->setValue(0); // 5 -> 0 (min)
std::cout << "Is at min: " << counter->isAtMinimum() << std::endl;
counter->reset(); // Resets to initial value (0)
// Create a counter for tracking progress (0-100)
auto progress = std::make_shared<Counter>(0, 0, 100);
auto progressBar = std::make_shared<ProgressBar>(); // Hypothetical component
// Connect counter changes to update progress bar
progress->connectData("valueChanged", [progressBar](const ArgumentPack& args) {
int newValue = args.get<int>(1);
progressBar->setProgress(newValue / 100.0f);
});
// Connect to limit signal to detect completion
progress->connectData("limitReached", [](const ArgumentPack& args) {
bool isMin = args.get<bool>(0);
if (!isMin) { // Max reached
std::cout << "Process completed 100%" << std::endl;
}
});
// Update progress as work is done
for (int i = 0; i <= 10; i++) {
// Do work...
std::this_thread::sleep_for(std::chrono::milliseconds(100));
// Update progress (0-100)
progress->setValue(i * 10);
}
// Counter with no constraints
auto unboundedCounter = std::make_shared<Counter>(0);
// Counter with only minimum value (>= 0)
auto nonNegativeCounter = std::make_shared<Counter>(0, 0, std::nullopt);
// Counter with only maximum value (<= 100)
auto limitedCounter = std::make_shared<Counter>(0, std::nullopt, 100);
// Counter with both min and max values (0-100)
auto boundedCounter = std::make_shared<Counter>(0, 0, 100);
// Test with various operations
unboundedCounter->increment(10); // No limit
unboundedCounter->decrement(20); // Can go negative
nonNegativeCounter->decrement(20); // Will clamp to 0
std::cout << nonNegativeCounter->getValue() << std::endl; // Outputs: 0
limitedCounter->increment(200); // Will clamp to 100
std::cout << limitedCounter->getValue() << std::endl; // Outputs: 100
// Dynamically changing bounds
boundedCounter->setMaxValue(50); // Change upper bound to 50
if(boundedCounter->getValue() > 50) {
std::cout << "Value was automatically adjusted to maximum" << std::endl;
}
The Counter supports optional minimum and maximum value constraints:
std::nullopt
), counter has
unlimited rangeWhen changing bounds, the counter ensures that min <= max and adjusts the current value if needed to stay within the new bounds.
// Configure bounds and observe behavior
void demonstrateBounds() {
auto counter = std::make_shared<Counter>(50); // Start at 50, no bounds
// Initially no bounds
counter->setValue(1000); // Works fine
counter->setValue(-1000); // Works fine
// Set a minimum
counter->setMinValue(0);
counter->setValue(-10); // Won't go below 0
std::cout << "After min constraint: " << counter->getValue() << std::endl; // 0
// Set a maximum
counter->setMaxValue(100);
counter->setValue(200); // Won't go above 100
std::cout << "After max constraint: " << counter->getValue() << std::endl; // 100
// Try setting min > max (should fail)
bool success = counter->setMinValue(200);
std::cout << "Set min > max success: " << success << std::endl; // false
// Remove constraints
counter->setMinValue(std::nullopt);
counter->setMaxValue(std::nullopt);
counter->setValue(-500); // Now works
std::cout << "After removing constraints: " << counter->getValue() << std::endl; // -500
}
The Counter validates values against configured bounds:
setValue(value)
: Returns false if value is outside bounds (after warning)increment(amount)
: Clamps result to maximum if it would exceed itdecrement(amount)
: Clamps result to minimum if it would go below itThe isInRange(int value)
private method checks if a value is within the configured bounds.
The Counter emits signals when its value changes:
The emitChangeSignals(int oldValue, int newValue)
private method handles all signal emissions
related to value changes.
// Set up a counter with comprehensive signal handling
void monitorCounter() {
auto counter = std::make_shared<Counter>(5, 0, 10);
// Track all value changes
counter->connectData("valueChanged", [](const ArgumentPack& args) {
int oldValue = args.get<int>(0);
int newValue = args.get<int>(1);
std::cout << "Value changed: " << oldValue << " -> " << newValue << std::endl;
});
// Special handling for limit events
counter->connectData("limitReached", [](const ArgumentPack& args) {
bool isMin = args.get<bool>(0);
int value = args.get<int>(1);
if (isMin) {
std::cout << "⚠️ Minimum limit reached: " << value << std::endl;
} else {
std::cout << "🏁 Maximum limit reached: " << value << std::endl;
}
});
// Monitor reset events
counter->connectSimple("reset", []() {
std::cout << "Counter has been reset" << std::endl;
});
// Test various operations
counter->increment(3); // 5 -> 8
counter->increment(5); // 8 -> 10 (max)
counter->decrement(15); // 10 -> 0 (min)
counter->reset(); // 0 -> 5
}
The Counter maintains an initial value that can be reset to:
reset()
method returns the counter to its initial value
// Initial value scenarios
void initialValueDemo() {
// Normal case - initial value within bounds
auto counter1 = std::make_shared<Counter>(5, 0, 10);
// Initial value outside bounds - will be adjusted
auto counter2 = std::make_shared<Counter>(20, 0, 10);
std::cout << "Initial value adjusted: " << counter2->getValue() << std::endl; // 10
// Initial value becomes new value after bounds adjustment
counter2->reset();
std::cout << "After reset: " << counter2->getValue() << std::endl; // 10, not 20
// Setting bounds after creation that conflict with initial value
auto counter3 = std::make_shared<Counter>(50);
counter3->setValue(100);
counter3->setMaxValue(75); // Current value (100) will be adjusted to 75
counter3->reset();
std::cout << "Reset goes to original: " << counter3->getValue() << std::endl; // 50
}
The Counter can be integrated with various components:
// Use counter to drive progress visualization
void showProgress(std::shared_ptr<ProgressBar> progressBar) {
auto counter = std::make_shared<Counter>(0, 0, 100);
// Connect counter to progress bar
counter->connectData("valueChanged", [progressBar](const ArgumentPack& args) {
int newValue = args.get<int>(1);
float progress = newValue / 100.0f;
progressBar->setProgress(progress);
});
// Simulate work with progress updates
for (int i = 0; i <= 10; i++) {
std::this_thread::sleep_for(std::chrono::milliseconds(200));
counter->setValue(i * 10);
}
}
// Use For loop with Counter
void countedIteration() {
auto counter = std::make_shared<Counter>(0);
auto loop = std::make_shared<For>(ForParameters(0, 10, 1));
// Connect loop ticks to counter
loop->connectData("tick", [counter](const ArgumentPack& args) {
int current = args.get<int>(2); // Current iteration value
counter->setValue(current);
});
// Run the loop
loop->start();
std::cout << "Final counter value: " << counter->getValue() << std::endl;
}
// Hypothetical StateMachine integration
void stateMachineExample(std::shared_ptr<StateMachine> stateMachine) {
auto counter = std::make_shared<Counter>(0, 0, 5);
// Trigger state transitions based on counter values
counter->connectData("valueChanged", [stateMachine](const ArgumentPack& args) {
int newValue = args.get<int>(1);
switch (newValue) {
case 1:
stateMachine->transition("initialize");
break;
case 3:
stateMachine->transition("process");
break;
case 5:
stateMachine->transition("complete");
break;
}
});
// Increment counter to trigger transitions
for (int i = 1; i <= 5; i++) {
counter->setValue(i);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
}
The Counter class provides thread-safe operations for concurrent access:
std::optional<int>
for flexible bound specification