Choisir automatiquement un type de variable suffisamment grand pour contenir un nombre spécifié

Existe-t-il un moyen en C ++ de définir un type suffisamment grand pour contenir au maximum un nombre spécifique, sans doute en utilisant un code de modèle intelligent. Par exemple, je veux pouvoir écrire: –

Integer::type dataItem; 

Et ce type se résout-il au plus petit type suffisamment grand pour contenir la valeur spécifiée?

Contexte: Je dois générer des définitions de variables à l’aide d’un script provenant d’un fichier de données externe. Je suppose que je pourrais faire en sorte que le script examine les valeurs et ensuite utiliser uint8_t , uint16_t , uint32_t , etc. en fonction de la valeur, mais il semble plus élégant de construire la taille dans le code C ++ généré.

Je ne vois aucun moyen de créer un modèle capable de faire cela, mais connaître les modèles C ++, je suis sûr qu’il y a un moyen. Des idées?

    Boost.Integer a déjà des fonctionnalités pour la sélection de type entier :

     boost::int_max_value_t::least 

    Le plus petit type intégral intégré pouvant contenir toutes les valeurs comsockets entre 0 et V. Le paramètre doit être un nombre positif.

     boost::uint_value_t::least 

    Le plus petit type intégral non signé intégré pouvant contenir toutes les valeurs positives jusqu’à V inclus. Le paramètre doit être un nombre positif.

    Bien sûr, c’est possible. Voici les ingrédients. Commençons par mes deux méta-fonctions préférées:

     template struct constant { enum { value = N }; }; template struct return_ { typedef T type; }; 

    Ensuite, une méta-fonction qui compte les bits nécessaires pour stocker un nombre:

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

    Ensuite, une méta-fonction qui compte les octets:

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

    Ensuite, une méta-fonction qui renvoie le plus petit type pour un nombre donné d’octets:

     template struct bytetype : return_ {}; template<> struct bytetype<4> : return_ {}; template<> struct bytetype<3> : return_ {}; template<> struct bytetype<2> : return_ {}; template<> struct bytetype<1> : return_ {}; 

    Et enfin, la méta-fonction que vous avez demandée:

     template struct Integer : bytetype::value> {}; 
     #include  template struct RequiredBits { enum { value = Max <= 0xff ? 8 : Max <= 0xffff ? 16 : Max <= 0xffffffff ? 32 : 64 }; }; template 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 struct SelectInteger : SelectInteger_::value> {}; int main() { SelectInteger<12345>::type x = 12345; } 

    Voulez-vous nécessairement le plus petit, par opposition à utiliser int pour les types plus petits que int?

    Sinon, et votre compilateur le supporte, pourriez-vous faire:

     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 } 

    Que diriez-vous d’un conditionnel:

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

    Cela devrait être étendu à tous les types souhaités, dans l’ordre; au stade final, vous pouvez utiliser enable_if plutôt que conditional pour avoir une erreur d’instanciation là-bas si la valeur est trop grande.

    Je pense qu’il devrait choisir le plus petit type qui contiendrait l’entier donné:

     class true_type {}; class false_type {}; template struct bool2type { typedef true_type type; }; template<> struct bool2type { typedef false_type type; }; template struct within_range { static const bool value = L <= M && M <=H; typedef typename bool2type::type type; }; template struct IntegerType; template struct IntegerType::type > { typedef char type; }; template struct IntegerType::type > { typedef short type; }; template struct IntegerType::type > { typedef int type; }; template  struct Integer { typedef typename IntegerType::type type; }; 

    Code de test:

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

    Sortie: (c = char, s = short, i = int - en raison de la manipulation du nom)

     c s i 

    Démo: http://www.ideone.com/diALB

    Note: bien sûr, je suppose la taille et la gamme des types, et même en dépit de cela, je pourrais avoir choisi la mauvaise plage; Si c'est le cas, puis en fournissant la plage correcte au modèle de classe within_range , on peut choisir le plus petit type pour un entier donné.

    Peasy facile avec C ++ 11:

     #include  #include  #include  template ::value, std::intmax_t, std::uintmax_t >::type> constexpr bool is_in_range (U x) { return (x >= std::numeric_limits::min()) && (x <= std::numeric_limits::max()); } template  using int_fit_type = typename std::conditional(x), std::int8_t, typename std::conditional(x), std::int16_t, typename std::conditional(x), std::int32_t, typename std::enable_if(x), std::int64_t>::type >::type >::type >::type; template  using uint_fit_type = typename std::conditional(x), std::uint8_t, typename std::conditional(x), std::uint16_t, typename std::conditional(x), std::uint32_t, typename std::enable_if(x), std::uint64_t>::type >::type >::type >::type; 
     #include  #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  // i dunno #endif template  struct Printer { static void print() { printf("uint64_t\n"); } }; template <> struct Printer { static void print() { printf("uint32_t\n"); } }; template <> struct Printer { static void print() { printf("uint16_t\n"); } }; template <> struct Printer { static void print() { printf("uint8_t\n"); } }; //----------------------------------------------------------------------------- template  struct Pick32 { typedef uint64_t type; }; template <> struct Pick32<0> { typedef uint32_t type; }; template  struct Pick16 { typedef typename Pick32<(N>>16)>::type type; }; template <> struct Pick16<0> { typedef uint16_t type; }; template  struct Pick8 { typedef typename Pick16<(N>>8)>::type type; }; template <> struct Pick8<0> { typedef uint8_t type; }; template  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 } 

    Vous voulez dire quelque chose comme:

     template  struct Integer { typedef typename Integer::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; }; 

    Et peut-être une autre structure basée sur des modèles pour UnsignedInteger.

    Vous pourriez peut-être même utiliser numeric_limits au lieu des valeurs codées en dur.

    Nous y voilà, pour les types non signés:

     #include  #include  #include  template  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"; } 

    Notez que cela ne sélectionne pas nécessairement le plus petit type applicable, car il n'y a rien en principe pour empêcher une implémentation d'avoir un entier de 24 bits. Mais pour les implémentations "normales", c'est correct, et pour y remédier, il suffit de changer la liste des spécialisations.

    Pour les implémentations qui ne possèdent pas de type 64 bits, vous devez modifier le type du paramètre N - ou utiliser uintmax_t . Dans le cas également, le changement de vitesse de 32 pourrait être difficile.

    Pour les implémentations qui ont un type plus grand que uint64_t , il y a aussi des problèmes.

     #define UINT8_T 256 #define UINT16_T 65536 #define UINT32_T 4294967296 template struct UInt16_t { typedef uint16_t type; }; template struct UInt16_t { typedef uint32_t type; }; template struct UInt8_t { typedef uint8_t type; }; template struct UInt8_t { typedef typename UInt16_t::type type; }; template struct Integer { typedef typename UInt8_t::type type; }; 

    Vous pouvez étendre jusqu’à uint64_t ou tout ce que votre plate-forme prend en charge.

    Démo .

    Pas de enum, juste du typedef.

     #include #include template  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