c ++:确保枚举值在编译时是唯一的

时间:2014-01-27 10:53:15

标签: c++ enums unique

我有以下enum来描述错误代码:

 typedef enum {
    et_general           = 0,
    et_INVALID_CLI_FLAG  = 1,
    ...
    et_undef = 500
  } EErrorType;

我明确写入枚举值的主要原因是为了简化调试过程 无论如何,我想知道是否有办法,让编译器抱怨非唯一值。 我总是可以在运行时轻松检查它,但我想避免这种情况。

我已阅读此post并审核了此answer。据我了解,这个答案建议以这样的方式生成枚举:“使错误更加难以发生”。
我想保留enum定义,或者接近它。

5 个答案:

答案 0 :(得分:5)

我不确定Boost在您的方案中是否可用,因此这里有一个解决方案,其中enum必须在预处理器序列中定义。然后,该序列用于构建枚举和相应的mpl::vector,我们计算vector的元素是否以奇数方式唯一。我们可能首先要定义一个合适的is_unique算法,但这应该做。

#include <boost/mpl/vector.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/sort.hpp>
#include <boost/mpl/unique.hpp>
#include <boost/mpl/size.hpp>

#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/seq/enum.hpp>
#include <boost/preprocessor/seq/transform.hpp>


#define MYENUM ((FOO, 0))((BAR, 1))((BAZ, 2))

#define GET_NAME(_, __, elem) BOOST_PP_TUPLE_ELEM(2, 0, elem) = BOOST_PP_TUPLE_ELEM(2, 1, elem)
#define GET_VALUE(_, __, elem) boost::mpl::int_<BOOST_PP_TUPLE_ELEM(2, 1, elem)>

enum E {
  BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(GET_NAME, _, MYENUM))
};

typedef boost::mpl::sort< 
  boost::mpl::vector<
    BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(GET_VALUE, _, MYENUM))
    > 
  >::type evalues;

typedef boost::mpl::unique< evalues, boost::is_same<boost::mpl::_1, boost::mpl::_2> >::type uniqued;
static_assert(boost::mpl::size<uniqued>::value == boost::mpl::size<evalues>::value, "enum values not unique");

int main()
{

  return 0;
}

如果将枚举定义更改为:

#define MYENUM ((FOO, 0))((BAR, 1))((BAZ, 2))((BAZZ, 2))

您将收到一条错误消息,指出static_assert failed "enum values not unique"

答案 1 :(得分:2)

这个解决方案确实会修改枚举定义,所以如果你能忍受......

这也假设这些值主要是顺序的。

不指定值。 在枚举定义之后,让static_assert检查人类是否知道值。

enum EErrorType : uint16_t
{
  et_general = 0,
  et_INVALID_CLI_FLAG,
  et_FOO,
  et_BAR,
  ...
  et_undef = 500
};
static_assert(EErrorType::et_general          == 0, "Wrong enum value");
static_assert(EErrorType::et_INVALID_CLI_FLAG == 1, "Wrong enum value");
static_assert(EErrorType::et_FOO              == 2, "Wrong enum value");
static_assert(EErrorType::et_BAR              == 3, "Wrong enum value");
...
static_assert(EErrorType::et_undef            == 500, "Wrong enum value");

因此,它们大多数是自动分配的,因此是唯一的,并且您还可以将人类可读的值用于调试和其他目的。

答案 2 :(得分:1)

最好确保值都是唯一的,只是不明确定义它们。对于您的调试,只需编写一个帮助程序,为您提供值,如:

awk 'NR>1{print $1, i; i +=1} eerror.h

这假设eerror.h的第一行是:

typedef enum {

答案 3 :(得分:1)

C ++没有提供任何以这种方式限制枚举的功能。

如果您准备使用宏来创建enum(见下文),您可以在运行时使用一点hackery来执行此操作,并且您可能能够使用类似gccxml,doxygen或者一些其他解析器,用于在构建过程中的某个时刻验证枚举列表。

更简单但非常虚弱,如果您采用每行枚举的编码标准,则可以将__LINE__数字中的差异与枚举相关联,但这不会导致例如重复,然后是差距。

使用宏的枚举的运行时索引示例(不是特别健壮 - 例如,如果枚举值可能包含逗号,注释等,则需要创建匹配<[{(的堆栈并且引用等..

#include <iostream>
#include <string>
#include <map>

namespace Benum
{
    struct Meta
    {
        Meta(const char* p, int* p_values)
        {
            while (*p)
            {
                if (isalnum(*p) || *p == '_')
                {
                    const char* p_from = p;
                    while (isalnum(*p) || *p == '_')
                        ++p;
                    std::string idn = std::string(p_from, p - p_from);
                    int_to_string_[*p_values] = idn;
                    string_to_int_[idn] = *p_values;
                    ++p_values;
                }
                else if (*p == '=')
                    while (*p && *p != ',')
                        ++p;
                else
                    ++p;
            }
        }
        std::ostream& out(std::ostream& os, int i) const
        {
            Int_To_String::const_iterator it = int_to_string_.find(i);
            if (it != int_to_string_.end())
                return os << it->second;
            else
                return os << "<unmatched enum " << i << '>';
        }
        typedef std::map<int, std::string> Int_To_String;
        std::map<int, std::string> int_to_string_;
        std::map<std::string, int> string_to_int_;
    };

    template <typename T>
    struct Incrementing
    {
        Incrementing(int n) : n_(n) { s_next_implicit_ = n + 1; }
        Incrementing() : n_(s_next_implicit_++) { }
        operator int() const { return n_; }
        int n_;
        static int s_next_implicit_;
    };

    template <typename T>
    int Incrementing<T>::s_next_implicit_;
}

#define BENUM(IDN, ...) \
    enum IDN ## _Enum { __VA_ARGS__ }; \
    struct IDN { \
        typedef IDN ## _Enum Enum; \
        IDN(Enum e) : e_(e) { } \
        IDN& operator=(Enum e) { e_ = e; return *this; } \
        operator Enum() const { return e_; } \
        friend std::ostream& operator<<(std::ostream& os, Enum e) { \
            return IDN::meta().out(os, e); \
        } \
        static const Benum::Meta& meta() { \
            static Benum::Incrementing<IDN> __VA_ARGS__; \
            static int values[] = { __VA_ARGS__ }; \
            static Benum::Meta m(#__VA_ARGS__, values); \
            return m; \
        } \
        Enum e_; \
    };

// benum example usage...

BENUM(My_Enum, One = 1, Two = 2, Three = 3, Four = 4, Whats_Next);

int main()
{
    std::cout << One << ' ' << Two << ' ' << Three << ' ' <<
        Whats_Next << '\n';
    const Benum::Meta& meta = My_Enum::meta();
    for (std::map<int, std::string>::const_iterator i = meta.int_to_string_.begin();
        i != meta.int_to_string_.end(); ++i)
        std::cout << i->first << ' ' << i->second << '\n';
}

答案 4 :(得分:0)

可以用枚举值写一个虚拟switch语句作为标签 - 将保证它们的唯一性。放在虚拟未引用函数中,它不会进入可执行文件。