From 6797f0c39a4ef13061cbc3bb850c35af7428fdc4 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 31 Aug 2024 10:08:08 -0700 Subject: [PATCH] Cleanup compile-time checks --- doc/index.md | 2 +- include/fmt/base.h | 64 +++++++++++++++++++++---------------------- include/fmt/compile.h | 2 +- include/fmt/format.h | 6 ++-- include/fmt/xchar.h | 3 +- test/format-test.cc | 2 +- 6 files changed, 40 insertions(+), 39 deletions(-) diff --git a/doc/index.md b/doc/index.md index 457857c4..b170f9f2 100644 --- a/doc/index.md +++ b/doc/index.md @@ -122,7 +122,7 @@ hide:

The library is highly portable and requires only a minimal subset of - C++11 features which are available in GCC 4.8, Clang 3.4, MSVC 19.0 + C++11 features which are available in GCC 4.9, Clang 3.4, MSVC 19.0 (2015) and later. Newer compiler and standard library features are used if available, and enable additional functionality.

diff --git a/include/fmt/base.h b/include/fmt/base.h index 017d582b..48e3d340 100644 --- a/include/fmt/base.h +++ b/include/fmt/base.h @@ -2765,7 +2765,7 @@ FMT_CONSTEXPR FMT_INLINE auto parse_replacement_field(const Char* begin, return begin + 1; } -template +template FMT_CONSTEXPR void parse_format_string(basic_string_view format_str, Handler&& handler) { auto begin = format_str.data(); @@ -2938,21 +2938,6 @@ template class format_string_checker { // A base class for compile-time strings. struct compile_string {}; -template -using is_compile_string = std::is_base_of; - -// Reports a compile-time error if S is not a valid format string for T. -template ::value)> -void check_format_string(S fmt) { - using char_type = typename S::char_type; - FMT_CONSTEXPR auto s = basic_string_view(fmt); - using checker = format_string_checker...>; - FMT_CONSTEXPR bool error = (parse_format_string(s, checker(s)), true); - ignore_unused(error); -} -template ::value)> -FMT_ALWAYS_INLINE void check_format_string(S) {} - // Use vformat_args and avoid type_identity to keep symbols short. template struct vformat_args { using type = basic_format_args>; @@ -3027,36 +3012,49 @@ template class basic_format_string { private: basic_string_view str_; - public: - template < - typename S, - FMT_ENABLE_IF( - std::is_convertible>::value || - (detail::is_compile_string::value && - std::is_constructible, const S&>::value))> - FMT_CONSTEVAL FMT_ALWAYS_INLINE basic_format_string(const S& s) : str_(s) { + using checker = detail::format_string_checker...>; + + template + FMT_CONSTEXPR FMT_ALWAYS_INLINE void check(const S& s) { static_assert( detail::count< (std::is_base_of>::value && - std::is_reference::value)...>() == 0, + std::is_reference::value)...>() == 0, "passing views as lvalues is disallowed"); + detail::ignore_unused(s); #if FMT_USE_CONSTEVAL if constexpr (detail::count_named_args() == detail::count_statically_named_args()) { - using checker = - detail::format_string_checker...>; - detail::parse_format_string(str_, checker(s)); + detail::parse_format_string(str_, checker(str_)); } -#else -# ifdef FMT_ENFORCE_COMPILE_STRING +#endif + } + + public: + // Reports a compile-time error if S is not a valid format string for Args. + template >::value)> + FMT_CONSTEVAL FMT_ALWAYS_INLINE basic_format_string(const S& s) : str_(s) { + check(s); +#ifdef FMT_ENFORCE_COMPILE_STRING static_assert( - detail::is_compile_string::value, + FMT_USE_CONSTEVAL && sizeof(S) != 0, "FMT_ENFORCE_COMPILE_STRING requires all format strings to use " "FMT_STRING."); -# endif - detail::check_format_string(s); #endif } + template ::value&& + std::is_constructible, + const S&>::value)> + FMT_CONSTEVAL FMT_ALWAYS_INLINE basic_format_string(const S& s) : str_(s) { + check(s); + if (FMT_USE_CONSTEVAL) return; + FMT_CONSTEXPR auto v = basic_string_view(S()); + FMT_CONSTEXPR int ignore = (detail::parse_format_string(v, checker(v)), 0); + detail::ignore_unused(ignore); + } basic_format_string(runtime_format_string fmt) : str_(fmt.str) {} FMT_ALWAYS_INLINE operator basic_string_view() const { return str_; } diff --git a/include/fmt/compile.h b/include/fmt/compile.h index 63fa19a9..4a5dc71d 100644 --- a/include/fmt/compile.h +++ b/include/fmt/compile.h @@ -36,7 +36,7 @@ struct is_compiled_string : std::is_base_of {}; * std::string s = fmt::format(FMT_COMPILE("{}"), 42); */ #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) -# define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::compiled_string, explicit) +# define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::compiled_string) #else # define FMT_COMPILE(s) FMT_STRING(s) #endif diff --git a/include/fmt/format.h b/include/fmt/format.h index 6278489a..c7d75fd1 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1869,7 +1869,7 @@ inline auto find_escape(const char* begin, const char* end) return result; } -#define FMT_STRING_IMPL(s, base, explicit) \ +#define FMT_STRING_IMPL(s, base) \ [] { \ /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \ /* Use a macro-like name to avoid shadowing warnings. */ \ @@ -1880,6 +1880,8 @@ inline auto find_escape(const char* begin, const char* end) return fmt::detail_exported::compile_string_to_view(s); \ } \ }; \ + typename FMT_COMPILE_STRING::char_type FMT_CHAR = {}; \ + (void)FMT_CHAR; \ return FMT_COMPILE_STRING(); \ }() @@ -1891,7 +1893,7 @@ inline auto find_escape(const char* begin, const char* end) * // A compile-time error because 'd' is an invalid specifier for strings. * std::string s = fmt::format(FMT_STRING("{:d}"), "foo"); */ -#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string, ) +#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string) template auto write_codepoint(OutputIt out, char prefix, uint32_t cp) -> OutputIt { diff --git a/include/fmt/xchar.h b/include/fmt/xchar.h index b1f39ed2..35618de9 100644 --- a/include/fmt/xchar.h +++ b/include/fmt/xchar.h @@ -34,7 +34,8 @@ struct format_string_char< }; template -struct format_string_char::value>> { +struct format_string_char< + S, enable_if_t::value>> { using type = typename S::char_type; }; diff --git a/test/format-test.cc b/test/format-test.cc index e6be9935..75baa290 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -2203,7 +2203,7 @@ TEST(format_test, vformat_to) { fmt::vformat_to(std::back_inserter(s), "{}", args); EXPECT_EQ(s, "42"); s.clear(); - fmt::vformat_to(std::back_inserter(s), FMT_STRING("{}"), args); + fmt::vformat_to(std::back_inserter(s), "{}", args); EXPECT_EQ(s, "42"); }