我有以下enum
来描述错误代码:
typedef enum {
et_general = 0,
et_INVALID_CLI_FLAG = 1,
...
et_undef = 500
} EErrorType;
我明确写入枚举值的主要原因是为了简化调试过程 无论如何,我想知道是否有办法,让编译器抱怨非唯一值。 我总是可以在运行时轻松检查它,但我想避免这种情况。
我已阅读此post并审核了此answer。据我了解,这个答案建议以这样的方式生成枚举:“使错误更加难以发生”。
我想保留enum定义,或者接近它。
答案 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
语句作为标签 - 将保证它们的唯一性。放在虚拟未引用函数中,它不会进入可执行文件。