在C ++中将int转换为枚举的通用方法

时间:2010-11-12 14:07:20

标签: c++ casting enums

是否有通用的方式将int投放到enum中的C++

如果int属于enum范围,则应返回enum值,否则抛出exception。有没有办法把它写成一般?应该支持多个enum type

背景:我对源代码有一个外部枚举类型和无控制。我想将这个值存储在数据库中并检索它。

9 个答案:

答案 0 :(得分: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更新数组,如果您不是e的作者,这会令人讨厌。正如Sjoerd所说,它可以通过任何体面的构建系统实现自动化。

无论如何,你面对7.2 / 6:

  

对于emin是枚举的枚举   最小的枚举器和emax是   最大的,枚举的值   是底层类型的值   在bmin到bmax的范围内,其中bmin   和bmax分别是   最小和最大的值   可存储emin的最小位域   和emax。可以定义一个   没有值的枚举   由任何一名调查员定义。

因此,如果您不是e的作者,则可能会或可能不会保证e的有效值实际出现在其定义中。

答案 1 :(得分:20)

难看。

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

但是这更加丑陋,更容易出错,并且不会按照你的意愿抛出。

答案 2 :(得分:3)

如果如您所描述的那样,值是在数据库中,为什么不编写一个读取此表的代码生成器并使用枚举和to_enum(int)函数创建.h和.cpp文件?

优点:

  • 轻松添加to_string(my_enum)功能。
  • 需要很少的维护
  • 数据库和代码同步

答案 3 :(得分:2)

不 - C ++中没有内省,也没有内置的“域检查”功能。

答案 4 :(得分: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"
    }
}

然后,您可以使用我发布的代码here来启用值。

答案 5 :(得分:1)

你不应该想要你所描述的东西存在,我担心你的代码设计会出现问题。

此外,您假设枚举来自某个范围,但情况并非总是如此:

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

这不在一个范围内:即使有可能,你是否应该检查0到2 ^ n之间的每个整数,看看它们是否与某个枚举值相匹配?

答案 6 :(得分:1)

如果您准备将枚举值列为模板参数,则可以使用varadic模板在C ++ 11中执行此操作。您可以将此视为一件好事,允许您在不同的上下文中接受有效枚举值的子集;在从外部源解析代码时通常很有用。

也许并不像你想要的那样通用,但是检查代码本身是通用的,你只需要指定一组值。这种方法可以处理间隙,任意值等。

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

答案 7 :(得分:0)

C ++ 0x替代“丑陋”版本,允许多个枚举。使用初始化列表而不是开关,IMO更清洁。不幸的是,这不需要对枚举值进行硬编码。

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

答案 8 :(得分:-4)

尝试这样的事情:

enum EType
{
  type1,
  type2
};

unsigned int number = 3;
EType e = static_cast<EType>(number);
if(static_cast<unsigned int>(e) != number)
  throw std::exception();