检测是否将枚举转换为枚举将导致非枚举值 [英] Detecting if casting an int to an enum results into a non-enumerated value

查看:120
本文介绍了检测是否将枚举转换为枚举将导致非枚举值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有这样的东西:

enum CardColor { HEARTS, DIAMONDS, CLUBS, SPADES};

CardColor MyColor = static_cast<CardColor>(100);

有一种简单的方法来检测,无论是在编译时还是运行时,值 MyColor 不对应于任何枚举值?

Is there a (simple) way to detect, either at compile-time or at runtime, that the value of MyColor doesn't correspond to any enumerated value ?

更一般地说,如果枚举值不是彼此跟随,例如:

And more generally, if the enum values are not following each other, for instance :

enum CardColor { HEARTS = 0, DIAMONDS, CLUBS = 4, SPADES};


推荐答案

CashCow提供体面的答复问题:写一个自定义函数来执行选中的转换肯定很简单。

CashCow presents a decent answer to this question: it's certainly straightforward to write a custom function to perform a checked cast.

不幸的是,它还有很多工作,你必须确保它与枚举同步,以便枚举定义中的枚举列表与选中的强制转换函数中的枚举列表。

Unfortunately, it's also a lot of work and you must make sure to keep it synchronized with the enumeration so that the list of enumerators in the enumeration definition is the same as the list of enumerators in the checked cast function. You also have to write one of these for each enumeration to which you want to be able to perform a checked cast.

我们可以自动执行所有这些手动工作,而不必执行所有这些手动工作。使用预处理器(在Boost预处理器库的帮助下)生成所有这些代码。这是一个生成枚举定义以及 checked_enum_cast 函数的宏。这可能是有点可怕的(代码生成宏通常可怕的看),但它是一个非常有用的技术,以熟悉。

Instead of doing all this manual work, we can automate generation of all of this code using the preprocessor (with a little help from the Boost Preprocessor library). Here is a macro that generates an enumeration definition along with a checked_enum_cast function. It's probably a bit scary looking (code generation macros are often horrible to look upon), but it's an extremely useful technique to become familiar with.

#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 example:

Here is how you would use that with your CardColor example:

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!
}

第一行代替您的枚举CardColor ... 定义;它定义了枚举并提供了一个特殊化,它允许您使用 checked_enum_cast 将整数转换为 CardColor

The first line replaces your enum CardColor ... definition; it defines the enumeration and provides a specialization that allows you to use checked_enum_cast to cast integers to CardColor.

这可能看起来很麻烦,只是为你的枚举获得一个检查的转换函数,但这种技术是非常有用和可扩展的。你可以添加执行各种事情的函数。例如,我有一个生成函数来转换枚举类型到字符串表示和函数,执行一些其他转换和检查,我用于大多数的枚举。

This may look like a lot of hassle just to get a checked cast function for your enums, but this technique is very useful and extensible. You can add functions that do all sorts of things. For example, I have one that generates functions to convert enumerated types to and from string representations and functions that perform several other conversions and checks that I use for most of my enumerations.

记住,你必须只编写和调试那个大而丑陋的宏,然后你可以随处使用它。

Remember, you have to write and debug that big, ugly macro just once, then you can use it everywhere.

这篇关于检测是否将枚举转换为枚举将导致非枚举值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆