简体   繁体   中英

QObject derived class callback function to a c library

I am trying to wrap libcurl as a helper QObject class. Unfortunately I am getting a mysterious segfault which does not happen when the exact same code is placed outside a class.

Sample code:

classless working code

//main.cpp
size_t writeCallback(char* ptr, size_t size, size_t nmemb, void* up)
{
    size_t data_size = size * nmemb;
    QByteArray *data = static_cast<QByteArray*>(up);
    data->append(ptr, data_size);
    return data_size;
}

int main(int argc, char *argv[]) {

    QCoreApplication a(argc, argv);  

    QByteArray buffer;  
    CURL *curl;
    CURLcode res;

    curl = curl_easy_init();
    if (curl) {
        curl_easy_setopt(curl, CURLOPT_URL, "http://cnn.com");
        /* example.com is redirected, so we tell libcurl to follow redirection */
        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);

        /* 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);
    }
    qDebug() << buffer;
    return a.exec();
}

QObject wrapper which segfaults

//Http.h 
class Http : public QObject
{
    Q_OBJECT
public:
    Http();
    void download();
signals:
    void finished(const QByteArray &buffer);
private:
    size_t writeCallback(char* ptr, size_t size, size_t nmemb, void* up);
};

//Http.cpp
Http::Http()
{

}

void Http::download()
{
    QByteArray buffer;

    CURL *curl;
    CURLcode res;

    curl = curl_easy_init();
    if (curl) {
        curl_easy_setopt(curl, CURLOPT_URL, "http://cnn.com");
        /* example.com is redirected, so we tell libcurl to follow redirection */
        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &Http::writeCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);

        /* 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);
    }

    qDebug() << buffer;
    emit finished(buffer);
}

size_t Http::writeCallback(char* ptr, size_t size, size_t nmemb, void* up)
{
    size_t data_size = size * nmemb;

    QByteArray *data = static_cast<QByteArray*>(up);
    data->append(ptr, data_size); //<--SEGFAULTS

    return data_size;
}

//main.cpp
int main(int argc, char *argv[]) {

    QCoreApplication a(argc, argv);  
    Http http;
    http.download();
    return a.exec();
}

The line that segfaults is present in Http::writeCallback(char* ptr, size_t size, size_t nmemb, void* up)

valgrind output

    ==11246== Invalid read of size 1
    ==11246==    at 0x4C2D7A2: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==11246==    by 0x511EA14: QByteArray::append(char const*, int) (in /usr/lib/x86_64-linux-gnu/libQt5Core.so.5.0.1)
    ==11246==    by 0x401B77: Http::writeCallback(char*, unsigned long, unsigned long, void*) (http.cpp:45)
    ==11246==    by 0x4E48717: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
    ==11246==    by 0x4E64A1B: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
    ==11246==    by 0x4E5F8B1: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
    ==11246==    by 0x4E68739: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
    ==11246==    by 0x4E693D4: curl_multi_perform (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
    ==11246==    by 0x4E60FDC: curl_easy_perform (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)

==11246==    by 0x401A45: Http::download() (http.cpp:26)
==11246==    by 0x401838: main (main.cpp:57)
==11246==  Address 0x1 is not stack'd, malloc'd or (recently) free'd
==11246== 
==11246== 
==11246== Process terminating with default action of signal 11 (SIGSEGV)
==11246==  Access not within mapped region at address 0x1
==11246==    at 0x4C2D7A2: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==11246==    by 0x511EA14: QByteArray::append(char const*, int) (in /usr/lib/x86_64-linux-gnu/libQt5Core.so.5.0.1)
==11246==    by 0x401B77: Http::writeCallback(char*, unsigned long, unsigned long, void*) (http.cpp:45)
==11246==    by 0x4E48717: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
==11246==    by 0x4E64A1B: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
==11246==    by 0x4E5F8B1: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
==11246==    by 0x4E68739: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
==11246==    by 0x4E693D4: curl_multi_perform (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
==11246==    by 0x4E60FDC: curl_easy_perform (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
==11246==    by 0x401A45: Http::download() (http.cpp:26)

I am completely lost why the code fails.

PS. I am aware this sample code is blocking and I know there's QNetworkAccessManager. I plan to move it to a QThread later on. The reason why I use libcurl is that a webservice I need to hit doesn't play nice with default headers sent by QNAM.

You can't pass C++ member methods as C callbacks . Make the callback a static method of the class, or a toplevel free function.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM