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? 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.

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