From fec2cc7af103aceb475480908859a30474a2fe6a Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 31 Aug 2024 19:18:20 -0700 Subject: [PATCH] Improve handling of named arguments --- include/fmt/base.h | 100 ++++++++++++++++++++---------------------- include/fmt/compile.h | 23 ++++++++++ 2 files changed, 71 insertions(+), 52 deletions(-) diff --git a/include/fmt/base.h b/include/fmt/base.h index 45822f1c..052c66bf 100644 --- a/include/fmt/base.h +++ b/include/fmt/base.h @@ -1341,7 +1341,6 @@ 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 { return count::value...>(); @@ -1758,6 +1757,20 @@ void init_named_arg(named_arg_info* named_args, int& arg_index, named_args[named_arg_index++] = {arg.name, arg_index++}; } +template ::value)> +FMT_CONSTEXPR void init_statically_named_arg(named_arg_info*, + int& arg_index, int&) { + ++arg_index; +} +template ::value)> +FMT_CONSTEXPR void init_statically_named_arg(named_arg_info* named_args, + int& arg_index, + int& named_arg_index) { + named_args[named_arg_index++] = {T::name, arg_index++}; +} + // An array of references to arguments. It can be implicitly converted to // `fmt::basic_format_args` for passing into type-erased formatting functions // such as `fmt::vformat`. @@ -2857,35 +2870,13 @@ FMT_CONSTEXPR inline auto check_char_specs(const format_specs& specs) -> bool { return true; } -#if FMT_USE_NONTYPE_TEMPLATE_ARGS -template -constexpr auto get_arg_index_by_name(basic_string_view name) -> int { - if constexpr (is_statically_named_arg()) { - if (name == T::name) return N; - } - if constexpr (sizeof...(Args) > 0) - return get_arg_index_by_name(name); - (void)name; // Workaround an MSVC bug about "unused" parameter. - return -1; -} -#endif - -template -FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view name) -> int { -#if FMT_USE_NONTYPE_TEMPLATE_ARGS - if constexpr (sizeof...(Args) > 0) - return get_arg_index_by_name<0, Args...>(name); -#endif - (void)name; - return -1; -} - template class format_string_checker { private: using parse_context_type = compile_parse_context; static constexpr int num_args = sizeof...(Args); + static constexpr int num_named_args = count_statically_named_args(); - // Format specifier parsing function. + // Format specifiers parsing function. // In the future basic_format_parse_context will replace compile_parse_context // here and will use is_constant_evaluated and downcasting to access the data // needed for compile-time checks: https://godbolt.org/z/GvWzcTjh1. @@ -2894,12 +2885,21 @@ template class format_string_checker { type types_[num_args > 0 ? static_cast(num_args) : 1]; parse_context_type context_; parse_func parse_funcs_[num_args > 0 ? static_cast(num_args) : 1]; + named_arg_info named_args_[num_named_args > 0 ? num_named_args : 1]; public: explicit FMT_CONSTEXPR format_string_checker(basic_string_view fmt) : types_{mapped_type_constant>::value...}, context_(fmt, num_args, types_), - parse_funcs_{&parse_format_specs...} {} + parse_funcs_{&parse_format_specs...}, + named_args_{} { + using dummy = int[]; + int arg_index = 0, named_arg_index = 0; + (void)dummy{0, (init_statically_named_arg(named_args_, arg_index, + named_arg_index), + 0)...}; + ignore_unused(arg_index, named_arg_index); + } FMT_CONSTEXPR void on_text(const Char*, const Char*) {} @@ -2908,15 +2908,11 @@ template class format_string_checker { return context_.check_arg_id(id), id; } FMT_CONSTEXPR auto on_arg_id(basic_string_view id) -> int { -#if FMT_USE_NONTYPE_TEMPLATE_ARGS - auto index = get_arg_index_by_name(id); - if (index < 0) on_error("named argument is not found"); - return index; -#else - (void)id; - on_error("compile-time checks for named arguments require C++20 support"); - return 0; -#endif + for (int i = 0; i < num_named_args; ++i) { + if (named_args_[i].name == id) return i; + } + on_error("named argument is not found"); + return -1; } FMT_CONSTEXPR void on_replacement_field(int id, const Char* begin) { @@ -2938,23 +2934,6 @@ template class format_string_checker { // A base class for compile-time strings. struct compile_string {}; -// Use vformat_args and avoid type_identity to keep symbols short. -template struct vformat_args { - using type = basic_format_args>; -}; -template <> struct vformat_args { - using type = format_args; -}; - -template -void vformat_to(buffer& buf, basic_string_view fmt, - typename vformat_args::type args, locale_ref loc = {}); - -FMT_API void vprint_mojibake(FILE*, string_view, format_args, bool = false); -#ifndef _WIN32 -inline void vprint_mojibake(FILE*, string_view, format_args, bool) {} -#endif - // TYPE can be different from type_constant, e.g. for __float128. template struct native_formatter { private: @@ -2982,6 +2961,23 @@ template struct native_formatter { FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const -> decltype(ctx.out()); }; + +// Use vformat_args and avoid type_identity to keep symbols short. +template struct vformat_args { + using type = basic_format_args>; +}; +template <> struct vformat_args { + using type = format_args; +}; + +template +void vformat_to(buffer& buf, basic_string_view fmt, + typename vformat_args::type args, locale_ref loc = {}); + +FMT_API void vprint_mojibake(FILE*, string_view, format_args, bool = false); +#ifndef _WIN32 +inline void vprint_mojibake(FILE*, string_view, format_args, bool) {} +#endif } // namespace detail FMT_BEGIN_EXPORT diff --git a/include/fmt/compile.h b/include/fmt/compile.h index 4a5dc71d..1da29240 100644 --- a/include/fmt/compile.h +++ b/include/fmt/compile.h @@ -71,6 +71,29 @@ constexpr const auto& get([[maybe_unused]] const T& first, return detail::get(rest...); } +#if FMT_USE_NONTYPE_TEMPLATE_ARGS +template +constexpr auto get_arg_index_by_name(basic_string_view name) -> int { + if constexpr (is_statically_named_arg()) { + if (name == T::name) return N; + } + if constexpr (sizeof...(Args) > 0) + return get_arg_index_by_name(name); + (void)name; // Workaround an MSVC bug about "unused" parameter. + return -1; +} +#endif + +template +FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view name) -> int { +#if FMT_USE_NONTYPE_TEMPLATE_ARGS + if constexpr (sizeof...(Args) > 0) + return get_arg_index_by_name<0, Args...>(name); +#endif + (void)name; + return -1; +} + template constexpr int get_arg_index_by_name(basic_string_view name, type_list) {