From e9c1c415b85bc0d6fdf5afa4269400bda9401aa2 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Tue, 18 May 2021 19:38:52 -0700 Subject: [PATCH] Improve compile-time checks --- include/fmt/core.h | 100 +++--------- include/fmt/format.h | 81 ++++++++-- include/fmt/wchar.h | 14 +- test/chrono-test.cc | 64 +++++--- test/format-test.cc | 357 ++++++++++++++++++++++--------------------- test/locale-test.cc | 6 +- test/ostream-test.cc | 12 +- test/wchar-test.cc | 7 + 8 files changed, 343 insertions(+), 298 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index cbb7aabb..36cbc997 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -1642,27 +1642,6 @@ constexpr format_arg_store make_format_args( return {args...}; } -/** - \rst - Constructs a `~fmt::format_arg_store` object that contains references - to arguments and can be implicitly converted to `~fmt::format_args`. - If ``format_str`` is a compile-time string then `make_args_checked` checks - its validity at compile time. - \endrst - */ -template > -FMT_INLINE auto make_args_checked(const S& format_str, - const remove_reference_t&... args) - -> format_arg_store, remove_reference_t...> { - static_assert( - detail::count<( - std::is_base_of>::value && - std::is_reference::value)...>() == 0, - "passing views as lvalues is disallowed"); - detail::check_format_string(format_str); - return {args...}; -} - /** \rst Returns a named argument to be used in a formatting function. @@ -2668,49 +2647,9 @@ void check_format_string(S format_str) { (void)invalid_format; } -// 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). */ \ - /* Use a macro-like name to avoid shadowing warnings. */ \ - struct FMT_GCC_VISIBILITY_HIDDEN FMT_COMPILE_STRING : base { \ - 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_COMPILE_STRING(); \ - }() - -/** - \rst - Constructs a compile-time format string from a string literal *s*. - - **Example**:: - - // A compile-time error because 'd' is an invalid specifier for strings. - std::string s = fmt::format(FMT_STRING("{:d}"), "foo"); - \endrst - */ -#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::compile_string, ) - template void vformat_to( - buffer>& buf, basic_string_view format_str, + buffer& buf, basic_string_view fmt, basic_format_args)> args, detail::locale_ref loc = {}); @@ -2730,8 +2669,6 @@ struct formatter specs_; public: - FMT_CONSTEXPR formatter() = default; - // Parses format specifiers stopping either at the end of the range or at the // terminating '}'. template @@ -2802,33 +2739,37 @@ struct formatter decltype(ctx.out()); }; +template struct basic_runtime { basic_string_view str; }; + template class basic_format_string { private: basic_string_view str_; public: -#if FMT_COMPILE_TIME_CHECKS - template - consteval basic_format_string(const char (&s)[N]) : str_(s) { - if constexpr (detail::count_named_args() == 0) { - using checker = detail::format_string_checker...>; - detail::parse_format_string(string_view(s, N), checker(s, {})); - } - } -#endif - template >::value)> - basic_format_string(const S& s) : str_(s) { +#if FMT_COMPILE_TIME_CHECKS + consteval +#endif + basic_format_string(const S& s) + : str_(s) { static_assert( detail::count< (std::is_base_of>::value && std::is_reference::value)...>() == 0, "passing views as lvalues is disallowed"); +#if FMT_COMPILE_TIME_CHECKS + if constexpr (detail::count_named_args() == 0) { + using checker = detail::format_string_checker...>; + detail::parse_format_string(str_, checker(s, {})); + } +#else detail::check_format_string(s); +#endif } + basic_format_string(basic_runtime r) : str_(r.str) {} FMT_INLINE operator basic_string_view() const { return str_; } }; @@ -2836,9 +2777,16 @@ template class basic_format_string { #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 // Workaround broken conversion on older gcc. template using format_string = string_view; +template auto runtime(const S& s) -> basic_string_view> { + return s; +} #else template using format_string = basic_format_string...>; +// Creates a runtime format string. +template auto runtime(const S& s) -> basic_runtime> { + return {{s}}; +} #endif FMT_API auto vformat(string_view fmt, format_args args) -> std::string; diff --git a/include/fmt/format.h b/include/fmt/format.h index 669260eb..e5efd1b4 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -2143,6 +2143,46 @@ 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). */ \ + /* Use a macro-like name to avoid shadowing warnings. */ \ + struct FMT_GCC_VISIBILITY_HIDDEN FMT_COMPILE_STRING : base { \ + 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_COMPILE_STRING(); \ + }() + +/** + \rst + Constructs a compile-time format string from a string literal *s*. + + **Example**:: + + // A compile-time error because 'd' is an invalid specifier for strings. + std::string s = fmt::format(FMT_STRING("{:d}"), "foo"); + \endrst + */ +#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::compile_string, ) + using format_func = void (*)(detail::buffer&, int, const char*); FMT_API void format_error_code(buffer& out, int error_code, @@ -2573,22 +2613,20 @@ FMT_MODULE_EXPORT_END template void detail::vformat_to( - detail::buffer>& buf, - basic_string_view format_str, + detail::buffer& buf, basic_string_view fmt, basic_format_args>> args, detail::locale_ref loc) { using iterator = typename buffer_context::iterator; auto out = buffer_appender(buf); - if (format_str.size() == 2 && equal2(format_str.data(), "{}")) { + if (fmt.size() == 2 && equal2(fmt.data(), "{}")) { auto arg = args.get(0); if (!arg) error_handler().on_error("argument not found"); visit_format_arg(default_arg_formatter{out, args, loc}, arg); return; } - format_handler> h(out, format_str, args, - loc); - parse_format_string(format_str, h); + format_handler> h(out, fmt, args, loc); + parse_format_string(fmt, h); } #ifndef FMT_HEADER_ONLY @@ -2630,6 +2668,27 @@ template using format_args_t FMT_DEPRECATED_ALIAS = basic_format_args>; +/** + \rst + Constructs a `~fmt::format_arg_store` object that contains references + to arguments and can be implicitly converted to `~fmt::format_args`. + If ``fmt`` is a compile-time string then `make_args_checked` checks + its validity at compile time. + \endrst + */ +template > +FMT_INLINE auto make_args_checked(const S& fmt, + const remove_reference_t&... args) + -> format_arg_store, remove_reference_t...> { + static_assert( + detail::count<( + std::is_base_of>::value && + std::is_reference::value)...>() == 0, + "passing views as lvalues is disallowed"); + detail::check_format_string(fmt); + return {args...}; +} + template , FMT_ENABLE_IF(detail::is_string::value)> inline void vformat_to( @@ -2736,9 +2795,9 @@ namespace detail { template struct udl_formatter { basic_string_view str; - template - std::basic_string operator()(Args&&... args) const { - return format(str, std::forward(args)...); + template + std::basic_string operator()(T&&... args) const { + return vformat(str, fmt::make_args_checked(str, args...)); } }; @@ -2790,10 +2849,6 @@ constexpr auto operator"" _format(const char* s, size_t n) -> detail::udl_formatter { return {{s, n}}; } -constexpr auto operator"" _format(const wchar_t* s, size_t n) - -> detail::udl_formatter { - return {{s, n}}; -} /** \rst diff --git a/include/fmt/wchar.h b/include/fmt/wchar.h index 73a5300f..172c1bf4 100644 --- a/include/fmt/wchar.h +++ b/include/fmt/wchar.h @@ -1,4 +1,4 @@ -// Formatting library for C++ - experimental wchar_t support +// Formatting library for C++ - optional wchar_t support // // Copyright (c) 2012 - present, Victor Zverovich // All rights reserved. @@ -12,7 +12,7 @@ #include "format.h" -namespace fmt { +FMT_BEGIN_NAMESPACE #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 // Workaround broken conversion on older gcc. @@ -28,6 +28,13 @@ constexpr format_arg_store make_wformat_args( return {args...}; } +inline namespace literals { +constexpr auto operator"" _format(const wchar_t* s, size_t n) + -> detail::udl_formatter { + return {{s, n}}; +} +} // namespace literals + inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) { wmemory_buffer buffer; detail::vformat_to(buffer, fmt, args); @@ -48,6 +55,7 @@ void print(std::FILE* f, wformat_string fmt, T&&... args) { template void print(wformat_string fmt, T&&... args) { return vprint(wstring_view(fmt), make_wformat_args(args...)); } -} // namespace fmt + +FMT_END_NAMESPACE #endif // FMT_WCHAR_H_ diff --git a/test/chrono-test.cc b/test/chrono-test.cc index 9288a15f..f7096ca7 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -9,6 +9,8 @@ #include "gtest-extra.h" // EXPECT_THROW_MSG +using fmt::runtime; + auto make_tm() -> std::tm { auto time = std::tm(); time.tm_mday = 1; @@ -52,7 +54,7 @@ TEST(chrono_test, grow_buffer) { for (int i = 0; i < 30; ++i) s += "%c"; s += "}\n"; auto t = std::time(nullptr); - fmt::format(s, *std::localtime(&t)); + fmt::format(fmt::runtime(s), *std::localtime(&t)); } TEST(chrono_test, format_to_empty_container) { @@ -197,25 +199,41 @@ TEST(chrono_test, format_specs) { TEST(chrono_test, invalid_specs) { auto sec = std::chrono::seconds(0); - EXPECT_THROW_MSG(fmt::format(+"{:%a}", sec), fmt::format_error, "no date"); - EXPECT_THROW_MSG(fmt::format(+"{:%A}", sec), fmt::format_error, "no date"); - EXPECT_THROW_MSG(fmt::format(+"{:%c}", sec), fmt::format_error, "no date"); - EXPECT_THROW_MSG(fmt::format(+"{:%x}", sec), fmt::format_error, "no date"); - EXPECT_THROW_MSG(fmt::format(+"{:%Ex}", sec), fmt::format_error, "no date"); - EXPECT_THROW_MSG(fmt::format(+"{:%X}", sec), fmt::format_error, "no date"); - EXPECT_THROW_MSG(fmt::format(+"{:%EX}", sec), fmt::format_error, "no date"); - EXPECT_THROW_MSG(fmt::format(+"{:%D}", sec), fmt::format_error, "no date"); - EXPECT_THROW_MSG(fmt::format(+"{:%F}", sec), fmt::format_error, "no date"); - EXPECT_THROW_MSG(fmt::format(+"{:%Ec}", sec), fmt::format_error, "no date"); - EXPECT_THROW_MSG(fmt::format(+"{:%w}", sec), fmt::format_error, "no date"); - EXPECT_THROW_MSG(fmt::format(+"{:%u}", sec), fmt::format_error, "no date"); - EXPECT_THROW_MSG(fmt::format(+"{:%b}", sec), fmt::format_error, "no date"); - EXPECT_THROW_MSG(fmt::format(+"{:%B}", sec), fmt::format_error, "no date"); - EXPECT_THROW_MSG(fmt::format(+"{:%z}", sec), fmt::format_error, "no date"); - EXPECT_THROW_MSG(fmt::format(+"{:%Z}", sec), fmt::format_error, "no date"); - EXPECT_THROW_MSG(fmt::format(+"{:%Eq}", sec), fmt::format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{:%a}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG(fmt::format(runtime("{:%A}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG(fmt::format(runtime("{:%c}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG(fmt::format(runtime("{:%x}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG(fmt::format(runtime("{:%Ex}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG(fmt::format(runtime("{:%X}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG(fmt::format(runtime("{:%EX}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG(fmt::format(runtime("{:%D}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG(fmt::format(runtime("{:%F}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG(fmt::format(runtime("{:%Ec}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG(fmt::format(runtime("{:%w}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG(fmt::format(runtime("{:%u}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG(fmt::format(runtime("{:%b}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG(fmt::format(runtime("{:%B}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG(fmt::format(runtime("{:%z}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG(fmt::format(runtime("{:%Z}"), sec), fmt::format_error, + "no date"); + EXPECT_THROW_MSG(fmt::format(runtime("{:%Eq}"), sec), fmt::format_error, "invalid format"); - EXPECT_THROW_MSG(fmt::format(+"{:%Oq}", sec), fmt::format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{:%Oq}"), sec), fmt::format_error, "invalid format"); } @@ -274,7 +292,7 @@ TEST(chrono_test, format_default_fp) { } TEST(chrono_test, format_precision) { - EXPECT_THROW_MSG(fmt::format(+"{:.2}", std::chrono::seconds(42)), + EXPECT_THROW_MSG(fmt::format(runtime("{:.2}"), std::chrono::seconds(42)), fmt::format_error, "precision not allowed for this argument type"); EXPECT_EQ("1.2ms", fmt::format("{:.1}", dms(1.234))); @@ -301,7 +319,7 @@ TEST(chrono_test, format_simple_q) { } TEST(chrono_test, format_precision_q) { - EXPECT_THROW_MSG(fmt::format(+"{:.2%Q %q}", std::chrono::seconds(42)), + EXPECT_THROW_MSG(fmt::format(runtime("{:.2%Q %q}"), std::chrono::seconds(42)), fmt::format_error, "precision not allowed for this argument type"); EXPECT_EQ("1.2 ms", fmt::format("{:.1%Q %q}", dms(1.234))); @@ -318,12 +336,12 @@ TEST(chrono_test, format_full_specs_q) { } TEST(chrono_test, invalid_width_id) { - EXPECT_THROW(fmt::format(+"{:{o}", std::chrono::seconds(0)), + EXPECT_THROW(fmt::format(runtime("{:{o}"), std::chrono::seconds(0)), fmt::format_error); } TEST(chrono_test, invalid_colons) { - EXPECT_THROW(fmt::format(+"{0}=:{0::", std::chrono::seconds(0)), + EXPECT_THROW(fmt::format(runtime("{0}=:{0::"), std::chrono::seconds(0)), fmt::format_error); } diff --git a/test/format-test.cc b/test/format-test.cc index e5f3d98c..0ff5a49e 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -29,6 +29,7 @@ using fmt::basic_memory_buffer; using fmt::format_error; using fmt::memory_buffer; +using fmt::runtime; using fmt::string_view; using fmt::detail::max_value; @@ -372,10 +373,12 @@ TEST(format_test, escape) { } TEST(format_test, unmatched_braces) { - EXPECT_THROW_MSG(fmt::format(+"{"), format_error, "invalid format string"); - EXPECT_THROW_MSG(fmt::format(+"}"), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{")), format_error, + "invalid format string"); + EXPECT_THROW_MSG(fmt::format(runtime("}")), format_error, "unmatched '}' in format string"); - EXPECT_THROW_MSG(fmt::format(+"{0{}"), format_error, "invalid format string"); + EXPECT_THROW_MSG(fmt::format(runtime("{0{}")), format_error, + "invalid format string"); } TEST(format_test, no_args) { EXPECT_EQ("test", fmt::format("test")); } @@ -391,38 +394,44 @@ TEST(format_test, args_in_different_positions) { } TEST(format_test, arg_errors) { - EXPECT_THROW_MSG(fmt::format(+"{"), format_error, "invalid format string"); - EXPECT_THROW_MSG(fmt::format(+"{?}"), format_error, "invalid format string"); - EXPECT_THROW_MSG(fmt::format(+"{0"), format_error, "invalid format string"); - EXPECT_THROW_MSG(fmt::format(+"{0}"), format_error, "argument not found"); - EXPECT_THROW_MSG(fmt::format(+"{00}", 42), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{")), format_error, + "invalid format string"); + EXPECT_THROW_MSG(fmt::format(runtime("{?}")), format_error, + "invalid format string"); + EXPECT_THROW_MSG(fmt::format(runtime("{0")), format_error, + "invalid format string"); + EXPECT_THROW_MSG(fmt::format(runtime("{0}")), format_error, + "argument not found"); + EXPECT_THROW_MSG(fmt::format(runtime("{00}"), 42), format_error, "invalid format string"); char format_str[buffer_size]; safe_sprintf(format_str, "{%u", INT_MAX); - EXPECT_THROW_MSG(fmt::format(+format_str), format_error, + EXPECT_THROW_MSG(fmt::format(runtime(format_str)), format_error, "invalid format string"); safe_sprintf(format_str, "{%u}", INT_MAX); - EXPECT_THROW_MSG(fmt::format(+format_str), format_error, + EXPECT_THROW_MSG(fmt::format(runtime(format_str)), format_error, "argument not found"); safe_sprintf(format_str, "{%u", INT_MAX + 1u); - EXPECT_THROW_MSG(fmt::format(+format_str), format_error, "number is too big"); + EXPECT_THROW_MSG(fmt::format(runtime(format_str)), format_error, + "number is too big"); safe_sprintf(format_str, "{%u}", INT_MAX + 1u); - EXPECT_THROW_MSG(fmt::format(+format_str), format_error, "number is too big"); + EXPECT_THROW_MSG(fmt::format(runtime(format_str)), format_error, + "number is too big"); } template struct test_format { - template - static std::string format(fmt::string_view format_str, const Args&... args) { - return test_format::format(format_str, N - 1, args...); + template + static std::string format(fmt::string_view fmt, const T&... args) { + return test_format::format(fmt, N - 1, args...); } }; template <> struct test_format<0> { - template - static std::string format(fmt::string_view format_str, const Args&... args) { - return fmt::format(format_str, args...); + template + static std::string format(fmt::string_view fmt, const T&... args) { + return fmt::format(runtime(fmt), args...); } }; @@ -452,22 +461,25 @@ TEST(format_test, named_arg) { fmt::arg("i", 0), fmt::arg("j", 0), fmt::arg("k", 0), fmt::arg("l", 0), fmt::arg("m", 0), fmt::arg("n", 0), fmt::arg("o", 0), fmt::arg("p", 0))); - EXPECT_THROW_MSG(fmt::format(+"{a}"), format_error, "argument not found"); - EXPECT_THROW_MSG(fmt::format(+"{a}", 42), format_error, "argument not found"); + EXPECT_THROW_MSG(fmt::format(runtime("{a}")), format_error, + "argument not found"); + EXPECT_THROW_MSG(fmt::format(runtime("{a}"), 42), format_error, + "argument not found"); } TEST(format_test, auto_arg_index) { EXPECT_EQ("abc", fmt::format("{}{}{}", 'a', 'b', 'c')); - EXPECT_THROW_MSG(fmt::format(+"{0}{}", 'a', 'b'), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0}{}"), 'a', 'b'), format_error, "cannot switch from manual to automatic argument indexing"); - EXPECT_THROW_MSG(fmt::format(+"{}{0}", 'a', 'b'), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{}{0}"), 'a', 'b'), format_error, "cannot switch from automatic to manual argument indexing"); EXPECT_EQ("1.2", fmt::format("{:.{}}", 1.2345, 2)); - EXPECT_THROW_MSG(fmt::format(+"{0}:.{}", 1.2345, 2), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0}:.{}"), 1.2345, 2), format_error, "cannot switch from manual to automatic argument indexing"); - EXPECT_THROW_MSG(fmt::format(+"{:.{0}}", 1.2345, 2), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{:.{0}}"), 1.2345, 2), format_error, "cannot switch from automatic to manual argument indexing"); - EXPECT_THROW_MSG(fmt::format(+"{}"), format_error, "argument not found"); + EXPECT_THROW_MSG(fmt::format(runtime("{}")), format_error, + "argument not found"); } TEST(format_test, empty_specs) { EXPECT_EQ("42", fmt::format("{0:}", 42)); } @@ -524,9 +536,9 @@ TEST(format_test, center_align) { } TEST(format_test, fill) { - EXPECT_THROW_MSG(fmt::format(+"{0:{<5}", 'c'), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:{<5}"), 'c'), format_error, "invalid fill character '{'"); - EXPECT_THROW_MSG(fmt::format(+"{0:{<5}}", 'c'), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:{<5}}"), 'c'), format_error, "invalid fill character '{'"); EXPECT_EQ("**42", fmt::format("{0:*>4}", 42)); EXPECT_EQ("**-42", fmt::format("{0:*>5}", -42)); @@ -545,31 +557,31 @@ TEST(format_test, fill) { EXPECT_EQ(std::string("\0\0\0*", 4), fmt::format(string_view("{:\0>4}", 6), '*')); EXPECT_EQ("жж42", fmt::format("{0:ж>4}", 42)); - EXPECT_THROW_MSG(fmt::format(+"{:\x80\x80\x80\x80\x80>}", 0), format_error, - "missing '}' in format string"); + EXPECT_THROW_MSG(fmt::format(runtime("{:\x80\x80\x80\x80\x80>}"), 0), + format_error, "missing '}' in format string"); } TEST(format_test, plus_sign) { EXPECT_EQ("+42", fmt::format("{0:+}", 42)); EXPECT_EQ("-42", fmt::format("{0:+}", -42)); EXPECT_EQ("+42", fmt::format("{0:+}", 42)); - EXPECT_THROW_MSG(fmt::format(+"{0:+}", 42u), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:+}"), 42u), format_error, "format specifier requires signed argument"); EXPECT_EQ("+42", fmt::format("{0:+}", 42l)); - EXPECT_THROW_MSG(fmt::format(+"{0:+}", 42ul), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:+}"), 42ul), format_error, "format specifier requires signed argument"); EXPECT_EQ("+42", fmt::format("{0:+}", 42ll)); - EXPECT_THROW_MSG(fmt::format(+"{0:+}", 42ull), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:+}"), 42ull), format_error, "format specifier requires signed argument"); EXPECT_EQ("+42", fmt::format("{0:+}", 42.0)); EXPECT_EQ("+42", fmt::format("{0:+}", 42.0l)); - EXPECT_THROW_MSG(fmt::format(+"{0:+", 'c'), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:+"), 'c'), format_error, "missing '}' in format string"); - EXPECT_THROW_MSG(fmt::format(+"{0:+}", 'c'), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:+}"), 'c'), format_error, "invalid format specifier for char"); - EXPECT_THROW_MSG(fmt::format(+"{0:+}", "abc"), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:+}"), "abc"), format_error, "format specifier requires numeric argument"); - EXPECT_THROW_MSG(fmt::format(+"{0:+}", reinterpret_cast(0x42)), + EXPECT_THROW_MSG(fmt::format(runtime("{0:+}"), reinterpret_cast(0x42)), format_error, "format specifier requires numeric argument"); } @@ -577,23 +589,23 @@ TEST(format_test, minus_sign) { EXPECT_EQ("42", fmt::format("{0:-}", 42)); EXPECT_EQ("-42", fmt::format("{0:-}", -42)); EXPECT_EQ("42", fmt::format("{0:-}", 42)); - EXPECT_THROW_MSG(fmt::format(+"{0:-}", 42u), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:-}"), 42u), format_error, "format specifier requires signed argument"); EXPECT_EQ("42", fmt::format("{0:-}", 42l)); - EXPECT_THROW_MSG(fmt::format(+"{0:-}", 42ul), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:-}"), 42ul), format_error, "format specifier requires signed argument"); EXPECT_EQ("42", fmt::format("{0:-}", 42ll)); - EXPECT_THROW_MSG(fmt::format(+"{0:-}", 42ull), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:-}"), 42ull), format_error, "format specifier requires signed argument"); EXPECT_EQ("42", fmt::format("{0:-}", 42.0)); EXPECT_EQ("42", fmt::format("{0:-}", 42.0l)); - EXPECT_THROW_MSG(fmt::format(+"{0:-", 'c'), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:-"), 'c'), format_error, "missing '}' in format string"); - EXPECT_THROW_MSG(fmt::format(+"{0:-}", 'c'), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:-}"), 'c'), format_error, "invalid format specifier for char"); - EXPECT_THROW_MSG(fmt::format(+"{0:-}", "abc"), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:-}"), "abc"), format_error, "format specifier requires numeric argument"); - EXPECT_THROW_MSG(fmt::format(+"{0:-}", reinterpret_cast(0x42)), + EXPECT_THROW_MSG(fmt::format(runtime("{0:-}"), reinterpret_cast(0x42)), format_error, "format specifier requires numeric argument"); } @@ -601,23 +613,23 @@ TEST(format_test, space_sign) { EXPECT_EQ(" 42", fmt::format("{0: }", 42)); EXPECT_EQ("-42", fmt::format("{0: }", -42)); EXPECT_EQ(" 42", fmt::format("{0: }", 42)); - EXPECT_THROW_MSG(fmt::format(+"{0: }", 42u), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0: }"), 42u), format_error, "format specifier requires signed argument"); EXPECT_EQ(" 42", fmt::format("{0: }", 42l)); - EXPECT_THROW_MSG(fmt::format(+"{0: }", 42ul), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0: }"), 42ul), format_error, "format specifier requires signed argument"); EXPECT_EQ(" 42", fmt::format("{0: }", 42ll)); - EXPECT_THROW_MSG(fmt::format(+"{0: }", 42ull), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0: }"), 42ull), format_error, "format specifier requires signed argument"); EXPECT_EQ(" 42", fmt::format("{0: }", 42.0)); EXPECT_EQ(" 42", fmt::format("{0: }", 42.0l)); - EXPECT_THROW_MSG(fmt::format(+"{0: ", 'c'), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0: "), 'c'), format_error, "missing '}' in format string"); - EXPECT_THROW_MSG(fmt::format(+"{0: }", 'c'), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0: }"), 'c'), format_error, "invalid format specifier for char"); - EXPECT_THROW_MSG(fmt::format(+"{0: }", "abc"), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0: }"), "abc"), format_error, "format specifier requires numeric argument"); - EXPECT_THROW_MSG(fmt::format(+"{0: }", reinterpret_cast(0x42)), + EXPECT_THROW_MSG(fmt::format(runtime("{0: }"), reinterpret_cast(0x42)), format_error, "format specifier requires numeric argument"); } @@ -668,13 +680,13 @@ TEST(format_test, hash_flag) { EXPECT_EQ("0.", fmt::format("{:#.0f}", 0.01)); EXPECT_EQ("0.50", fmt::format("{:#.2g}", 0.5)); EXPECT_EQ("0.", fmt::format("{:#.0f}", 0.5)); - EXPECT_THROW_MSG(fmt::format(+"{0:#", 'c'), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:#"), 'c'), format_error, "missing '}' in format string"); - EXPECT_THROW_MSG(fmt::format(+"{0:#}", 'c'), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:#}"), 'c'), format_error, "invalid format specifier for char"); - EXPECT_THROW_MSG(fmt::format(+"{0:#}", "abc"), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:#}"), "abc"), format_error, "format specifier requires numeric argument"); - EXPECT_THROW_MSG(fmt::format(+"{0:#}", reinterpret_cast(0x42)), + EXPECT_THROW_MSG(fmt::format(runtime("{0:#}"), reinterpret_cast(0x42)), format_error, "format specifier requires numeric argument"); } @@ -688,33 +700,34 @@ TEST(format_test, zero_flag) { EXPECT_EQ("00042", fmt::format("{0:05}", 42ull)); EXPECT_EQ("-000042", fmt::format("{0:07}", -42.0)); EXPECT_EQ("-000042", fmt::format("{0:07}", -42.0l)); - EXPECT_THROW_MSG(fmt::format(+"{0:0", 'c'), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:0"), 'c'), format_error, "missing '}' in format string"); - EXPECT_THROW_MSG(fmt::format(+"{0:05}", 'c'), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:05}"), 'c'), format_error, "invalid format specifier for char"); - EXPECT_THROW_MSG(fmt::format(+"{0:05}", "abc"), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:05}"), "abc"), format_error, "format specifier requires numeric argument"); - EXPECT_THROW_MSG(fmt::format(+"{0:05}", reinterpret_cast(0x42)), - format_error, "format specifier requires numeric argument"); + EXPECT_THROW_MSG( + fmt::format(runtime("{0:05}"), reinterpret_cast(0x42)), + format_error, "format specifier requires numeric argument"); } TEST(format_test, width) { char format_str[buffer_size]; safe_sprintf(format_str, "{0:%u", UINT_MAX); increment(format_str + 3); - EXPECT_THROW_MSG(fmt::format(+format_str, 0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error, "number is too big"); size_t size = std::strlen(format_str); format_str[size] = '}'; format_str[size + 1] = 0; - EXPECT_THROW_MSG(fmt::format(+format_str, 0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error, "number is too big"); safe_sprintf(format_str, "{0:%u", INT_MAX + 1u); - EXPECT_THROW_MSG(fmt::format(+format_str, 0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error, "number is too big"); safe_sprintf(format_str, "{0:%u}", INT_MAX + 1u); - EXPECT_THROW_MSG(fmt::format(+format_str, 0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error, "number is too big"); EXPECT_EQ(" -42", fmt::format("{0:4}", -42)); EXPECT_EQ(" 42", fmt::format("{0:5}", 42u)); @@ -739,47 +752,47 @@ TEST(format_test, runtime_width) { char format_str[buffer_size]; safe_sprintf(format_str, "{0:{%u", UINT_MAX); increment(format_str + 4); - EXPECT_THROW_MSG(fmt::format(+format_str, 0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error, "number is too big"); size_t size = std::strlen(format_str); format_str[size] = '}'; format_str[size + 1] = 0; - EXPECT_THROW_MSG(fmt::format(+format_str, 0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error, "number is too big"); format_str[size + 1] = '}'; format_str[size + 2] = 0; - EXPECT_THROW_MSG(fmt::format(+format_str, 0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error, "number is too big"); - EXPECT_THROW_MSG(fmt::format(+"{0:{", 0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:{"), 0), format_error, "invalid format string"); - EXPECT_THROW_MSG(fmt::format(+"{0:{}", 0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:{}"), 0), format_error, "cannot switch from manual to automatic argument indexing"); - EXPECT_THROW_MSG(fmt::format(+"{0:{?}}", 0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:{?}}"), 0), format_error, "invalid format string"); - EXPECT_THROW_MSG(fmt::format(+"{0:{1}}", 0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:{1}}"), 0), format_error, "argument not found"); - EXPECT_THROW_MSG(fmt::format(+"{0:{0:}}", 0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:{0:}}"), 0), format_error, "invalid format string"); - EXPECT_THROW_MSG(fmt::format(+"{0:{1}}", 0, -1), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:{1}}"), 0, -1), format_error, "negative width"); - EXPECT_THROW_MSG(fmt::format(+"{0:{1}}", 0, (INT_MAX + 1u)), format_error, - "number is too big"); - EXPECT_THROW_MSG(fmt::format(+"{0:{1}}", 0, -1l), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:{1}}"), 0, (INT_MAX + 1u)), + format_error, "number is too big"); + EXPECT_THROW_MSG(fmt::format(runtime("{0:{1}}"), 0, -1l), format_error, "negative width"); if (fmt::detail::const_check(sizeof(long) > sizeof(int))) { long value = INT_MAX; - EXPECT_THROW_MSG(fmt::format(+"{0:{1}}", 0, (value + 1)), format_error, - "number is too big"); + EXPECT_THROW_MSG(fmt::format(runtime("{0:{1}}"), 0, (value + 1)), + format_error, "number is too big"); } - EXPECT_THROW_MSG(fmt::format(+"{0:{1}}", 0, (INT_MAX + 1ul)), format_error, - "number is too big"); + EXPECT_THROW_MSG(fmt::format(runtime("{0:{1}}"), 0, (INT_MAX + 1ul)), + format_error, "number is too big"); - EXPECT_THROW_MSG(fmt::format(+"{0:{1}}", 0, '0'), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:{1}}"), 0, '0'), format_error, "width is not integer"); - EXPECT_THROW_MSG(fmt::format(+"{0:{1}}", 0, 0.0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:{1}}"), 0, 0.0), format_error, "width is not integer"); EXPECT_EQ(" -42", fmt::format("{0:{1}}", -42, 4)); @@ -800,53 +813,53 @@ TEST(format_test, precision) { char format_str[buffer_size]; safe_sprintf(format_str, "{0:.%u", UINT_MAX); increment(format_str + 4); - EXPECT_THROW_MSG(fmt::format(+format_str, 0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error, "number is too big"); size_t size = std::strlen(format_str); format_str[size] = '}'; format_str[size + 1] = 0; - EXPECT_THROW_MSG(fmt::format(+format_str, 0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error, "number is too big"); safe_sprintf(format_str, "{0:.%u", INT_MAX + 1u); - EXPECT_THROW_MSG(fmt::format(+format_str, 0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error, "number is too big"); safe_sprintf(format_str, "{0:.%u}", INT_MAX + 1u); - EXPECT_THROW_MSG(fmt::format(+format_str, 0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error, "number is too big"); - EXPECT_THROW_MSG(fmt::format(+"{0:.", 0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:."), 0), format_error, "missing precision specifier"); - EXPECT_THROW_MSG(fmt::format(+"{0:.}", 0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.}"), 0), format_error, "missing precision specifier"); - EXPECT_THROW_MSG(fmt::format(+"{0:.2", 0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.2"), 0), format_error, "precision not allowed for this argument type"); - EXPECT_THROW_MSG(fmt::format(+"{0:.2}", 42), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.2}"), 42), format_error, "precision not allowed for this argument type"); - EXPECT_THROW_MSG(fmt::format(+"{0:.2f}", 42), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.2f}"), 42), format_error, "precision not allowed for this argument type"); - EXPECT_THROW_MSG(fmt::format(+"{0:.2}", 42u), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.2}"), 42u), format_error, "precision not allowed for this argument type"); - EXPECT_THROW_MSG(fmt::format(+"{0:.2f}", 42u), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.2f}"), 42u), format_error, "precision not allowed for this argument type"); - EXPECT_THROW_MSG(fmt::format(+"{0:.2}", 42l), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.2}"), 42l), format_error, "precision not allowed for this argument type"); - EXPECT_THROW_MSG(fmt::format(+"{0:.2f}", 42l), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.2f}"), 42l), format_error, "precision not allowed for this argument type"); - EXPECT_THROW_MSG(fmt::format(+"{0:.2}", 42ul), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.2}"), 42ul), format_error, "precision not allowed for this argument type"); - EXPECT_THROW_MSG(fmt::format(+"{0:.2f}", 42ul), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.2f}"), 42ul), format_error, "precision not allowed for this argument type"); - EXPECT_THROW_MSG(fmt::format(+"{0:.2}", 42ll), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.2}"), 42ll), format_error, "precision not allowed for this argument type"); - EXPECT_THROW_MSG(fmt::format(+"{0:.2f}", 42ll), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.2f}"), 42ll), format_error, "precision not allowed for this argument type"); - EXPECT_THROW_MSG(fmt::format(+"{0:.2}", 42ull), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.2}"), 42ull), format_error, "precision not allowed for this argument type"); - EXPECT_THROW_MSG(fmt::format(+"{0:.2f}", 42ull), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.2f}"), 42ull), format_error, "precision not allowed for this argument type"); - EXPECT_THROW_MSG(fmt::format(+"{0:3.0}", 'x'), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:3.0}"), 'x'), format_error, "precision not allowed for this argument type"); EXPECT_EQ("1.2", fmt::format("{0:.2}", 1.2345)); EXPECT_EQ("1.2", fmt::format("{0:.2}", 1.2345l)); @@ -899,14 +912,15 @@ TEST(format_test, precision) { EXPECT_EQ("1e+01", fmt::format("{:.0e}", 9.5)); EXPECT_EQ("1.0e-34", fmt::format("{:.1e}", 1e-34)); - EXPECT_THROW_MSG(fmt::format(+"{0:.2}", reinterpret_cast(0xcafe)), - format_error, - "precision not allowed for this argument type"); - EXPECT_THROW_MSG(fmt::format(+"{0:.2f}", reinterpret_cast(0xcafe)), - format_error, - "precision not allowed for this argument type"); - EXPECT_THROW_MSG(fmt::format(+"{:.{}e}", 42.0, fmt::detail::max_value()), - format_error, "number is too big"); + EXPECT_THROW_MSG( + fmt::format(runtime("{0:.2}"), reinterpret_cast(0xcafe)), + format_error, "precision not allowed for this argument type"); + EXPECT_THROW_MSG( + fmt::format(runtime("{0:.2f}"), reinterpret_cast(0xcafe)), + format_error, "precision not allowed for this argument type"); + EXPECT_THROW_MSG( + fmt::format(runtime("{:.{}e}"), 42.0, fmt::detail::max_value()), + format_error, "number is too big"); EXPECT_EQ("st", fmt::format("{0:.2}", "str")); } @@ -915,85 +929,85 @@ TEST(format_test, runtime_precision) { char format_str[buffer_size]; safe_sprintf(format_str, "{0:.{%u", UINT_MAX); increment(format_str + 5); - EXPECT_THROW_MSG(fmt::format(+format_str, 0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error, "number is too big"); size_t size = std::strlen(format_str); format_str[size] = '}'; format_str[size + 1] = 0; - EXPECT_THROW_MSG(fmt::format(+format_str, 0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error, "number is too big"); format_str[size + 1] = '}'; format_str[size + 2] = 0; - EXPECT_THROW_MSG(fmt::format(+format_str, 0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error, "number is too big"); - EXPECT_THROW_MSG(fmt::format(+"{0:.{", 0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.{"), 0), format_error, "invalid format string"); - EXPECT_THROW_MSG(fmt::format(+"{0:.{}", 0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.{}"), 0), format_error, "cannot switch from manual to automatic argument indexing"); - EXPECT_THROW_MSG(fmt::format(+"{0:.{?}}", 0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.{?}}"), 0), format_error, "invalid format string"); - EXPECT_THROW_MSG(fmt::format(+"{0:.{1}", 0, 0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}"), 0, 0), format_error, "precision not allowed for this argument type"); - EXPECT_THROW_MSG(fmt::format(+"{0:.{1}}", 0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 0), format_error, "argument not found"); - EXPECT_THROW_MSG(fmt::format(+"{0:.{0:}}", 0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.{0:}}"), 0), format_error, "invalid format string"); - EXPECT_THROW_MSG(fmt::format(+"{0:.{1}}", 0, -1), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 0, -1), format_error, "negative precision"); - EXPECT_THROW_MSG(fmt::format(+"{0:.{1}}", 0, (INT_MAX + 1u)), format_error, - "number is too big"); - EXPECT_THROW_MSG(fmt::format(+"{0:.{1}}", 0, -1l), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 0, (INT_MAX + 1u)), + format_error, "number is too big"); + EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 0, -1l), format_error, "negative precision"); if (fmt::detail::const_check(sizeof(long) > sizeof(int))) { long value = INT_MAX; - EXPECT_THROW_MSG(fmt::format(+"{0:.{1}}", 0, (value + 1)), format_error, - "number is too big"); + EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 0, (value + 1)), + format_error, "number is too big"); } - EXPECT_THROW_MSG(fmt::format(+"{0:.{1}}", 0, (INT_MAX + 1ul)), format_error, - "number is too big"); + EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 0, (INT_MAX + 1ul)), + format_error, "number is too big"); - EXPECT_THROW_MSG(fmt::format(+"{0:.{1}}", 0, '0'), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 0, '0'), format_error, "precision is not integer"); - EXPECT_THROW_MSG(fmt::format(+"{0:.{1}}", 0, 0.0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 0, 0.0), format_error, "precision is not integer"); - EXPECT_THROW_MSG(fmt::format(+"{0:.{1}}", 42, 2), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 42, 2), format_error, "precision not allowed for this argument type"); - EXPECT_THROW_MSG(fmt::format(+"{0:.{1}f}", 42, 2), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}f}"), 42, 2), format_error, "precision not allowed for this argument type"); - EXPECT_THROW_MSG(fmt::format(+"{0:.{1}}", 42u, 2), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 42u, 2), format_error, "precision not allowed for this argument type"); - EXPECT_THROW_MSG(fmt::format(+"{0:.{1}f}", 42u, 2), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}f}"), 42u, 2), format_error, "precision not allowed for this argument type"); - EXPECT_THROW_MSG(fmt::format(+"{0:.{1}}", 42l, 2), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 42l, 2), format_error, "precision not allowed for this argument type"); - EXPECT_THROW_MSG(fmt::format(+"{0:.{1}f}", 42l, 2), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}f}"), 42l, 2), format_error, "precision not allowed for this argument type"); - EXPECT_THROW_MSG(fmt::format(+"{0:.{1}}", 42ul, 2), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 42ul, 2), format_error, "precision not allowed for this argument type"); - EXPECT_THROW_MSG(fmt::format(+"{0:.{1}f}", 42ul, 2), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}f}"), 42ul, 2), format_error, "precision not allowed for this argument type"); - EXPECT_THROW_MSG(fmt::format(+"{0:.{1}}", 42ll, 2), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 42ll, 2), format_error, "precision not allowed for this argument type"); - EXPECT_THROW_MSG(fmt::format(+"{0:.{1}f}", 42ll, 2), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}f}"), 42ll, 2), format_error, "precision not allowed for this argument type"); - EXPECT_THROW_MSG(fmt::format(+"{0:.{1}}", 42ull, 2), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 42ull, 2), format_error, "precision not allowed for this argument type"); - EXPECT_THROW_MSG(fmt::format(+"{0:.{1}f}", 42ull, 2), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}f}"), 42ull, 2), format_error, "precision not allowed for this argument type"); - EXPECT_THROW_MSG(fmt::format(+"{0:3.{1}}", 'x', 0), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:3.{1}}"), 'x', 0), format_error, "precision not allowed for this argument type"); EXPECT_EQ("1.2", fmt::format("{0:.{1}}", 1.2345, 2)); EXPECT_EQ("1.2", fmt::format("{1:.{0}}", 2, 1.2345l)); - EXPECT_THROW_MSG(fmt::format(+"{0:.{1}}", reinterpret_cast(0xcafe), 2), - format_error, - "precision not allowed for this argument type"); EXPECT_THROW_MSG( - fmt::format(+"{0:.{1}f}", reinterpret_cast(0xcafe), 2), + fmt::format(runtime("{0:.{1}}"), reinterpret_cast(0xcafe), 2), + format_error, "precision not allowed for this argument type"); + EXPECT_THROW_MSG( + fmt::format(runtime("{0:.{1}f}"), reinterpret_cast(0xcafe), 2), format_error, "precision not allowed for this argument type"); EXPECT_EQ("st", fmt::format("{0:.{1}}", "str", 2)); @@ -1026,13 +1040,14 @@ void check_unknown_types(const T& value, const char* types, const char*) { if (std::strchr(types, c) || std::strchr(special, c) || !c) continue; safe_sprintf(format_str, "{0:10%c}", c); const char* message = "invalid type specifier"; - EXPECT_THROW_MSG(fmt::format(+format_str, value), format_error, message) + EXPECT_THROW_MSG(fmt::format(runtime(format_str), value), format_error, + message) << format_str << " " << message; } } TEST(format_test, format_int) { - EXPECT_THROW_MSG(fmt::format(+"{0:v", 42), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:v"), 42), format_error, "missing '}' in format string"); check_unknown_types(42, "bBdoxXnLc", "integer"); EXPECT_EQ("x", fmt::format("{:c}", static_cast('x'))); @@ -1305,7 +1320,8 @@ TEST(format_test, format_char) { int n = 'x'; for (const char* type = types + 1; *type; ++type) { std::string format_str = fmt::format("{{:{}}}", *type); - EXPECT_EQ(fmt::format(format_str, n), fmt::format(format_str, 'x')) + EXPECT_EQ(fmt::format(runtime(format_str), n), + fmt::format(runtime(format_str), 'x')) << format_str; } EXPECT_EQ(fmt::format("{:02X}", n), fmt::format("{:02X}", 'x')); @@ -1333,8 +1349,9 @@ TEST(format_test, format_cstring) { EXPECT_EQ("test", fmt::format("{0:s}", "test")); char nonconst[] = "nonconst"; EXPECT_EQ("nonconst", fmt::format("{0}", nonconst)); - EXPECT_THROW_MSG(fmt::format(+"{0}", static_cast(nullptr)), - format_error, "string pointer is null"); + EXPECT_THROW_MSG( + fmt::format(runtime("{0}"), static_cast(nullptr)), + format_error, "string pointer is null"); } TEST(format_test, format_schar_string) { @@ -1480,8 +1497,8 @@ template <> struct formatter : formatter { FMT_END_NAMESPACE TEST(format_test, format_custom) { - EXPECT_THROW_MSG(fmt::format(+"{:s}", date(2012, 12, 9)), format_error, - "unknown format specifier"); + EXPECT_THROW_MSG(fmt::format(runtime("{:s}"), date(2012, 12, 9)), + format_error, "unknown format specifier"); EXPECT_EQ("42", fmt::format("{0}", Answer())); EXPECT_EQ("0042", fmt::format("{:04}", Answer())); } @@ -1513,7 +1530,6 @@ TEST(format_test, format_examples) { EXPECT_EQ("The answer is 42", message); EXPECT_EQ("42", fmt::format("{}", 42)); - EXPECT_EQ("42", fmt::format(std::string("{}"), 42)); memory_buffer out; format_to(out, "The answer is {}.", 42); @@ -1566,7 +1582,7 @@ TEST(format_test, format_examples) { fmt::format("int: {0:d}; hex: {0:#x}; oct: {0:#o}", 42)); EXPECT_EQ("The answer is 42", fmt::format("The answer is {}", 42)); - EXPECT_THROW_MSG(fmt::format(+"The answer is {:d}", "forty-two"), + EXPECT_THROW_MSG(fmt::format(runtime("The answer is {:d}"), "forty-two"), format_error, "invalid type specifier"); EXPECT_EQ(L"Cyrillic letter \x42e", @@ -1741,10 +1757,10 @@ TEST(format_test, custom_format_compile_time_string) { using namespace fmt::literals; TEST(format_test, format_udl) { - auto udl_format = "{}c{}"_format("ab", 1); - EXPECT_EQ(fmt::format("{}c{}", "ab", 1), udl_format); - auto udl_format_w = L"{}c{}"_format(L"ab", 1); - EXPECT_EQ(fmt::format(L"{}c{}", L"ab", 1), udl_format_w); + EXPECT_EQ("{}c{}"_format("ab", 1), fmt::format("{}c{}", "ab", 1)); + EXPECT_EQ("foo"_format(), "foo"); + EXPECT_EQ("{0:10}"_format(42), " 42"); + EXPECT_EQ("{}"_format(date(2015, 10, 21)), "2015-10-21"); } TEST(format_test, named_arg_udl) { @@ -1762,15 +1778,6 @@ TEST(format_test, named_arg_udl) { fmt::arg(L"second", L"cad"), fmt::arg(L"third", 99)), udl_a_w); } - -TEST(format_test, udl_template) { - EXPECT_EQ("foo", "foo"_format()); - EXPECT_EQ(" 42", "{0:10}"_format(42)); -} - -TEST(format_test, udl_pass_user_defined_object_as_lvalue) { - EXPECT_EQ("2015-10-21", "{}"_format(date(2015, 10, 21))); -} #endif // FMT_USE_USER_DEFINED_LITERALS TEST(format_test, enum) { EXPECT_EQ("0", fmt::format("{}", foo)); } @@ -1815,21 +1822,21 @@ TEST(format_test, dynamic_formatter) { EXPECT_EQ("42", fmt::format("{:d}", num)); EXPECT_EQ("foo", fmt::format("{:s}", str)); EXPECT_EQ(" 42 foo ", fmt::format("{:{}} {:{}}", num, 3, str, 4)); - EXPECT_THROW_MSG(fmt::format(+"{0:{}}", num), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:{}}"), num), format_error, "cannot switch from manual to automatic argument indexing"); - EXPECT_THROW_MSG(fmt::format(+"{:{0}}", num), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{:{0}}"), num), format_error, "cannot switch from automatic to manual argument indexing"); - EXPECT_THROW_MSG(fmt::format(+"{:+}", str), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{:+}"), str), format_error, "format specifier requires numeric argument"); - EXPECT_THROW_MSG(fmt::format(+"{:-}", str), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{:-}"), str), format_error, "format specifier requires numeric argument"); - EXPECT_THROW_MSG(fmt::format(+"{: }", str), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{: }"), str), format_error, "format specifier requires numeric argument"); - EXPECT_THROW_MSG(fmt::format(+"{:#}", str), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{:#}"), str), format_error, "format specifier requires numeric argument"); - EXPECT_THROW_MSG(fmt::format(+"{:0}", str), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{:0}"), str), format_error, "format specifier requires numeric argument"); - EXPECT_THROW_MSG(fmt::format(+"{:.2}", num), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{:.2}"), num), format_error, "precision not allowed for this argument type"); } diff --git a/test/locale-test.cc b/test/locale-test.cc index 0a7da952..69be7cea 100644 --- a/test/locale-test.cc +++ b/test/locale-test.cc @@ -149,9 +149,9 @@ template struct formatter, charT> { "{:" + specs + "}", c.imag()); auto fill_align_width = std::string(); if (specs_.width > 0) fill_align_width = fmt::format(">{}", specs_.width); - return format_to( - ctx.out(), "{:" + fill_align_width + "}", - fmt::format(c.real() != 0 ? "({0}+{1}i)" : "{1}i", real, imag)); + return format_to(ctx.out(), runtime("{:" + fill_align_width + "}"), + c.real() != 0 ? fmt::format("({}+{}i)", real, imag) + : fmt::format("{}i", imag)); } }; FMT_END_NAMESPACE diff --git a/test/ostream-test.cc b/test/ostream-test.cc index 2bc2b1cb..99bc6e5b 100644 --- a/test/ostream-test.cc +++ b/test/ostream-test.cc @@ -7,6 +7,8 @@ #include "fmt/format.h" +using fmt::runtime; + struct test {}; // Test that there is no issues with specializations when fmt/ostream.h is @@ -74,15 +76,15 @@ TEST(ostream_test, format_specs) { EXPECT_EQ(" def", fmt::format("{0:>5}", test_string("def"))); EXPECT_EQ(" def ", fmt::format("{0:^5}", test_string("def"))); EXPECT_EQ("def**", fmt::format("{0:*<5}", test_string("def"))); - EXPECT_THROW_MSG(fmt::format(+"{0:+}", test_string()), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:+}"), test_string()), format_error, "format specifier requires numeric argument"); - EXPECT_THROW_MSG(fmt::format(+"{0:-}", test_string()), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:-}"), test_string()), format_error, "format specifier requires numeric argument"); - EXPECT_THROW_MSG(fmt::format(+"{0: }", test_string()), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0: }"), test_string()), format_error, "format specifier requires numeric argument"); - EXPECT_THROW_MSG(fmt::format(+"{0:#}", test_string()), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:#}"), test_string()), format_error, "format specifier requires numeric argument"); - EXPECT_THROW_MSG(fmt::format(+"{0:05}", test_string()), format_error, + EXPECT_THROW_MSG(fmt::format(runtime("{0:05}"), test_string()), format_error, "format specifier requires numeric argument"); EXPECT_EQ("test ", fmt::format("{0:13}", test_string("test"))); EXPECT_EQ("test ", fmt::format("{0:{1}}", test_string("test"), 13)); diff --git a/test/wchar-test.cc b/test/wchar-test.cc index aaac87cf..edb5910f 100644 --- a/test/wchar-test.cc +++ b/test/wchar-test.cc @@ -9,6 +9,13 @@ #include "gtest/gtest.h" +#if FMT_USE_USER_DEFINED_LITERALS +TEST(format_test, format_udl) { + using namespace fmt::literals; + EXPECT_EQ(L"{}c{}"_format(L"ab", 1), fmt::format(L"{}c{}", L"ab", 1)); +} +#endif + TEST(wchar_test, print) { // Check that the wide print overload compiles. if (fmt::detail::const_check(false)) fmt::print(L"test");