自动select一个足够容纳指定数字的variablestypes

有没有什么办法在C ++中定义一个足够大的types来保存至多一个特定的数字,大概是使用一些聪明的模板代码。 例如,我想能够写: –

Integer<10000>::type dataItem; 

并有这种typesparsing为最小的types足够大,以保持指定的值?

背景:我需要使用来自外部数据文件的脚本来生成一些variables定义。 我想我可以让脚本看看值,然后根据值使用uint8_tuint16_tuint32_t等等,但是看起来更优雅的是将大小构build到生成的C ++代码中。

我看不出有什么办法可以做到这一点,但知道C ++模板,我敢肯定有一种方法。 有任何想法吗?

Boost.Integer已经有整型select的function :

 boost::int_max_value_t<V>::least 

最小的内置带符号整数types,可以包含0到V范围内的所有值。参数应该是一个正数。

 boost::uint_value_t<V>::least 

最小的内置无符号整数types,可以保存所有的正值,并且包括V.参数应该是一个正数。

当然,这是可能的。 这里是成分。 让我们从我最喜欢的两个元函数开始:

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

然后,一个元函数来计算存储数字所需的位数:

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

然后,计算字节的元函数:

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

然后,一个元函数返回给定字节数的最小types:

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

最后,你要求的元函数:

 template<uint64_t N> struct Integer : bytetype<bytecount<N>::value> {}; 
 #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; } 

你是否一定想要最小的,而不是使用int小于int的types?

如果没有,并且你的编译器支持它,你可以这样做:

 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 } 

如何有条件的:

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

这将不得不扩展到包括所有需要的types, 在最后阶段,如果值太大,可以使用enable_if而不是conditional在那里有一个实例化错误。

我认为它应该select最小的types,它将保持给定的整数:

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

testing代码:

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

输出:(c = char,s = short,i = int – 由于名称改变)

 c s i 

演示: http : //www.ideone.com/diALB

注意:当然,我假设了这些types的大小范围 ,即使这样,我也可能select了错误的范围。 如果是的话,那么给within_range类模板提供正确的范围,可以为给定的整数select最小的types。

易于使用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; 
 #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 } 

你的意思是:

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

也许另一个UnsignedInteger的模板结构。

你甚至可以使用numeric_limits而不是硬编码的值。

在这里,我们去,无符号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"; } 

请注意,这不一定会select最小的适用types,因为原则上没有什么能够阻止实现具有24位整数。 但是,对于“正常”的实现,这是可以的,并且包括不寻常的大小,你需要做的只是修改专业化列表。

对于根本不具有64位types的实现,您需要更改模板参数N的types,或者可以使用uintmax_t 。 同样在这种情况下,右移32可能是狡猾的。

对于types比uint64_t更大的实现,也有麻烦。

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

您可以扩展到uint64_t或任何您的平台支持。

演示 。

没有枚举,只是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