C++ class member function and callback from C API

我只是一个虾纸丫 提交于 2019-12-05 14:19:40
Ben Voigt

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);

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);

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);
Michael Petrov

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!