lambda object + c callback sigsegv
curl_easy_setopt
is defined as (in curl/easy.h
):
CURL_EXTERN CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...);
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).
I've just write a similar lambda with libcurl, and got crash, after checking carefully, I got the following code working like charm.
The magic is, add the leading +
at the non-captured lambda expression, which will trigger conversion to plain C function pointer.
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
/* NOTE: Leader '+' trigger conversion from non-captured Lambda Object to plain C pointer */
+[](void *buffer, size_t size, size_t nmemb, void *userp) -> size_t {
// invoke the member function via userdata
return size * nmemb;
});
My understand is, the curl_easy_setopt()
wants a void*
, not an explicit function type, so the compiler just gives the address of lambda OBJECT; if we do function pointer operation on lambda object, the compiler will return the function pointer from lambda object.