Search code examples
c++dllerror-handlingboost-asioasio

error_category mismatch in asio when used across dlls


I have a problem with handling asio::error_code values when they are received from another dll or executable. For instance, I may run an asynchronous operation with a handler:

socket.async_receive([](const asio::error_code& errorCode)
{
    if (errorCode == asio::error::operation_aborted)
       return;
}

If the handler is created within a dll, and an errorCode is passed from another dll or executable, operator== would return false, since their error_category values differ. For example:

(gdb) info sym 0x55f1c0
asio::system_category()::instance in section .data of C:\dev\builds\UWCASdk\Debug-MinGW-w64\Sandbox\ServerL410F\bin\sb.ServerL410F.exe
(gdb) info sym 0x6f0420d0
asio::system_category()::instance in section .data of C:\dev\builds\UWCASdk\Debug-MinGW-w64\Sandbox\ServerL410F\bin\libproviders.protobuf.client_d.dll

Clearly, these two instances are defined in different units and I basically have ODR violation.

instance is defined within asio/impl/error_code.ipp:

const error_category& system_category()
{
  static detail::system_category instance;
  return instance;
}

ASIO documentation doesn't mention anything about error_cateegory when handling errors - it only specifies enum values, so using errorCode.value == asio::error::operation_aborted seems to be a valid workaround. But still this seems ugly.

  • Is it somehow possible to compare errors across dlls as intended (with overloaded operator==)?
  • Is it normal or expected to compare only errorCode.value() against enums?

Solution

  • This is what I meant with this comment

    though boost::system::system_error could invite issues back

    The trouble is, error categories are global singleton instances, with object identity (i.e. compared for equality by their address).

    You'r ending up with multiple instances in multiple modules. The usual solution is to

    • dynamically link to Boost System, so all libraries use the same copy (this does however sometimes run into initialization order issues)
    • if that doesn't solve it, make sure all modules actually load the same library (version?)
    • In recent boosts I think there's the option to build Boost System completely header-only. This may or may not involve new-fangled C++14 inline, I haven't checked.

    If all else fails, do your own translation. There's related issues that might give you ideas:


    Is it normal or expected to compare only errorCode.value() against enums?

    No it is not. According to some sources Boost as well as standard library promise to map generic error category to errc which is standard - so you could do that, but you still have to figure out whether that is the category, so doesn't help your scenario.