C++ C++;11

C++ C++;11,c++,c++11,curl,lambda,callback,C++,C++11,Curl,Lambda,Callback,在一个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)(p

在一个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++成员函数的问题。p> 如果您觉得更吸引人的话,可以用无捕获lambda替换
c\u callback\u wrapper

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);
                 });
curl\u easy\u setopt(curl,CURLOPT\u WRITEFUNCTION,
[](char*ptr、size\u t size、size\u t nmemb、void*userdata){
//通过userdata调用成员函数
自动p=静态_转换(用户数据);
返回p->actualCallback(ptr、size、nmemb、userdata);
});
请注意,您可能应该去掉
actualCallback()
成员函数的最后一个参数,因为这只是
this
指针,非静态成员函数不需要显式传递该指针。

下面是我要做的(请注意,我只知道C++对偶尔的脚的吹嘘):

#包括
#包括
void外部函数(void cb(void*),void*用户数据)
{
cb(用户数据);
}
void c_回调_包装(void*userdata)
{
auto&lambda=*static_cast(userdata);
标准::cout

在中间做一个<>空> */c>比较棘手。

如果您有调用约定问题,那么我们需要使用helper对象

pvoid\u在_末端
返回一个
cast\u helper\u pvoid\u last
,而不是对其调用
()

然后,添加
操作符
重载以强制转换到需要支持的每个调用约定的函数指针。主体与
操作符()相同,因为lambda应该支持其中任何一个


或者,通过一些C++14支持,您可以更改
操作符()的返回类型
auto
,并保持代码的完整性,依靠lambda的直接转换来获得正确的调用约定。

我已经尝试过这一点,但是我的应用程序最终陷入了一个segfault-即使我尝试使用非捕获lambda…将第三方c风格库与直接的c++11代码结合起来总是会杀死b很多代码…我讨厌它…:-(@rralf上面的解决方案,所以可能你还有其他事情要做。如果你使用的是MSVC,请确保你匹配C库使用的调用约定。哦,谢谢你,使用cast它是有效的!(似乎gcc也需要cast来调用约定)这应该只在lambda不捕获任何变量的情况下才有效!是的,我知道。如果在使用捕获时有一些技巧使其工作,那就太好了。。。
#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";
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>{}();
  }