QObject派生的类回调函数到c库

QObject derived class callback function to a c library

本文关键字:函数 回调 派生 QObject      更新时间:2023-10-16

我正试图将libcurl包装为一个helper QObject类。不幸的是,我得到了一个神秘的segfault,当完全相同的代码被放在类之外时,它不会发生。

样本代码:

无类工作代码

//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: %sn",
                    curl_easy_strerror(res));
 
        /* always cleanup */
        curl_easy_cleanup(curl);
    }
    qDebug() << buffer;
    return a.exec();
}

分段故障的QObject包装器

//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: %sn",
                    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();
}

分段故障出现在Http::writeCallback(char* ptr, size_t size, size_t nmemb, void* up) 中的线路

valgrind输出

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

我完全不明白为什么代码会失败。

PS。我知道这个示例代码正在阻塞,我知道有QNetworkAccessManager。我计划稍后将其转移到QThread。我之所以使用libcurl,是因为我需要访问的Web服务与QNAM发送的默认标头不兼容。

不能将C++成员方法作为C回调传递。使回调成为类的静态方法,或顶级自由函数。