首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将boost异步函数封装到协同线中

将boost异步函数封装到协同线中
EN

Stack Overflow用户
提问于 2022-08-10 09:33:52
回答 1查看 173关注 0票数 3

嘿,我正在尝试包装第三方库提供的类来使用Boost协同器。库还使用Boost,但对于异步操作,使用的是完全处理程序。下面是一个可以尝试的简化示例。我认为我已经接近了,但出于某种原因,从async_connect返回的数据类型是无效的,而我希望返回boost::error_code。我遗漏了什么?

代码语言:javascript
复制
#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();
}

看来我找到了什么。我稍微修改了代码,删除了本示例不需要的所有代码。

代码语言:javascript
复制
#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作为第一个参数。

我跟踪了

EN

回答 1

Stack Overflow用户

发布于 2022-08-11 16:21:05

嗯,我设法解决了这个问题。但是,我不明白为什么处理程序的签名必须以一种形式(std:::exception_ptr,error_code)才能返回error_code。我也没能在boost文档中找到它。如果有人能提供某种解释,我将不胜感激。我认为这可能更好,也更通用。到目前为止,它还不能处理。处理多个返回值,就像我们在async_send中所做的那样,其中的完全处理程序必须能够处理像error_code和bytes_sent这样的两个参数。

代码语言:javascript
复制
 #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();
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73303949

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档