On Tue, 16 Dec 2025 at 10:11, Tomasz Kaminski <[email protected]> wrote:
>
>
>
> On Tue, Dec 16, 2025 at 11:04 AM Jonathan Wakely <[email protected]> wrote:
>>
>> On Tue, 16 Dec 2025 at 07:07, Tomasz Kaminski <[email protected]> wrote:
>> >
>> >
>> >
>> > On Tue, Dec 16, 2025 at 8:03 AM Tomasz Kaminski <[email protected]> 
>> > wrote:
>> >>
>> >>
>> >>
>> >> On Tue, Dec 16, 2025 at 12:33 AM Jonathan Wakely <[email protected]> 
>> >> wrote:
>> >>>
>> >>> Replace the O(n) definitions using __is_one_of with constant-time
>> >>> checks that look for a static member in the __is_integral_helper class
>> >>> template. That class template is already specialized for every signed
>> >>> and unsigned integer type, so we don't need to define any additional
>> >>> specializations. We can just add a static data member that says whether
>> >>> the type is a signed integer type, an unsigned integer type, or neither.
>> >>> The __is_signed_integer and __is_unsigned_integer traits can then
>> >>> inspect that value.
>> >>>
>> >>> The new enum type could be extended in future to distinguish the
>> >>> character types (char, wchar_t, char8_t, char16_t, and char32_t) and
>> >>> bool from non-integer types, but that isn't needed for now.
>> >>>
>> >>> libstdc++-v3/ChangeLog:
>> >>>
>> >>>         * include/std/type_traits (_Integer_kind): New enum type.
>> >>>         (__is_integral_helper::_S_kind): New static data member in
>> >>>         primary template and each explicit specialization.
>> >>>         (__is_signed_integer, __is_unsigned_integer): Use _S_kind
>> >>>         instead of O(n) disjunction with is_same.
>> >>> ---
>> >>>
>> >>> Tested x86_64-linux.
>> >>>
>> >>>  libstdc++-v3/include/std/type_traits | 158 +++++++++++++--------------
>> >>>  1 file changed, 79 insertions(+), 79 deletions(-)
>> >>>
>> >>> diff --git a/libstdc++-v3/include/std/type_traits 
>> >>> b/libstdc++-v3/include/std/type_traits
>> >>> index 3f0bcc4e77d2..b9a7b12d7e6e 100644
>> >>> --- a/libstdc++-v3/include/std/type_traits
>> >>> +++ b/libstdc++-v3/include/std/type_traits
>> >>> @@ -348,78 +348,102 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> >>>      : public true_type { };
>> >>>
>> >>>    /// @cond undocumented
>> >>> +
>> >>> +  // Every integral type is either one of the character types, one of 
>> >>> the
>> >>> +  // signed integer types, one of the unsigned integer types, or bool,
>> >>> +  // or a cv-qualified version of one of those types 
>> >>> ([basic.fundamental]).
>> >>> +  // For now we only need to distinguish the signed/unsigned integer 
>> >>> types.
>> >>> +  enum class _Integer_kind { _Signed, _Unsigned, _None = -1 };
>> >
>> > I would use value _Signed = -1, _None = 0, _Unsigned = 1
>>
>> I'm not opposed to that, but does it really offer any advantage except
>> looking nice, and seeming elegant?
>
> None, I was sure that I didn't send this suggestion, after writing it,
> as I realized the same.

:-)

I think I'm leaning towards this now:

  enum class _Integer_kind { _None, _Signed, _Unsigned };

none == zero, because it's none.

But there's no need to be cute and use a negative value for signed,
all that matters is that they're distinct values.
This is cleanly extensible to a _Character enumerator if we want that later.

>>
>>
>>
>> I don't think it will change how we
>> write any code, and these values are only ever likely to be tested at
>> compile-time, so there's no real need to optimize for "not zero"
>> instructions being efficient at runtime.
>>
>> Also, I'm anticipating that we might want to add an enumerator for
>> "character type" in future, and maybe bool. Those are integral types,
>> but not signed integer types or unsigned integer types. If we added
>> _Character=2 then the nice property of -1/0/1 becomes -1/0/1/2 which
>> is less elegant. And potentially confusing (does it imply that
>> character types are unsigned, because the enum value is not negative?
>> Because some character types are signed on some targets).
>>
>>
>> >>>
>> >>> +
>> >>>    template<typename>
>> >>>      struct __is_integral_helper
>> >>> -    : public false_type { };
>> >>> +    : public false_type
>> >>> +    { static constexpr auto _S_kind = _Integer_kind::_None; };
>> >>>
>> >>>    template<>
>> >>>      struct __is_integral_helper<bool>
>> >>> -    : public true_type { };
>> >>> +    : public true_type
>> >>> +    { static constexpr auto _S_kind = _Integer_kind::_None; };
>> >>>
>> >>>    template<>
>> >>>      struct __is_integral_helper<char>
>> >>> -    : public true_type { };
>> >>> +    : public true_type
>> >>> +    { static constexpr auto _S_kind = _Integer_kind::_None; };
>> >>>
>> >>>    template<>
>> >>>      struct __is_integral_helper<signed char>
>> >>> -    : public true_type { };
>> >>> +    : public true_type
>> >>> +    { static constexpr auto _S_kind = _Integer_kind::_Signed; };
>> >>>
>> >>>    template<>
>> >>>      struct __is_integral_helper<unsigned char>
>> >>> -    : public true_type { };
>> >>> +    : public true_type
>> >>> +    { static constexpr auto _S_kind = _Integer_kind::_Unsigned; };
>> >>>
>> >>>    // We want is_integral<wchar_t> to be true (and make_signed/unsigned 
>> >>> to work)
>> >>>    // even when libc doesn't provide working <wchar.h> and related 
>> >>> functions,
>> >>>    // so don't check _GLIBCXX_USE_WCHAR_T here.
>> >>>    template<>
>> >>>      struct __is_integral_helper<wchar_t>
>> >>> -    : public true_type { };
>> >>> +    : public true_type
>> >>> +    { static constexpr auto _S_kind = _Integer_kind::_None; };
>> >>>
>> >>>  #ifdef _GLIBCXX_USE_CHAR8_T
>> >>>    template<>
>> >>>      struct __is_integral_helper<char8_t>
>> >>> -    : public true_type { };
>> >>> +    : public true_type
>> >>> +    { static constexpr auto _S_kind = _Integer_kind::_None; };
>> >>>  #endif
>> >>>
>> >>>    template<>
>> >>>      struct __is_integral_helper<char16_t>
>> >>> -    : public true_type { };
>> >>> +    : public true_type
>> >>> +    { static constexpr auto _S_kind = _Integer_kind::_None; };
>> >>>
>> >>>    template<>
>> >>>      struct __is_integral_helper<char32_t>
>> >>> -    : public true_type { };
>> >>> +    : public true_type
>> >>> +    { static constexpr auto _S_kind = _Integer_kind::_None; };
>> >>>
>> >>>    template<>
>> >>>      struct __is_integral_helper<short>
>> >>> -    : public true_type { };
>> >>> +    : public true_type
>> >>> +    { static constexpr auto _S_kind = _Integer_kind::_Signed; };
>> >>>
>> >>>    template<>
>> >>>      struct __is_integral_helper<unsigned short>
>> >>> -    : public true_type { };
>> >>> +    : public true_type
>> >>> +    { static constexpr auto _S_kind = _Integer_kind::_Unsigned; };
>> >>>
>> >>>    template<>
>> >>>      struct __is_integral_helper<int>
>> >>> -    : public true_type { };
>> >>> +    : public true_type
>> >>> +    { static constexpr auto _S_kind = _Integer_kind::_Signed; };
>> >>>
>> >>>    template<>
>> >>>      struct __is_integral_helper<unsigned int>
>> >>> -    : public true_type { };
>> >>> +    : public true_type
>> >>> +    { static constexpr auto _S_kind = _Integer_kind::_Unsigned; };
>> >>>
>> >>>    template<>
>> >>>      struct __is_integral_helper<long>
>> >>> -    : public true_type { };
>> >>> +    : public true_type
>> >>> +    { static constexpr auto _S_kind = _Integer_kind::_Signed; };
>> >>>
>> >>>    template<>
>> >>>      struct __is_integral_helper<unsigned long>
>> >>> -    : public true_type { };
>> >>> +    : public true_type
>> >>> +    { static constexpr auto _S_kind = _Integer_kind::_Unsigned; };
>> >>>
>> >>>    template<>
>> >>>      struct __is_integral_helper<long long>
>> >>> -    : public true_type { };
>> >>> +    : public true_type
>> >>> +    { static constexpr auto _S_kind = _Integer_kind::_Signed; };
>> >>>
>> >>>    template<>
>> >>>      struct __is_integral_helper<unsigned long long>
>> >>> -    : public true_type { };
>> >>> +    : public true_type
>> >>> +    { static constexpr auto _S_kind = _Integer_kind::_Unsigned; };
>> >>>
>> >>>    // Conditionalizing on __STRICT_ANSI__ here will break any port that
>> >>>    // uses one of these types for size_t.
>> >>> @@ -427,59 +451,87 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> >>>    __extension__
>> >>>    template<>
>> >>>      struct __is_integral_helper<__GLIBCXX_TYPE_INT_N_0>
>> >>> -    : public true_type { };
>> >>> +    : public true_type
>> >>> +    { static constexpr auto _S_kind = _Integer_kind::_Signed; };
>> >>>
>> >>>    __extension__
>> >>>    template<>
>> >>>      struct __is_integral_helper<unsigned __GLIBCXX_TYPE_INT_N_0>
>> >>> -    : public true_type { };
>> >>> +    : public true_type
>> >>> +    { static constexpr auto _S_kind = _Integer_kind::_Unsigned; };
>> >>>  #endif
>> >>>  #if defined(__GLIBCXX_TYPE_INT_N_1)
>> >>>    __extension__
>> >>>    template<>
>> >>>      struct __is_integral_helper<__GLIBCXX_TYPE_INT_N_1>
>> >>> -    : public true_type { };
>> >>> +    : public true_type
>> >>> +    { static constexpr auto _S_kind = _Integer_kind::_Signed; };
>> >>>
>> >>>    __extension__
>> >>>    template<>
>> >>>      struct __is_integral_helper<unsigned __GLIBCXX_TYPE_INT_N_1>
>> >>> -    : public true_type { };
>> >>> +    : public true_type
>> >>> +    { static constexpr auto _S_kind = _Integer_kind::_Unsigned; };
>> >>>  #endif
>> >>>  #if defined(__GLIBCXX_TYPE_INT_N_2)
>> >>>    __extension__
>> >>>    template<>
>> >>>      struct __is_integral_helper<__GLIBCXX_TYPE_INT_N_2>
>> >>> -    : public true_type { };
>> >>> +    : public true_type
>> >>> +    { static constexpr auto _S_kind = _Integer_kind::_Signed; };
>> >>>
>> >>>    __extension__
>> >>>    template<>
>> >>>      struct __is_integral_helper<unsigned __GLIBCXX_TYPE_INT_N_2>
>> >>> -    : public true_type { };
>> >>> +    : public true_type
>> >>> +    { static constexpr auto _S_kind = _Integer_kind::_Unsigned; };
>> >>>  #endif
>> >>>  #if defined(__GLIBCXX_TYPE_INT_N_3)
>> >>>    __extension__
>> >>>    template<>
>> >>>      struct __is_integral_helper<__GLIBCXX_TYPE_INT_N_3>
>> >>> -    : public true_type { };
>> >>> +    : public true_type
>> >>> +    { static constexpr auto _S_kind = _Integer_kind::_Signed; };
>> >>>
>> >>>    __extension__
>> >>>    template<>
>> >>>      struct __is_integral_helper<unsigned __GLIBCXX_TYPE_INT_N_3>
>> >>> -    : public true_type { };
>> >>> +    : public true_type
>> >>> +    { static constexpr auto _S_kind = _Integer_kind::_Unsigned; };
>> >>>  #endif
>> >>>
>> >>>  #if defined __SIZEOF_INT128__ && defined __STRICT_ANSI__
>> >>>    __extension__
>> >>>    template<>
>> >>>      struct __is_integral_helper<__int128>
>> >>> -    : public true_type { };
>> >>> +    : public true_type
>> >>> +    { static constexpr auto _S_kind = _Integer_kind::_Signed; };
>> >>>
>> >>>    __extension__
>> >>>    template<>
>> >>>      struct __is_integral_helper<unsigned __int128>
>> >>> -    : public true_type { };
>> >>> +    : public true_type
>> >>> +    { static constexpr auto _S_kind = _Integer_kind::_Unsigned; };
>> >>>  #endif
>> >>>
>> >>> +  // Check if a type is one of the signed integer types.
>> >>> +  template<typename _Tp>
>> >>> +    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.
>> >>> +  template<typename _Tp>
>> >>> +    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.
>> >>> +  template<typename _Tp>
>> >>> +    using __is_signed_or_unsigned_integer
>> >>> +      = __or_<__is_signed_integer<_Tp>, __is_unsigned_integer<_Tp>>;
>> >>
>> >> We could also replace __or_ here with, reducing the specialization even 
>> >> more.
>> >>  = __bool_constant<__is_integral_helper<_Tp>::_S_kind  != 
>> >> _Integer_kind::_None>;
>>
>> Ah yes. I originally had a _Integer_kind::_Character enumerator and so
>> that didn't work.
>
> In that case we could use the following, which still avoid using __or_, but 
> is not
> affected by new enumerator values:
>   __bool_constant<__is_integral_helper<_Tp>::_S_kind  == 
> _Integer_kind::_Signed
>                                 || __is_integral_helper<_Tp>::_S_kind  == 
> _Integer_kind::_Unsigned>;

Yes, but for now just != _None is simpler.

>>
>>
>> But I removed that (we don't need it yet) and now that the enumerators
>> are only _None, _Signed and _Unsigned, it works to just check !=
>> _None.
>>
>> If we add more enumerators later tests will fail, so I'm not worried
>> that we'd forget to change this.
>>

Reply via email to