ProgramingTip

숫자를 담을 수있을만큼 큰 변수 유형을 자동으로 선택

bestdevel 2021. 1. 9. 16:28
반응형

숫자를 담을 수있을만큼 큰 변수 유형을 자동으로 선택


C ++에서 아마도 영리한 템플릿 코드를 사용하여 특정 숫자를 담을 수있을 가능성이 큰 유형을 정의하는 방법이 있습니까? 예를 들어 다음과 같이 쓸 수 있기를 원합니다.

Integer<10000>::type dataItem;

그리고 그 유형이 지정된 값을 보유 충분히 큰 가장 작은 유형으로 검증?

배경 : 외부 데이터 파일의 펼쳐보기를 사용하여 몇 가지 변수 정의를 생성해야합니다. 나는 내가 사용하는 다음의 값에 스크립트 모습을하고있을 것 같아요 uint8_t, uint16_t, uint32_t값에 따라, 등, 더 보인다하지만 우아한 생성 된 C ++ 코드로 크기를 구축 할 수 있습니다.

이 작업을 수행 할 수있는 템플릿을 만드는 방법은 없지만 C ++ 템플릿을 알면 방법이 확신합니다. 어떤 아이디어?


Boost.Integer에는 이미 Integer Type Selection 기능이 있습니다 .

boost::int_max_value_t<V>::least

포함 범위 0-V의 모든 값을 보유 할 수있는 가장 작은 내장 부호있는 정수 유형입니다. 매개 변수는 양 수집합니다.

boost::uint_value_t<V>::least

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

그런 다음 주어진 바이트 수에 대해 가장 작은 유형을 반환하는 메타 함수 :

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

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
}

조건부 :

#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.


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;

주어진 정수를 보유 할 가장 작은 유형을 선택해야합니다.

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클래스 템플릿에 올바른 범위를 제공하면 주어진 정수에 대해 추론 작은 유형을 선택할 수 있습니다.


#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를 사용할 수도 있습니다.


서명되지 않은 유형의 경우 다음과 같습니다.

#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의 경우에도 문제가 있습니다.


#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

참조 URL : https://stackoverflow.com/questions/7038797/automatically-pick-a-variable-type-big-enough-to-hold-a-specified-number

반응형