I am trying to pass a const std::function<void()> &
as void *
to SDL_AddTimer, which its 3rd parameter is void *
.
I tried with reinterpret_cast
SDL_AddTimer(interval, wrapper, reinterpret_cast<void *>(const_cast<std::function<void()> *>(fn)));
But on my wrapper, it crashes
uint32_t wrapper(uint32_t interval, void *param) {
auto fn = static_cast<std::function<void()> *>(param);
(*fn)(); // crashes here.
return interval;
}
What I am doing wrong?
EDIT: Usage:
timer t;
t.set(1000, []() {
std::cout << "Hello, world!" << std::endl;
});
You can't cast a std::function
into a void*
like you are doing (you can't even cast a reference into a pointer the way you are doing).
But, you can pass the address of the std::function
object itself. That is what your wrapper
is expecting anyway, eg:
auto &fn_ref = const_cast<std::function<void()> &>(fn); // note & not *
SDL_AddTimer(interval, wrapper, &fn_ref);
uint32_t wrapper(uint32_t interval, void *param) {
auto fn = static_cast<std::function<void()> *>(param);
(*fn)();
return interval;
}
Just make sure the std::function
object stays alive while the SDL timer is running. However, based on your usage example, it appears the std::function
object may go out of scope before the timer ever has a chance to call it. So, you will have to save the std::function
object somewhere until the timer stops running, eg:
class timer {
SDL_TimerID timerID = 0;
std::function<void()> timerFunc;
public:
timer() = default;
~timer();
void set(uint32_t interval, const std::function<void()> &fn);
void stop();
};
uint32_t wrapper(uint32_t interval, void *param) {
auto fn = static_cast<std::function<void()> *>(param);
(*fn)();
return interval;
}
timer::~timer() {
stop();
}
void timer::set(uint32_t interval, const std::function<void()> &fn) {
stop();
timerFunc = fn;
timerID = SDL_AddTimer(interval, wrapper, &timerFunc);
}
void timer::stop() {
if (timerID != 0) {
SDL_RemoveTimer(timerID);
timerID = 0;
}
}