Search code examples
c++arduinoscheduled-tasks

C++ How to call methods within a class as function pointer for TaskScheduler - Arduino


I try to implement a task which is called within a C++Class and it needs a callback-function. As I'm pretty new to C++ and although I have a rough understanding for pointers this is something I couldn't figure out yet:

I'm using the TaskScheduler library to setup everything needed in class I want to call the following function. The normal examples for arduino don't use classes and I'm struggling to get it work in my refactored code.

void Weather::setup(std::shared_ptr<Scheduler> s ,int id, String appid, uint32_t interval) 
{
  ...
  weatherTask(interval, TASK_FOREVER, &Weather::updateWeatherCallback, *scheduler, true);
}

the updateWeatherCallback-function is implemented in this class too:

void Weather::updateWeatherCallback() {...}

Asides using the Code above I tried some lambda Variants suggested here on Stackoverflow and simply the following:

weatherTask(interval,TASK_FOREVER, [this](){this->updateWeatherCallback;},*scheduler,true);

But I figured that my understanding of these answers on Stackoverflow is not good -its more trial and error- which makes searching for "the right answers" difficult

Some details from the TaskScheduler-Library:

typedef void (*TaskCallback)();
class Task {
  friend class Scheduler;
  public:
    INLINE Task(unsigned long aInterval=0, long aIterations=0, TaskCallback aCallback=NULL, 
       Scheduler* aScheduler=NULL, bool aEnable=false, TaskOnEnable aOnEnable=NULL, 
       TaskOnDisable aOnDisable=NULL);
...

Solution

  • weatherTask wants a function pointer and not a pointer to a member. A possible way to solve this problem is to create a helper struct and bind the function to a std::function.

    #include <functional>
    
    template <typename T>
    struct Callback;
    
    template <typename Ret, typename... Params>
    struct Callback<Ret(Params...)> {
       template <typename... Args> 
       static Ret callback(Args... args) {                    
          func(args...);  
       }
       static std::function<Ret(Params...)> func; 
    };
    
    template <typename Ret, typename... Params>
    std::function<Ret(Params...)> Callback<Ret(Params...)>::func;
    

    When creating the weatherTask use the following to bind the member function to your callback:

    Callback<void(void)>::func = std::bind(&Weather::updateWeatherCallback, this);
    TaskCallback func = static_cast<TaskCallback>(Callback<void(void)>::callback);
    weatherTask(interval, TASK_FOREVER, func, *scheduler, true);
    

    The solution was presented as an anwser to this question. Since I cannot comment I decided to provide this as an answer.


    Alternatively you can make use of a trampoline like function. Therefore you would be required to store an global pointer to your Weather object.

    Weather global_ptr;
    

    Then you would be able to create a weatherTask using a lambda function.

    weatherTask(interval, TASK_FOREVER, [](){global_ptr->updateWeatherCallback();}, *scheduler, true);