On Mon, 8 Dec 2025, Jonathan Wakely wrote:

> On Mon, 8 Dec 2025 at 15:23, Patrick Palka <[email protected]> wrote:
> >
> >
> >
> > On Mon, 8 Dec 2025, Jonathan Wakely wrote:
> >
> > > We have __is_signed_integer and __is_unsigned_integer traits which
> > > should have been updated by r16-2190-g4faa42ac0dee2c when making
> > > __int128 an extended integer type (for PR libstdc++/96710). Currently
> > > they check whether the type is a signed integer type or an unsigned
> > > integer type, or a cv-qualified version of one of those. This doesn't
> > > match the standard's definition, which does not include cv-qualified
> > > types. This change ensures that signed __int128 and unsigned __int128
> > > are included in those traits in strict -std modes, and it removes the
> > > use of remove_cv_t so that they are not true for cv-qualified types.
> > > This makes the traits match the meaning of "signed integer type" and
> > > "unsigned integer type" in the standard ([basic.fundamental]).
> > >
> > > We also have an __is_standard_integer trait, which is true if either
> > > __is_signed_integer or __is_unsigned_integer is true, but that's also
> > > not a match for the definition in the standard. The definitions of
> > > "signed integer type" and "unsigned integer type" include both standard
> > > and extended integer types, so only saying "standard" in the trait name
> > > is misleading (even before this change, because in non-strict -std modes
> > > the __GLIBCXX_TYPE_INT_N_0 .. __GLIBCXX_TYPE_INT_N_3 types were always
> > > included in the trait, and they aren't standard integer types).
> > >
> > > This change renames __is_standard_integer to the more accurate
> > > __is_signed_or_unsigned_integer_type. Because the set of signed and
> >
> > In the code __is_signed_or_unsigned_integer is the new name, no
> > _type suffix?  Besides that LGTM, this is a nice improvement.
> 
> Oops, yes, and then I used vim's autocomplete for the rest of the
> commit message and so it says _type everywhere below.
> 
> I'll fix that, thanks.
> 
> > Btw we probably should define __is_signed/unsigned_integer via explicit
> > specializations instead of the __is_one_of helper, so that it's O(1)
> > instead of O(n) to check.
> 
> Yes, it's tedious, but probably worth it.
> 
> I think the reason I didn't do that originally is because we already
> have explicit specializations of __is_integral_helper for every
> integral type, and it seemed like error-prone duplication to have two
> more sets of explicit specializations that would need updating for new
> integer types. But apart from char8_t, there haven't been any new
> integral types added since the INT_N ones.
> 
> Another option would be to add a boolean to __is_integral_helper that
> says whether it's a signed integer type, an unsigned integer type, or
> a character type. __is_integral_helper already only handles
> cv-unqualified types (because is_integral uses remove_cv_t) and every
> cv-unqualified integral type is either a signed integer type, and
> unsigned integer type, a character type, or bool.

That'd be a nice net simplification!

> 
> So something like:
> 
> --- a/libstdc++-v3/include/std/type_traits
> +++ b/libstdc++-v3/include/std/type_traits
> @@ -348,78 +348,81 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     : public true_type { };
> 
>   /// @cond undocumented
> +
> +  enum class _Integer_kind { _Character, _Signed, _Unsigned, _Other };
> +
>   template<typename>
>     struct __is_integral_helper
> -    : public false_type { };
> +    : false_type { static constexpr _S_kind = _Integer_kind::_Other; };
> 
>   template<>
>     struct __is_integral_helper<bool>
> -    : public true_type { };
> +    : true_type { static constexpr _S_kind = _Integer_kind::_Other; };
> 
>   template<>
>     struct __is_integral_helper<char>
> 
> etc.
> 
> and then:
> 
> @@ -824,50 +827,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     using __is_one_of = __or_<is_same<_Tp, _Types>...>;
> 
>   // Check if a type is one of the signed integer types.
> -  __extension__
>   template<typename _Tp>
> -    using __is_signed_integer = __is_one_of<_Tp,
> -         signed char, signed short, signed int, signed long,
> -         signed long long
> -#if defined(__GLIBCXX_TYPE_INT_N_0)
> -         , signed __GLIBCXX_TYPE_INT_N_0
> -#endif
> -#if defined(__GLIBCXX_TYPE_INT_N_1)
> -         , signed __GLIBCXX_TYPE_INT_N_1
> -#endif
> -#if defined(__GLIBCXX_TYPE_INT_N_2)
> -         , signed __GLIBCXX_TYPE_INT_N_2
> -#endif
> -#if defined(__GLIBCXX_TYPE_INT_N_3)
> -         , signed __GLIBCXX_TYPE_INT_N_3
> -#endif
> -#if defined __STRICT_ANSI__ && defined __SIZEOF_INT128__
> -         , signed __int128
> -#endif
> -         >;
> +    using __is_signed_integer
> +      = __bool_constant<__is_integral_helper<_Tp>::_S_kind
> +                         == _Integer_kind::_Signed>
> 
>   // Check if a type is one of the unsigned integer types.
>   __extension__
>   template<typename _Tp>
> -    using __is_unsigned_integer = __is_one_of<_Tp,
> -         unsigned char, unsigned short, unsigned int, unsigned long,
> -         unsigned long long
> -#if defined(__GLIBCXX_TYPE_INT_N_0)
> -         , unsigned __GLIBCXX_TYPE_INT_N_0
> -#endif
> -#if defined(__GLIBCXX_TYPE_INT_N_1)
> -         , unsigned __GLIBCXX_TYPE_INT_N_1
> -#endif
> -#if defined(__GLIBCXX_TYPE_INT_N_2)
> -         , unsigned __GLIBCXX_TYPE_INT_N_2
> -#endif
> -#if defined(__GLIBCXX_TYPE_INT_N_3)
> -         , unsigned __GLIBCXX_TYPE_INT_N_3
> -#endif
> -#if defined __STRICT_ANSI__ && defined __SIZEOF_INT128__
> -         , unsigned __int128
> -#endif
> -         >;
> +    using __is_unsigned_integer
> +      = __bool_constant<__is_integral_helper<_Tp>::_S_kind
> +                         == _Integer_kind::_Unsigned>
> 
>   // Check if a type is one of the signed or unsigned integer types.
>   // i.e. an integral type except bool, char, wchar_t, and charN_t.

Reply via email to