Development Tip

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

yourdevel 2020. 10. 10. 12:06
반응형

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


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

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

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


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

// 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실제로 정의에 표시 된다는 보장이 없을 수도 있습니다 .


추한.

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

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


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

장점 :

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

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


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

#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"
    }
}

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


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

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

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

This is not in a range: even if it was possible, are you supposed to check every integer from 0 to 2^n to see if they match some enum's value?


If you are prepared to list your enum values as template parameters you can do this in C++ 11 with varadic templates. You can look at this as a good thing, allowing you to accept subsets of the valid enum values in different contexts; often useful when parsing codes from external sources.

Perhaps not quite as generic as you'd like, but the checking code itself is generalised, you just need to specify the set of values. This approach handles gaps, arbitrary values, etc.

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

C++0x alternative to the "ugly" version, allows for multiple enums. Uses initializer lists rather than switches, a bit cleaner IMO. Unfortunately, this doesn't work around the need to hard-code the enum values.

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

참고URL : https://stackoverflow.com/questions/4165439/generic-way-to-cast-int-to-enum-in-c

반응형