On Tue, Mar 11, 2025 at 11:46 PM Jonathan Wakely <[email protected]> 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
>
>