将 const std::function<void()> 作为 void* 作为 void* 中的参数传递SDL_AddTimer [closed]

Pass const std::function<void()> as parameter in SDL_AddTimer as void* [closed]

提问人:Rodrigo 提问时间:10/31/2023 最后编辑:Rodrigo 更新时间:10/31/2023 访问量:63

问:


编辑问题以包括所需的行为、特定问题或错误以及重现问题所需的最短代码。这将帮助其他人回答这个问题。

23天前关闭。

我正在尝试将 a as 传递给 SDL_AddTimer,它的第三个参数是 .const std::function<void()> &void *void *

我试过reinterpret_cast

SDL_AddTimer(interval, wrapper, reinterpret_cast<void *>(const_cast<std::function<void()> *>(fn)));

但是在我的包装纸上,它崩溃了

uint32_t wrapper(uint32_t interval, void *param) {
  auto fn = static_cast<std::function<void()> *>(param);
  (*fn)(); // crashes here.
  return interval;
}

我做错了什么?

编辑:用法:

timer t;
t.set(1000, []() {
  std::cout << "Hello, world!" << std::endl;
});
C++ SDL

评论

1赞 463035818_is_not_an_ai 10/31/2023
在哪里?如果不是指针,则没有意义。请发布一个最小的可重现示例const std::function<void()> &fnconst_cast<std::function<void()> *>(fn)
1赞 NathanOliver 10/31/2023
什么是寿命?如果它超出范围,则您正在访问悬空指针。fn
0赞 Rodrigo 10/31/2023
我已经更新了用法,fn 应该具有与计时器类相同的寿命

答:

1赞 Remy Lebeau 10/31/2023 #1

你不能像你正在做的那样将引用转换为指针(你甚至不能像你正在做的那样将引用转换为指针)。std::functionvoid*

但是,您可以传递对象本身的地址。无论如何,这就是您的期望,例如:std::functionwrapper

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;
}

只需确保对象在 SDL 计时器运行时保持活动状态即可。但是,根据您的使用示例,该对象似乎可能在计时器有机会调用它之前就超出了范围。因此,您必须将对象保存在某个地方,直到计时器停止运行,例如:std::functionstd::functionstd::function

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;
    }
}

评论

0赞 Rodrigo 10/31/2023
它完美无缺,谢谢。一个问题,我可以传递 fn 作为参考还是应该传递副本?
1赞 Remy Lebeau 10/31/2023
@Rodrigo您必须传递一份副本,因为原件是临时的。我已经更新了我的示例来展示这一点。fn