diff --git a/include/asio/detail/config.hpp b/include/asio/detail/config.hpp index 1172b870cb..cb960b02df 100644 --- a/include/asio/detail/config.hpp +++ b/include/asio/detail/config.hpp @@ -1658,4 +1658,38 @@ # define ASIO_VERSIONED_NAME(name) asio_ ## name #endif // defined(ASIO_VERSION_NAMESPACE) +// Named C++20 modules require CPOs (customization point objects) to have +// external linkage. The anonymous-namespace singleton idiom used by asio's +// prefer/require/query/require_concept CPOs creates TU-local entities that +// GCC 15+ (and future Clang in strict mode) reject when those headers are +// #included inside a named module's global module fragment. +// +// Define ASIO_CPO_INLINE_CONSTEXPR to replace the anonymous-namespace +// reference pattern with an inline constexpr variable. This is safe for all +// compilers: the impl types are stateless function objects with constexpr +// constructors, so `inline constexpr impl cpo{}` has identical ODR semantics. +// This flag is compiler-neutral; enable it for any C++20 named-module build. +#if !defined(ASIO_CPO_INLINE_CONSTEXPR) \ + && !defined(ASIO_DISABLE_CPO_INLINE_CONSTEXPR) +// Automatically activate when the user opts in via a build-system flag. +// In GCC named-module builds, pass -DASIO_GCC_NAMED_MODULES to trigger this. +# if defined(ASIO_GCC_NAMED_MODULES) +# define ASIO_CPO_INLINE_CONSTEXPR 1 +# endif // defined(ASIO_GCC_NAMED_MODULES) +#endif // !defined(ASIO_CPO_INLINE_CONSTEXPR) + +// Namespace-scope `const` variables (non-inline) have internal linkage in +// C++. When included in a named module's global module fragment they become +// TU-local entities; GCC 15+ rejects module functions that reference them. +// ASIO_DETAIL_CONSTEXPR_VAR expands to `inline constexpr` under +// ASIO_CPO_INLINE_CONSTEXPR, giving these variables external linkage and +// making them safe to reference from named modules. +// NOTE: this block must come AFTER the ASIO_CPO_INLINE_CONSTEXPR detection +// above so that the #if test is evaluated with the correct macro state. +#if defined(ASIO_CPO_INLINE_CONSTEXPR) +# define ASIO_DETAIL_CONSTEXPR_VAR inline constexpr +#else +# define ASIO_DETAIL_CONSTEXPR_VAR const +#endif // defined(ASIO_CPO_INLINE_CONSTEXPR) + #endif // ASIO_DETAIL_CONFIG_HPP diff --git a/include/asio/detail/socket_types.hpp b/include/asio/detail/socket_types.hpp index 97585fad11..55c59ec580 100644 --- a/include/asio/detail/socket_types.hpp +++ b/include/asio/detail/socket_types.hpp @@ -99,8 +99,8 @@ ASIO_INLINE_NAMESPACE_BEGIN namespace detail { #if defined(ASIO_WINDOWS_RUNTIME) -const int max_addr_v4_str_len = 256; -const int max_addr_v6_str_len = 256; +ASIO_DETAIL_CONSTEXPR_VAR int max_addr_v4_str_len = 256; +ASIO_DETAIL_CONSTEXPR_VAR int max_addr_v6_str_len = 256; typedef unsigned __int32 u_long_type; typedef unsigned __int16 u_short_type; struct in4_addr_type { u_long_type s_addr; }; @@ -185,10 +185,10 @@ typedef int signed_size_type; # define ASIO_OS_DEF_SA_NOCLDWAIT 0x4 #elif defined(ASIO_WINDOWS) || defined(ASIO_CYGWIN_W32_SOCKETS) typedef SOCKET socket_type; -const SOCKET invalid_socket = INVALID_SOCKET; -const int socket_error_retval = SOCKET_ERROR; -const int max_addr_v4_str_len = 256; -const int max_addr_v6_str_len = 256; +ASIO_DETAIL_CONSTEXPR_VAR SOCKET invalid_socket = INVALID_SOCKET; +ASIO_DETAIL_CONSTEXPR_VAR int socket_error_retval = SOCKET_ERROR; +ASIO_DETAIL_CONSTEXPR_VAR int max_addr_v4_str_len = 256; +ASIO_DETAIL_CONSTEXPR_VAR int max_addr_v6_str_len = 256; typedef sockaddr socket_addr_type; typedef in_addr in4_addr_type; typedef ip_mreq in4_mreq_type; @@ -291,22 +291,22 @@ struct sockaddr_un_type { u_short sun_family; char sun_path[108]; }; # define ASIO_OS_DEF_AI_ADDRCONFIG 0 # endif # if defined (_WIN32_WINNT) -const int max_iov_len = 64; +ASIO_DETAIL_CONSTEXPR_VAR int max_iov_len = 64; # else -const int max_iov_len = 16; +ASIO_DETAIL_CONSTEXPR_VAR int max_iov_len = 16; # endif # define ASIO_OS_DEF_SA_RESTART 0x1 # define ASIO_OS_DEF_SA_NOCLDSTOP 0x2 # define ASIO_OS_DEF_SA_NOCLDWAIT 0x4 #else typedef int socket_type; -const int invalid_socket = -1; -const int socket_error_retval = -1; -const int max_addr_v4_str_len = INET_ADDRSTRLEN; +ASIO_DETAIL_CONSTEXPR_VAR int invalid_socket = -1; +ASIO_DETAIL_CONSTEXPR_VAR int socket_error_retval = -1; +ASIO_DETAIL_CONSTEXPR_VAR int max_addr_v4_str_len = INET_ADDRSTRLEN; #if defined(INET6_ADDRSTRLEN) -const int max_addr_v6_str_len = INET6_ADDRSTRLEN + 1 + IF_NAMESIZE; +ASIO_DETAIL_CONSTEXPR_VAR int max_addr_v6_str_len = INET6_ADDRSTRLEN + 1 + IF_NAMESIZE; #else // defined(INET6_ADDRSTRLEN) -const int max_addr_v6_str_len = 256; +ASIO_DETAIL_CONSTEXPR_VAR int max_addr_v6_str_len = 256; #endif // defined(INET6_ADDRSTRLEN) typedef sockaddr socket_addr_type; typedef in_addr in4_addr_type; @@ -412,10 +412,10 @@ typedef int signed_size_type; # define ASIO_OS_DEF_AI_ADDRCONFIG 0 # endif # if defined(IOV_MAX) -const int max_iov_len = IOV_MAX; +ASIO_DETAIL_CONSTEXPR_VAR int max_iov_len = IOV_MAX; # else // POSIX platforms are not required to define IOV_MAX. -const int max_iov_len = 16; +ASIO_DETAIL_CONSTEXPR_VAR int max_iov_len = 16; # endif # define ASIO_OS_DEF_SA_RESTART SA_RESTART # define ASIO_OS_DEF_SA_NOCLDSTOP SA_NOCLDSTOP @@ -425,9 +425,9 @@ const int max_iov_len = 16; # define ASIO_OS_DEF_SA_NOCLDWAIT 0 # endif // defined(SA_NOCLDWAIT) #endif -const int custom_socket_option_level = 0xA5100000; -const int enable_connection_aborted_option = 1; -const int always_fail_option = 2; +ASIO_DETAIL_CONSTEXPR_VAR int custom_socket_option_level = 0xA5100000; +ASIO_DETAIL_CONSTEXPR_VAR int enable_connection_aborted_option = 1; +ASIO_DETAIL_CONSTEXPR_VAR int always_fail_option = 2; } // namespace detail ASIO_INLINE_NAMESPACE_END diff --git a/include/asio/prefer.hpp b/include/asio/prefer.hpp index 48890a14c8..af42d52f32 100644 --- a/include/asio/prefer.hpp +++ b/include/asio/prefer.hpp @@ -520,12 +520,16 @@ const T static_instance::instance = {}; } // namespace ASIO_VERSIONED_NAME(prefer_fn) namespace asio { ASIO_INLINE_NAMESPACE_BEGIN +#if defined(ASIO_CPO_INLINE_CONSTEXPR) +inline constexpr ASIO_VERSIONED_NAME(prefer_fn)::impl prefer{}; +#else // defined(ASIO_CPO_INLINE_CONSTEXPR) namespace { static constexpr const ASIO_VERSIONED_NAME(prefer_fn)::impl& prefer = ASIO_VERSIONED_NAME(prefer_fn)::static_instance<>::instance; } // namespace +#endif // defined(ASIO_CPO_INLINE_CONSTEXPR) typedef ASIO_VERSIONED_NAME(prefer_fn)::impl prefer_t; diff --git a/include/asio/query.hpp b/include/asio/query.hpp index ebb1791db4..051e191433 100644 --- a/include/asio/query.hpp +++ b/include/asio/query.hpp @@ -257,12 +257,16 @@ const T static_instance::instance = {}; } // namespace ASIO_VERSIONED_NAME(query_fn) namespace asio { ASIO_INLINE_NAMESPACE_BEGIN +#if defined(ASIO_CPO_INLINE_CONSTEXPR) +inline constexpr ASIO_VERSIONED_NAME(query_fn)::impl query{}; +#else // defined(ASIO_CPO_INLINE_CONSTEXPR) namespace { static constexpr const ASIO_VERSIONED_NAME(query_fn)::impl& query = ASIO_VERSIONED_NAME(query_fn)::static_instance<>::instance; } // namespace +#endif // defined(ASIO_CPO_INLINE_CONSTEXPR) typedef ASIO_VERSIONED_NAME(query_fn)::impl query_t; diff --git a/include/asio/require.hpp b/include/asio/require.hpp index 3a1029b2a3..78884883ee 100644 --- a/include/asio/require.hpp +++ b/include/asio/require.hpp @@ -375,12 +375,16 @@ const T static_instance::instance = {}; } // namespace ASIO_VERSIONED_NAME(require_fn) namespace asio { ASIO_INLINE_NAMESPACE_BEGIN +#if defined(ASIO_CPO_INLINE_CONSTEXPR) +inline constexpr ASIO_VERSIONED_NAME(require_fn)::impl require{}; +#else // defined(ASIO_CPO_INLINE_CONSTEXPR) namespace { static constexpr const ASIO_VERSIONED_NAME(require_fn)::impl& require = ASIO_VERSIONED_NAME(require_fn)::static_instance<>::instance; } // namespace +#endif // defined(ASIO_CPO_INLINE_CONSTEXPR) typedef ASIO_VERSIONED_NAME(require_fn)::impl require_t; diff --git a/include/asio/require_concept.hpp b/include/asio/require_concept.hpp index d1ba31654e..3befd3b286 100644 --- a/include/asio/require_concept.hpp +++ b/include/asio/require_concept.hpp @@ -285,6 +285,9 @@ const T static_instance::instance = {}; } // namespace ASIO_VERSIONED_NAME(require_concept_fn) namespace asio { ASIO_INLINE_NAMESPACE_BEGIN +#if defined(ASIO_CPO_INLINE_CONSTEXPR) +inline constexpr ASIO_VERSIONED_NAME(require_concept_fn)::impl require_concept{}; +#else // defined(ASIO_CPO_INLINE_CONSTEXPR) namespace { static constexpr const ASIO_VERSIONED_NAME(require_concept_fn)::impl& @@ -292,6 +295,7 @@ static constexpr const ASIO_VERSIONED_NAME(require_concept_fn)::impl& require_concept_fn)::static_instance<>::instance; } // namespace +#endif // defined(ASIO_CPO_INLINE_CONSTEXPR) typedef ASIO_VERSIONED_NAME(require_concept_fn)::impl require_concept_t;