Ответ 1
CashCow представляет достойный ответ на этот вопрос: для выполнения проверенного акта, безусловно, просто написать пользовательскую функцию.
К сожалению, это также большая работа, и вы должны следить за ее синхронизацией с перечислением, чтобы список счетчиков в определении перечисления был таким же, как список счетчиков в проверенной функции трансляции. Вы также должны написать один из них для каждого перечисления, которому вы хотите выполнить проверенный листинг.
Вместо того, чтобы выполнять всю эту ручную работу, мы можем автоматизировать генерацию всего этого кода с помощью препроцессора (с небольшой помощью из библиотеки Boost Preprocessor). Вот макрос, который генерирует определение перечисления вместе с функцией checked_enum_cast
. Это, вероятно, немного страшно выглядит (макросы генерации кода часто ужасны, чтобы смотреть на них), но это чрезвычайно полезный метод, чтобы ознакомиться с ним.
#include <stdexcept>
#include <boost/preprocessor.hpp>
// Internal helper to provide partial specialization for checked_enum_cast
template <typename Target, typename Source>
struct checked_enum_cast_impl;
// Exception thrown by checked_enum_cast on cast failure
struct invalid_enum_cast : std::out_of_range
{
invalid_enum_cast(const char* s)
: std::out_of_range(s) { }
};
// Checked cast function
template <typename Target, typename Source>
Target checked_enum_cast(Source s)
{
return checked_enum_cast_impl<Target, Source>::do_cast(s);
}
// Internal helper to help declare case labels in the checked cast function
#define X_DEFINE_SAFE_CAST_CASE(r, data, elem) case elem:
// Macro to define an enum with a checked cast function. name is the name of
// the enumeration to be defined and enumerators is the preprocessing sequence
// of enumerators to be defined. See the usage example below.
#define DEFINE_SAFE_CAST_ENUM(name, enumerators) \
enum name \
{ \
BOOST_PP_SEQ_ENUM(enumerators) \
}; \
\
template <typename Source> \
struct checked_enum_cast_impl<name, Source> \
{ \
static name do_cast(Source s) \
{ \
switch (s) \
{ \
BOOST_PP_SEQ_FOR_EACH(X_DEFINE_SAFE_CAST_CASE, 0, enumerators) \
return static_cast<name>(s); \
default: \
throw invalid_enum_cast(BOOST_PP_STRINGIZE(name)); \
} \
return name(); \
} \
};
Вот как вы могли бы использовать это со своим примером CardColor
:
DEFINE_SAFE_CAST_ENUM(CardColor, (HEARTS) (CLUBS) (SPADES) (DIAMONDS))
int main()
{
checked_enum_cast<CardColor>(1); // ok
checked_enum_cast<CardColor>(400); // o noez! an exception!
}
Первая строка заменяет ваше определение enum CardColor ...
; он определяет перечисление и предоставляет специализацию, которая позволяет использовать checked_enum_cast
для целых чисел для CardColor
.
Это может показаться большим количеством хлопот, чтобы получить проверенную функцию трансляции для ваших перечислений, но эта техника очень полезна и расширяема. Вы можете добавлять функции, которые выполняют всевозможные действия. Например, у меня есть функция, которая генерирует функции для преобразования перечисленных типов в представления строк и из строковых представлений и функций, которые выполняют несколько других преобразований и проверок, которые я использую для большинства моих перечислений.
Помните, что вам нужно написать и отладить этот большой, уродливый макрос только один раз, а затем вы можете использовать его везде.