C ++에서 int를 enum으로 캐스트하는 일반적인 방법


81

캐스팅에 일반적인 방법이 있나요 int에 enum에가 C++?

경우 int의 범위 내에 enum그것이 반환해야합니다 enum, 값을 그렇지 않으면 던져 exception. 일반적으로 작성하는 방법이 있습니까? 둘 이상 enum type이 지원되어야합니다.

배경 : 외부 열거 형 유형이 있고 소스 코드를 제어 할 수 없습니다 . 이 값을 데이터베이스에 저장하고 검색하고 싶습니다.


enum e{x = 10000};이 경우 9999범위에 속합니까 enum?
— Armen Tsirunyan 2010

아니, 9999떨어지지 않습니다.
— Leonid

9
좋은 질문. "왜?" 이것이 나타날 것입니다. "역 직렬화"라고 말하겠습니다. 저에게는 충분한 이유가 될 것 같습니다. .NET에 대한 C ++ 0x- 컴파일 답변도 듣고 싶습니다 enum class.
— Kos

9
여기서 "범위"는 잘못된 단어입니다. "도메인"일까요?
— Constantin

boost :: numeric_cast <>는 값이 범위를 벗어난 경우 양수 또는 음수 오버플로 예외를 throw합니다. 그러나 열거 형 유형에도 좋은지 확실하지 않습니다. 시도해 볼 수 있습니다.
— yasouser

답변:


37

분명한 것은 열거 형에 주석을 추가하는 것입니다.

// generic code
#include <algorithm>

template <typename T>
struct enum_traits {};

template<typename T, size_t N>
T *endof(T (&ra)[N]) {
    return ra + N;
}

template<typename T, typename ValType>
T check(ValType v) {
    typedef enum_traits<T> traits;
    const T *first = traits::enumerators;
    const T *last = endof(traits::enumerators);
    if (traits::sorted) { // probably premature optimization
        if (std::binary_search(first, last, v)) return T(v);
    } else if (std::find(first, last, v) != last) {
        return T(v);
    }
    throw "exception";
}

// "enhanced" definition of enum
enum e {
    x = 1,
    y = 4,
    z = 10,
};

template<>
struct enum_traits<e> {
    static const e enumerators[];
    static const bool sorted = true;
};
// must appear in only one TU,
// so if the above is in a header then it will need the array size
const e enum_traits<e>::enumerators[] = {x, y, z};

// usage
int main() {
    e good = check<e>(1);
    e bad = check<e>(2);
}

배열을 최신 상태로 유지해야하는데 e, .NET의 작성자가 아닌 경우 이는 성가신 일입니다 e. Sjoerd가 말했듯이 괜찮은 빌드 시스템으로 자동화 할 수 있습니다.

어쨌든, 당신은 7.2 / 6입니다.

emin이 가장 작은 열거 자이고 emax가 가장 큰 열거 형의 경우 열거 형 값은 bmin에서 bmax 범위의 기본 유형 값이며, 여기서 bmin 및 bmax는 각각 가장 작은 값의 최소값과 최대 값입니다 emin과 emax를 저장할 수있는 비트 필드. 열거 자에 의해 정의되지 않은 값이있는 열거를 정의 할 수 있습니다.

따라서의 작성자 e가 아닌 경우의 유효한 값이 e실제로 정의에 표시 된다는 보장이 없을 수도 있습니다 .


22

추한.

enum MyEnum { one = 1, two = 2 };

MyEnum to_enum(int n)
{
  switch( n )
  {
    case 1 :  return one;
    case 2 : return two;
  }
  throw something();
}

이제 진짜 질문입니다. 왜 이것이 필요합니까? 코드는보기 흉하고 작성하기도 쉽지 않으며 (*?) 유지 관리하기도 쉽지 않으며 코드에 통합하기도 쉽지 않습니다. 그것이 틀렸다는 것을 알려주는 코드. 왜 싸울까요?

편집하다:

또는 열거 형이 C ++에서 정수 유형 인 경우 :

enum my_enum_val = static_cast<MyEnum>(my_int_val);

그러나 이것은 위의 것보다 더 추하고 오류가 발생하기 쉽고 원하는대로 던지지 않습니다.


이것은 MyEnum이라는 하나의 유형 만 지원합니다.
— Simone

2
@Leonid : 내 지식으로는 일반적으로 할 수 없습니다. 어떤 수준에서는 throw유효하지 않은 유형에 대해 (또는 특별한 작업을 수행 하는) 모든 솔루션에 제가 게시 한 것과 같은 스위치가 있어야합니다.
— John Dibling

2
이것이 -1'ed 인 이유는 무엇입니까? 정답입니다. 일부 사람들이 바라고있는 대답이 아니라고해서 그것이 틀렸다는 의미는 아닙니다.
— John Dibling

12
A static_cast<MyEnum>도 작동하며 reinterpret_cast<MyEnum>
— Sjoerd

1
이 접근 방식은 잘 작동하며 도구를 사용하여 함수를 생성하는 경우 더 좋습니다.
— Nick

3

설명했듯이 값이 데이터베이스에있는 경우이 테이블을 읽고 열거 형과 to_enum(int)함수 를 모두 사용하여 .h 및 .cpp 파일을 만드는 코드 생성기를 작성하는 것이 어떻습니까?

장점 :

  • to_string(my_enum)기능 추가가 쉽습니다 .
  • 유지 보수가 거의 필요하지 않음
  • 데이터베이스와 코드가 동기화 됨

열거 형 소스 코드 는 제어 할 수 없습니다 . 전환을 구현하는 시설을 생성 할 수 있다는 데 동의합니다. 그러나 시설은 외부 열거 형 에 대한 변경 / 확장을 인식하지 못합니다 (컴파일시 매번 실행되지 않는 한).
— Leonid

@Leonid 그런 다음 해당 열거 형 헤더를 읽고이를 to_enum(int)기반으로 함수를 생성합니다 .
— Sjoerd

@Leonid 모든 진지한 프로젝트 관리 시스템은 심지어 make두 파일의 날짜를 비교하여 생성기를 다시 실행해야하는지 여부를 확인할 수 있습니다.
— Sjoerd

지금은 발전기에 대한 더 간단한 솔루션으로 갈 것이라고 생각합니다. 그러나 아이디어에 감사드립니다.
— Leonid

우리는 직장에서이 체계를 사용합니다. 도구는 템플릿에서 .hpp 코드를 생성하며 템플릿은 최소한입니다.
— Nick

3

C ++에는 내부 검사가 없으며 "도메인 검사"기능이 내장되어 있지 않습니다.


2

이것에 대해 어떻게 생각하세요?

#include <iostream>
#include <stdexcept>
#include <set>
#include <string>

using namespace std;

template<typename T>
class Enum
{
public:
    static void insert(int value)
    {
        _set.insert(value);
    }

    static T buildFrom(int value)
    {
        if (_set.find(value) != _set.end()) {
            T retval;
            retval.assign(value);
            return retval;
        }
        throw std::runtime_error("unexpected value");
    }

    operator int() const { return _value; }

private:
    void assign(int value)
    {
        _value = value;
    }

    int _value;
    static std::set<int> _set;
};

template<typename T> std::set<int> Enum<T>::_set;

class Apples: public Enum<Apples> {};

class Oranges: public Enum<Oranges> {};

class Proxy
{
public:
    Proxy(int value): _value(value) {}

    template<typename T>
    operator T()
    {
        T theEnum;
        return theEnum.buildFrom(_value);
    }

    int _value;
};

Proxy convert(int value)
{
    return Proxy(value);
}

int main()
{    
    Apples::insert(4);
    Apples::insert(8);

    Apples a = convert(4); // works
    std::cout << a << std::endl; // prints 4

    try {
        Apples b = convert(9); // throws    
    }
    catch (std::exception const& e) {
        std::cout << e.what() << std::endl; // prints "unexpected value"
    }
    try {
        Oranges b = convert(4); // also throws  
    }
    catch (std::exception const& e) {
        std::cout << e.what() << std::endl; // prints "unexpected value"
    }
}

그런 다음 여기 에 게시 한 코드를 사용 하여 값을 전환 할 수 있습니다.


여전히 Apples::insert(4)어딘가에 추가해야 하므로 스위치에 비해 이점이 없습니다.
— Sjoerd

1
그는 값이 데이터베이스에서 나온다고 말 했으므로 "삽입"메소드를 추가했습니다.
— Simone

1

설명하는 것과 같은 것이 존재하는 것을 원하지 않아야합니다. 코드 디자인에 문제가있는 것 같습니다.

또한 열거 형이 범위에 있다고 가정하지만 항상 그런 것은 아닙니다.

enum Flags { one = 1, two = 2, four = 4, eigh = 8, big = 2000000000 };

이것은 범위에 있지 않습니다. 가능하더라도 0에서 2 ^ n까지의 모든 정수를 검사하여 열거 형 값과 일치하는지 확인해야합니까?


그렇지 않으면 데이터베이스에서 열거 형 값을 어떻게 검색합니까? 정수는 컴파일 타임에 알려져 있는데 왜 템플릿을 기반으로하는 일반 변환이 불가능할까요?
— Leonid

2
@Leonid : 어떤 수준에서는 내가 말했듯이 스위치가 필요하기 때문입니다.
— John Dibling

2
@Leonid 템플릿은 생각할 수있는 모든 문제를 해결하는 은색 총알이 아닙니다.
— Sjoerd

존이 옳다. 원하는 작업을 수행하려면 열거 형보다 복잡한 유형이 필요합니다. 클래스 계층 구조에서 가능하다고 생각합니다.
— Simone

클래스 계층 구조를 사용하는 솔루션을 게시했습니다.
— Simone

1

열거 형 값을 템플릿 매개 변수로 나열 할 준비가 되었으면 C ++ 11에서 varadic 템플릿을 사용하여이를 수행 할 수 있습니다. 이를 좋은 것으로 볼 수 있으며, 다른 컨텍스트에서 유효한 열거 형 값의 하위 집합을 허용 할 수 있습니다. 외부 소스의 코드를 구문 분석 할 때 종종 유용합니다.

원하는만큼 일반적이지는 않지만 검사 코드 자체는 일반화되어 있으므로 값 집합을 지정하기 만하면됩니다. 이 접근 방식은 간격, 임의의 값 등을 처리합니다.

template<typename EnumType, EnumType... Values> class EnumCheck;

template<typename EnumType> class EnumCheck<EnumType>
{
public:
    template<typename IntType>
    static bool constexpr is_value(IntType) { return false; }
};

template<typename EnumType, EnumType V, EnumType... Next>
class EnumCheck<EnumType, V, Next...> : private EnumCheck<EnumType, Next...>
{
    using super = EnumCheck<EnumType, Next...>;

public:
    template<typename IntType>
    static bool constexpr is_value(IntType v)
    {
        return v == static_cast<typename std::underlying_type<EnumType>::type>(V) || super::is_value(v);
    }

    EnumType convert(IntType v)
    {
        if (!is_value(v)) throw std::runtime_error("Enum value out of range");
        return static_cast<EnumType>(v);
};

enum class Test {
    A = 1,
    C = 3,
    E = 5
};

using TestCheck = EnumCheck<Test, Test::A, Test::C, Test::E>;

void check_value(int v)
{
    if (TestCheck::is_value(v))
        printf("%d is OK\n", v);
    else
        printf("%d is not OK\n", v);
}

int main()
{
    for (int i = 0; i < 10; ++i)
        check_value(i);
}

이 링크가 질문에 답할 수 있지만 여기에 답변의 필수 부분을 포함하고 참조 용 링크를 제공하는 것이 좋습니다. 링크 된 페이지가 변경되면 링크 전용 답변이 무효화 될 수 있습니다. - 리뷰에서
— Tas

1
@Tas 다른 SO 답변에 대한 링크입니다-외부 링크와 동일한 문제가 없습니다. 어쨌든 업데이트되었습니다.
— janm

0

"추악한"버전 대신 C ++ 0x는 여러 열거 형을 허용합니다. 스위치보다는 이니셜 라이저 목록을 사용합니다. 불행히도 이것은 열거 형 값을 하드 코딩해야하는 필요성을 해결하지 못합니다.

#include <cassert>  // assert

namespace  // unnamed namespace
{
    enum class e1 { value_1 = 1, value_2 = 2 };
    enum class e2 { value_3 = 3, value_4 = 4 };

    template <typename T>
    int valid_enum( const int val, const T& vec )
    {
        for ( const auto item : vec )
            if ( static_cast<int>( item ) == val ) return val;

        throw std::exception( "invalid enum value!" );  // throw something useful here
    }   // valid_enum
}   // ns

int main()
{
    // generate list of valid values
    const auto e1_valid_values = { e1::value_1, e1::value_2 };
    const auto e2_valid_values = { e2::value_3, e2::value_4 };

    auto result1 = static_cast<e1>( valid_enum( 1, e1_valid_values ) );
    assert( result1 == e1::value_1 );

    auto result2 = static_cast<e2>( valid_enum( 3, e2_valid_values ) );
    assert( result2 == e2::value_3 );

    // test throw on invalid value
    try
    {
        auto result3 = static_cast<e1>( valid_enum( 9999999, e1_valid_values ) );
        assert( false );
    }
    catch ( ... )
    {
        assert( true );
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.