1

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 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);
...

Thank you :)

Mina
  • 15
  • 6
  • Just *guessing* here... When you have a member function, you need an object to call it on. Unless `updateWeatherTask` needs to access member variables, you could make the function `static`. – Some programmer dude May 08 '18 at 13:21
  • What is the signature of `weatherTask()`? Also, you might want to read about pointer-to-member-function in C++. They have different semantics compared to regular function pointers. – Dan M. May 08 '18 at 13:21
  • @Someprogrammerdude The updateWeatherTask-Method calls other methods of this class that need access to member variables. ;) I read about the "Object to call it on"-Issue already. However I couldn't figure out how to use this myself – Mina May 08 '18 at 13:29
  • @DanM. the signature is as follows: `Task(Task(unsigned long aInterval=0, long aIterations=0, TaskCallback aCallback=NULL, Scheduler* aScheduler=NULL, bool aEnable=false, TaskOnEnable OnEnable=NULL, TaskOnDisable aOnDisable=NULL);` Thank you for your suggestion, I'll do some research on it – Mina May 08 '18 at 13:34
  • @Mina Hm. Is TaskCallback supposed to be a member function or a static one? From the looks of it (and seeing as it's not a template, or is it?) it doesn't look like it can be anything fancy. Is there a way to pass arguments to callback? – Dan M. May 08 '18 at 13:39
  • @DanM. as far as I understood it is not possible(but you can always prove me wrong ;P) Its a typedef in the TaskScheduler-Library: `typedef void (*TaskCallback)();` – Mina May 08 '18 at 13:46
  • @Mina I figured. When something like in the answer suggested by rmm19433 might be the only clean choice. Though, I think in this case in can be simplified and it can use lambdas, instead of bind + function. – Dan M. May 08 '18 at 14:05
  • @DanM.well, the main Problem right now is, that I have problems to figure out how to use lambdas within the class. I would call the updateWeatherCallback with this - however I'm not sure if this is appropriate/working here :/ I also try the suggested answers here - but it'll take some time ;) – Mina May 08 '18 at 14:29

2 Answers2

0

While using function pointers above is the old way to do it, the newer way is to make use of the function objects, and the <functional> library. See here for an overview of the entire library, with the examples in std::function giving a good "this is how it works" tutorial.

Also look into lambdas for this type of thing. That's also quite useful, and can be stored inside a std::function as well.

Kevin Anderson
  • 5,815
  • 3
  • 29
  • 52
0

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);
rmm19433
  • 240
  • 2
  • 9
  • I implemented the first suggestion I had to instantiate `func` as `TaskCallback` instead of `callback_t`- both are void-pointers - and `Callback` instead of `Callback` – Mina May 09 '18 at 12:01