diff --git a/include/fmt/compile.h b/include/fmt/compile.h index 9528b57d..00000c92 100644 --- a/include/fmt/compile.h +++ b/include/fmt/compile.h @@ -164,7 +164,8 @@ struct is_compiled_string : std::is_base_of {}; #endif #if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS -template Str> +template Str> struct udl_compiled_string : compiled_string { using char_type = Char; constexpr operator basic_string_view() const { @@ -622,7 +623,7 @@ void print(const S& format_str, const Args&... args) { #if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS inline namespace literals { -template +template constexpr detail::udl_compiled_string< remove_cvref_t, sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str> diff --git a/include/fmt/format.h b/include/fmt/format.h index d51ea955..3e839f74 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -778,6 +778,33 @@ FMT_INLINE auto make_args_checked(const S& fmt, return {args...}; } +// compile-time support +namespace detail_exported { +#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +template struct fixed_string { + constexpr fixed_string(const Char (&str)[N]) { + detail::copy_str(static_cast(str), + str + N, data); + } + Char data[N]{}; +}; +#endif + +// Converts a compile-time string to basic_string_view. +template +constexpr auto compile_string_to_view(const Char (&s)[N]) + -> basic_string_view { + // Remove trailing NUL character if needed. Won't be present if this is used + // with a raw character array (i.e. not defined as a string). + return {s, N - (std::char_traits::to_int_type(s[N - 1]) == 0 ? 1 : 0)}; +} +template +constexpr auto compile_string_to_view(detail::std_string_view s) + -> basic_string_view { + return {s.data(), s.size()}; +} +} // namespace detail_exported + FMT_BEGIN_DETAIL_NAMESPACE inline void throw_format_error(const char* message) { @@ -2098,20 +2125,6 @@ FMT_CONSTEXPR void handle_dynamic_spec(int& value, } } -// Converts a compile-time string to basic_string_view. -template -constexpr auto compile_string_to_view(const Char (&s)[N]) - -> basic_string_view { - // Remove trailing NUL character if needed. Won't be present if this is used - // with a raw character array (i.e. not defined as a string). - return {s, N - (std::char_traits::to_int_type(s[N - 1]) == 0 ? 1 : 0)}; -} -template -constexpr auto compile_string_to_view(std_string_view s) - -> basic_string_view { - return {s.data(), s.size()}; -} - #define FMT_STRING_IMPL(s, base, explicit) \ [] { \ /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \ @@ -2120,7 +2133,7 @@ constexpr auto compile_string_to_view(std_string_view s) using char_type = fmt::remove_cvref_t; \ FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit \ operator fmt::basic_string_view() const { \ - return fmt::detail::compile_string_to_view(s); \ + return fmt::detail_exported::compile_string_to_view(s); \ } \ }; \ return FMT_COMPILE_STRING(); \ @@ -2138,16 +2151,6 @@ constexpr auto compile_string_to_view(std_string_view s) */ #define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::compile_string, ) -#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS -template struct fixed_string { - constexpr fixed_string(const Char (&str)[N]) { - copy_str(static_cast(str), str + N, - data); - } - Char data[N]{}; -}; -#endif - #if FMT_USE_USER_DEFINED_LITERALS template struct udl_formatter { basic_string_view str; @@ -2159,7 +2162,8 @@ template struct udl_formatter { }; # if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS -template Str> +template Str> struct statically_named_arg : view { static constexpr auto name = Str.data; @@ -2167,14 +2171,18 @@ struct statically_named_arg : view { statically_named_arg(const T& v) : value(v) {} }; -template Str> +template Str> struct is_named_arg> : std::true_type {}; -template Str> +template Str> struct is_statically_named_arg> : std::true_type {}; -template Str> struct udl_arg { +template Str> +struct udl_arg { template auto operator=(T&& value) const { return statically_named_arg(std::forward(value)); } @@ -2721,7 +2729,7 @@ inline namespace literals { \endrst */ #if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS -template +template constexpr auto operator""_a() -> detail::udl_arg, sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str> { diff --git a/src/fmt.cc b/src/fmt.cc index b4910336..d0d6e7fb 100644 --- a/src/fmt.cc +++ b/src/fmt.cc @@ -1,4 +1,8 @@ module; +#ifndef __cpp_modules +# error Module not supported. +#endif + // put all implementation-provided headers into the global module fragment // to prevent attachment to this module #if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER) @@ -76,10 +80,6 @@ export module fmt; } \ export { -#if defined(_MSC_FULL_VER) && _MSC_FULL_VER > 192930036 -#define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 0 -#endif - // all library-provided declarations and definitions // must be in the module purview to be exported #include "fmt/args.h" diff --git a/test/module-test.cc b/test/module-test.cc index 5a5171a1..cf18a324 100644 --- a/test/module-test.cc +++ b/test/module-test.cc @@ -445,3 +445,11 @@ TEST(module_test, has_formatter) { TEST(module_test, is_formattable) { EXPECT_FALSE(fmt::is_formattable::value); } + +TEST(module_test, compile_format_string) { + using namespace fmt::literals; + EXPECT_EQ("42", fmt::format("{0:x}"_cf, 0x42)); + EXPECT_EQ(L"42", fmt::format(L"{:}"_cf, 42)); + EXPECT_EQ("4.2", fmt::format("{arg:3.1f}"_cf, "arg"_a = 4.2)); + EXPECT_EQ(L" 42", fmt::format(L"{arg:>3}"_cf, L"arg"_a = L"42")); +}