58

おそらくいくつかの巧妙なテンプレートコードを使用して、最大で特定の数を保持するのに十分な大きさの型をC++で定義する方法はありますか? たとえば、私は書くことができるようになりたい:-

Integer<10000>::type dataItem;

そして、その型は、指定された値を保持するのに十分な大きさの最小の型に解決されますか?

背景: 外部データ ファイルからスクリプトを使用して、いくつかの変数定義を生成する必要があります。スクリプトで値を確認し、値に応じて 、 、 などを使用できると思いますが、生成された C++ コードにサイズを組み込む方がuint8_tエレガントuint16_tに思えますuint32_t

これを行うことができるテンプレートを作成する方法はわかりませんが、C++ テンプレートを知っているので、方法があると確信しています。何か案は?

4

15 に答える 15

62

Boost.Integer にはすでにInteger Type Selectionの機能があります:

boost::int_max_value_t<V>::least

0 から V までの範囲のすべての値を保持できる最小の組み込みの符号付き整数型。パラメーターは正の数値である必要があります。

boost::uint_value_t<V>::least

V まで (V を含む) のすべての正の値を保持できる最小の組み込みの符号なし整数型。パラメーターは正の数である必要があります。

于 2011-08-12T12:07:27.737 に答える
47

もちろん、それは可能です。成分はこちら。私のお気に入りの 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> {};
于 2011-08-12T11:24:48.257 に答える
27
#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;
}
于 2011-08-12T11:12:28.490 に答える
7

intint より小さい型に使用するのではなく、必ず最小のものを使用しますか?

そうでない場合、およびコンパイラがそれをサポートしている場合、次のことを行うことができます。

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
}
于 2011-08-12T14:20:30.107 に答える
6

条件付きはどうですか:

#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

于 2011-08-12T10:43:17.423 に答える
5

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;
于 2015-07-07T15:02:59.603 に答える
3

指定された整数を保持する最小の型を選択する必要があると思います:

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、i=int - 名前マングリングのため)

c
s
i

デモ : http://www.ideone.com/diALB

注: もちろん、型のサイズ範囲を想定していますが、それにもかかわらず、間違った範囲を選択した可能性があります。その場合、within_rangeクラス テンプレートに正しい範囲を指定すると、指定された整数の最小の型を選択できます。

于 2011-08-12T11:06:24.757 に答える
2
#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
}
于 2011-08-12T11:02:04.497 に答える
1

unsigned 型の場合は次のようになります。

#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の場合、問題もあります。

于 2011-08-12T10:48:10.813 に答える
1
#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プラットフォームがサポートするものまで拡張できます。

デモ

于 2011-08-12T11:36:45.977 に答える
0

あなたは次の行に沿って何かを意味します:

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 を使用することもできます。

于 2011-08-12T10:36:00.827 に答える