嘿,我正在尝试包装第三方库提供的类来使用Boost协同器。库还使用Boost,但对于异步操作,使用的是完全处理程序。下面是一个可以尝试的简化示例。我认为我已经接近了,但出于某种原因,从async_connect返回的数据类型是无效的,而我希望返回boost::error_code。我遗漏了什么?
#include <boost/asio.hpp>
#include <boost/asio/awaitable.hpp>
#include <boost/asio/co_spawn.hpp>
#include <iostream>
using AsyncHandler = std::function<void(boost::system::error_code)>;
struct LibraryClient
{
LibraryClient(boost::asio::io_context& ioc)
: socket{ioc}
{}
boost::asio::ip::tcp::socket socket;
void async_connect(AsyncHandler handler = {})
{
boost::system::error_code ec;
boost::asio::ip::address ip_address = boost::asio::ip::address::from_string("127.0.0.1", ec);
boost::asio::ip::tcp::endpoint ep(ip_address, 9999);
socket.async_connect(ep, std::move(handler));
}
};
template<class CompletitionToken = boost::asio::use_awaitable_t<>>
auto do_async_connect(LibraryClient& client, CompletitionToken&& token = {})
{
auto initiate = [&client]<class H>(H&& self) mutable
{
client.async_connect([self = std::make_shared<H>(std::forward<H>(self))](auto&& r)
{
(*self)(r);
});
};
return boost::asio::async_initiate<
CompletitionToken, boost::system::error_code(boost::system::error_code)
>(initiate, token);
}
struct LibraryClientWrapper
{
LibraryClient client;
boost::asio::awaitable<boost::system::error_code> async_connect()
{
//auto ec = co_await do_something_with_client();
const auto ec = co_await do_async_connect(client);
}
};
int main()
{
auto ioc = boost::asio::io_context{};
auto client = LibraryClientWrapper{LibraryClient{ioc}};
ioc.run();
}看来我找到了什么。我稍微修改了代码,删除了本示例不需要的所有代码。
#include <boost/asio.hpp>
#include <boost/asio/awaitable.hpp>
#include <boost/asio/co_spawn.hpp>
#include <iostream>
#include <cassert>
template<class CompletitionToken>
auto do_async_connect(LibraryClient& client, CompletitionToken&& token)
{
auto initiate = [](auto&& handler) {
handler(nullptr, 90);
};
return boost::asio::async_initiate<CompletitionToken, void(Eptr, int)>(
initiate, std::forward<CompletitionToken>(token)
);
}
struct LibraryClientWrapper
{
LibraryClient client;
boost::asio::awaitable<void> async_connect()
{
const auto ec = co_await do_async_connect(client, boost::asio::use_awaitable);
assert(ec == 90);
}
};
void rethrow_exception(std::exception_ptr eptr)
{
if (eptr)
{
std::rethrow_exception(eptr);
}
}
int main()
{
auto ioc = boost::asio::io_context{};
auto client = LibraryClientWrapper{LibraryClient{ioc}};
boost::asio::co_spawn(ioc, client.async_connect(), rethrow_exception);
ioc.run();
}正如您所看到的,我将签名更改为同时使用std::exception_ptr和int,这导致从coroutine正确返回int。但我不明白为什么需要这个签名,特别是std::exception_ptr作为第一个参数。
发布于 2022-08-11 16:21:05
嗯,我设法解决了这个问题。但是,我不明白为什么处理程序的签名必须以一种形式(std:::exception_ptr,error_code)才能返回error_code。我也没能在boost文档中找到它。如果有人能提供某种解释,我将不胜感激。我认为这可能更好,也更通用。到目前为止,它还不能处理。处理多个返回值,就像我们在async_send中所做的那样,其中的完全处理程序必须能够处理像error_code和bytes_sent这样的两个参数。
#include <boost/asio.hpp>
#include <iostream>
using AsyncHandler = std::function<void(boost::system::error_code)>;
void rethrow_exception(std::exception_ptr eptr)
{
if (eptr)
{
std::rethrow_exception(eptr);
}
}
using Eptr = std::exception_ptr;
struct LibraryClient
{
LibraryClient(LibraryClient&&) = default;
LibraryClient(boost::asio::io_context& ioc)
: socket{ioc}
{}
boost::asio::ip::tcp::socket socket;
void async_connect(AsyncHandler handler = {})
{
boost::system::error_code ec;
boost::asio::ip::address ip_address = boost::asio::ip::address::from_string("127.0.0.1", ec);
boost::asio::ip::tcp::endpoint ep(ip_address, 9999);
socket.async_connect(ep, std::move(handler));
}
void async_disconnect(AsyncHandler handler = {})
{
auto ec = boost::system::error_code{};
socket.shutdown(boost::asio::ip::tcp::socket::shutdown_send, ec);
}
};
template<class R, class Func, class CompletitionHandler = boost::asio::use_awaitable_t<>>
auto awaitable_call(Func&& func, CompletitionHandler&& handler = {})
{
using Signature_t = void(Eptr, R);
auto initiate = [func = std::forward<Func>(func)]<class Handler>(Handler&& self) mutable
{
std::invoke(func, [self = std::make_shared<Handler>(std::forward<Handler>(self))](auto&&... args) {
(*self)(std::current_exception(), std::forward<decltype(args)>(args)...);
});
};
return boost::asio::async_initiate<CompletitionHandler, Signature_t>(initiate, handler);
}
template<class Func, class O, class...Args>
auto bind_awaitable_func(Func&& func, O&& o, Args&&...args)
{
return std::bind(std::forward<Func>(func),
std::forward<O>(o),
std::forward<Args>(args)...,
std::placeholders::_1
);
}
struct LibraryClientWrapper
{
LibraryClient client;
using Impl = LibraryClient;
boost::asio::awaitable<void> async_connect()
{
const auto r1 = co_await awaitable_call<boost::system::error_code>(
bind_awaitable_func(&LibraryClient::async_connect, std::ref(client))
);
}
boost::asio::awaitable<void> async_disconnect()
{
const auto r1 = co_await awaitable_call<boost::system::error_code>(
bind_awaitable_func(&LibraryClient::async_disconnect, std::ref(client))
);
}
};
int main()
{
auto ioc = boost::asio::io_context{};
auto client = LibraryClientWrapper{LibraryClient{ioc}};
boost::asio::co_spawn(ioc, client.async_disconnect(), rethrow_exception);
ioc.run();
}https://stackoverflow.com/questions/73303949
复制相似问题