依靠枚举 C++ 自动
我在用 C++ 编写枚举时发现了一种模式.是这样的:
I've come to a pattern when writing enums in C++. It is like this:
class Player
{
public:
class State
{
public:
typedef enum
{
Stopped,
Playing,
Paused
}PossibleValues;
static const int Count() {return Paused+1;};
static const PossibleValues Default() {return Stopped;};
};
//...
}
这解决了枚举的一些常见问题,例如外部命名空间的污染等.但还有一点我不喜欢:Count() 是手动完成的.我知道的方法只有两种:一种是从 Last+1 计算出来的;或编写普通的硬编码.
This solves a some of the usual issues with enums, like pollution of outside namespaces, etc. But there is still a thing I don't like: The Count() is done manually. There are only two ways I know how to do it: this one is calculated from Last+1; or write plain hardcoded.
问题是:有没有办法,比如使用预处理器宏,自动获取计数,将其放在 Count() 方法之后?注意:我不想在枚举中有最后一个叫做 Count 的假元素,污染它!
Question is: Is there some way, like using preprocessor macros, that automatically gets the count, to put it after in the Count() method? Attention: I don't want to have a last fake element called Count inside the enum, polluting it!
提前致谢!
更新 1:
有一个关于 实施的有趣讨论标准 C++11 中的 N4428 枚举反射(部分) 用于更高级枚举的提议.
There is an interesting discussion on Implementation of N4428 enum reflection in standard C++11 (partial) for a proposal of more advanced enums.
更新 2:
有趣的文档 N4451-静态反射(修订版. 3) 关于 MetaEnums 和 MetaEnumClasses 的第 3.16、3.17、A.7、A.8 节.
Interesting document N4451- Static reflection (rev. 3) on its sections 3.16, 3.17, A.7, A.8 about MetaEnums and MetaEnumClasses.
更新 3:
在我看到 https://bytes.com/topic/c/answers/127908-numeric_limits-specialization#post444962.如果枚举类的枚举器列表是连续整数,通过定义它的最大值和最小值,我们可以检查一个值是否属于它.
I came to another interesting pattern using an enum class, after I've seen https://bytes.com/topic/c/answers/127908-numeric_limits-specialization#post444962. If the enum class's enumerator list is continuously integer, by defining its maximum and its minimum, we can check whether a value belongs to it or not.
如果在 Player::State
上使用 Count()
方法的目的是检查枚举中是否有一个值,那么这个目的也达到了使用 numeric_limits 方法,甚至更胜一筹,因为它不需要枚举器列表以零值项目开头!
If the purpose of using the Count()
method on the Player::State
was to check if a value was in the enum, that purpose has also been achieved with the numeric_limits approach, and is even superior, as it is not required the enumerator list begins with a ZERO valued item!
enum class Drink
{
Water,
Beer,
Wine,
Juice,
};
#pragma push_macro("min")
#undef min
#pragma push_macro("max")
#undef max
namespace std
{
template <> class numeric_limits < Drink >
{
public:
static const/*expr*/ bool is_specialized = true;
static const/*expr*/ Drink min() /*noexcept*/ { return Drink::Water; }
static const/*expr*/ Drink max() /*noexcept*/ { return Drink::Juice; }
static const/*expr*/ Drink lowest() /*noexcept*/ { return Drink::Water; }
static const/*expr*/ Drink default() /*noexcept*/ { return Drink::Beer; }
};
}
#pragma pop_macro("min")
#pragma pop_macro("max")
使用案例:
来自应用程序的变量:
Drink m_drink;
在构造函数中初始化为:
which in constructor is initialized with:
m_drink = numeric_limits<Drink>::default();
关于一个表单的初始化,我可以这样做:
On the initialization of a form, I can do:
pComboDrink->SetCurSel(static_cast<int>(theApp.m_drink));
在它上面,为了使界面适应用户所做的更改,我可以使用作用域枚举类值进行切换:
On it, for adapting the interface to changes done by the user, I can do a switch with scoped enum class values:
switch (static_cast<Drink>(pComboDrink->GetCurSel()))
{
case Drink::Water:
case Drink::Juice:
pAlcohoolDegreesControl->Hide();
break;
case Drink::Beer:
case Drink::Wine:
pAlcohoolDegreesControl->Show();
break;
default:
break;
}
在对话框的确认过程 (OnOK
) 中,我可以检查该值是否超出边界,然后再将其保存到相应的应用程序变量中:
And on the dialog's confirmation procedure (OnOK
), I can check if the value is out of boundaries, before saving it to the respective app var:
int ix= pComboDrink->GetCurSel();
if (ix == -1)
return FALSE;
#pragma push_macro("min")
#undef min
#pragma push_macro("max")
#undef max
if (ix < static_cast<int> (std::numeric_limits<Drink>::min()) || ix > static_cast<int> (std::numeric_limits<Drink>::max()) )
return FALSE;
#pragma pop_macro("min")
#pragma pop_macro("max")
theApp.m_drink= static_cast<Drink>(ix);
注意:
- 关键字
constexpr
(我注释了/*expr*/
,将其保留为const
)和noexcept
被评论只是因为我使用的编译器 (Visual C++ 2013) 在当前版本中还不支持它们. - 也许您不需要临时取消定义最小和最大宏的逻辑.
- 我知道
default()
不适合数字限制"范围;但它似乎是一个方便的地方;甚至它与default
词重合,在某些情况下它是一个关键字!
- The keywords
constexpr
(I commented/*expr*/
, leaving it asconst
) andnoexcept
are commented only because the compiler I am using (Visual C++ 2013) does not support them yet at the current version. - Maybe you do not need the logic to temporary undefine the min and max macros.
- I know that the
default()
does not fit on a "numeric limits" scope; but it seemed an handy place to put it on; even it coincides with thedefault
word that in some contexts is a keyword!
推荐答案
重新发布来自类似问题的答案 (非序列整数 c++ 枚举的最佳方法是什么),因为它与其他方面非常相关未回答的问题.
Reposting an answer from a similar question (What is the best way for non sequencial integer c++ enums) because it was kind of relevant to an otherwise pretty much unanswered question.
一种可以用来获取所需内容的模式是使用 std::initializer_list 来存储枚举的所有值.
A pattern you could use to get what you want is to use an std::initializer_list to store all the values of your enum.
namespace PossibleValues
{
enum Type
{
ZERO= 0,
PLUS180= 180,
PLUS90= 90,
MINUS90= -90
};
constexpr auto Values = {ZERO, PLUS180, PLUS90, MINUS90};
size_t Count() { return Values.size(); }
Type Default() { return *begin(Values); }
}
这也有一个好处,即即使枚举值没有线性值,也可以迭代它们.
This also has the benefit of being able to iterate of the values of the enum even if they don't have linear values.
而且我认为您可以使用可变参数宏从单个宏生成枚举、初始化列表和函数,尽管在最好的情况下,这种事情应该在标准中.
And I think you could possibly generate both the enum, the initializer list and the functions from a single macro with a variadic macro, though in the best of worlds this kind of thing should be in the standard.
当我使用可能的值作为枚举或使用可能值的结构时,我的编译器会抱怨类型不完整.为枚举使用命名空间有点不寻常,但它工作正常.
When I used PossibleValues as an enum or used a struct for PossibleValues, my compiler would complain about incomplete type. Using a namespace for an enum is a bit unusual, but it works fine.
相关文章