【问题标题】:using custom deleter (curl_formfree()) in unique_ptr<>在 unique_ptr<> 中使用自定义删除器 (curl_formfree())
【发布时间】:2019-12-24 08:39:33
【问题描述】:

我有以下代码可以使用 libcurl 发布文件。

bool FileUploadDownload::upload(const std::string& filename, const std::string& url) {

    CURLcode res;
    CURLFORMcode form_res;
    curl_off_t speed_upload = 0;
    struct curl_httppost* form_post = nullptr;
    struct curl_httppost* last_ptr = nullptr;
    struct curl_slist* header_list = nullptr;
    static const char buf[] = "Expect: 100-continue";

    std::string pure_fname = filename.substr(filename.find_last_of("/\\") + 1);

    res = curl_global_init(CURL_GLOBAL_ALL);
    if (res != CURLE_OK) {
        m_logger->errorf("curl_global_init(CURL_GLOBAL_ALL) failed: %s", curl_easy_strerror(res));
        return false;
    }

    form_res = curl_formadd(&form_post,
                            &last_ptr,
                            CURLFORM_COPYNAME, "file",
                            CURLFORM_FILENAME, pure_fname.c_str(),
                            CURLFORM_FILE, filename.c_str(),
                            CURLFORM_CONTENTTYPE, "text/plain",
                            CURLFORM_END);
    if (form_res != 0) {
        m_logger->errorf("curl_formadd() failed: %s", curl_easy_strerror((CURLcode)form_res));
        curl_formfree(form_post);
        return false;
    }

    form_res = curl_formadd(&form_post,
                            &last_ptr,
                            CURLFORM_COPYNAME, "submit",
                            CURLFORM_COPYCONTENTS, "send",
                            CURLFORM_END);
    if (form_res != 0) {
        m_logger->errorf("curl_formadd() failed: %s", curl_easy_strerror((CURLcode)form_res));
        curl_formfree(form_post);
        return false;
    }

    if (m_curl) {

        header_list = curl_slist_append(header_list, buf);
        if(nullptr == header_list){
            return false;
        }

        res = curl_easy_setopt(m_curl, CURLOPT_URL, url.c_str());
        if (res != CURLE_OK) {
            m_logger->errorf("curl_easy_setopt(CURLOPT_URL) failed: %s", curl_easy_strerror(res));
            curl_formfree(form_post);
            curl_slist_free_all(header_list);
            return false;
        }

        res = curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, header_list);
        if (res != CURLE_OK) {
            m_logger->errorf("curl_easy_setopt(CURLOPT_HTTPHEADER) failed: %s", curl_easy_strerror(res));
            curl_formfree(form_post);
            curl_slist_free_all(header_list);
            return false;
        }

        res = curl_easy_setopt(m_curl, CURLOPT_HTTPPOST, form_post);
        if (res != CURLE_OK) {
            m_logger->errorf("curl_easy_setopt(CURLOPT_HTTPPOST) failed: %s", curl_easy_strerror(res));
            curl_formfree(form_post);
            curl_slist_free_all(header_list);
            return false;
        }

        res = curl_easy_setopt(m_curl, CURLOPT_VERBOSE, 1L);
        if (res != CURLE_OK) {
            m_logger->errorf("curl_easy_setopt(CURLOPT_VERBOSE) failed: %s", curl_easy_strerror(res));
            curl_formfree(form_post);
            curl_slist_free_all(header_list);
            return false;
        }

        res = curl_easy_perform(m_curl);
        if (res != CURLE_OK) {
            m_logger->errorf("curl_easy_perform() failed: %s", curl_easy_strerror(res));
            curl_formfree(form_post);
            curl_slist_free_all(header_list);
            return false;
        }

        res = curl_easy_getinfo(m_curl, CURLINFO_SPEED_UPLOAD, &speed_upload);
        if (res != CURLE_OK) {
            m_logger->errorf("curl_easy_getinfo(CURLINFO_SPEED_UPLOAD) failed: %s", curl_easy_strerror(res));
            curl_formfree(form_post);
            curl_slist_free_all(header_list);
            return false;
        }
        m_logger->infof("Average upload speed: %" CURL_FORMAT_CURL_OFF_T
                            " megabyte/sec.\n", speed_upload / (1024 * 1024));
        curl_formfree(form_post);
        curl_slist_free_all(header_list);
        return true;
    }
    return false;
}

上述函数失败返回false的情况很多。在所有这些情况下,我都需要致电 curl_formfree() 和 curl_slist_free_all()。

我想用std::unique_ptr&lt;&gt;优化这个

我已经重写了上面的函数,如下所示。

bool FileUploadDownload::upload(const std::string& filename, const std::string& url) {

    CURLcode res;
    CURLFORMcode form_res;
    curl_off_t speed_upload = 0;
    auto form_delete = [&](curl_httppost * p) {
        curl_formfree(p);
    };
    //struct curl_httppost* form_post = nullptr;
    //struct curl_httppost* last_ptr = nullptr;
    std::unique_ptr<curl_httppost*, decltype(form_delete)> form_post;
    std::unique_ptr<curl_httppost*, decltype(form_delete)> last_ptr;

    struct curl_slist* header_list = nullptr;
    static const char buf[] = "Expect: 100-continue";

    std::string pure_fname = filename.substr(filename.find_last_of("/\\") + 1);

    res = curl_global_init(CURL_GLOBAL_ALL);
    if (res != CURLE_OK) {
        m_logger->errorf("curl_global_init(CURL_GLOBAL_ALL) failed: %s", curl_easy_strerror(res));
        return false;
    }

    form_res = curl_formadd(form_post.get(),
                            last_ptr.get(),
                            CURLFORM_COPYNAME, "file",
                            CURLFORM_FILENAME, pure_fname.c_str(),
                            CURLFORM_FILE, filename.c_str(),
                            CURLFORM_CONTENTTYPE, "text/plain",
                            CURLFORM_END);
    if (form_res != 0) {
        m_logger->errorf("curl_formadd() failed: %s", curl_easy_strerror((CURLcode)form_res));
        //curl_formfree(form_post);
        return false;
    }

    form_res = curl_formadd(form_post.get(),
                            last_ptr.get(),
                            CURLFORM_COPYNAME, "submit",
                            CURLFORM_COPYCONTENTS, "send",
                            CURLFORM_END);
    if (form_res != 0) {
        m_logger->errorf("curl_formadd() failed: %s", curl_easy_strerror((CURLcode)form_res));
        //curl_formfree(form_post);
        return false;
    }

    if (m_curl) {

        header_list = curl_slist_append(header_list, buf);
        if(nullptr == header_list){
            return false;
        }

        res = curl_easy_setopt(m_curl, CURLOPT_URL, url.c_str());
        if (res != CURLE_OK) {
            m_logger->errorf("curl_easy_setopt(CURLOPT_URL) failed: %s", curl_easy_strerror(res));
            //curl_formfree(form_post);
            curl_slist_free_all(header_list);
            return false;
        }

        res = curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, header_list);
        if (res != CURLE_OK) {
            m_logger->errorf("curl_easy_setopt(CURLOPT_HTTPHEADER) failed: %s", curl_easy_strerror(res));
            //curl_formfree(form_post);
            curl_slist_free_all(header_list);
            return false;
        }

        res = curl_easy_setopt(m_curl, CURLOPT_HTTPPOST, form_post.get());
        if (res != CURLE_OK) {
            m_logger->errorf("curl_easy_setopt(CURLOPT_HTTPPOST) failed: %s", curl_easy_strerror(res));
            //curl_formfree(form_post);
            curl_slist_free_all(header_list);
            return false;
        }

        res = curl_easy_setopt(m_curl, CURLOPT_VERBOSE, 1L);
        if (res != CURLE_OK) {
            m_logger->errorf("curl_easy_setopt(CURLOPT_VERBOSE) failed: %s", curl_easy_strerror(res));
            //curl_formfree(form_post);
            curl_slist_free_all(header_list);
            return false;
        }

        res = curl_easy_perform(m_curl);
        if (res != CURLE_OK) {
            m_logger->errorf("curl_easy_perform() failed: %s", curl_easy_strerror(res));
            //curl_formfree(form_post);
            curl_slist_free_all(header_list);
            return false;
        }

        res = curl_easy_getinfo(m_curl, CURLINFO_SPEED_UPLOAD, &speed_upload);
        if (res != CURLE_OK) {
            m_logger->errorf("curl_easy_getinfo(CURLINFO_SPEED_UPLOAD) failed: %s", curl_easy_strerror(res));
            //curl_formfree(form_post);
            curl_slist_free_all(header_list);
            return false;
        }
        m_logger->infof("Average upload speed: %" CURL_FORMAT_CURL_OFF_T
                            " megabyte/sec.\n", speed_upload / (1024 * 1024));
        //curl_formfree(form_post);
        curl_slist_free_all(header_list);
        return true;
    }
    return false;
}

我收到以下编译错误。

error: no match for call to ‘(std::unique_ptr<curl_httppost*, FileUploadDownload::upload(const string&, const string&)::<lambda(curl_httppost*)> >::deleter_type {aka FileUploadDownload::upload(const string&, const string&)::<lambda(curl_httppost*)>}) (curl_httppost**&)’
    get_deleter()(__ptr);

【问题讨论】:

    标签: c++ c++11 libcurl unique-ptr


    【解决方案1】:

    你必须初始化指针。 lambda 的参数或指针的第一个参数也是错误的。 unique_ptr 需要它引用的类型,而 deleter 接受指向该类型的指针。

    std::unique_ptr<curl_httppost, decltype(form_delete)> 
                              form_post( nullptr /* required by first call of curl_formadd */, 
                                         form_delete);
    

    也许您想创建自己的“make”函数来用更少的代码构造这些指针。

    curl_formadd 需要访问指针进行初始化,并且它的第一个参数是curl_httppost**,因此使用.get() 不会削减它。您要么必须使用中间指针(这违背了重构的目的?),要么围绕这些指针创建一个包装器。注意,curl_formadd 已弃用。有点尴尬的解决方法是管理指向指针的指针。

    auto form_delete = [&](curl_httppost ** p) {
            curl_formfree(*p);
            delete p;
        };
    
    std::unique_ptr<curl_httppost*, decltype(form_delete)> 
                        form_post( new curl_httppost*(nullptr) /* required by first call of curl_formadd */, 
                                   form_delete);
    
    form_res = curl_formadd(form_post.get(),
    

    这并没有消除重置指针的要求,因为底层指针是非托管的。

    【讨论】:

    • 同样的技术不适用于curl_slist_free_all()。它确实适用于curl_formfree()
    • 即使对于curl_httppost() 也不起作用导致段错误。
    猜你喜欢
    • 2013-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-13
    • 2023-03-11
    • 2016-11-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多