diff --git a/CMakeLists.txt b/CMakeLists.txt index 016e78e25..197888d1f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,7 +42,8 @@ option(BOOST_HANA_ENABLE_DEBUG_MODE "Enable Hana's debug mode." OFF) option(BOOST_HANA_ENABLE_STRING_UDL "Enable the GNU extension allowing the special string literal operator\ - template, which enables the _s suffix for creating compile-time strings." ON) +template, which enables the _s suffix for creating compile-time strings\ +(unnecessary if you have a C++20 compiler)." ON) option(BOOST_HANA_ENABLE_EXCEPTIONS "Build with exceptions enabled. Note that Hana does not make use of exceptions,\ diff --git a/example/string/literal.cpp b/example/string/literal.cpp index b20e1dd71..237963f03 100644 --- a/example/string/literal.cpp +++ b/example/string/literal.cpp @@ -13,7 +13,7 @@ namespace hana = boost::hana; using namespace hana::literals; -// By default, this is disabled +// This requires C++20 or a compiler extension which is disabled by default #ifdef BOOST_HANA_CONFIG_ENABLE_STRING_UDL constexpr auto str = "Hello world!"_s; diff --git a/include/boost/hana/config.hpp b/include/boost/hana/config.hpp index 7b7805696..2ffbc9ec4 100644 --- a/include/boost/hana/config.hpp +++ b/include/boost/hana/config.hpp @@ -80,6 +80,35 @@ Distributed under the Boost Software License, Version 1.0. # endif #endif +////////////////////////////////////////////////////////////////////////////// +// C++20 string literal UDL suport +////////////////////////////////////////////////////////////////////////////// +#if defined(_MSC_VER) && !defined(__clang__) +# if _MSVC_LANG > 201703 && _MSC_FULL_VER >= 192829921 +# define BOOST_HANA_CONFIG_HAS_CXX20_STRING_UDL +# endif +#elif defined(__clang__) +# if (__cplusplus > 201703 && \ + BOOST_HANA_CONFIG_CLANG >= BOOST_HANA_CONFIG_VERSION(12, 0, 0)) +# define BOOST_HANA_CONFIG_HAS_CXX20_STRING_UDL +# if BOOST_HANA_CONFIG_CLANG < BOOST_HANA_CONFIG_VERSION(18, 0, 0) +# define BOOST_HANA_CONFIG_CXX20_STRING_UDL_CLANG_WORKAROUND +# endif +# endif +#elif defined(__GNUC__) +# if (__cplusplus > 201703 && \ + BOOST_HANA_CONFIG_GCC >= BOOST_HANA_CONFIG_VERSION(9, 3, 0)) +# define BOOST_HANA_CONFIG_HAS_CXX20_STRING_UDL +# endif +#elif (__cpp_deduction_guides >= 201703 && \ + __cpp_nontype_template_args >= 201911) +# define BOOST_HANA_CONFIG_HAS_CXX20_STRING_UDL +#endif +#if defined(BOOST_HANA_CONFIG_HAS_CXX20_STRING_UDL) && \ + !defined(BOOST_HANA_CONFIG_ENABLE_STRING_UDL) +# define BOOST_HANA_CONFIG_ENABLE_STRING_UDL +#endif + ////////////////////////////////////////////////////////////////////////////// // Caveats and other compiler-dependent options ////////////////////////////////////////////////////////////////////////////// @@ -145,17 +174,21 @@ Distributed under the Boost Software License, Version 1.0. # define BOOST_HANA_CONFIG_DISABLE_CONCEPT_CHECKS #endif -#if defined(BOOST_HANA_DOXYGEN_INVOKED) +#if defined(BOOST_HANA_DOXYGEN_INVOKED) && \ + !defined(BOOST_HANA_CONFIG_ENABLE_STRING_UDL) //! @ingroup group-config - //! Enables usage of the "string literal operator template" GNU extension. + //! Enables usage of the "string literal operator template" GNU extension + //! before C++20. //! - //! That operator is not part of the language yet, but it is supported by - //! both Clang and GCC. This operator allows Hana to provide the nice `_s` - //! user-defined literal for creating compile-time strings. + //! C++20 added functionality allowing Hana to provide the nice `_s` + //! user-defined literal for creating compile-time strings; prior to that, + //! both Clang and GCC supported an extension allowing the same + //! functionality but via a different mechanism. //! - //! When this macro is not defined, the GNU extension will be not used - //! by Hana. Because this is a non-standard extension, the macro is not - //! defined by default. + //! When C++20 support is detected, this macro is defined by default and + //! the C++20 mechanism is used. Otherwise, if this macro is not defined, + //! the macro is not defined by default and the GNU extension will not be + //! used by Hana because it is a non-standard extension. # define BOOST_HANA_CONFIG_ENABLE_STRING_UDL #endif diff --git a/include/boost/hana/fwd/string.hpp b/include/boost/hana/fwd/string.hpp index 91ee4de57..b983ade56 100644 --- a/include/boost/hana/fwd/string.hpp +++ b/include/boost/hana/fwd/string.hpp @@ -233,13 +233,21 @@ namespace boost { namespace hana { #endif #ifdef BOOST_HANA_CONFIG_ENABLE_STRING_UDL +# ifdef BOOST_HANA_CONFIG_HAS_CXX20_STRING_UDL + namespace string_detail { + template + struct literal_helper; + } +# endif + namespace literals { //! Creates a compile-time string from a string literal. //! @relatesalso boost::hana::string //! //! The string literal is parsed at compile-time and the result is - //! returned as a `hana::string`. This feature is an extension that - //! is disabled by default; see below for details. + //! returned as a `hana::string`. This feature requires C++20 or a + //! compiler extension that is disabled by default; see below for + //! details. //! //! @note //! Only narrow string literals are supported right now; support for @@ -248,15 +256,15 @@ namespace boost { namespace hana { //! [Hana.issue80] if you need this. //! //! @warning - //! This user-defined literal is an extension which requires a special - //! string literal operator that is not part of the standard yet. - //! That operator is supported by both Clang and GCC, and several - //! proposals were made for it to enter C++17. However, since it is - //! not standard, it is disabled by default and defining the - //! `BOOST_HANA_CONFIG_ENABLE_STRING_UDL` config macro is required - //! to get this operator. Hence, if you want to stay safe, just use - //! the `BOOST_HANA_STRING` macro instead. If you want to be fast and - //! furious (I do), define `BOOST_HANA_CONFIG_ENABLE_STRING_UDL`. + //! This user-defined literal requires a special string literal operator + //! that was added to the standard in C++20. Prior to that, an extension + //! supported by both Clang and GCC provides similar functionality, but + //! since it is not standard, it is disabled by default in pre=C++20 + //! mode and defining the `BOOST_HANA_CONFIG_ENABLE_STRING_UDL` config + //! macro is required to get this operator. Hence, if you do not have + //! C++20 and want to stay safe, just use the `BOOST_HANA_STRING` macro + //! instead. If you want to be fast and furious (I do), define + //! `BOOST_HANA_CONFIG_ENABLE_STRING_UDL`. //! //! //! Example @@ -264,8 +272,18 @@ namespace boost { namespace hana { //! @include example/string/literal.cpp //! //! [Hana.issue80]: https://github.com/boostorg/hana/issues/80 +# if defined(BOOST_HANA_DOXYGEN_INVOKED) + template + constexpr auto operator"" _s(); +# elif defined(BOOST_HANA_CONFIG_CXX20_STRING_UDL_CLANG_WORKAROUND) + // Older versions of clang get confused by a forward declaration. +# elif defined(BOOST_HANA_CONFIG_HAS_CXX20_STRING_UDL) + template + constexpr auto operator"" _s(); +# else template constexpr auto operator"" _s(); +# endif } #endif }} // end namespace boost::hana diff --git a/include/boost/hana/string.hpp b/include/boost/hana/string.hpp index 7c1c4741d..b2d0e06db 100644 --- a/include/boost/hana/string.hpp +++ b/include/boost/hana/string.hpp @@ -109,10 +109,34 @@ namespace boost { namespace hana { /**/ #ifdef BOOST_HANA_CONFIG_ENABLE_STRING_UDL +# ifdef BOOST_HANA_CONFIG_HAS_CXX20_STRING_UDL + namespace string_detail { + template + struct literal_helper { + constexpr literal_helper(char const (&s)[N]) { + for (unsigned i = 0; i != N; ++i) + buf[i] = s[i]; + } + static constexpr unsigned size() { return N - 1; } + char buf[N] = {}; + }; + template + literal_helper(char const (&)[N]) -> literal_helper; + } +# endif + ////////////////////////////////////////////////////////////////////////// // _s user-defined literal ////////////////////////////////////////////////////////////////////////// namespace literals { +# ifdef BOOST_HANA_CONFIG_HAS_CXX20_STRING_UDL + template + constexpr auto operator"" _s() { + return [](std::index_sequence) { + return hana::string_c; + }(std::make_index_sequence()); + } +# else template constexpr auto operator"" _s() { static_assert(std::is_same::value, @@ -121,6 +145,7 @@ namespace boost { namespace hana { "if you need support for fancier types of compile-time strings."); return hana::string_c; } +# endif } #endif