On Tue, Mar 11, 2025 at 11:46 PM Jonathan Wakely <jwak...@redhat.com> wrote:

> This change makes the consteval function slightly faster to compile.
> Instead of keeping the counts in an array and looping over that array,
> we can just keep a sum of how many valid types are present, and check
> that it equals the total number of types in the pack. We can also avoid
> doing the check entirely for the common cases where the call comes from
> check_dynamic_spec_integer or check_dynamic_spec_string, because those
> always use valid lists of types.
>
> The diagnostic is slightly worse now, because there's only a single
> "invalid template argument types" string that appears in the output,
> where previously we had either "non-unique template argument type" or
> "disallowed template argument type" depending on the failure mode.
>
> Given that most users will never use this function directly, and
> probably won't use invalid types anyway, the inferior diagnostic seems
> acceptable.
>
> libstdc++-v3/ChangeLog:
>
>         * include/std/format (basic_format_parse_context::__check_types):
>         New variable template and partial specializations.
>         (basic_format_parse_context::__check_dynamic_spec_types):
>         Simplify for faster compilation.
>         (basic_format_parse_context::check_dynamic_spec): Only use
>         __check_dynamic_spec_types when __check_types is true. Use it as
>         the condition for a constexpr if statement instead of as the
>         initializer for a constexpr variable.
>         (basic_format_parse_context::check_dynamic_spec_string): Use
>         _CharT instead of char_type consistently.
> ---
>
> Tested x86_64-linux.
>
>  libstdc++-v3/include/std/format | 81 +++++++++++++++++++--------------
>  1 file changed, 47 insertions(+), 34 deletions(-)
>
> diff --git a/libstdc++-v3/include/std/format
> b/libstdc++-v3/include/std/format
> index 0d6cc7f6bef..6269cbb80e6 100644
> --- a/libstdc++-v3/include/std/format
> +++ b/libstdc++-v3/include/std/format
> @@ -305,41 +305,51 @@ namespace __format
>        constexpr void
>        check_dynamic_spec_string(size_t __id) noexcept
>        {
> -       check_dynamic_spec<const char_type*,
> basic_string_view<_CharT>>(__id);
> +       check_dynamic_spec<const _CharT*, basic_string_view<_CharT>>(__id);
>        }
>
>      private:
> -      // Check the Mandates: condition for check_dynamic_spec<Ts...>(n)
> +      template<typename _Tp, typename... _Ts>
> +       static constexpr bool __once = (is_same_v<_Tp, _Ts> + ...) == 1;
> +
> +      // True if we need to call __check_dynamic_spec_types for the pack
> Ts
> +      template<typename... _Ts>
> +       static constexpr bool __check_types = sizeof...(_Ts) > 0;
> +      // The pack used by check_dynamic_spec_integral is valid, don't
> check it.
> +      // FIXME: simplify these when PR c++/85282 is supported.
> +      template<typename _Tp>
> +       static constexpr bool
> +       __check_types<_Tp, unsigned, long long, unsigned long long>
> +         = ! is_same_v<_Tp, int>;
>
These seem to produce wrong answer (false) if the user called
check_dynamic_spec<basic_string_view<_CharT>, unsigned, long long, unsigned
long long>,
which is a valid set of types.

> +      // The pack used by check_dynamic_spec_string is valid, don't check
> it.
> +      template<typename _Tp>
> +       static constexpr bool
> +       __check_types<_Tp, basic_string_view<_CharT>>
> +         = ! is_same_v<const _CharT*, _Tp>;
>
As above check_dynamic_spec<int, basic_string_view<_CharT>>.
I think much batter solution would be to have __check_dynamic_spec<Ts...>
private function,
that would not perform the check at all and will be called without checking
from check_dynamic_spec_integrral, e.t.c.
And in check_dynamic_spec, we would do __check_dynamic_spec_types<_Ts...>()
and sizeof..(Ts) > 0, before
calling __check_dynamic_spec.

+
> +      // Check the Mandates: conditions for check_dynamic_spec<Ts...>(n)
>        template<typename... _Ts>
>         static consteval bool
>         __check_dynamic_spec_types()
>         {
> -         if constexpr (sizeof...(_Ts))
> -           {
> -             int __counts[] = {
> -               (is_same_v<bool, _Ts> + ...),
> -               (is_same_v<_CharT, _Ts> + ...),
> -               (is_same_v<int, _Ts> + ...),
> -               (is_same_v<unsigned, _Ts> + ...),
> -               (is_same_v<long long, _Ts> + ...),
> -               (is_same_v<unsigned long long, _Ts> + ...),
> -               (is_same_v<float, _Ts> + ...),
> -               (is_same_v<double, _Ts> + ...),
> -               (is_same_v<long double, _Ts> + ...),
> -               (is_same_v<const _CharT*, _Ts> + ...),
> -               (is_same_v<basic_string_view<_CharT>, _Ts> + ...),
> -               (is_same_v<const void*, _Ts> + ...)
> -             };
> -             int __sum = 0;
> -             for (int __c : __counts)
> -               {
> -                 __sum += __c;
> -                 if (__c > 1)
> -                   __invalid_dynamic_spec("non-unique template argument
> type");
> -               }
> -             if (__sum != sizeof...(_Ts))
> -               __invalid_dynamic_spec("disallowed template argument
> type");
> -           }
> +         // The types in Ts... are unique. Each type in Ts... is one of
> +         // bool, char_type, int, unsigned int, long long int,
> +         // unsigned long long int, float, double, long double,
> +         // const char_type*, basic_string_view<char_type>, or const
> void*.
> +         unsigned __sum = __once<bool, _Ts...>
> +                        + __once<char_type, _Ts...>
> +                        + __once<int, _Ts...>
> +                        + __once<unsigned int, _Ts...>
> +                        + __once<long long int, _Ts...>
> +                        + __once<unsigned long long int, _Ts...>
> +                        + __once<float, _Ts...>
> +                        + __once<double, _Ts...>
> +                        + __once<long double, _Ts...>
> +                        + __once<const char_type*, _Ts...>
> +                        + __once<basic_string_view<char_type>, _Ts...>
> +                        + __once<const void*, _Ts...>;
> +         if (__sum != sizeof...(_Ts))
> +           __invalid_dynamic_spec("invalid template argument types");
>           return true;
>         }
>
> @@ -4341,12 +4351,15 @@ namespace __format
>        // _GLIBCXX_RESOLVE_LIB_DEFECTS
>        // 4142. check_dynamic_spec should require at least one type
>        static_assert(sizeof...(_Ts) >= 1);
> -      // This call enforces the Mandates: condition that _Ts contains
> valid
> -      // types and each type appears at most once. It could be a
> static_assert
> -      // but this way failures give better diagnostics, due to calling the
> -      // non-constexpr __invalid_dynamic_spec function.
> -      [[maybe_unused]]
> -      constexpr bool __ok = __check_dynamic_spec_types<_Ts...>();
> +
> +      if constexpr (__check_types<_Ts...>)
> +       if constexpr (__check_dynamic_spec_types<_Ts...>())
> +         {
> +           // The call above enforces the Mandates: condition that _Ts
> +           // contains valid types and each type appears at most once.
> +           // Checking it in `if constexpr` means that failures give
> better
> +           // diagnostics than if we used it as a static_assert condition.
> +         }
>
>        if consteval {
>         if (__id >= _M_num_args)
> --
> 2.48.1
>
>

Reply via email to