feat: bind tasks instead of passing fn ptrs when registering a task
This commit is contained in:
180
docs/TaskManager.md
Normal file
180
docs/TaskManager.md
Normal file
@@ -0,0 +1,180 @@
|
||||
# TaskManager
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Including Required Headers
|
||||
|
||||
```cpp
|
||||
#include <functional> // For std::bind
|
||||
#include "TaskManager.h"
|
||||
```
|
||||
|
||||
### Registering Member Functions
|
||||
|
||||
```cpp
|
||||
class MyClass {
|
||||
public:
|
||||
void myMethod() {
|
||||
Serial.println("My method called");
|
||||
}
|
||||
|
||||
void methodWithParams(int value, String text) {
|
||||
Serial.printf("Method called with %d and %s\n", value, text.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
// Create an instance
|
||||
MyClass myObject;
|
||||
|
||||
// Register member function
|
||||
taskManager.registerTask("my_task", 1000,
|
||||
std::bind(&MyClass::myMethod, &myObject));
|
||||
|
||||
// Register method with parameters
|
||||
taskManager.registerTask("param_task", 2000,
|
||||
std::bind(&MyClass::methodWithParams, &myObject, 42, "hello"));
|
||||
```
|
||||
|
||||
### Registering Lambda Functions
|
||||
|
||||
```cpp
|
||||
// Simple lambda
|
||||
taskManager.registerTask("lambda_task", 3000, []() {
|
||||
Serial.println("Lambda executed");
|
||||
});
|
||||
|
||||
// Lambda with capture
|
||||
int counter = 0;
|
||||
taskManager.registerTask("counter_task", 4000, [&counter]() {
|
||||
counter++;
|
||||
Serial.printf("Counter: %d\n", counter);
|
||||
});
|
||||
|
||||
// Lambda that calls multiple methods
|
||||
taskManager.registerTask("multi_task", 5000, [&myObject]() {
|
||||
myObject.myMethod();
|
||||
// Do other work...
|
||||
});
|
||||
```
|
||||
|
||||
### Registering Global Functions
|
||||
|
||||
```cpp
|
||||
void globalFunction() {
|
||||
Serial.println("Global function called");
|
||||
}
|
||||
|
||||
// Still supported for backward compatibility
|
||||
taskManager.registerTask("global_task", 6000, globalFunction);
|
||||
```
|
||||
|
||||
## Advanced Examples
|
||||
|
||||
### Binding to Different Object Types
|
||||
|
||||
```cpp
|
||||
class NetworkManager {
|
||||
public:
|
||||
void sendHeartbeat() { /* ... */ }
|
||||
void checkConnection() { /* ... */ }
|
||||
};
|
||||
|
||||
class SensorManager {
|
||||
public:
|
||||
void readSensors() { /* ... */ }
|
||||
void calibrate() { /* ... */ }
|
||||
};
|
||||
|
||||
NetworkManager network;
|
||||
SensorManager sensors;
|
||||
|
||||
// Bind to different objects
|
||||
taskManager.registerTask("heartbeat", 1000,
|
||||
std::bind(&NetworkManager::sendHeartbeat, &network));
|
||||
taskManager.registerTask("sensor_read", 500,
|
||||
std::bind(&SensorManager::readSensors, &sensors));
|
||||
```
|
||||
|
||||
### Using std::placeholders for Complex Binding
|
||||
|
||||
```cpp
|
||||
#include <functional>
|
||||
|
||||
class ConfigManager {
|
||||
public:
|
||||
void updateConfig(int interval, bool enabled) {
|
||||
Serial.printf("Updating config: interval=%d, enabled=%d\n", interval, enabled);
|
||||
}
|
||||
};
|
||||
|
||||
ConfigManager config;
|
||||
|
||||
// Use placeholders for complex parameter binding
|
||||
using namespace std::placeholders;
|
||||
taskManager.registerTask("config_update", 10000,
|
||||
std::bind(&ConfigManager::updateConfig, &config, _1, _2));
|
||||
```
|
||||
|
||||
### Conditional Task Execution
|
||||
|
||||
```cpp
|
||||
class TaskController {
|
||||
public:
|
||||
bool shouldExecute() {
|
||||
return millis() % 10000 < 5000; // Execute only in first 5 seconds of each 10-second cycle
|
||||
}
|
||||
|
||||
void conditionalTask() {
|
||||
if (shouldExecute()) {
|
||||
Serial.println("Conditional task executed");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TaskController controller;
|
||||
|
||||
taskManager.registerTask("conditional", 1000,
|
||||
std::bind(&TaskController::conditionalTask, &controller));
|
||||
```
|
||||
|
||||
## Benefits of Using std::bind
|
||||
|
||||
1. **Cleaner Code**: No need for wrapper functions
|
||||
2. **Direct Binding**: Bind member functions directly to objects
|
||||
3. **Parameter Passing**: Easily pass parameters to bound methods
|
||||
4. **Lambda Support**: Use lambdas for complex logic
|
||||
5. **Type Safety**: Better type checking than function pointers
|
||||
6. **Flexibility**: Mix and match different callable types
|
||||
|
||||
## Migration from Wrapper Functions
|
||||
|
||||
### Before (with wrapper functions):
|
||||
```cpp
|
||||
void discoverySendTask() { cluster.sendDiscovery(); }
|
||||
void discoveryListenTask() { cluster.listenForDiscovery(); }
|
||||
|
||||
taskManager.registerTask("discovery_send", interval, discoverySendTask);
|
||||
taskManager.registerTask("discovery_listen", interval, discoveryListenTask);
|
||||
```
|
||||
|
||||
### After (with std::bind):
|
||||
```cpp
|
||||
taskManager.registerTask("discovery_send", interval,
|
||||
std::bind(&ClusterManager::sendDiscovery, &cluster));
|
||||
taskManager.registerTask("discovery_listen", interval,
|
||||
std::bind(&ClusterManager::listenForDiscovery, &cluster));
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
- `std::bind` creates a callable object that may have a small overhead compared to direct function pointers
|
||||
- For high-frequency tasks, consider the performance impact
|
||||
- The overhead is typically negligible for most embedded applications
|
||||
- The TaskManager stores bound functions efficiently in a registry
|
||||
|
||||
## Compatibility
|
||||
|
||||
- The new `std::bind` support is fully backward compatible
|
||||
- Existing code using function pointers will continue to work
|
||||
- You can mix both approaches in the same project
|
||||
- All existing TaskManager methods remain unchanged
|
||||
Reference in New Issue
Block a user