Как ограничить преобразование из int в enum class? - PullRequest
1 голос
/ 06 ноября 2019

Если у меня есть enum class, например:

enum class Weekday {
  Monday,
  Tuesday,
  Wednesday,
  Thursday,
  Friday,
  Saturday,
  Sunday
}

, и у меня есть преобразование из int в Weekday:

Weekday day = static_cast<int>(value);

Чтобы проверить, что значениеесли конверсия действительна, у меня должен быть код:

if (value != 0 && value != 1 && value !=2 && value !=3 && value != 4 && value != 5 && value != 6) { 
  do_conversion(value); 
}

Это так ужасно. Есть ли хороший способ сделать это?

Ответы [ 2 ]

1 голос
/ 06 ноября 2019

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

#include <stdexcept>
template <class TYPE>
TYPE do_conversion(int value)
{
    if (value >= static_cast<int>(TYPE::First) &&
        value < static_cast<int>(TYPE::Last))
    {
        return static_cast<TYPE>(value);
    }
    throw std::out_of_range("Inv value");
}

Все преобразования переведены в функцию, чтобы подавить шум. Допустимый диапазон абстрагируется с дополнительными перечисляемыми значениями First and Last. Если заданное значение находится в диапазоне (First, Last], значение присваивается. Если нет, выведите исключение. Исключение может быть неправильным в зависимости от частоты недопустимых входов, вряд ли это исключение, еслиПлохой ввод является обычным явлением, но здесь он помогает сохранить простой пример.

Теперь вызов - это простой вопрос

enum class Weekday
{
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday,
    Last,           // added
    First = Monday  // added
};

и более поздних

Weekday day = do_conversion<Weekday>(value);

Всешум от преобразования не имеет никакого значения для преобразования, поэтому любой, кто читает или пишет этот фрагмент кода, может продолжить свою работу.

Но что, если вы используете его на enum безFirst и Last? Сообщения компилятора могут быть причудливыми, так что это хорошая идея, чтобы немного помочь пользователю. Следующее было извлечено из Как определить, существует ли конкретная переменная-член в классе? , чтобы сделать это немного проще в использовании. Определенно потратьте время, чтобы прочитать ответ, чтобы понять, что происходит. Я не нашел приличного способа объединить HasFirst и HasLast. Пожалуйста, dдобавьте комментарий и дайте мне знать, если он у вас есть.

#include <stdexcept>
#include <type_traits>
template <typename T, typename = int>
struct HasFirst : std::false_type { };

template <typename T>
struct HasFirst <T, decltype((void) T::First, 0)> : std::true_type { };

template <typename T, typename = int>
struct HasLast : std::false_type { };

template <typename T>
struct HasLast <T, decltype((void) T::Last, 0)> : std::true_type { };

template <class TYPE>
TYPE do_conversion(int value)
{
    static_assert(HasFirst<TYPE>::value, "enum missing First");
    static_assert(HasLast<TYPE>::value, "enum missing Last");
    if (value >= static_cast<int>(TYPE::First) &&
        value < static_cast<int>(TYPE::Last))
    {
        return static_cast<TYPE>(value);
    }
    throw std::out_of_range("Inv value");
}

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

Контрольный пример

1 голос
/ 06 ноября 2019

Базовым типом по умолчанию для класса enum является целое число. Таким образом, чтобы проверить, попадает ли целое число в диапазон допустимых значений перечисления, вы можете сравнить его с границами допустимых значений перечисления, используя static_cast<int>:

if (value >= static_cast<int>(Weekday::Monday) && value <= static_cast<int>(Weekday::Sunday))
{
    do_conversion(value);
}
...