Автоматически выбирайте тип переменной, достаточно большой, чтобы удерживать указанное число
Есть ли какой-либо способ в С++ определить тип, который достаточно велик, чтобы содержать не более определенного числа, предположительно используя какой-то умный код шаблона. Например, я хочу иметь возможность писать: -
Integer<10000>::type dataItem;
И разрешите ли этот тип самому маленькому типу, который достаточно велик, чтобы удерживать указанное значение?
Фон: мне нужно сгенерировать некоторые переменные с использованием script из внешнего файла данных. Я думаю, я мог бы сделать script посмотреть на значения, а затем использовать uint8_t
, uint16_t
, uint32_t
и т.д. В зависимости от значения, но кажется более элегантным построить размер в сгенерированном С++-коде.
Я не вижу способа сделать шаблон, который может это сделать, но, зная шаблоны С++, я уверен, что есть способ. Любые идеи?
Ответы
Ответ 1
Boost.Integer уже имеет возможности для Выбор типа целого:
boost::int_max_value_t<V>::least
Самый маленький встроенный интегрированный тип, который может содержать все значения в инклюзивном диапазоне 0 - V. Параметр должен быть положительным.
boost::uint_value_t<V>::least
Самый маленький встроенный неподписанный интегральный тип, который может содержать все положительные значения до V. включая параметр. Параметр должен быть положительным числом.
Ответ 2
Конечно, это возможно. Вот ингредиенты. Начните с двух моих любимых мета-функций:
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)> {};
Затем, мета-функция, которая возвращает наименьший тип для заданного количества байтов:
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> {};
Ответ 3
#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;
}
Ответ 4
Требуется ли вам наименьшее значение, в отличие от использования int
для типов, меньших, чем int?
Если нет, и ваш компилятор поддерживает его, вы могли бы сделать:
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
}
Ответ 5
Как насчет условного:
#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;
};
Это должно быть расширено, чтобы охватить все желаемые типы по порядку; на заключительном этапе вы можете использовать enable_if
, а не conditional
, чтобы иметь ошибку инстанцирования прямо там, если значение слишком велико.
Ответ 6
Я думаю, он должен выбрать самый маленький тип, который удерживал бы заданное целое число:
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;
};
Тестовый код:
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, я = int - из-за изменения имени)
c
s
i
Демо: http://www.ideone.com/diALB
Примечание: конечно, я предполагаю размер и диапазон типов, и даже несмотря на это, я мог бы выбрать неправильный диапазон; если это так, то, предоставляя правильный диапазон шаблону класса within_range
, можно выбрать наименьший тип для заданного целого числа.
Ответ 7
Легкий peasy с С++ 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;
Ответ 8
#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
}
Ответ 9
Вы имеете в виду что-то вроде:
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 вместо жестко закодированных значений.
Ответ 10
Здесь мы идем для неподписанных типов:
#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";
}
Обратите внимание, что это необязательно выбирает наименьший применимый тип, поскольку в принципе нет возможности остановить реализацию из 24-битного целого. Но для "нормальных" реализаций это нормально, а для включения необычных размеров все, что вам нужно сделать, чтобы исправить это, - это изменить список специализаций.
Для реализаций, которые не имеют 64-битного типа, вам нужно изменить тип параметра шаблона N
- или вы можете использовать uintmax_t
. Также в случае, если правая смена на 32 может быть изворотливой.
Для реализаций, имеющих тип больше, чем uint64_t
, также возникают проблемы.
Ответ 11
#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
или независимо от того, что поддерживает ваша платформа.
Демо.
Ответ 12
Нет перечисления, просто 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