11

I tried to use a C++11 lambda expression with CURLOPT_WRITEFUNCTION, but the program crashes at runtime with an access violation. I'm not sure how to look further into this due to lack of C++11 knowledge, but maybe someone else has an idea how to make this work.

The function:

#ifndef CURL_GET_H
#define CURL_GET_H

#include <curl/curl.h>
#include <curl/easy.h>
#include <vector>
#include <string>

std::vector<std::string> curl_get(const char* url)
{
    CURL *curl;
    CURLcode res;

    std::vector<std::string> content;
    auto curl_callback = [](void *ptr, size_t size, size_t nmemb, void *stream) -> size_t {
        // does nothing at the moment due to testing...
        return size * nmemb;
    };

    curl = curl_easy_init();
    if (curl)
    {
        curl_easy_setopt(curl, CURLOPT_URL, "http://localhost/aaa.txt");
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_callback);
        res = curl_easy_perform(curl);
        curl_easy_cleanup(curl);
    }

    return content;
}

#endif // CURL_GET_H

The error:

Unbehandelte Ausnahme bei 0x000000cc in lpip_dl.exe: 0xC0000005: Zugriffsverletzung bei Position 0x00000000000000cc.

(Access violation at position 0x00000000000000cc)

Happens when curl wants to use the callback:

wrote = data->set.fwrite_func(ptr, 1, len, data->set.out);
Community
  • 1
  • 1
Strayer
  • 3,050
  • 3
  • 30
  • 40
  • What compiler are you using? VC++? If so, VC++ does not support implicit lambda-to-function-pointer conversions (yet?). – ildjarn Jul 08 '11 at 13:46
  • Yes, I'm using VS2010 SP1. I already noticed the other questions covering lambda functions and function pointers but couldn't see the connection to my question. I guess I'll just have to wait for the next VS release :/ The workarounds were quite... confusing. – Strayer Jul 08 '11 at 14:03

4 Answers4

8

You actually can do that by casting the lambda function to function pointer. You can first make a typedef to make cast easier.

typedef size_t(*CURL_WRITEFUNCTION_PTR)(void*, size_t, size_t, void*);

Then you use static_cast.

curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, static_cast<CURL_WRITEFUNCTION_PTR>(curl_callback));

Note: In order to convert to C function pointer, you can only use empty captures [].

  • I guess that it is a luckly coincidence that the lambda function class has a function pointer as first and only field. – André Puel Feb 07 '13 at 03:38
  • Funny. sizeof empty captured lambda function is 1. While sizeof reference captured lambda function is 8. – André Puel Feb 07 '13 at 03:41
  • I gotta correct myself. I did take a look at the generated assembly. The static_cast is not resolved at compile time, it invokes a function at runtime which "converts" the lambda to a function pointer. – André Puel Feb 07 '13 at 04:41
  • 3
    @AndréPuel All stateless lambda functions have an implicit operator convert-to-function-pointer. In every compiler with calling convention support I have played with, it even supports convert-to-arbitrary-calling-convention-function-pointer. You don't even have to cast explicitly, you can just initialize a variable of the appropriate type. The casting is required because without it, you stick the stateless lambda directly on the stack, not the function pointer the engine expects. See [http://stackoverflow.com/questions/25513380/c-style-callbacks-in-c11/25533447#25533447] – Yakk - Adam Nevraumont Aug 27 '14 at 19:56
5

This can be done with the + prefix, which returns a C-style function pointer instead. But this only works with stateless lambdas (empty capture list i.e. []).

auto lambda = +[](void *ptr, size_t size, size_t nmemb, void *stream) -> size_t {
    // does nothing at the moment due to testing...
    return size * nmemb;
};

or

curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, +[](void *ptr, size_t size, size_t nmemb, void *stream) -> size_t {
    // does nothing at the moment due to testing...
    return size * nmemb;
});
Stefan Lazovy
  • 61
  • 1
  • 2
3

libcurl is plain C library, you need to set a callback that can be called from a such. This means funny C++ things need to be "C'ified" first to work. Like into an old-style function pointer.

This is also addressed in the libcurl FAQ entry "Using C++ non-static functions for callbacks?"

See also: C-style Callbacks in C++11

Daniel Stenberg
  • 54,736
  • 17
  • 146
  • 222
0

Casting the lambda to the proper function pointer works for me in Linux:

auto curl_callback = static_cast<size_t(*)(char*,size_t,size_t,void*)>([](...){...});