9
\$\begingroup\$

This is a C++17 is_integral trait implementation.

Most implementations exhaust all integral types:

  • bool
  • char, char16_t, char32_t, wchar_t
  • [unsigned] short
  • [unsigned] [long [long]] int

However, it is possible that the implementation defines some extended signed integer types and extended unsigned integer types. One workaround is to exhaust all the implementations using #ifdefs, but this workaround is not theoretically (in the language lawyer's point of view) portable. For example, if Mickey Mouse just rolls out a brand-new standard-conforming extended integer type, such workarounds fail.

I theorized that for any type T, is_integral<T>::value is true if and only if it is convertible to std::intmax_t or std::uintmax_t via a non-narrowing standard conversion sequence. I tried to deploy this fact, enforcing the two restrictions respectively, by:

  • using the so-called "uniform initialization" syntax (formally list-initialization) to prevent narrowing conversions; and

  • using a dummy class (which I call a generator class) with a conversion operator to prevent user-defined conversion sequences.

I tested with the cases of standard integral types (expecting true,) standard floating point types (expecting false,) user-defined types convertible to some standard integral type (expecting false.) All tests passed.

Here is my code, within 40 lines.

// C++17 is_integral implementation
// which handles extended integer types
// in a portable way
// (without resorting to #ifdef chains)

#include <cinttypes> // for std::intmax_t, std::uintmax_t
#include <type_traits> // for std::is_same, std::bool_constant
                       // can be trivially defined manually, just lazy

namespace my_std {
    namespace detail {
        // Generator type to bypass user-defined conversions
        template <typename T>
        struct Generator {
            operator T();
        };
        // helper type to designate substitution failure
        struct Failed {};
        // check functions (exploit SFINAE)
        // uses brace syntax to prevent narrowing conversions
        template <typename T>
        auto check_signed(int) -> decltype(std::intmax_t{Generator<T>{}});
        template <typename T>
        auto check_signed(...) -> Failed;
        template <typename T>
        auto check_unsigned(int) -> decltype(std::uintmax_t{Generator<T>{}});
        template <typename T>
        auto check_unsigned(...) -> Failed;
        // function invoker trait
        template <typename T>
        struct Int_trait {
            static constexpr bool is_signed_int = !std::is_same_v<
                Failed, decltype(check_signed<T>(0))
            >;
            static constexpr bool is_unsigned_int = !std::is_same_v<
                Failed, decltype(check_unsigned<T>(0))
            >;
            static constexpr bool is_int = is_signed_int || is_unsigned_int;
        };
    }
    // "the" trait
    template <typename T>
    struct is_integral
        :std::bool_constant<detail::Int_trait<T>::is_int>
        { };
}

Any criticism or suggestion is highly appreciated!

<joke>The absolute best implementation with maximum portability and simplicity and clarity that is guaranteed to work on all machines is:

#include <type_traits>
namespace my_std {
    template <typename T>
    struct is_integral
        :std::is_integral<T>
        { };
}

</joke>

\$\endgroup\$
8
  • 3
    \$\begingroup\$ I like your joke! More seriously, why would you "manually [replace] intmax_ts with __int128_t"? Doesn't it defeat you initial purpose to provide a platform-independent implementation? And doesn't it prove that another plat-form could as well provide a similar extension that your code wouldn't take into account? Besides, to comply with the standard, std::is_integral_v<__int128_t> should be false, because it is neither a standard integer type nor an extended integer type, but a language extension. \$\endgroup\$
    – papagaga
    Feb 20, 2019 at 16:43
  • \$\begingroup\$ @papagaga Well... You are very helpful. I explained it too badly... The problem is, I am unable to find a single accessible implementation with a real extended integer type. And __int128_t was the closest I could find, although it is still not a real one. As a workaround, I faked intmax_t to test it with a (fake) one... I was appreciate if there is actually one I can test on ;-) \$\endgroup\$
    – L. F.
    Feb 21, 2019 at 8:32
  • \$\begingroup\$ "I found that for any type T, is_integral<T>::value is true if and only if: it is convertible to std::intmax_t or std::uintmax_t via a non-narrowing standard conversion sequence." — This is true for the standard integral types, but as you yourself realized, it's not true for the implementation-defined extended integral types. For the situation with __int128_t, see Is __int128 integral?. \$\endgroup\$ Mar 15, 2019 at 19:32
  • \$\begingroup\$ @Quuxplusone I know __int128_t is not an integral type at all. Just have no nondirty way to test... I will delete that part anyway. \$\endgroup\$
    – L. F.
    Mar 16, 2019 at 0:58
  • \$\begingroup\$ "I know __int128_t is not an integral type at all." — No, you think __int128_t is not an integral type at all. Both libc++ (in all modes) and libstdc++ (in -std=gnu++XX mode) disagree with you, though. See the blog post Is __int128 integral? for more details. \$\endgroup\$ Mar 16, 2019 at 1:05

1 Answer 1

2
\$\begingroup\$

I tested with the cases of standard integral types (expecting true,) standard floating point types (expecting false,) user-defined types convertible to some standard integral type (expecting false.) All tests passed.

You should have tested with all the other kinds of types in C++. Reference types, pointer types, enum types, nullptr_t, void, pointer-to-member types, etc. etc.

Your is_integral incorrectly reports that int& is an integral type. (Godbolt.)

It's not a hard fix, of course; but the code-review feedback is "Write more tests."

\$\endgroup\$
1
  • \$\begingroup\$ You're right. I will need more tests when writing template stuff. Or simply stop reinventing the wheel ... :P \$\endgroup\$
    – L. F.
    Mar 28, 2020 at 7:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.