c++libcurl

Sending lambda to CURLOPT_WRITEFUNCTION crashes?


I have the following code:

std::string stream;

curl_easy_setopt(curl, CURLOPT_WRITEDATA, & stream);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, [](void *buffer, size_t size, size_t count, std::string *stream) -> size_t {
  auto s_size = size * count;
  stream->append((char *)buffer, 0, s_size);
  return s_size;
});

It compiles and it crashes. If I replace the lambda by a function, it works fine:

static size_t _writefunction(void *buffer, size_t size, size_t count, std::string *stream) {
  auto s_size = size * count;
  stream->append((char *)buffer, 0, s_size);
  return s_size;
}

Is there any way to use a lambda instead of a separated function?


Solution

  • curl_easy_setopt is documented with the signature

    CURLcode curl_easy_setopt(CURL *handle, CURLoption option, parameter);
    

    where the parameter is allowed to be one of several types depending on the option. Clearly, (as a C API, where C has no overloading) it must be doing something strange, and this is borne out by looking at the source, where we see curl_easy_setopt is implemented with varargs.

    CURL_EXTERN CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...);
    

    So the problem with passing a lambda to curl_easy_setopt is that it is getting passed in as is, since curl_easy_setopt says it can take objects of any type. The actual object represented by a lambda expression is just a struct of the captures; in this case, you're just passing an empty struct object to curl_easy_setopt when it is expecting a function pointer. Chaos ensues.

    Lambdas with no captures may be explicitly converted to function pointers by using unary +.

    //                                            v one char fix!
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, +[](void *buffer, size_t size, size_t count, std::string *stream) -> size_t {
      auto s_size = size * count;
      stream->append((char *)buffer, 0, s_size);
      return s_size;
    });
    

    This conversion can also happen implicitly, but curl_easy_setopt doesn't provide any type information and thus doesn't trigger the conversion automatically.