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