Jump to content

Libcurl CURLOPT_WRITEFUNCTION callback function error

Go to solution Solved by Mr_KoKa,

DownloadFile::write_callback has to be static. Add static keyword before function return type (size_t) like so:

static size_t DownloadFile::write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)

 

I figured out how to get curl to POST and GET to a webpage. It was working fine and writing the stream to a file perfectly. Now I am trying to convert it to a class called DownloadFile. The end result being able to call member functions like:

download.HTTPPOST(http, postData, filename);

I have the following code in the HTTPPOST member function:

void DownloadFile::HTTPPOST(const char * http, const char *postData, std::string filePath)
{
    CURL *curl;
    CURLcode res;
    std::ofstream fout;
    fout.open(filePath, std::ofstream::out | std::ofstream::app);

    /* In windows, this will init the winsock stuff */
    curl_global_init(CURL_GLOBAL_ALL);

    /* get a curl handle */
    curl = curl_easy_init();
    if (curl) 
    {
        /* First set the URL that is about to receive our POST. This URL can
        just as well be a https:// URL if that is what should receive the
        data. */
        curl_easy_setopt(curl, CURLOPT_URL, http);

        /* Now specify the POST data */
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData);

        /* send all data to this function  */
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);

        /* we pass our 'chunk' struct to the callback function */
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &fout);

        /* Perform the request, res will get the return code */
        res = curl_easy_perform(curl);

        /* Check for errors */
        if (res != CURLE_OK)
            fprintf(stderr, "curl_easy_perform() failed: %s\n",
                curl_easy_strerror(res));

        /* always cleanup */
        curl_easy_cleanup(curl);
    }
    curl_global_cleanup();

    DownloadFile::setStatus(res);
}

This is the code I have for the write_callback member function:

size_t DownloadFile::write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
{
    std::ofstream *fout = (std::ofstream *)userdata;
    for (size_t x = 0; x < nmemb; x++)
    {
        *fout << ptr[x];
    }

    return size * nmemb;
}

When I try to build this I get an error:

error C3867: 'DownloadFile::write_callback': non-standard syntax; use '&' to create a pointer to member

Passing the write_callback function by address was working fine before? I did what it suggested '&' operator before the function and recived this error:

error C2276: '&': illegal operation on bound member function expression

I am at a loss trying to figure this out. Why doesn't it recognize the write_callback as an memory address? I am now under the impression that it doesn't have a memory address at compile time so it's confused or something? Any help would be appreciated.

Thanks.

Link to comment
Share on other sites

Link to post
Share on other sites

DownloadFile::write_callback has to be static. Add static keyword before function return type (size_t) like so:

static size_t DownloadFile::write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)

 

Link to comment
Share on other sites

Link to post
Share on other sites

2 minutes ago, Mr_KoKa said:

DownloadFile::write_callback has to be static. Add static keyword before function return type (size_t) like so:


static size_t DownloadFile::write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)

 

Thanks solved my problem. Everything is working perfectly now.

So was I right then? Static member functions are processed at compile time instead of runtime. So it wasn't recognizing the function as an address because it gets done at runtime?

Link to comment
Share on other sites

Link to post
Share on other sites

It is more like class members functions have implicit this parameter so it is like (Class *this, char *ptr, size_t size, size_t nmemb, void *userdata) when it is not static. There is not this in static method, so function declaration matches.

Link to comment
Share on other sites

Link to post
Share on other sites

5 minutes ago, Mr_KoKa said:

It is more like class members functions have implicit this parameter so it is like (Class *this, char *ptr, size_t size, size_t nmemb, void *userdata) when it is not static. There is not this in static method, so function declaration matches.

I think I get it. CURLOPT_WRITEFUNCTION is expecting a declaration of this format:
 

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

However within a nonstatic member function there is an extra parameter that is add to know which instance called it so its declaration is really:
 

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

So there is a mismatch in what it expects? If I understand that correctly it gives a bizarre error in response.

Link to comment
Share on other sites

Link to post
Share on other sites

You do understand it, but that implicit this argument is more like conceptual, and is not included into arguments list, it is there but it isn't : P Also wven if you could include it to your function declaration, libcurl expect you will privide size_t (*f)(char *, size_t, size_t, void *) not a size_t (*f)(SomeClass *, char *, size_t, size_t, void *)

So it wouldn't work anyway, there is a way to to use non-static callback function using std::function and std::bind (but libcurl would have make use of them I guess), but since your function has no use of your object you will be fine with static function, and even if you want to do anything with your object, you can attach it as userdata pointer, just reinterpret_cast<void*>(&classInstance) with https://curl.haxx.se/libcurl/c/CURLOPT_WRITEDATA.html and then

Class *classInstance = reinterpret_cast<Class*>(userdata);

Edit: Those reinterpret_cast<void*> are c++ cast operators can be replaced with (void*) c style casts.

Link to comment
Share on other sites

Link to post
Share on other sites

8 minutes ago, Mr_KoKa said:

You do understand it, but that implicit this argument is more like conceptual, and is not included into arguments list, it is there but it isn't : P Also wven if you could include it to your function declaration, libcurl expect you will privide size_t (*f)(char *, size_t, size_t, void *) not a size_t (*f)(SomeClass *, char *, size_t, size_t, void *)

So it wouldn't work anyway, there is a way to to use non-static callback function using std::function and std::bind (but libcurl would have make use of them I guess), but since your function has no use of your object you will be fine with static function, and even if you want to do anything with your object, you can attach it as userdata pointer, just reinterpret_cast<void*>(&classInstance) with https://curl.haxx.se/libcurl/c/CURLOPT_WRITEDATA.html and then


Class *classInstance = reinterpret_cast<Class*>(userdata);

 

Ahh, ya, I don't know a thing about templates yet and that sounded really confusing haha

Thanks for the info. I will keep this in mind when I learn templates. Maybe I will get that method you described to work as practice.

Link to comment
Share on other sites

Link to post
Share on other sites

As I said curlib would need to make use of std::function too, I guess it won't work. 

Link to comment
Share on other sites

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×