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

后端 未结 14 2000
耶瑟儿~
耶瑟儿~ 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:18

    You mean something along the lines of:

    template <int MAX>
    struct Integer {
        typedef typename Integer<MAX+1>::type type;
    };
    
    template <>
    struct Integer<2147483647> {
        typedef int32_t type;
    };
    
    template <>
    struct Integer<32767> {
        typedef int16_t type;
    };
    
    template <>
    struct Integer<127> {
        typedef int8_t type;
    };
    

    And maybe another templated struct for UnsignedInteger.

    You could maybe even use numeric_limits instead of the hard coded values.

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

    Here we go, for unsigned types:

    #include <stdint.h>
    #include <typeinfo>
    #include <iostream>
    
    template <uint64_t N>
    struct Integer {
        static const uint64_t S1 = N | (N>>1);
        static const uint64_t S2 = S1 | (S1>>2);
        static const uint64_t S4 = S2 | (S2>>4);
        static const uint64_t S8 = S4 | (S4>>8);
        static const uint64_t S16 = S8 | (S8>>16);
        static const uint64_t S32 = S16 | (S16>>32);
        typedef typename Integer<(S32+1)/4>::type type;
    };
    
    template <> struct Integer<0> {
        typedef uint8_t type;
    };
    
    template <> struct Integer<1> {
        typedef uint8_t type;
    };
    
    template <> struct Integer<256> {
        typedef uint16_t type;
    };
    
    template <> struct Integer<65536> {
        typedef uint32_t type;
    };
    
    template <> struct Integer<4294967296LL> {
        typedef uint64_t type;
    };
    
    int main() {
        std::cout << 8 << " " << typeid(uint8_t).name() << "\n";
        std::cout << 16 << " " << typeid(uint16_t).name() << "\n";
        std::cout << 32 << " " << typeid(uint32_t).name() << "\n";
        std::cout << 64 << " " << typeid(uint64_t).name() << "\n";
        Integer<1000000>::type i = 12;
        std::cout << typeid(i).name() << "\n";
        Integer<10000000000LL>::type j = 12;
        std::cout << typeid(j).name() << "\n";
    }
    

    Note that this doesn't necessarily pick the smallest applicable type, since there's nothing in principle to stop an implementation from having a 24 bit integer. But for "normal" implementations it's OK, and to include unusual sizes all you need to do to fix it is to change the list of specializations.

    For implementations that don't have a 64-bit type at all you need to change the type of the template parameter N - or you could use uintmax_t. Also in the case the right shift by 32 might be dodgy.

    For implementations that have a type bigger than uint64_t, there's trouble too.

    0 讨论(0)
  • 2020-12-07 14:19
    #define UINT8_T   256
    #define UINT16_T  65536
    #define UINT32_T  4294967296
    
    template<uint64_t RANGE, bool = (RANGE < UINT16_T)>
    struct UInt16_t { typedef uint16_t type; };
    template<uint64_t RANGE>
    struct UInt16_t<RANGE, false> { typedef uint32_t type; };
    
    template<uint64_t RANGE, bool = (RANGE < UINT8_T)>
    struct UInt8_t { typedef uint8_t type; };
    template<uint64_t RANGE>
    struct UInt8_t<RANGE, false> { typedef typename UInt16_t<RANGE>::type type; };
    
    template<uint64_t RANGE>
    struct Integer {
      typedef typename UInt8_t<RANGE>::type type;
    };
    

    You can extend upto uint64_t or whatever your platform supports.

    Demo.

    0 讨论(0)
  • 2020-12-07 14:21
    #include <stdint.h>
    
    template<unsigned long long Max>
    struct RequiredBits
    {
        enum { value =
            Max <= 0xff       ?  8 :
            Max <= 0xffff     ? 16 :
            Max <= 0xffffffff ? 32 :
                                64
        };
    };
    
    template<int bits> struct SelectInteger_;
    template<> struct SelectInteger_ <8> { typedef uint8_t type; };
    template<> struct SelectInteger_<16> { typedef uint16_t type; };
    template<> struct SelectInteger_<32> { typedef uint32_t type; };
    template<> struct SelectInteger_<64> { typedef uint64_t type; };
    
    template<unsigned long long Max>
    struct SelectInteger : SelectInteger_<RequiredBits<Max>::value> {};
    
    int main()
    {
        SelectInteger<12345>::type x = 12345;
    }
    
    0 讨论(0)
  • 2020-12-07 14:25

    Do you necessarily want the smallest, as opposed to using int for types smaller than int?

    If not, and your compiler supports it, could you do:

    int main()
    {
        typeof('A') i_65 = 0; // declare variable 'i_65' of type 'char'
        typeof(10) i_10 = 0; // int
        typeof(10000) i_10000 = 0; // int
        typeof(1000000000000LL) i_1000000000000 = 0; // int 64
    }
    
    0 讨论(0)
  • 2020-12-07 14:28

    I think it should pick the smallest type which would hold the given integer:

    class true_type {};
    class false_type {};
    
    template<bool> 
    struct bool2type 
    { 
      typedef true_type  type; 
    };
    
    template<>
    struct bool2type<false>
    {
      typedef false_type  type;
    };
    
    template<int M, int L, int H>
    struct within_range
    {
       static const bool value = L <= M && M <=H;
       typedef typename bool2type<value>::type type;
    };
    
    template<int M, class booltype> 
    struct IntegerType;
    
    template<int Max> 
    struct IntegerType<Max,typename within_range<Max, 0, 127>::type >
    {
       typedef char type;
    };
    
    template<int Max> 
    struct IntegerType<Max,typename within_range<Max, 128, 32767>::type >
    {
       typedef short type;
    };
    
    template<int Max> 
    struct IntegerType<Max,typename within_range<Max, 32768, INT_MAX>::type >
    {
       typedef int type;
    };
    
    template <int Max>
    struct Integer {
        typedef typename IntegerType<Max, true_type>::type type;
    };
    

    Test code:

    int main() {
            cout << typeid(Integer<122>::type).name() << endl;
            cout << typeid(Integer<1798>::type).name() << endl;
            cout << typeid(Integer<890908>::type).name() << endl;
            return 0;
    }
    

    Output: (c=char, s=short, i=int - due to name mangling)

    c
    s
    i
    

    Demo : http://www.ideone.com/diALB

    Note: of course, I'm assuming the size and the range of the types, and even despite of this I might have choosen the wrong range; if so, then providing the correct range to the within_range class template, one can pick smallest type for a given integer.

    0 讨论(0)
提交回复
热议问题