Automatically select a variable type large enough to hold the specified number

Is there any way in C ++ to determine a type that is large enough to contain no more than a certain number, presumably using some kind of smart template code. For example, I want to be able to write: -

Integer<10000>::type dataItem; 

And do you allow this type to the smallest type that is large enough to hold the specified value?

Background: I need to generate some variables using a script from an external data file. I think I could make a script look at the values โ€‹โ€‹and then use uint8_t , uint16_t , uint32_t , etc. Depending on the value, but it seems more elegant to build the size in the generated C ++ code.

I donโ€™t see a way to make a template that can do this, but knowing C ++ templates, Iโ€™m sure there is a way. Any ideas?

+53
c ++ templates template-meta-programming
Aug 12 '11 at 10:25
source share
12 answers

Boost.Integer already has the ability to select an integer type :

 boost::int_max_value_t<V>::least 

The smallest built-in integrated type that can contain all values โ€‹โ€‹in the inclusive range 0 - V. The parameter must be positive.

 boost::uint_value_t<V>::least 

The smallest built-in unsigned integral type that can contain all positive values โ€‹โ€‹up to V. including the parameter. The parameter must be a positive number.

+56
Aug 12 2018-11-12T00:
source share

Of course it is possible. Here are the ingredients. Start with two of my favorite meta features:

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

Then, a meta function that counts the bit needed to store the 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 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 you requested:

 template<uint64_t N> struct Integer : bytetype<bytecount<N>::value> {}; 
+38
Aug 12 2018-11-11T00:
source share
 #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; } 
+22
Aug 12 '11 at 11:12
source share

Do you need the smallest value as opposed to using int for types smaller than int?

If not, and your compiler supports it, you could 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 } 
+7
Aug 12 2018-11-11T00:
source share

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 should be expanded to cover all the desired types in order; in the final step, you can use enable_if rather than conditional to have a instantiation error right there if the value is too large.

+6
Aug 12 2018-11-11T00:
source share

I think he should choose the smallest type that 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 change)

 c s i 

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

Note: of course, I assume the size and range of types, and even so, I might choose the wrong range; if so, then by providing the correct range to the within_range class within_range , you can select the smallest type for the given integer.

+3
Aug 12 2018-11-11T00:
source share

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; 
+3
Jul 07 '15 at 15:02
source share
 #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 } 
+2
Aug 12 '11 at 11:02
source share

You mean something like:

 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 perhaps another template structure for UnsignedInteger.

Perhaps you can even use numeric_limits instead of hard-coded values.

+1
Aug 12 2018-11-12T00:
source share

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"; } 

Please note that this does not necessarily select the smallest applicable type, since in principle there is no way to stop the implementation from a 24-bit integer. But for โ€œnormalโ€ implementations, this is normal, and to include unusual sizes, all you have to do to fix this is change the list of specializations.

For implementations that do not have a 64-bit type, you need to change the type of the template parameter N - or you can use uintmax_t . Also, if the right shift to 32 can be dodgy.

For implementations having a type greater than uint64_t , problems also arise.

+1
Aug 12 2018-11-11T00:
source share
 #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 expand to uint64_t or whatever your platform supports.

Demo

+1
Aug 12 2018-11-11T00:
source share

No listing, just typedef.

 #include<stdio.h> #include<stdint.h> template <unsigned long long V> struct valuetype { typedef typename valuetype<(V & (V-1)) ? (V & (V-1)) : (V >> 1)>::val val; }; template <> struct valuetype<(1ull << 0)> { typedef uint8_t val; }; template <> struct valuetype<(1ull << 8)> { typedef uint16_t val; }; template <> struct valuetype<(1ull << 16)> { typedef uint32_t val; }; template <> struct valuetype<(1ull << 32)> { typedef uint64_t val; }; int main () { valuetype<123>::val a = ~0; printf ("%llu\n", (unsigned long long) a); valuetype<456>::val b = ~0; printf ("%llu\n", (unsigned long long) b); valuetype<123456>::val c = ~0; printf ("%llu\n", (unsigned long long) c); valuetype<123456123>::val d = ~0; printf ("%llu\n", (unsigned long long) d); valuetype<123456123456>::val e = ~0; printf ("%llu\n", (unsigned long long) e); return 0; } 

255
65535
4294967295
4294967295
18446744073709551615

0
Jan 24 '17 at 20:08
source share



All Articles