Исправление ошибки async_connect в Boost.Beast при обновлении с версии 1.78 до 1.89
Узнайте, как исправить ошибки компиляции async_connect в Boost.Beast при обновлении с версии Boost 1.78 до 1.89. Полное руководство с примерами кода и советами по миграции.
Как исправить ошибку компиляции async_connect в Boost.Beast после обновления с Boost 1.78 до 1.89?
У меня есть класс WebSocket, который подключается асинхронно с использованием Boost.Asio и Boost.Beast. Следующий код работал нормально с Boost 1.78, но после обновления до последней версии Boost теперь я получаю ошибку компиляции:
std::future<void> Websocket::async_connect() {
io_thread_ = std::thread([this]() {
ioc_.run();
});
// Начинаем разрешение имени
auto resolve_fut = resolver_.async_resolve(host_, port_, net::use_future);
return std::async(std::launch::async, [this, resolve_fut = std::move(resolve_fut)]() mutable {
try {
auto results = resolve_fut.get();
auto connect_fut = beast::get_lowest_layer(ws_).async_connect(*results, net::use_future);
connect_fut.get();
auto ssl_handshake_fut = ws_.next_layer().async_handshake(ssl::stream_base::client, net::use_future);
ssl_handshake_fut.get();
ws_.set_option(websocket::stream_base::timeout::suggested(beast::role_type::client));
auto ws_handshake_fut = ws_.async_handshake(host_, target_, net::use_future);
ws_handshake_fut.get();
webSocketLogger_.info("Connected to WebSocket Successfully!!!");
// std::cout << "Connected to WebSocket Successfully!!!\n";
ReceiveMsg();
} catch (const std::exception& e) {
// std::cerr << "Connection failed: " << e.what() << "\n";
webSocketLogger_.error("Connection Failed: {}", e.what());
throw;
}
Ошибка, которую я получаю:
No operator* matches argument of type
std::tuple<boost::system::error_code, boost::asio::ip::basic_resolver_results<tcp>>
В Boost 1.78 resolve_fut.get() возвращал тип, который можно было напрямую разыменовать для получения результатов tcp::endpoint. В последней версии Boost, похоже, async_resolve(..., net::use_future) теперь возвращает кортеж вместо одного объекта.
Я пробовал такие варианты:
auto endpoints = std::get<1>(results);
auto [err,endpoints] = resolve_fut.get();
но это все равно не компилируется.
Как мне обновить этот код, чтобы он работал с последней версией Boost? Как правильно извлечь конечные точки из future, возвращаемого async_resolve?
Ошибка компиляции возникает из-за того, что в Boost 1.89 изменился тип возвращаемого значения функции async_resolve(..., net::use_future). Теперь она возвращает кортеж, содержащий как код ошибки, так и результаты резолвера, а не только результаты напрямую. Вот как исправить ваш код:
В Boost 1.89 async_resolve(..., net::use_future) теперь возвращает std::future<std::tuple<error_code, resolver_results>> вместо std::future<resolver_results>. Вам необходимо извлечь как код ошибки, так и результаты из кортежа.
Вот исправленная версия вашего кода:
std::future<void> Websocket::async_connect() {
io_thread_ = std::thread([this]() {
ioc_.run();
});
// Начинаем разрешение имени
auto resolve_fut = resolver_.async_resolve(host_, port_, net::use_future);
return std::async(std::launch::async, [this, resolve_fut = std::move(resolve_fut)]() mutable {
try {
// Извлекаем код ошибки и результаты из кортежа
auto [err, results] = resolve_fut.get();
if (err) {
throw boost::system::system_error(err);
}
// Используем resolver_results с async_connect
auto connect_fut = beast::get_lowest_layer(ws_).async_connect(results, net::use_future);
connect_fut.get();
auto ssl_handshake_fut = ws_.next_layer().async_handshake(ssl::stream_base::client, net::use_future);
ssl_handshake_fut.get();
ws_.set_option(websocket::stream_base::timeout::suggested(beast::role_type::client));
auto ws_handshake_fut = ws_.async_handshake(host_, target_, net::use_future);
ws_handshake_fut.get();
webSocketLogger_.info("Успешное подключение к WebSocket!!!");
ReceiveMsg();
} catch (const std::exception& e) {
webSocketLogger_.error("Ошибка подключения: {}", e.what());
throw;
}
});
}
Основные изменения:
- Извлечение ошибки и результатов из кортежа:
auto [err, results] = resolve_fut.get(); - Проверка ошибок: Добавлена обработка ошибок перед продолжением подключения
- Обновление вызова async_connect: Изменено с
*resultsна простоresults, так какasync_connectтеперь принимает resolver_results напрямую
Содержание
- Понимание изменений в Boost.Beast
- Полный пример исправленного кода
- Альтернативные подходы к обработке ошибок
- Руководство по миграции с Boost 1.78 на 1.89
- Распространенные проблемы и решения
Понимание изменений в Boost.Beast
Ошибка возникает из-за того, что в Boost 1.89 были внесены изменения в функцию async_resolve при использовании с net::use_future. Тип возвращаемого значения изменился с:
- Boost 1.78:
std::future<ip::tcp::resolver::results_type> - Boost 1.89:
std::future<std::tuple<error_code, ip::tcp::resolver::results_type>>
Это изменение обеспечивает лучшую обработку ошибок и согласованность между асинхронными операциями Asio. Формат кортежа соответствует шаблону, используемому другими асинхронными операциями Asio при комбинировании с use_future.
Полный пример исправленного кода
Вот полный, работающий версии вашего метода async_connect:
std::future<void> Websocket::async_connect() {
io_thread_ = std::thread([this]() {
ioc_.run();
});
// Начинаем разрешение имени
auto resolve_fut = resolver_.async_resolve(host_, port_, net::use_future);
return std::async(std::launch::async, [this, resolve_fut = std::move(resolve_fut)]() mutable {
try {
// Ключевое изменение: извлекаем и ошибку, и результаты из кортежа
auto [err, results] = resolve_fut.get();
if (err) {
throw boost::system::system_error(err);
}
// Подключаемся к первому разрешенному конечному пункту
auto connect_fut = beast::get_lowest_layer(ws_).async_connect(results, net::use_future);
connect_fut.get();
// Выполняем SSL-рукопожатие
auto ssl_handshake_fut = ws_.next_layer().async_handshake(ssl::stream_base::client, net::use_future);
ssl_handshake_fut.get();
// Настраиваем таймаут WebSocket
ws_.set_option(websocket::stream_base::timeout::suggested(beast::role_type::client));
// Выполняем рукопожатие WebSocket
auto ws_handshake_fut = ws_.async_handshake(host_, target_, net::use_future);
ws_handshake_fut.get();
webSocketLogger_.info("Успешное подключение к WebSocket!!!");
ReceiveMsg();
} catch (const std::exception& e) {
webSocketLogger_.error("Ошибка подключения: {}", e.what());
throw;
}
});
}
Альтернативные подходы к обработке ошибок
Использование std::get для доступа к элементам кортежа
Если вы предпочитаете не использовать структурированное связывание, вы можете получить доступ к элементам кортежа напрямую:
auto resolve_result = resolve_fut.get();
auto err = std::get<0>(resolve_result);
auto results = std::get<1>(resolve_result);
if (err) {
throw boost::system::system_error(err);
}
Использование .then() для лучшего асинхронного потока
Для более современных асинхронных шаблонов рассмотрите использование .then():
auto resolve_fut = resolver_.async_resolve(host_, port_, net::use_future);
return std::async(std::launch::async, [this, resolve_fut = std::move(resolve_fut)]() mutable {
return resolve_fut.then([this](auto fut) {
auto [err, results] = fut.get();
if (err) {
throw boost::system::system_error(err);
}
// Продолжаем с логикой подключения...
return std::async(std::launch::async, [this, results]() mutable {
// Остальной код подключения
});
});
});
Руководство по миграции с Boost 1.78 на 1.89
При обновлении с Boost 1.78 на 1.89 обратите внимание на следующие изменения:
1. Тип возвращаемого значения async_resolve
// Старый (1.78)
auto results = resolve_fut.get(); // Только результаты
// Новый (1.89)
auto [err, results] = resolve_fut.get(); // Ошибка + результаты
2. Параметр async_connect
// Старый (1.78)
auto connect_fut = beast::get_lowest_layer(ws_).async_connect(*results, net::use_future);
// Новый (1.89)
auto connect_fut = beast::get_lowest_layer(ws_).async_connect(results, net::use_future);
3. Шаблон обработки ошибок
Всегда проверяйте код ошибки из асинхронных операций:
auto [err, results] = resolve_fut.get();
if (err) {
throw boost::system::system_error(err);
}
Распространенные проблемы и решения
Проблема: “Нет совпадающей функции для вызова ‘async_connect’”
Решение: Удалите оператор разыменования (*) из results:
// Неправильно
auto connect_fut = beast::get_lowest_layer(ws_).async_connect(*results, net::use_future);
// Правильно
auto connect_fut = beast::get_lowest_layer(ws_).async_connect(results, net::use_future);
Проблема: Структурированное связывание не работает
Решение: Убедитесь, что ваш компилятор поддерживает структурированное связывание C++17 (большинство современных компиляторов поддерживают).
Проблема: Утечки памяти с futures
Решение: Убедитесь, что вы перемещаете (move) futures, а не копируете их:
// Хорошо
[resolve_fut = std::move(resolve_fut)]
// Плохо (может вызвать проблемы)
[resolve_fut]
Проблема: Обработка нескольких конечных точек
Если вы хотите обрабатывать несколько разрешенных конечных точек (попробовать следующую, если текущая не удалась):
auto [err, results] = resolve_fut.get();
if (err) {
throw boost::system::system_error(err);
}
for (auto const& endpoint : results) {
try {
auto connect_fut = beast::get_lowest_layer(ws_).async_connect(endpoint, net::use_future);
connect_fut.get();
break; // Успех, выходим из цикла
} catch (const std::exception&) {
continue; // Пробуем следующую конечную точку
}
}
Эти изменения должны решить вашу ошибку компиляции и сделать ваш код совместимым с Boost 1.89, сохраняя ту же функциональность.