Automatically pick a variable type big enough to hold a specified number

后端 未结 14 2001
耶瑟儿~
耶瑟儿~ 2020-12-07 14:01

Is there any way in C++ define a type that is big enough to hold at most a specific number, presumably using some clever template code. For example I want to be able to writ

相关标签:
14条回答
  • 2020-12-07 14:31

    Easy peasy with C++11:

    #include <cstdint>
    #include <limits>
    #include <type_traits>
    
    
    template <class T, class U =
        typename std::conditional<std::is_signed<T>::value,
          std::intmax_t,
          std::uintmax_t
        >::type>
    constexpr bool is_in_range (U x) {
      return (x >= std::numeric_limits<T>::min())
          && (x <= std::numeric_limits<T>::max());
    }
    
    template <std::intmax_t x>
    using int_fit_type =
        typename std::conditional<is_in_range<std::int8_t>(x),
          std::int8_t,
          typename std::conditional<is_in_range<std::int16_t>(x),
            std::int16_t,
            typename std::conditional<is_in_range<std::int32_t>(x),
              std::int32_t,
              typename std::enable_if<is_in_range<std::int64_t>(x), std::int64_t>::type
            >::type
          >::type
        >::type;
    
    template <std::uintmax_t x>
    using uint_fit_type =
        typename std::conditional<is_in_range<std::uint8_t>(x),
          std::uint8_t,
          typename std::conditional<is_in_range<std::uint16_t>(x),
            std::uint16_t,
            typename std::conditional<is_in_range<std::uint32_t>(x),
              std::uint32_t,
              typename std::enable_if<is_in_range<std::uint64_t>(x), std::uint64_t>::type
            >::type
          >::type
        >::type;
    
    0 讨论(0)
  • 2020-12-07 14:32

    Boost.Integer already has facilities for Integer Type Selection:

    boost::int_max_value_t<V>::least
    

    The smallest, built-in, signed integral type that can hold all the values in the inclusive range 0 - V. The parameter should be a positive number.

    boost::uint_value_t<V>::least
    

    The smallest, built-in, unsigned integral type that can hold all positive values up to and including V. The parameter should be a positive number.

    0 讨论(0)
  • 2020-12-07 14:32

    How about a conditional:

    #include <type_traits>
    #include <limits>
    
    template <unsigned long int N>
    struct MinInt
    {
      typedef typename std::conditional< N < std::numeric_limits<unsigned char>::max(),
           unsigned char, std::conditional< N < std::numeric_limits<unsigned short int>::max(),
             unsigned short int>::type,
             void*>::type>::type
        type;
    };
    

    This would have to be extended to encompass all desired types, in order; at the final stage you could use enable_if rather than conditional to have an instantiation error right there if the value is too large.

    0 讨论(0)
  • 2020-12-07 14:36

    A (IMHO) much more readable C++11 version :

    #include <inttypes.h>
    #include <cstdlib>
    #include <type_traits>
    #include <typeinfo>
    #include <iostream>
    
    
    template <long long n, typename ...An>
    struct inttype;
    
    template <long long n, typename A0, typename ...An>
    struct inttype<n, A0, An...>{
      typedef A0 least;
    };
    
    template <long long n, typename A0, typename A1, typename ...An>
    struct inttype<n, A0, A1, An...>{
      typedef typename std::conditional< n == (A0) n, A0, typename inttype<n, A1, An...>::least >::type least ;
    };
    
    template <long long n>
    struct inttype<n>{
      typedef typename inttype<n, uint8_t, uint16_t, uint32_t, uint64_t>::least least;
    };
    
    
    int main(int argc, char * argv[])
    {
      std::cout << sizeof(inttype<0x0ULL>::least) << std::endl;
      std::cout << sizeof(inttype<0xFFULL>::least) << std::endl;
      std::cout << sizeof(inttype<0xFFFULL>::least) << std::endl;
      std::cout << sizeof(inttype<0xFFFFFULL>::least) << std::endl;
      std::cout << sizeof(inttype<0xFFFFFFFFFULL>::least) << std::endl;
    }
    

    Pass to inttype the number you want to hold, and optionally the type you want to try, in ascending sizeof. It will then pick the first type so that the casting n doesn't change n. If no type specified (the case of the Op) defaults to all uint types

    0 讨论(0)
  • 2020-12-07 14:37
    #include <stdio.h>
    
    #ifdef _MSC_VER
    typedef unsigned __int8 uint8_t;
    typedef unsigned __int16 uint16_t;
    typedef unsigned __int32 uint32_t;
    typedef unsigned __int64 uint64_t;
    #else
    #include <stdint.h> // i dunno
    #endif
    
    template <class T> struct Printer       { static void print()   { printf("uint64_t\n"); } };
    template <> struct Printer<uint32_t>    { static void print()   { printf("uint32_t\n"); } };
    template <> struct Printer<uint16_t>    { static void print()   { printf("uint16_t\n"); } };
    template <> struct Printer<uint8_t>     { static void print()   { printf("uint8_t\n"); } };
    
    //-----------------------------------------------------------------------------
    
    template <long long N> struct Pick32 { typedef uint64_t type; };
    template <> struct Pick32<0> { typedef uint32_t type; };
    
    template <long long N> struct Pick16 { typedef typename Pick32<(N>>16)>::type type; };
    template <> struct Pick16<0> { typedef uint16_t type; };
    
    template <long long N> struct Pick8 { typedef typename Pick16<(N>>8)>::type type; };
    template <> struct Pick8<0> { typedef uint8_t type; };
    
    template <long long N> struct Integer
    {
        typedef typename Pick8<(N>>8)>::type type;
    };
    
    
    int main()
    {
        Printer< Integer<0ull>::type >::print(); // uint8_t
        Printer< Integer<255ull>::type >::print(); // uint8_t
    
        Printer< Integer<256ull>::type >::print(); // uint16_t
        Printer< Integer<65535ull>::type >::print(); // uint16_t
    
        Printer< Integer<65536ull>::type >::print(); // uint32_t
        Printer< Integer<0xFFFFFFFFull>::type >::print(); // uint32_t
    
        Printer< Integer<0x100000000ULL>::type >::print(); // uint64_t
        Printer< Integer<1823465835443ULL>::type >::print(); // uint64_t
    }
    
    0 讨论(0)
  • 2020-12-07 14:38

    Sure, it's possible. Here are the ingredients. Let's start with my two favorite meta-functions:

    template<uint64_t N>
    struct constant
    {
        enum { value = N };
    };
    
    template<typename T>
    struct return_
    {
        typedef T type;
    };
    

    Then, a meta-function that counts the bits required to store a number:

    template<uint64_t N>
    struct bitcount : constant<1 + bitcount<(N>>1)>::value> {};
    
    template<>
    struct bitcount<0> : constant<1> {};
    
    template<>
    struct bitcount<1> : constant<1> {};
    

    Then, a meta-function that counts the bytes:

    template<uint64_t N>
    struct bytecount : constant<((bitcount<N>::value + 7) >> 3)> {};
    

    Then, a meta-function that returns the smallest type for a given number of bytes:

    template<uint64_t N>
    struct bytetype : return_<uint64_t> {};
    
    template<>
    struct bytetype<4> : return_<uint32_t> {};
    
    template<>
    struct bytetype<3> : return_<uint32_t> {};
    
    template<>
    struct bytetype<2> : return_<uint16_t> {};
    
    template<>
    struct bytetype<1> : return_<uint8_t> {};
    

    And finally, the meta-function that you asked for:

    template<uint64_t N>
    struct Integer : bytetype<bytecount<N>::value> {};
    
    0 讨论(0)
提交回复
热议问题