检测是否将int转换为枚举结果为非枚举值

Jér*_*ôme 12 c++ enums

假设我有这样的事情:

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

CardColor MyColor = static_cast<CardColor>(100);
Run Code Online (Sandbox Code Playgroud)

是否有(简单)方法在编译时或运行时检测到值MyColor与任何枚举值不对应?

更一般地说,如果枚举值不相互跟随,例如:

enum CardColor { HEARTS = 0, DIAMONDS, CLUBS = 4, SPADES};
Run Code Online (Sandbox Code Playgroud)

Jam*_*lis 15

CashCow 为这个问题提供了一个不错的答案:编写一个自定义函数来执行一个检查演员表肯定是直截了当的.

不幸的是,它也是很多工作,你必须确保它与枚举保持同步,以便枚举定义中的枚举数列表与已检查的强制转换函数中的枚举数列表相同.您还必须为每个枚举编写其中一个,以便能够执行检查的强制转换.

我们可以使用预处理器(在Boost预处理器库的帮助下)自动生成所有这些代码,而不是完成所有这些手动工作.这是一个生成枚举定义和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();                                                 \
        }                                                                  \
    };
Run Code Online (Sandbox Code Playgroud)

以下是您在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!
}
Run Code Online (Sandbox Code Playgroud)

第一行取代了你的enum CardColor ...定义; 它定义了枚举并提供了一个允许您使用checked_enum_cast整数转换的特化CardColor.

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

记住,你必须只编写和调试那个大而丑陋的宏,然后你可以在任何地方使用它.


Cas*_*Cow 9

最简单的运行时解决方案是不使用static_cast,而是使用一个为您进行检查的函数.如果你把你的枚举放在一个类中,你可以通过课程来完成.就像是:

class CardCheck
{
public:
  enum CardColor { HEARTS, DIAMONDS, CLUBS, SPADES };

  explicit CardCheck( int x ) : c( static_cast< CardColor >( x ) )
  {
     switch( c )
     {
       case HEARTS: case DIAMONDS: case CLUBS: case SPADES:
          break;

       default:
         // assert or throw
    }
  }

  CardColor get() const
  {
     return c;
  }

 private:
  CardColor c;      
};
Run Code Online (Sandbox Code Playgroud)

  • 有趣的是,我们在代码中做了类似的事情来验证整数被强制转换为枚举.一个编译器决定优化switch语句,大概是因为所有可能的情况都被覆盖所以它假设它始终有效.也许是包含CARD_COUNT的一个很好的理由.然后至少有一个可能的坏值,编译器将不会优化. (2认同)

ZAB*_*ZAB 5

clang支持动态溢出检查。请参阅-fsanitize=enum开关。使用此开关编译的程序将通过 stderr 输出发出枚举分配错误的信号。这将允许您进行调试测试。不适合在官方构建中测试可疑输入。