Files
spore/docs/TaskManager.md

4.6 KiB

TaskManager

Basic Usage

Including Required Headers

#include <functional>  // For std::bind
#include "TaskManager.h"

Registering Member Functions

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

// 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

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

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

#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

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):

void discoverySendTask() { cluster.sendDiscovery(); }
void discoveryListenTask() { cluster.listenForDiscovery(); }

taskManager.registerTask("discovery_send", interval, discoverySendTask);
taskManager.registerTask("discovery_listen", interval, discoveryListenTask);

After (with std::bind):

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