4

I am trying to learn how to call this write_data(…) function from the funmain() function in the class as shown in the code bellow. (I know this program works if I just list these two functions without putting it inside a class).

curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data) line gives me error and wouldn’t let me call the write_data(…) function. Can you please correct my code and tell me how I can achieve this. Any help would be greatly appreciated. Thanks.

error C3867: 'go_website::write_data': function call missing argument list; use '&go_website::write_data' to create a pointer to member

//Microsoft Visual Studio 10 in C++
#define CURL_STATICLIB
#include <stdio.h>
#include <curl/curl.h>
#include <curl/types.h>
#include <iostream>
#include <curl/easy.h>
#include <string>
using namespace std;

extern "C" typedef size_t curl_write_callback(void *ptr, size_t size, size_t nmemb, FILE *stream);
class go_website
{
public:
static curl_write_callback write_data;

void funmain()
{
    CURL *curl;
    FILE *fp;
    CURLcode res;
    char *url = "http://www.shorturl.com/";
    char outfilename[FILENAME_MAX] = "C:\\bbb.txt";
    curl = curl_easy_init();
    if (curl) {
        fp = fopen(outfilename,"wb");
        curl_easy_setopt(curl, CURLOPT_URL, url);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &write_data); 
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fp);
        res = curl_easy_perform(curl);
        curl_easy_cleanup(curl);
        fclose(fp);
    }
}};

extern "C" size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) 
{
size_t written;
written = fwrite(ptr, size, nmemb, stream);
return written;
}

int main()
{
   go_website a;
   a.funmain();
   return 0;
}
Learner_51
  • 1,075
  • 3
  • 22
  • 38
  • Please post the exact error message you are receiving. Saying "this line gives me error" doesn't help anyone. – Marlon Feb 12 '12 at 05:43

4 Answers4

10

It is possible http://curl.haxx.se/docs/faq.html#Using_C_non_static_functions_f

 // f is the pointer to your object.
 static YourClass::func(void *buffer, size_t sz, size_t n, void *f)
 {
   // Call non-static member function.
   static_cast<YourClass*>(f)->nonStaticFunction();
 }
 // This is how you pass pointer to the static function:
 curl_easy_setopt(hcurl, CURLOPT_WRITEFUNCTION, YourClass:func);
 curl_easy_setopt(hcurl, CURLOPT_WRITEDATA, this);
Evalds Urtans
  • 6,436
  • 1
  • 41
  • 31
  • 1
    Brilliant, a pointer to the FAQ. I hope that was added after this question. Also, I suspect Ben Voigt could explain why this violates the standard(s) in five different ways :) But it worked on my machine {tm} – Móż Oct 08 '13 at 01:14
2

These two lines won't work:

curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fp);

The second is the easiest to fix: fp is a file pointer, not a function, you're setting the wrong attribute, I guess you want CURLOPT_WRITEDATA.

For the callback function, you need a function pointer. The name of an ordinary function automatically decays to its address, although using the address-of operator (&functionname) is cleaner.

Class member functions do not automatically decay. In fact, a non-static class member function is totally incompatible with a normal function pointer, since there's no way to handle this. Luckily you don't need a non-static member function, since no non-static members are used inside the callback.

Make the callback function static andextern "C":

extern "C" typedef size_t curl_write_callback(void *ptr, size_t size, size_t nmemb, FILE *stream);

class go_website
{
public:
    static curl_write_callback write_data;

    // ...
};

extern "C" size_t go_website::write_data(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
   size_t written;
   written = fwrite(ptr, size, nmemb, stream);
   return written;
}

and then take its address, using the address-of operator and fully-qualified function name:

curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &go_website::write_data);

This part was even explained in the error message. But it didn't tell you that you needed static or extern "C", this is the problem with variable argument lists, they aren't typesafe.

After reading the Standard (section 7.5 [dcl.link], and especially paragraph 4 and its examples), this isn't allowed. The member function still has C++ language linkage, for both its name (not important) and its type (this is the deal-breaker).

You have to use a global function for the callback:

extern "C" size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
   size_t written;
   written = fwrite(ptr, size, nmemb, stream);
   return written;
}

and then pass a pointer to it:

curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &write_data);
Community
  • 1
  • 1
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • @Bet Voigt Thanks a lot for all the information. It complied fine and run. But while running, it crashed and give me the following error: `Unhandled exception at 0x56abe4f8 (msvcr100d.dll) in WebRead.exe: 0xC0000005: Access violation.` I can't seems to use `CURLOPT_WRITEDATA`. It says `identifier CURLOPT_WRITEDATA is undefined` – Learner_51 Feb 12 '12 at 06:01
  • @NewGuy: I just fixed some stuff, which may or may not make any difference. – Ben Voigt Feb 12 '12 at 06:05
  • @NewGuy: Also, the documentation says that option used to be called `CURLOPT_FILE`. – Ben Voigt Feb 12 '12 at 06:07
  • @Bet Voigt when I use `typedef extern "C" ....` at the top, it says `Error: linkage specification is not allowed` under the word extern – Learner_51 Feb 12 '12 at 06:19
  • @NewGuy: Try turning it around so it's `extern "C" typedef` instead? – Ben Voigt Feb 12 '12 at 06:26
  • @NewGuy: Actually, after reading the standard, it's impossible for a member function to have "C" language linkage, which the callback requires. So you have to use a global function for this :( – Ben Voigt Feb 12 '12 at 06:29
  • @Bet Voigt I have updated my original code and that method didn’t work. But Thanks for all the information and your time. I really appreciate it. – Learner_51 Feb 12 '12 at 06:52
  • @NewGuy: In my revised answer, you don't need a typedef at all. Just move the `extern "C"` function before the class definition, and things should be working. – Ben Voigt Feb 12 '12 at 06:58
1

I know this program works if I just list these two functions without putting it inside a class

If it works outside of a class, but not inside, then you likely need to use the "this" pointer.

        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, this->write_data);
Glen Nelson
  • 333
  • 2
  • 13
0

The problem is that you want the WRITEFUNCTION to be a member function of your class. However, curl doesn't know which instance of the class to call it on. You need to create a static function that you pass to WRITEFUNCTION and then pass your this pointer as the CURLOPT_WRITEDATA parameter. Then in your static member function you can use the user_data pointer (which is your "this" from WRITE_DATA) as the instance of the class.

Maybe this question will help: curl WRITEFUNCTION and classes

Community
  • 1
  • 1
Michael Petrov
  • 2,247
  • 15
  • 15