curl 为什么将参数作为lambda函数发送不起作用,而将其作为普通函数指针发送则起作用

nxowjjhe  于 2022-11-13  发布在  其他
关注(0)|答案(1)|浏览(197)

我一直在开发libcurl库,我需要为API提供一个回调函数,说明它应该如何处理接收到的数据。我尝试将此回调函数作为lambda提供给它,但它给了我访问冲突错误。而当我在其他地方定义函数后给予相同的函数作为函数指针时,它工作得很好!我想知道这两者之间的区别是什么,因为我以为它们是同一件事。
以下部分中使用的代码来自https://curl.se/libcurl/c/CURLOPT_WRITEFUNCTION.html,它是libcurl的文档。
下面是发送lambda函数的代码(这会产生访问冲突错误):

int main() {
    // Irrelevant initial code...

    struct memory {
        char* response;
        size_t size;
    } chunk = { 0 };
    curl_easy_setopt(handle, CURLOPT_WRITEDATA, (void*) &chunk);
    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, // I define a lambda function for the callback...
        [](void* data, size_t size, size_t nmemb, void* chunk) -> size_t {
            cout << "This function will never get called.. :(" << endl;
            size_t realSize = size * nmemb;
            memory* mem = (memory*) chunk;
    
            char* ptr = (char*) realloc(mem->response, mem->size + realSize + 1);
            if (ptr == NULL) return 0;
    
            mem->response = ptr;
            memcpy(&(mem->response[mem->size]), data, realSize);
            mem->size += realSize;
            mem->response[mem->size] = 0;
    
            return realSize;
        });
    
    CURLcode success = curl_easy_perform(handle);
    return 0;
}

在这里,lambda函数从未被调用,因此This function will never get called.. :(行从未显示在控制台中。它在名为sendf. c的文件的第563行中给出libcurl库内的访问冲突错误。
这里是完全一样的,但我定义了函数的外部:

struct memory {
    char* response;
    int size;
};

// defined the callback function outside...
size_t cb(void* data, size_t size, size_t nmemb, void* userp)
{
    cout << "Working!" << endl;
    size_t realsize = size * nmemb;
    memory* mem = (memory*)userp;

    char* ptr = (char*) realloc(mem->response, mem->size + realsize + 1);
    if (ptr == NULL)
        return 0;

    mem->response = ptr;
    memcpy(&(mem->response[mem->size]), data, realsize);
    mem->size += realsize;
    mem->response[mem->size] = 0;

    return realsize;
}

int main() {
    // Irrelevant initial code...

    memory chunk = { 0 };
    curl_easy_setopt(handle, CURLOPT_WRITEDATA, (void*) &chunk);
    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, cb);

    CURLcode success = curl_easy_perform(handle);
    return 0;
}

这一个工作,它显示Working!在控制台中。
我不明白为什么这两个是不同的,为什么其中一个工作,另一个不工作。还有,是否有可能使这与lambda函数的方法,因为我认为它看起来更整洁。

x6h2sr28

x6h2sr281#

编辑

@user17732522提醒我你可以get the same effect without all the drama by simply using + as the prefix to your lambda的主要道具。对我来说太晚了,我无法创作合理的答案。我完全忽略了这一点。

原件

curl_easy_setopt接受一个变量参数栈,并根据所建立的选项将其拆分成子组件。

CURL_EXTERN CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...);

变量参数很重要。它几乎可以接受任何东西,并期望它是它的原生类型。所以......那个lambda的原生类型是什么。一个“作弊”的方法是使用一个带有漂亮的打印选项的泛型模板来宣布扩展中接收到的是什么类型(听起来很混乱,我保证,一分钟内不会):

#include <iostream>

template<class T>
void foo(T&&)
{
    std::cout << __PRETTY_FUNCTION__ << '\n';
}

int main() 
{
    foo([](void *, size_t , size_t , void *) -> size_t
    {
        return size_t();
    });

    return 0;
}

输出

void foo(T &&) [T = (lambda at main.cpp:11:9)]

因此,是的,它很高兴地将一个'lambda'推入变量arg列表。合法地强制转换为等效的函数指针类型(我们可以这样做,因为lambda是非捕获的),给我们:

#include <iostream>

template<class T>
void foo(T&&)
{
    std::cout << __PRETTY_FUNCTION__ << '\n';
}

int main() 
{
    foo(static_cast<size_t (*)(void *, size_t, size_t, void *)>(
        [](void *, size_t , size_t , void *) -> size_t
        {
            return size_t();
        }));

    return 0;
}

输出

void foo(T &&) [T = unsigned long (*)(void *, unsigned long, unsigned long, void *)]

现在,一个实际的函数指针被推入arg列表,这是CURLOPT_WRITEFUNCTION所期望的
注意:如果上下文调用函数指针期望,则可以在函数指针上下文中隐式使用非捕获lambda。例如,以下代码不需要强制转换;转换是隐式的,因为lambda是非捕获的,因此是限定的,并且上下文专门调用相应匹配的函数指针:

#include <iostream>

void bar(size_t (*)(void *, size_t, size_t, void *))
{
}

int main() 
{
    bar([](void *, size_t , size_t , void *) -> size_t
        {
            return size_t();
        });

    return 0;
}

简短的回答是:curl_easy_setopt变量参数将您的lambda作为lambda使用;而不是作为一个函数指针。如果你想要一个函数指针(你确实这样做了)在这种情况下,你需要“哄”它屈服。

相关问题