【问题标题】:std::function.target returns nullstd::function.target 返回 null
【发布时间】:2014-08-14 00:29:44
【问题描述】:

我正在使用 cURL 通过 http 下载文件。 cURL 需要回调来处理数据,我的类中有一个回调,我正在使用 std::bind 和 std::function 的组合来创建具有正确类型的函数。

size_t NetworkResource::writeFunction(char *ptr,size_t size,size_t nmemb,void *userdata)
{
...
}

void NetworkResource::loadFunction(void)
{
    using namespace std::placeholders;
    typedef size_t CurlCallback(char*,size_t,size_t,void*);
    auto function=std::function<CurlCallback>(std::bind(&NetworkResource::writeFunction,this,_1,_2,_3,_4)).target<CurlCallback*>();
    CURL *curl=curl_easy_init();
    CURLcode err;

    ...

    err=curl_easy_setopt(curl,CURLOPT_WRITEDATA,nullptr);
    if(err!=CURLE_OK) std::cout<<curl_easy_strerror(err)<<std::endl;

    err=curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,*function);
    if(err!=CURLE_OK) std::cout<<curl_easy_strerror(err)<<std::endl;

    ...
}

问题是函数为空。根据文档,当返回的类型与函数的目标类型不匹配时会发生这种情况,但据我所知它们确实匹配。

【问题讨论】:

  • 如果这是您尝试将成员函数转换为静态函数的方式,它是行不通的。您是否有理由不 CURLOPT_WRITEDATAthis 传递给静态回调,该回调在解压缩指针后将调用转发给非静态成员? (see similar question).
  • 为什么这不起作用? std::function.target 返回一个指向函数指针的指针。

标签: c++ c++11 curl


【解决方案1】:
auto function = std::function<CurlCallback>(
                  std::bind(&NetworkResource::writeFunction,this,
                             _1,_2,_3,_4)).target<CurlCallback*>();

如果对std::function::target 的调用按预期工作,您正在构建的std::function 对象将在上述表达式的末尾被销毁,并且变量function 将指向无效内存。在这种情况下,它不会,并且函数调用返回nullptr

这是因为std::function 的目标函数类型与CurlCallback 的类型不同。 This example 显示了该调用有效和失败的情况。


完全不用std::function就可以解决您的问题。

根据curl_easy_setopt的文档,当第二个参数是CURLOPT_WRITEFUNCTION时,第三个参数应该是指向具有签名的函数的指针

size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata);

最后一个参数userdata 可以通过调用CURLOPT_WRITEDATA 来设置。使用它来传递指向NetworkResource 实例的指针(this 指针)。

至于write_callback,创建一个静态成员函数来执行你需要的功能。

class NetworkResource
{
  // ...
  static size_t writeFunction(char *ptr,size_t size,size_t nmemb,void *userdata);
};

size_t NetworkResource::writeFunction(char *ptr,size_t size,size_t nmemb,void *userdata)
{
  // userdata points to the NetworkResource instance
  auto res = static_cast<NetworkResource *>(userdata);

  // use res and the remaining function arguments to handle the call
}

void NetworkResource::loadFunction(void)
{
    CURL *curl=curl_easy_init();
    CURLcode err;

    ...

    err=curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,&NetworkResource::writeFunction);
    if(err!=CURLE_OK) std::cout<<curl_easy_strerror(err)<<std::endl;

    err=curl_easy_setopt(curl,CURLOPT_WRITEDATA,static_cast<void *>(this));
    if(err!=CURLE_OK) std::cout<<curl_easy_strerror(err)<<std::endl;

    ...
}

【讨论】:

  • 我没有将 std::function 传递给 curl_easy_setopt,而是传递了一个函数指针。 std::function.target 返回一个指向函数指针的指针。
【解决方案2】:

今天我遇到了这个问题,我可以建议你一个解决方案: 这是我的演示

 #include <iostream>
 #include <stdio.h>
 #include <stdlib.h>
 #include <algorithm>
 #include <thread>
 #include <unistd.h>
 #include <string.h>
 #include <functional>
 using namespace std;

 class foo {
 public:
     virtual int sum(int a, int b) { return -1; };
 };

 class bar : public foo{
 public:
     virtual int sum(int a, int b) override;
 };

 int bar::sum(int a, int b) {
     return a + b;
 }

 int main() {
     bar *b = new bar();
     std::function<int(int, int)> f = std::bind(&bar::sum, b, std::placeholders::_1, std::placeholders::_2);
     printf("result : %d\n", f(1, 3));
     printf("result : %s\n", f.target_type().name());
     printf("null : %d\n", (*f.target<int(int, int)>())(1, 2));
     printf("null : %d\n", f);
     return 0;
 }

在这个demo中,如果你把type int(int, int)写成std::function,像这样

std::function<int(int, int)>

返回类型必须为null,因为int(int, int)不等于内部类型

要获取std::function的内部类型,你应该首先使用target_type().name(),像这样:

printf("%s\n", f.target_type().name());

结果是:

St5_BindIFSt7_Mem_fnIM3barFiiiEEPS1_St12_PlaceholderILi1EES6_ILi2EEEE

接下来,您可以使用c++filt 来解析此符号,如下所示:

c++filt -t St5_BindIFSt7_Mem_fnIM3barFiiiEEPS1_St12_PlaceholderILi1EES6_ILi2EEEE

结果是:

std::_Bind<std::_Mem_fn<int (bar::*)(int, int)> (bar*, std::_Placeholder<1>, std::_Placeholder<2>)>

上面的符号就是std::function&lt;...&gt;中应该写的,所以最终的代码是这样的:

int main() {
     bar *b = new bar();
     std::function<int(int, int)> f = std::bind(&bar::sum, b, std::placeholders::_1, std::placeholders::_2);
     printf("result : %d\n", f(1, 3));
     printf("result : %s\n", f.target_type().name());
     printf("null : %d\n", (*f.target<std::_Bind<std::_Mem_fn<int (bar::*)(int, int)> (bar*, std::_Placeholder<1>, std::_Placeholder<2>)>>())(1, 2));
     printf("null : %d\n", f);
     return 0;
 }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-08-16
    • 1970-01-01
    • 2015-11-27
    • 2021-05-27
    • 1970-01-01
    • 2018-03-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多