简体   繁体   中英

Wrapping boost async function into coroutine

Hey I'm trying to wrap class provided by third party library to use Boost coroutines. The library also uses Boost but for the purpose of async operations uses completition handlers. Below is a simplified example that one could try. I think I'm close but for some reason awaitable returned from async_connect is of type void, whereas I would like to boost::error_code being returned. What am I missing?

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

EDIT It seems that I've found something. I slightly modified code, removed all code not needed for the purpose of this example

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

As you can see I changed signatute to take both std::exception_ptr and int, and this resulted in int being returned properly from coroutine. But I don't get why exactly this signature is required, in particular std::exception_ptr as first parameter.

I followed this and this

Well I managed to let's say solve it. But I don't get why the signature of handler has to be in a form of void(std:::exception_ptr, error_code) in order to return error_code. Also I failed to find it in boost documentation. I would be grateful if anyone could provide some kind of explanation.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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