Общий способ привести int к enum в C ++

Есть ли общий способ приведения int в enum в C++?

Если int попадает в диапазон enum должно возвращать значение enum, в противном случае выдается exception. Есть ли способ написать это в общем ? Должно поддерживаться более одного enum type.

Фон: у меня есть внешний тип enum и нет контроля над исходным кодом. Я хотел бы сохранить это значение в базе данных и получить его.

79 голосов | спросил Leonid 12 52010vEurope/Moscow11bEurope/MoscowFri, 12 Nov 2010 17:07:20 +0300 2010, 17:07:20

9 ответов


0

Очевидная вещь состоит в том, чтобы аннотировать ваше перечисление:

// 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, что неприятно, если вы не являетесь автором ---- +: = 2 =: + ----. Как говорит Sjoerd, это может быть автоматизировано с любой приличной системой сборки.

В любом случае, вы против 7.2 /6:

  

Для перечисления, где emin - это   самый маленький перечислитель и emax является   Наибольшие значения перечисления   значения базового типа   в диапазоне от bmin до bmax, где bmin   и bmax, соответственно,   наименьшие и самые большие значения   наименьшее битовое поле, которое может хранить emin   и emax. Можно определить   перечисление, которое имеет значения не   определяется любым из его перечислителей.

Поэтому, если вы не являетесь автором e, вы можете или не можете гарантировать, что допустимые значения e фактически появляется в его определении.

ответил Steve Jessop 12 52010vEurope/Moscow11bEurope/MoscowFri, 12 Nov 2010 17:42:10 +0300 2010, 17:42:10
0

Гадкий.

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

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

Теперь по настоящему вопросу. Зачем тебе это нужно? Код уродлив, его нелегко написать (*?) И не легко поддерживать, и его нелегко включить в ваш код. Код это говорит вам, что это неправильно. Зачем бороться с этим?

EDIT:

В качестве альтернативы, учитывая, что перечисления являются целочисленными типами в C ++:

enum my_enum_val = static_cast<MyEnum>(my_int_val);

но это еще страшнее, чем выше, гораздо более подвержено ошибкам, и не выдает, как вы хотите.

ответил John Dibling 12 52010vEurope/Moscow11bEurope/MoscowFri, 12 Nov 2010 17:11:04 +0300 2010, 17:11:04
0

Если, как вы описываете, значения находятся в базе данных, почему бы не написать генератор кода, который читает эту таблицу и создает файлы .h и .cpp с перечислением и to_enum(int) функция?

Преимущества:

  • Легко добавить функцию to_string(my_enum).
  • Требуется мало обслуживания
  • База данных и код синхронизированы
ответил Sjoerd 12 52010vEurope/Moscow11bEurope/MoscowFri, 12 Nov 2010 17:23:43 +0300 2010, 17:23:43
0

Нет - в C ++ нет самоанализа и нет встроенной функции проверки домена.

ответил luke 12 52010vEurope/Moscow11bEurope/MoscowFri, 12 Nov 2010 17:20:54 +0300 2010, 17:20:54
0

Что вы думаете об этом?

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

Затем вы можете использовать код, который я разместил здесь , чтобы включить значения.

ответил Simone 12 52010vEurope/Moscow11bEurope/MoscowFri, 12 Nov 2010 18:12:02 +0300 2010, 18:12:02
0

Вы не должны хотеть, чтобы существовало что-то похожее на то, что вы описываете, я боюсь, что в вашем коде есть проблемы.

Кроме того, вы предполагаете, что перечисления входят в диапазон, но это не всегда так:

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

Это не в диапазоне: даже если это было возможно, вы должны проверять каждое целое число от 0 до 2 ^ n, чтобы увидеть, соответствуют ли они некоторому значению перечисления?

ответил Simone 12 52010vEurope/Moscow11bEurope/MoscowFri, 12 Nov 2010 17:16:32 +0300 2010, 17:16:32
0

Если вы готовы перечислить значения перечисления в качестве параметров шаблона, вы можете сделать это в 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);
}
ответил janm 13 +03002015-10-13T02:49:01+03:00312015bEurope/MoscowTue, 13 Oct 2015 02:49:01 +0300 2015, 02:49:01
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 );
    }
}
ответил Tom 6 Jam1000000amMon, 06 Jan 2014 00:23:31 +040014 2014, 00:23:31
0

Попробуйте что-то вроде этого:

enum EType
{
  type1,
  type2
};

unsigned int number = 3;
EType e = static_cast<EType>(number);
if(static_cast<unsigned int>(e) != number)
  throw std::exception();
ответил 0xC0DEGURU 26 FebruaryEurope/MoscowbWed, 26 Feb 2014 22:28:31 +0400000000pmWed, 26 Feb 2014 22:28:31 +040014 2014, 22:28:31

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132