lambda对象+ c回调sigsegv

use*_*108 5 c c++ lambda c++11

如果我实现这样的C回调:

register_callback([](/*some args*/){/*some stuff*/});
Run Code Online (Sandbox Code Playgroud)

触发时我得到一个SIGSEGV,但如果我这样注册:

auto const f([](/*some args*/){/*some stuff*/});

register_callback(f);
Run Code Online (Sandbox Code Playgroud)

然后它工作正常.(对我来说)特别感兴趣的是地址消毒剂产生的堆栈跟踪:

ASAN:SIGSEGV
=================================================================
==22904==ERROR: AddressSanitizer: SEGV on unknown address 0x7f1582c54701 (pc 0x7f1582c54701 sp 0x7f1582c544a8 bp 0x7f1582c54510 T2)
    #0 0x7f1582c54700 ([stack:22906]+0x7fc700)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV ??:0 ??
Run Code Online (Sandbox Code Playgroud)

它看起来好像函数指针指向堆栈.将lambda推入堆栈会将代码压入堆栈吗?由于我没有捕获任何函数指针的位置对我来说是一个谜.怎么了?没有使用优化标志.我不是在寻找解决方法.

编辑:显然'+'是一个工作示例的关键.我不知道为什么有必要.使用compile删除'+'和示例,但将使用clang-3.5和触发SIGSEGV gcc-4.9.

#include <curl/curl.h>

#include <ostream>

#include <iostream>

int main()
{
  auto const curl(curl_easy_init());

  if (curl)
  {
    curl_easy_setopt(curl, CURLOPT_URL, "cnn.com");

    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);

/*
    auto const f([](char* const ptr, size_t const size, size_t const nmemb,
      void* const data)
      {
        *static_cast<::std::ostream*>(data) << ptr;

        return size * nmemb;
      }
    );

    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, +f);
*/

    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
      +[](char* const ptr, size_t const size, size_t const nmemb,
        void* const data)
        {
          *static_cast<::std::ostream*>(data) << ptr;

          return size * nmemb;
        }
    );

    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &::std::cout);

    curl_easy_perform(curl);

    curl_easy_cleanup(curl);
  }

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

eca*_*mur 5

curl_easy_setopt定义为(in curl/easy.h):

CURL_EXTERN CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...);
Run Code Online (Sandbox Code Playgroud)

That means that the third argument param must be of a type that can be passed as a C variadic. Unfortunately, while curl_easy_setopt is expecting a function pointer, passing class objects (and lambdas are class objects) is "conditionally-supported with implementation-defined semantics" ([expr.call]/7), so the compiler accepts it but then curl_easy_setopt tries to interpret the lambda object as a function pointer, with catastrophic results.

The object that you actually pass is a captureless lambda which means that it is an empty class object, of size 1 byte (all most-derived objects must be at least one byte in size). The compiler will promote that argument to a word-size integer (4 bytes on 32-bit, 8 bytes on 64-bit) and either pass 0 or leave that register/stack slot unset, meaning that garbage gets passed (since the lambda doesn't actually use its memory footprint when called).

  • @ user1095108如果`param`是一个类型化的参数,但是因为它是可变参数,所以你有责任确保传递一个与curl期望的类型完全相同的参数. (4认同)