2

I'm writing a C++11 networking library that uses Boost.Asio under the hood. I want to expose an API that allows users to use stackful coroutines.

boost::asio::yield_context overloads the [] operator so that an asynchronous operation may set an error code instead of throwing an exception. For example:

std::size_t n = my_socket.async_read_some(buffer, yield[ec]);
if (ec)
{
    // An error occurred.
}

My library uses std::error_code and std::system_error to report errors. My question is how can I make boost::asio::yield_context set a std::error_code instead of boost::system::error_code? I'd like users of my library to be able to do this:

std::error_code ec;
auto result = remoteProdedureCall(args, yield[ec]);
if (ec)
    handleError();

where remoteProcedureCall would look something like:

Result remoteProcedureCall(Args args, boost::asio::yield_context yield)
{
    //...
    boost::asio::async_write(socket_, argsBuffer, yield);
    boost::asio::async_read(socket_, resultBuffer, yield);
    if (invalidResult())
        // Return a std::error_code via the yield object somehow???
        // (My error codes belong to a custom error_category)
    // ...
    return result;
}

P.S. I should indicate that my library uses error codes that belong to a custom error_category.

Emile Cormier
  • 28,391
  • 15
  • 94
  • 122
  • 1
    I realize that what I'm asking for may be impossible, but someone may find a clever trick or hack that would do the job. Or perhaps I'm going about this the wrong way. – Emile Cormier Jan 15 '15 at 22:31
  • An idea might be to use some kind of wrapper around `boost::asio::basic_yield_context`. – Emile Cormier Jan 15 '15 at 22:33
  • I could overload `operator+(yield_context&, std::error_code&)` so that it returns a `yield_context_wrapper` that contains the original `yield_context`, plus a pointer to the user's `std::error_code` variable. I would also provide a `yield_context_wrapper` conversion constructor that takes a `yield_context`. My API functions would then take a `yield_context_wrapper` argument instead of `yield_context`, thus gaining access to the user's `std::error_code` variable. – Emile Cormier Jan 15 '15 at 22:58

2 Answers2

2

I ended-up doing something much simpler than trying to coerce yield_context into setting an std::error_code:

// This overload sets a std::error_code if there's an error.
Result remoteProcedureCall(Args args, boost::asio::yield_context yield,
                           std::error_code& userErrorCode)
{
    //...
    boost::system::error_code ec;
    boost::async_write(socket_, buffer, yield[ec];
    if (ec)
    {
        userErrorCode = toStdErrorCode(ec);
        return Result();
    }
    // ...
    if (someNonBoostError)
    {
        userErrorCode = make_error_code(myCustomErrorCode);
        return Result();
    }
    // ...
    return result;
}

// This overload throws an exception if there's an error
Result remoteProcedureCall(Args args, boost::asio::yield_context yield)
{
    std::error_code ec;
    auto result = remoteProcedureCall(args, yield, ec);
    if (ec)
        throw std::system_error(ec);
    return result;
}

where toStdErrorCode converts from a boost::system::error_code to a std::error_code, as per Sam's answer (also see this related question).


Even simpler would be to make remoteProcedureCall take an optional pointer to an error_code. This avoids having duplicate functions: one that sets the error code, and another that throws an exception.

Community
  • 1
  • 1
Emile Cormier
  • 28,391
  • 15
  • 94
  • 122
1

This should work, assuming ec is a boost::system_error::error_code

std::make_error_code( static_cast<std::errc::errc>(ec.value()) );

I believe the boost::system::errc::errc_t enums are mapped to the same value in std::errc.

Sam Miller
  • 23,808
  • 4
  • 67
  • 87
  • Thanks, but I need to go the other way around. My function wants to return a `std::error_code` via the `yield` object. My error codes (which belong to custom error categories) don't map to the ones in `std::errc`, nor to `boost::system::errc`. – Emile Cormier Jan 15 '15 at 22:10