C++11中的C风格回调
C-style Callbacks in C++11
在一个C++11项目中,我使用了一个C风格的第三方库(在我的例子中是curl),它需要C风格的回调。
为了实现这一点,我使用了"poiner-To-member"操作符:
size_t c_callback_wrapper(char *ptr, size_t size, size_t nmemb, void *userdata)
{
MyClass *p = (MyClass*)userdata;
return (p->*&MyClass::actualCallback)(ptr, size, nmemb, userdata);
}
void Myclass::performSomething() {
// register callback function
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, c_callback_wrapper);
// register userdata
curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);
...
res = curl_easy_perform(curl);
}
这个解决方案确实有效,但对我来说并不令人满意
我实际想做的是在注册函数中编写一个lambda。
一般来说,优势在于局部性:我可以捕获一些局部变量,并且不需要编写复杂的"actualCallback"成员例程。
我已经读到,当Lambdas捕获变量时,似乎不可能将其用作C样式函数。
有什么方法可以通过一些技巧来实现这一点吗?(例如,使用lambdas的调用约定,…)
感谢
捕获上下文变量的lambda不能转换为空函数指针,因为这将使捕获的状态无法携带。您在示例中展示的是处理通过C回调调用C++成员函数问题的正确方法。
如果你觉得c_callback_wrapper
更有吸引力的话,你可以用一个无捕获的lambda来代替它。
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
[](char *ptr, size_t size, size_t nmemb, void *userdata) {
// invoke the member function via userdata
auto p = static_cast<MyClass *>(userdata);
return p->actualCallback(ptr, size, nmemb, userdata);
});
请注意,您可能应该去掉actualCallback()
成员函数的最后一个参数,因为这只是this
指针,非静态成员函数不需要显式传递该指针。
下面是我要做的(请注意,我对C++的了解只够偶尔吹一下):
#include <iostream>
#include <functional>
void external_c_function(void cb(void *), void *userdata)
{
cb(userdata);
}
void c_callback_wrapper(void *userdata)
{
auto &lambda = *static_cast< std::function<void(void)>* >(userdata);
std::cout << "calling lambda" << std::endl;
lambda();
}
int main(void)
{
int foo = 42;
std::function<void(void)> lambda = [&] { std::cout << foo << std::endl; };
external_c_function(c_callback_wrapper, &lambda);
return 0;
}
template<class T>using type=T; // makes some declarations easier
template<class F>
struct callback_t;
template<class F, class Sig>
struct cast_helper_pvoid_last;
template<class F, class R, class... Args>
struct cast_helper_pvoid_last<F, R(Args...)> {
type<R(*)(Args..., void*)> operator()() const {
return [](Args... args, void* pvoid)->R {
auto* callback = static_cast<callback_t<F>*>(pvoid);
return callback->f( std::forward<Args>(args)... );
};
}
};
template<class F>
struct callback_t {
F f;
void* pvoid() { return this; }
template<class Sig>
auto pvoid_at_end()->decltype( cast_helper_pvoid_last<F, Sig>{}() ) {
return cast_helper_pvoid_last<F,Sig>{}();
}
};
template<class T>using decay_t=typename std::decay<T>::type;
template<class F>
callback_t<decay_t<F>> make_callback( F&& f ) { return {std::forward<F>(f)}; }
示例用法:
int x = 3;
auto callback = make_callback( [&]( int y ) { return x+y; } );
int (*func)(int, void*) = callback.pvoid_at_end<int(int)>();
std::cout << func( 1, callback.pvoid() ) << "n";
应打印4。(实例)
callback_t
的生存期必须超过它生成的pvoid
。
我可以自动推导函数指针的签名,而不需要传递<int(int)>
(没有void*
的签名),但这再次使代码变得更容易。
如果你想让void*
成为第一个,就加上这个:
template<class F, class Sig>
struct cast_helper_pvoid_first;
template<class F, class R, class... Args>
struct cast_helper_pvoid_first<class F, R(Args...)> {
type<R(*)(void*, Args...)> operator()() const {
return [](void* pvoid, Args... args)->R {
auto* callback = static_cast<callback<F>*>(pvoid);
return callback->f( std::forward<Args>(args)... );
};
}
};
// inside the callback_t<?> template:
template<class Sig>
auto pvoid_at_start()->decltype( cast_helper_pvoid_first<F, Sig>{}() ) {
return cast_helper_pvoid_first<F,Sig>{}();
}
在中间执行void*
会变得更加棘手。
如果您有调用约定问题,那么我们需要使用帮助对象
让pvoid_at_end
返回一个cast_helper_pvoid_last
,而不是在其上调用()
。
然后,添加operator
重载以强制转换到您需要支持的每个调用约定的函数指针。主体与operator()
相同,因为lambda应该支持其中任何一个。
或者,通过一些C++14支持,您可以将operator()
的返回类型更改为auto
,并在其他方面保持代码不变,并依靠lambda的直接转换来获得正确的调用约定。
- 架构决策:返回std::future还是提供回调
- 正在为Xtensa simcall函数编写回调函数
- 如何在C++中使用非静态成员函数作为回调函数
- FLTK:按下哪个按钮 - 将数字传递给按钮的回调 (lambda)
- 在简单示例中,Python3 + ctypes 回调会导致内存泄漏
- 用于在回调中调用解析器的设计模式
- 如何使用C++对象的成员函数作为 C 样式回调?
- Java从C++回调到C++回调
- 如何将成员函数作为回调参数传递给需要"typedef-ed"自由函数指针的函数?
- 从不同的 cpp 调用回调函数会导致bad_function_call
- pcap_handler回调仅在使用 NPCAP v0.9991 时包含空数据包
- 不带轮询的 SDL2 事件回调
- C++存储带有可变参数的回调
- 如何使用 Node-addon-API 实现 node-nan 回调
- 处理影响跨不同线程共享对象的定时回调的最佳方法是什么?
- 访问类C++ C 样式回调
- 处理类内的回调时,必须调用对非静态成员函数的引用
- C++11风格的回调
- 如何在带有swig的c中使用c++11风格的回调
- C++11中的C风格回调