diff --git a/include/fmt/base.h b/include/fmt/base.h index 51bab05e..282f7f07 100644 --- a/include/fmt/base.h +++ b/include/fmt/base.h @@ -1341,8 +1341,7 @@ template constexpr auto count() -> size_t { template constexpr auto count_named_args() -> size_t { return count::value...>(); } -template -constexpr auto count_statically_named_args() -> size_t { +template constexpr auto count_static_named_args() -> size_t { return count::value...>(); } @@ -2828,12 +2827,19 @@ FMT_CONSTEXPR void parse_format_string(basic_string_view format_str, } } -template ::value> struct strip_named_arg { - using type = T; -}; -template struct strip_named_arg { - using type = remove_cvref_t; -}; +// Checks char specs and returns true iff the presentation type is char-like. +FMT_CONSTEXPR inline auto check_char_specs(const format_specs& specs) -> bool { + auto type = specs.type(); + if (type != presentation_type::none && type != presentation_type::chr && + type != presentation_type::debug) { + return false; + } + if (specs.align() == align::numeric || specs.sign() != sign::none || + specs.alt()) { + report_error("invalid format specifier for char"); + } + return true; +} template FMT_VISIBILITY("hidden") // Suppress an ld warning on macOS (#3769). @@ -2856,23 +2862,9 @@ FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx) #endif } -// Checks char specs and returns true iff the presentation type is char-like. -FMT_CONSTEXPR inline auto check_char_specs(const format_specs& specs) -> bool { - auto type = specs.type(); - if (type != presentation_type::none && type != presentation_type::chr && - type != presentation_type::debug) { - return false; - } - if (specs.align() == align::numeric || specs.sign() != sign::none || - specs.alt()) { - report_error("invalid format specifier for char"); - } - return true; -} - template struct arg_pack {}; -template +template class format_string_checker { private: using parse_context_type = compile_parse_context; @@ -2908,13 +2900,14 @@ class format_string_checker { FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); } FMT_CONSTEXPR auto on_arg_id(int id) -> int { - return context_.check_arg_id(id), id; + context_.check_arg_id(id); + return id; } FMT_CONSTEXPR auto on_arg_id(basic_string_view id) -> int { for (int i = 0; i < NUM_NAMED_ARGS; ++i) { - if (named_args_[i].name == id) return i; + if (named_args_[i].name == id) return named_args_[i].id; } - on_error("named argument is not found"); + if (!DYNAMIC_NAMES) on_error("argument not found"); return -1; } @@ -2922,11 +2915,12 @@ class format_string_checker { on_format_specs(id, begin, begin); // Call parse() on empty specs. } - FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char*) + FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char* end) -> const Char* { context_.advance_to(begin); - // id >= 0 check is a workaround for gcc 10 bug (#2065). - return id >= 0 && id < NUM_ARGS ? parse_funcs_[id](context_) : begin; + if (id >= 0 && id < NUM_ARGS) return parse_funcs_[id](context_); + while (begin != end && *begin != '}') ++begin; + return begin; } FMT_NORETURN FMT_CONSTEXPR void on_error(const char* message) { @@ -3012,20 +3006,14 @@ template class basic_format_string { private: basic_string_view str_; - using checker = detail::format_string_checker< - Char, static_cast(sizeof...(Args)), - detail::count_statically_named_args()>; + static constexpr int num_static_named_args = + detail::count_static_named_args(); - FMT_CONSTEXPR FMT_ALWAYS_INLINE static void check( - basic_string_view fmt) { - using namespace detail; - static_assert( - count<(std::is_base_of>::value && - std::is_reference::value)...>() == 0, - "passing views as lvalues is disallowed"); - if (count_named_args() == count_statically_named_args()) - parse_format_string(fmt, checker(fmt, arg_pack())); - } + using checker = detail::format_string_checker< + Char, static_cast(sizeof...(Args)), num_static_named_args, + num_static_named_args != detail::count_named_args()>; + + using arg_pack = detail::arg_pack; public: // Reports a compile-time error if S is not a valid format string for Args. @@ -3033,7 +3021,13 @@ template class basic_format_string { FMT_ENABLE_IF( std::is_convertible>::value)> FMT_CONSTEVAL FMT_ALWAYS_INLINE basic_format_string(const S& s) : str_(s) { - if (FMT_USE_CONSTEVAL) check(s); + using namespace detail; + static_assert( + detail::count<(std::is_base_of>::value && + std::is_reference::value)...>() == 0, + "passing views as lvalues is disallowed"); + if (FMT_USE_CONSTEVAL) + parse_format_string(s, checker(s, arg_pack())); #ifdef FMT_ENFORCE_COMPILE_STRING static_assert( FMT_USE_CONSTEVAL && sizeof(S) != 0, @@ -3045,7 +3039,9 @@ template class basic_format_string { FMT_ENABLE_IF(std::is_base_of::value&& std::is_same::value)> FMT_ALWAYS_INLINE basic_format_string(const S& s) : str_(s) { - FMT_CONSTEXPR int ignore = (check(basic_string_view(S())), 0); + FMT_CONSTEXPR auto fmt = basic_string_view(S()); + FMT_CONSTEXPR int ignore = + (parse_format_string(fmt, checker(fmt, arg_pack())), 0); detail::ignore_unused(ignore); } basic_format_string(runtime_format_string fmt) : str_(fmt.str) {}