繁体   English   中英

将 boost async function 包装到协程中

[英]Wrapping boost async function into coroutine

嘿,我正在尝试包装第三方库提供的 class 以使用 Boost 协程。 该库也使用 Boost,但出于异步操作的目的,使用了完成处理程序。 下面是一个可以尝试的简化示例。 我想我已经接近了,但由于某种原因,从 async_connect 返回的 awaitable 是 void 类型,而我想 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();
}

如您所见,我将 signatute 更改为同时采用 std::exception_ptr 和 int,这导致 int 从协程中正确返回。 但我不明白为什么需要这个签名,特别是 std::exception_ptr 作为第一个参数。

我跟着这个这个

好吧,我设法让我们说解决它。 但我不明白为什么处理程序的签名必须采用 void(std:::exception_ptr, error_code) 的形式才能返回 error_code。 我也未能在 boost 文档中找到它。 如果有人能提供某种解释,我将不胜感激。

#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("35.156.20.134", ec);
        boost::asio::ip::tcp::endpoint ep(ip_address, 1883);

        socket.async_connect(ep, std::move(handler));
    }
};


template<typename CompletitionHandler = boost::asio::use_awaitable_t<>>
auto do_async_connect(LibraryClient& client, CompletitionHandler&& handler = {})
{
    auto initiate = [&client]<class Handler>(Handler&& self) mutable
    {
        client.async_connect([self = std::make_shared<Handler>(std::forward<Handler>(self))](auto error_code)
        {
            (*self)(std::current_exception(), error_code);
        });
    };
    return boost::asio::async_initiate<CompletitionHandler, void(Eptr, boost::system::error_code)>(initiate, handler);
}


struct LibraryClientWrapper
{
    LibraryClient client;


    boost::asio::awaitable<void> async_connect()
    {
        const auto ec = co_await do_async_connect(client, boost::asio::use_awaitable);
        std::cout << ec.message() << std::endl;
    }
};



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();
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM