приведение enum-int: оператор или функция - PullRequest
8 голосов
/ 15 декабря 2008

Во внешнем коде, который я использую, есть enum:

enum En {VALUE_A, VALUE_B, VALUE_C};

В другом внешнем коде, который я использую, есть 3 директивы #define:

#define ValA 5
#define ValB 6
#define ValC 7

Во многих случаях у меня есть int X, равное ValA или ValB или ValC, и мне приходится приводить его к соответствующему значению En (ValA к VALUE_A, ValB к VALUEB и т. Д.), Потому что сигнатура некоторых функций имеет enum En. И много раз мне приходилось делать противоположную операцию, переводить enum En в ValA или ValB или ValC. Я не могу изменить подписи этих функций, и таких функций много.

Вопрос: как сделать перевод? Должен ли я создать 2 оператора приведения, которые будут использоваться неявно? Или я должен просто иметь 2 функции перевода, которые будут использоваться явно:

En ToEn(int)
int FromEn(En)

Или любое другое решение?

Ответы [ 5 ]

11 голосов
/ 15 декабря 2008

Так как вы не можете просто привести здесь, я бы использовал бесплатную функцию, и, если есть вероятность, что есть другие перечисления, которые также нуждаются в преобразовании, попробуйте сделать так, чтобы это выглядело немного как встроенное приведение:

template<typename T>
T my_enum_convert(int);

template<>
En my_enum_convert<En>(int in) {
    switch(in) {
        case ValA: return VALUE_A;
        case ValB: return VALUE_B;
        case ValC: return VALUE_C;
        default: throw std::logic_error(__FILE__ ": enum En out of range");
    }
}

int my_enum_convert(En in) {
    switch(in) {
        case VALUE_A: return ValA;
        case VALUE_B: return ValB;
        case VALUE_C: return ValC;
        // no default, so that GCC will warn us if we've forgotten a case
    }
}

En enumValue = my_enum_convert<En>(ValA);
int hashDefineValue = my_enum_convert(VALUE_A);
enumValue = my_enum_convert<En>(0); // throws exception

Или что-то в этом роде - может настроить его, если при его использовании возникнут проблемы.

Причина, по которой я бы не использовал неявное преобразование, состоит в том, что уже является неявным преобразованием из En в int, что дает неправильный ответ. Даже если вы можете надежно заменить это на что-то, что дает правильный ответ, полученный код не будет выглядеть так, как будто он выполняет какое-либо преобразование. IMO, это будет мешать любому, кто позже будет смотреть на код больше, чем набирает вызов процедуры преобразования.

Если вы хотите, чтобы преобразование в int и преобразование из int выглядело совсем по-другому, вы можете присвоить шаблону и функции над разными именами.

В качестве альтернативы, если вы хотите, чтобы они выглядели одинаково (и больше как static_cast), вы можете сделать:

template<typename T>
T my_enum_convert(En in) {
    switch(in) {
        case VALUE_A: return ValA;
        case VALUE_B: return ValB;
        case VALUE_C: return ValC;
    }
}

int hashDefineValue = my_enum_convert<int>(VALUE_A);

Как написано, T должен иметь неявное преобразование из int. Если вы хотите поддерживать T, который имеет только явное преобразование, используйте «return T (ValA);» вместо этого (или «return static_cast (ValA);», если вы считаете, что конструкторы с одним аргументом являются приведениями в стиле C и, следовательно, недопустимы).

2 голосов
/ 15 декабря 2008

Хотя неявное приведение более удобно, чем функции перевода, также менее очевидно, что происходит. Удобный и очевидный подход может заключаться в использовании вашего собственного класса с перегруженными операторами приведения. При приведении пользовательского типа в enum или int будет нелегко игнорировать некоторые пользовательские типы.

Если по какой-либо причине создать класс для этого не вариант, я бы остановился на функциях перевода, поскольку удобство чтения при обслуживании более важно, чем удобство при написании кода.

1 голос
/ 15 декабря 2008

Нельзя перегрузить операторы для перечислений. Или я что-то упустил? Ну, вы могли бы создать некие фиктивные классы, которые бы имели неявный конструктор, принимающий int, а затем имели бы оператор приведения к перечислению (и наоборот).

Итак, единственное решение - иметь функции. Кроме того, я бы сделал перегрузки, как предлагает Патрик.

1 голос
/ 15 декабря 2008

Есть функции, а затем перегрузить библиотечные функции?

//libFunc( enum a );

libFuncOverload( define a ) {
    libFunc( toEn( a ) );
}
0 голосов
/ 15 декабря 2008

Преобразование Перечисление в int , например, int (VALUE_A), происходит автоматически / прозрачно.

Преобразование int-to-enum , например, Ru (ValA) , может воспользоваться проверкой работоспособности, чтобы убедиться, что значение int является действительным членом перечисления . (хотя, надеюсь, библиотека код не предполагает, что его значения enum действительны в первую очередь.)

Хотя это не поможет в делах " int x ", вы можете немного помочь, изменив:

#define ValA 5

Кому:

#define ValA VALUE_A

При условии enum En () включено / определено везде, оба ValA и VALUE_A будут работать для обоих foo (int) и bar (En) везде автоматически / прозрачно.

Вы можете использовать:

#ifdef ValA
STATIC_ASSERT( ValA == VALUE_A, ValA_equal_VALUE_A );
#undef ValA
#else
#warning "ValA undefined.  Defining as VALUE_A"
#endif
#define ValA VALUE_A

Где STATIC_ASSERT что-то вроде:

    /* Use CONCATENATE_4_AGAIN to expand the arguments to CONCATENATE_4 */
#define CONCATENATE_4(      a,b,c,d)  CONCATENATE_4_AGAIN(a,b,c,d)
#define CONCATENATE_4_AGAIN(a,b,c,d)  a ## b ## c ## d

    /* Creates a typedef that's legal/illegal depending on EXPRESSION.       *
     * Note that IDENTIFIER_TEXT is limited to "[a-zA-Z0-9_]*".              *
     * (This may be replaced by static_assert() in future revisions of C++.) */
#define STATIC_ASSERT( EXPRESSION, IDENTIFIER_TEXT)                     \
  typedef char CONCATENATE_4( static_assert____,      IDENTIFIER_TEXT,  \
                              ____failed_at_line____, __LINE__ )        \
            [ (EXPRESSION) ? 1 : -1 ]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...