用于包装采用 void* 参数的 C 回调的模板魔术?

2024-01-09

假设我正在使用 C API,它可以让您注册回调函数void*关闭:

void register_callback(void (*func)(void*), void *closure);

在 C++ 中,拥有比void*所以我想创建一个包装器,让我注册强类型的 C++ 回调:

template <typename T, void F(T*)>
void CallbackWrapper(void *p) {
  return F(static_cast<T*>(p));
}

void MyCallback(int* param) {}

void f(void *closure) {
  register_callback(CallbackWrapper<int, MyCallback>, closure);
}

这工作正常。该解决方案的一个很好的特性是它可以将我的回调内联到包装器中,因此该包装方案的开销为零。我认为这是一个要求。

但如果我能让 API 看起来更像这样就好了:

void f2() {
  RegisterCallback(MyCallback, closure);
}

我希望能够通过推断模板参数来实现上述目的。但我不太清楚如何让它发挥作用。到目前为止我的尝试是:

template <typename T>
void RegisterCallback(void (*f)(T*), T* closure) {
  register_callback(CallbackWrapper<T, f>, closure);
}

但这行不通。任何人都有一个神奇的咒语,可以使f2()工作上面,同时保留零开销性能特征?我想要一些可以在 C++98 中工作的东西。


该模板函数稍微改进了语法。

template <typename T, void F(T*)>
void RegisterCallback (T *x) {
    register_callback(CallbackWrapper<T, F>, x);
}

int x = 4;
RegisterCallback<int, MyCallback>(&x);

如果您愿意使用函子而不是函数来定义回调,那么您可以进一步简化事情:

#ifdef HAS_EXCEPTIONS
# define BEGIN_TRY try {
# define END_TRY } catch (...) {}
#else
# define BEGIN_TRY
# define END_TRY
#endif

template <typename CB>
void CallbackWrapper(void *p) {
    BEGIN_TRY
    return (*static_cast<CB*>(p))();
    END_TRY
}

struct MyCallback {
    MyCallback () {}
    void operator () () {}
};

template <typename CB>
void RegisterCallback (CB &x) {
    register_callback(CallbackWrapper<CB>, &x);
}

MyCallback cb;
RegisterCallback(cb);

但是,正如其他人所提到的,您面临着代码无法正确移植到 C ABI 和 C++ ABI 不同的系统的风险。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

用于包装采用 void* 参数的 C 回调的模板魔术? 的相关文章

随机推荐