Skip to content

net: declare io_context in the namespace Asio actually uses - #241

Merged
jcelerier merged 3 commits into
masterfrom
fix/asio-version-namespace
Aug 5, 2026
Merged

net: declare io_context in the namespace Asio actually uses#241
jcelerier merged 3 commits into
masterfrom
fix/asio-version-namespace

Conversation

@jcelerier

Copy link
Copy Markdown
Member

backends/net/config.hpp forward-declares boost::asio::io_context so the configuration structs can hold a pointer to it without pulling in io_context.hpp. Since Boost 1.91 that declaration can name a different type than the real one.

The problem

Asio gained an inline version namespace in 1.91. With BOOST_ASIO_ENABLE_VERSION_NAMESPACE, its types live in boost::asio::v<version>_<tags>, tagged with the configuration they were built with, so two differently-configured Asios cannot silently share a mangled name. A declaration in plain boost::asio is then a second, distinct io_context, and every use is ambiguous:

boost/asio/io_context.hpp: error C2872: 'io_context': ambiguous symbol
  libremidi/backends/net/config.hpp(9): could be 'boost::asio::io_context'
  boost/asio/io_context.hpp(193):      or  'boost::asio::v103801_bdemo::io_context'

Every translation unit including this header fails to compile.

The fix

Declare it inside whichever namespace Asio itself would use:

#include <boost/version.hpp>
#if BOOST_VERSION >= 109100
#include <boost/asio/detail/config.hpp>
namespace boost::asio {
BOOST_ASIO_INLINE_NAMESPACE_BEGIN
class io_context;
BOOST_ASIO_INLINE_NAMESPACE_END
}
#else
namespace boost::asio { class io_context; }
#endif

The macros are empty when the feature is off, so this is the same plain declaration as before in that case — it does not impose the version namespace on anyone. They only exist from 1.91 on, hence the version check rather than an unconditional include. boost/version.hpp is cheap, and detail/config.hpp is far cheaper than the io_context.hpp this declaration exists to avoid.

Also declares it as class rather than struct, matching Asio (io_context.hpp:193) — the mismatch is what -Wmismatched-tags warns about.

Why it matters downstream

ossia/libossia#917 needs BOOST_ASIO_ENABLE_VERSION_NAMESPACE on Windows: with Boost 1.91, Boost.Asio's global symbols lose their boost_ prefix and collide with the standalone Asio that score's LSL addon bundles (duplicate symbol: asio_signal_handler). This header was the only thing blocking it.

Verified

Building libossia with MSVC / VS2026 against Boost 1.91, with the version namespace enabled:

  • before: libremidi.cpp, observer.cpp, midi_in.cpp, midi_out.cpp all fail with C2872
  • after: 593/593 targets, zero errors, and the symbol is emitted as asio_v103801_bdemo_signal_handler rather than the bare asio_signal_handler

🤖 Generated with Claude Code

https://claude.ai/code/session_01XGta2LPAedDP3Txvyq8kTW

backends/net/config.hpp forward-declares boost::asio::io_context so that the
configuration structs can hold a pointer to it without pulling in
io_context.hpp. Since Boost 1.91 that declaration can name a different type
than the real one.

Asio gained an inline version namespace in 1.91: with
BOOST_ASIO_ENABLE_VERSION_NAMESPACE its types live in
boost::asio::v<version>_<tags>, tagged with the configuration they were built
with, so two differently-configured Asios cannot silently share a mangled name.
A declaration in plain boost::asio is then a second, distinct io_context, and
every use of boost::asio::io_context is ambiguous between the two:

  boost/asio/io_context.hpp: error C2872: 'io_context': ambiguous symbol
    libremidi/backends/net/config.hpp(9): could be 'boost::asio::io_context'
    boost/asio/io_context.hpp(193):      or  'boost::asio::v103801_bdemo::io_context'

Declare it inside whichever namespace Asio itself would use. Asio's config
header defines the namespace macros - as empty, when the feature is off - so
pulling it in first both answers the question and costs far less than the
io_context.hpp this declaration exists to avoid; the macros only exist from
1.91 on, which is the version test, no version number needed. This header is
reachable in builds with no Boost at all, so the include is guarded by
__has_include, the same way config.hpp already tests for Boost.

Also declare it as a class rather than a struct, which is how Asio declares it;
the mismatch is what -Wmismatched-tags warns about.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XGta2LPAedDP3Txvyq8kTW
@jcelerier
jcelerier force-pushed the fix/asio-version-namespace branch from 5ca9375 to 80b74b9 Compare August 5, 2026 13:39
jcelerier and others added 2 commits August 5, 2026 10:53
The winmm, winmidi, winuwp and kdmapi headers each defined both macros
unconditionally, as 1. Anything that had already defined them differently -
Boost.Asio's config defines WIN32_LEAN_AND_MEAN empty, and build systems
commonly pass them on the command line - made every translation unit reaching
these headers warn, which under -Werror is a build failure.

Guard them, the way backends/linux/dylib_loader.hpp already does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XGta2LPAedDP3Txvyq8kTW
Removed comments regarding Boost.Asio version namespace handling.
@jcelerier
jcelerier merged commit 127fc87 into master Aug 5, 2026
90 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant