Получение C2280 (попытка обратиться к удаленной функции) при использовании в будущем - PullRequest
2 голосов
/ 21 октября 2019

Я использую библиотеки poco и пытаюсь обернуть их в более удобный HTTPClient, который я могу использовать везде в нескольких строках, а также сделать их асинхронными. Для этого я использую std :: future и структуру пользовательского ответа. Однако по какой-то причине он говорит мне, что пытается ссылаться на удаленную функцию. Я ничего не удаляю, поэтому я не знаю, почему это будет сказано.

httpclient.h

#include <Poco/Net/HTTPSClientSession.h>
#include <Poco/Net/HTTPRequest.h>
#include <Poco/Net/HTTPResponse.h>
#include <Poco/Exception.h>
#include <Poco/URI.h>
#include <future>
#include <map>

typedef struct response {
    std::istream& is;
    Poco::Net::HTTPResponse& response;
} RES;

class HttpClient {
public:
    static std::future<RES> get(Poco::Net::HTTPSClientSession& session, Poco::URI url, std::map<std::string, std::string> headers = {});
};

httpclient.cpp

#include "httpclient.h"

std::future<RES> HttpClient::get(Poco::Net::HTTPSClientSession& session, Poco::URI url, std::map<std::string, std::string> headers) {
    return std::async(std::launch::async, [&session, url, headers](){
        try {
            std::string path(url.getPathAndQuery());
            if (path.empty()) path = "/";

            Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, path, Poco::Net::HTTPMessage::HTTP_1_1);
            request.add("Content-Length", "0");

            if (headers.size() > 0) {
                for (std::map<std::string, std::string>::const_iterator itr = headers.begin(); itr != headers.end(); ++itr) {
                    request.add(itr->first, itr->second);
                }
            }

            Poco::Net::HTTPResponse _res;
            session.sendRequest(request);
            std::istream& is = session.receiveResponse(_res);
            return RES { is, _res };
        }
        catch (Poco::Exception & exc) {
            OutputDebugStringA(exc.displayText().c_str());
        }
    });
}

main.cpp

Poco::Net::initializeSSL();

    Poco::URI uri("https://www.google.com");
    const Poco::Net::Context::Ptr context = new Poco::Net::Context(Poco::Net::Context::CLIENT_USE, "", "", "", Poco::Net::Context::VERIFY_NONE, 9, false, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
    Poco::Net::HTTPSClientSession session(uri.getHost(), uri.getPort(), context);

    std::future<RES> response = HttpClient::get(session, uri, {});
    response.get();

Это точная ошибка, которую я получил: C2280: response &response::operator =(const response &)': attempting to reference a deleted function future:line 332.

Спасибо!

Ответы [ 2 ]

1 голос
/ 21 октября 2019

Ошибка говорит вам, что response объекты не могут быть скопированы, и что вы пытаетесь сделать именно это.

struct response { 
    std::istream& is; // can't be copied: istream( const istream&) = delete;
    Poco::Net::HTTPResponse& response; // also not copyable or movable
};

Однако в коде, который вы показали, нет ничего, что пытаетсясделайте это.

receiveResponse() возвращает ссылку на std::istream, что становится проблематичным в случае его выброса. Когда вы ловите исключение, вам нечего возвращать, поэтому вы не можете - и входите в страну неопределенного поведения.

Вы также можете прочитать данные в лямбде async и сохранить их. в вашем RES напрямую.

#include <Poco/Exception.h>
#include <Poco/Net/HTTPRequest.h>
#include <Poco/Net/HTTPResponse.h>
#include <Poco/Net/HTTPSClientSession.h>
#include <Poco/Net/SecureStreamSocket.h>
#include <Poco/URI.h>

#include <future>
#include <iostream>
#include <map>
#include <vector>
#include <memory>

// a slightly modified version of your RES struct
struct RES {
    std::vector<std::string> data{}; // the document data

    // using a unique_ptr to make HTTPResponse easier to move
    std::unique_ptr<Poco::Net::HTTPResponse> 
        response = std::make_unique<Poco::Net::HTTPResponse>();

    bool ok = false;                 // if reading was done without an exception
};

class HttpClient {
public:
    static std::future<RES> get(Poco::Net::HTTPSClientSession& session,
                                Poco::URI url,
                                std::map<std::string, std::string> headers = {});
};

std::future<RES> HttpClient::get(Poco::Net::HTTPSClientSession& session,
                                 Poco::URI url,
                                 std::map<std::string, std::string> headers) {
    return std::async(std::launch::async, [&session, url, headers]() {

        RES res;

        try {
            Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET,
                                           url.getPathAndQuery(),
                                           Poco::Net::HTTPMessage::HTTP_1_1);

            // add request headers
            for(const auto&[field, value]:  headers)
                request.add(field, value);

            session.sendRequest(request);
            std::istream& is = session.receiveResponse(*res.response);

            // read document data
            std::string line;
            while(std::getline(is, line))
                res.data.push_back(line);

            res.ok = true; // reading was done without an exception
        } catch(Poco::Exception& exc) {
            std::cout << exc.displayText().c_str() << "\n";
        }

        // always return according to what you declared
        return res;
    });
}

Пример использования:

class SSLInitializer {
public:
    SSLInitializer() { Poco::Net::initializeSSL(); }
    ~SSLInitializer() { Poco::Net::uninitializeSSL(); }
};

int main() {
    SSLInitializer sslInitializer;

    Poco::URI uri{"https://www.google.com/"};

    const Poco::Net::Context::Ptr context = new Poco::Net::Context(
        Poco::Net::Context::CLIENT_USE, "", "", "", Poco::Net::Context::VERIFY_NONE, 9,
        false, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");

    Poco::Net::HTTPSClientSession sess(uri.getHost(), uri.getPort(), context);

    std::future<RES> fut_res = HttpClient::get(sess, uri);

    fut_res.wait();

    RES res = fut_res.get();
    std::cout << std::boolalpha << "Response OK: " << res.ok << "\n---\n";
    if(res.ok) {
        Poco::Net::HTTPResponse& header = *res.response

        std::cout << "HTTPResponse header:\n";
        for(const auto& [field, value] : header) {
            std::cout << field << " = " << value << "\n";
        }

        std::cout << "--- DOCUMENT DATA ---\n";
        for(const auto& s : res.data) {
            std::cout << s << "\n";
        }
    }
}
0 голосов
/ 22 октября 2019

Проблема в том, что класс Poco::Net::HTTPResponse не подлежит копированию. Его конструктор копирования и оператор присваивания объявлены как закрытые. Поэтому невозможно сделать копию этого экземпляра.

Я действительно считаю, что создавать новый поток для каждого http-запроса - это излишне. Я могу понять ваши аргументы о желании сделать это, но вы должны иметь в виду, что при создании нового потока возникают некоторые накладные расходы. Вам лучше просто использовать классы Poco или, если хотите, обернуть их. Ваши http-запросы, вероятно, будут выполняться медленнее, создавая новый поток для каждого запроса.

Могу ли я предложить небольшое изменение вашей структуры RES:

typedef struct response {
    Poco::Net::HTTPResponse::HTTPStatus status;
    std::string contents;
} RES;

Эта структура теперь может использоваться дляхранить данные из объекта Poco::Net::HTTPResponse после выполнения запроса. Затем с помощью вспомогательной функции, которая выводит содержимое std::istream в std::string:

std::string Gulp(std::istream& in)
{
    std::string response(std::istreambuf_iterator<char>(in), {});
    return response;
}

, вы можете сделать это в main.cpp:

Poco::Net::initializeSSL();
Poco::URI uri("https://www.google.com");
const Poco::Net::Context::Ptr context = new 
Poco::Net::Context(Poco::Net::Context::CLIENT_USE, "", "", "", Poco::Net::Context::VERIFY_NONE, 9, false, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
Poco::Net::HTTPSClientSession session(uri.getHost(), uri.getPort(), context);

std::string path(uri.getPathAndQuery());
if (path.empty()) path = "/";

Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, path, Poco::Net::HTTPMessage::HTTP_1_1);
Poco::Net::HTTPResponse _res;
session.sendRequest(request);
std::istream& is = session.receiveResponse(_res);
RES response{ _res.getStatus(), Gulp(is) };
...