问题描述
是否可以基于C ++中的常量创建枚举?
如果我的常量值为5,我希望枚举数具有如下5个元素
typedef enum {
LIC_0,LIC_1,LIC_2,LIC_3,LIC4,} licfiles_t;
如果我的常数为10,我希望枚举数改为这样,
typedef enum {
LIC_0,LIC5,LIC6,LIC7,LIC8,LIC9,} licfiles_t;
我想要这个的原因是,我有一个常量“ MaxLicenses”,现在定义为30,并且有一个对应的枚举器,其中包含30个枚举值。如果有人将“ MaxLicenses”更改为20,那么他们也不必麻烦更改枚举数。这是我的基本想法。
解决方法
如果愿意稍微更改语法,可以使用它。
enum class Licence : unsigned {
LIC_FIXED,LIC_FLOATING,LIC_MAGIC,LIC_NUMBERED
};
const int MaxLicence = 20;
consteval Licence LIC(unsigned l) {
if (l >= MaxLicence) throw "Invalid licence";
return Licence(l+(unsigned)Licence::LIC_NUMBERED);
}
现在您可以拥有:
auto l1 = LIC(1); // OK
auto l19 = LIC(19); // OK
auto l42 = LIC(42); // Compile-time error,invalid licence
auto x = 3;
auto lx = LIC(x); // Compile-time error,x is not a constant
如果您真的想花哨的话,可以创建一个用户定义的文字运算符,以便编写例如19_LIC
而不是LIC(19)
。
函数LIC
可能不是那么有用,因为您不提前知道MaxLicence
,因此您不知道例如LIC(19)
有效。但是如果您真的想要的话,可以拥有它。
一个更有用的版本可能不是保密的,而是在运行时将许可证号转换为Licence
类型的值,当值超出范围时会报告错误。
不,不可能有枚举数取决于常数。
以复杂的构建过程为代价,您可以使用元编程:编写一个生成枚举定义和常量定义的程序。
理想情况下,常量应定义为枚举数之一。这样,它必须与其他枚举数匹配:
enum licfiles_t {
LIC_0,LIC_1,LIC_2,LIC_3,LIC4,MaxLicenses // 5
};
,
模板不能应用于枚举项,因此您必须使用预处理器宏。宏不允许递归到它们自己,但是您仍然可以为“最多N个”解决方案展开它们。例如,对于最多128个许可证,您可以执行以下操作:
#define LICD_0
#define LICD_1 LIC_0
#define LICD_2 LICD_1,LIC_1
#define LICD_3 LICD_2,LIC_2
/* ... */
#define LICD_128 LICD_127,LIC_127
#define _LIC(n) LICD_##n /* jump to N unrolls */
#define LIC(n) _LIC(n) /* to unpack MaxLicenses */
LIC(n)
将泛型N
(LIC_0
至LIC_<N-1>
)枚举项。您可以像这样将其与MaxLicences
配对:
#define MaxLicenses 30
typedef enum {
LIC(MaxLicenses)
} licfiles_t;
static_assert(LIC_29 == 29,"");
//static_assert(LIC_30 == 30,""); // compile error,LIC_30 not defined
演示:https://godbolt.org/z/b1E1KG
当然,输入所有内容很麻烦,因此您可以编写一个小程序来生成代码:
#define MaxSupportedLicenses 128
std::cout << "#define LICD_0" << std::endl;
std::cout << "#define LICD_1 LIC_0" << std::endl;
for (int i=2; i <= MaxSupportedLicenses; ++i) {
std::cout << "#define LICD_" << i << " LICD_" << (i - 1) << ",LIC_" << (i - 1) << std::endl;
}