At one point I had looked at implementing a class/template in C++ that would support an Enum that would behave like it does in Ada. It has been some time since I thought about this problem and I was wondering if anyone has ever solved this problem?
有一次,我曾考虑在C ++中实现一个类/模板,它将支持一个行为与Ada一样的Enum。自从我想到这个问题已经有一段时间了,我想知道是否有人解决了这个问题?
EDIT:
My apologies, I should clarify what functionality I thought were useful in the Ada implementation of the Enum. Given the enumeration
我道歉,我应该澄清一下我认为在Ena的Ada实现中有用的功能。鉴于列举
type fruit is (apple, banana, cherry, peach, grape);
We know that fruit is one of the listed fruits: apple, banana, cherry, peach, grape. Nothing really different there from C++.
我们知道水果是列出的水果之一:苹果,香蕉,樱桃,桃子,葡萄。与C ++没有什么不同。
What is very useful are the following pieces of functionality that you get with every enum in Ada without any additional work:
非常有用的是您在Ada中的每个枚举中获得的以下功能,而无需任何额外的工作:
打印出枚举值会生成字符串版本
你可以增加枚举变量
你可以减少枚举变量
I hope this defines the problem a bit more.
我希望这可以更多地定义问题。
Notes added from comments:
注释中添加了注释:
见:维基百科(Judah Himango,2008-11-19 at 0:09)
fruit'first
which gives apple
.枚举中的第一个值是fruit'first,它给出了苹果。
fruit'last
which gives grape
.枚举中的最后一个值是fruit'last,它给出了葡萄。
fruit'succ(apple)
which gives banana
.增量操作是fruit'succ(apple),它给出了香蕉。
fruit'pred(cherry)
which also gives banana
.减量操作是fruit'pred(樱桃),也给香蕉。
fruit'pos(cherry)
which returns 2
because Ada uses 0-based enumerations.从枚举到整数的转换是fruit'pos(cherry),它返回2,因为Ada使用从0开始的枚举。
fruit'val(2)
which returns cherry
.从整数到枚举的转换是fruit'val(2),它返回cherry。
fruit'Image(apple)
which returns the (upper-case) string "APPLE"
.从枚举到字符串的转换是fruit'Image(apple),它返回(大写)字符串“APPLE”。
fruit'Value("apple")
which returns the value apple
.从字符串到枚举的转换是fruit'Value(“apple”),它返回值apple。
See also related SO questions:
另见相关的SO问题:
您使用的是哪种C ++中的Typesafe枚举?
下一个或上一个枚举
3
Okay, let's leave C++ aside for a moment. C++ is just a superset of C (which means everything that can be done in C can be done in C++ as well). So let's concentrate on plain-C (because that's a language I know well). C has enumerations:
好吧,让我们暂时离开C ++吧。 C ++只是C的超集(这意味着可以在C中完成的所有事情也可以在C ++中完成)。所以让我们专注于普通C(因为这是我熟悉的语言)。 C有枚举:
enum fruit { apple, banana, cherry, peach, grape };
This is perfectly legal C and the values are contiguous, and apple has the value zero and banana has the value apple + 1. You can create enumerations with holes, but only if you explicitly make holes like this
这是完全合法的C,值是连续的,apple的值为零,banana的值为apple + 1.您可以创建带孔的枚举,但前提是您明确地设置了这样的洞
enum fruit { apple = 0, banana, cherry = 20, peach, grape };
While apple is 0 and banana is 1, cherry is 20, thus peach is 21 and grape is 22 and everything between 1 and 20 is undefined. Usually you don't want holes. You can do the following:
虽然苹果是0而香蕉是1,但是樱桃是20,因此桃子是21,葡萄是22,1到20之间的一切都是未定义的。通常你不想要洞。您可以执行以下操作:
enum fruit { apple = 0, banana, cherry, peach, grape };
enum fruit myFruit = banana;
myFruit++;
// myFruit is now cherry
printf("My fruit is cherry? %s\n", myFruit == cherry ? "YES" : "NO");
This will print YES. You can also do the following:
这将打印YES。您还可以执行以下操作:
enum fruit { apple = 0, banana, cherry = 20, peach, grape };
enum fruit myFruit = banana;
myFruit++;
// myFruit is now cherry
printf("My fruit is cherry? %s\n", myFruit == cherry ? "YES" : "NO");
This will print NO, and the value of myFruit is not the same as any of the enumeration constants.
这将打印NO,myFruit的值与任何枚举常量不同。
BTW, to avoid that you must say "enum fruit myFruit", you can avoid the enum with a typedef. Just use "typedef enum fruit fruit;" on an own line. Now you can say "fruit myFruit" without enum in front. It is often done directly when the enum is defined:
顺便说一句,为了避免你必须说“enum fruit myFruit”,你可以避免使用typedef枚举。只需使用“typedef enum fruit fruit;”在一条自己的路上。现在你可以在前面没有枚举的情况下说“水果myFruit”。通常在定义枚举时直接完成:
typedef enum fruit { apple = 0, banana, cherry, peach, grape } fruit;
fruit myFruit;
Disadvantage is that you don't know anymore that fruit is an enum, it might be an object, a structure or anything else. I usually avoid these type of typedefs, I rather write enum in front if an enum and struct in front if a struct (I will just use them here because it looks nicer).
缺点是你不再知道水果是一个枚举,它可能是一个物体,一个结构或其他任何东西。我通常会避免使用这种类型的typedef,如果一个结构中的枚举和结构,我宁愿在前面写枚举(我会在这里使用它们,因为它看起来更好)。
Getting the string value is not possible. At runtime an enumeration is just a number. That means, it's not possible if you don't know what kind of enumeration that is (as 0 might be apple, but it might also be a different thing of a different enumeration set). However, if you know it is a fruit, then it's easy to write a function that will do it for you. The preprocessor is your friend :-)
无法获取字符串值。在运行时,枚举只是一个数字。这意味着,如果你不知道那是什么类型的枚举是不可能的(因为0可能是苹果,但它也可能是不同枚举集的不同之处)。但是,如果你知道它是一种水果,那么编写一个可以为你做的功能很容易。预处理器是你的朋友:-)
typedef enum fruit {
apple = 0,
banana,
cherry,
peach,
grape
} fruit;
#define STR_CASE(x) case x: return #x
const char * enum_fruit_to_string(fruit f) {
switch (f) {
STR_CASE(apple); STR_CASE(banana); STR_CASE(cherry);
STR_CASE(peach); STR_CASE(grape);
}
return NULL;
}
#undef STR_CASE
static void testCall(fruit f) {
// I have no idea what fruit will be passed to me, but I know it is
// a fruit and I want to print the name at runtime
printf("I got called with fruit %s\n", enum_fruit_to_string(f));
}
int main(int argc, char ** argv) {
printf("%s\n", enum_fruit_to_string(banana));
fruit myFruit = cherry;
myFruit++; // myFruit is now peach
printf("%s\n", enum_fruit_to_string(myFruit));
// I can also pass an enumeration to a function
testCall(grape);
return 0;
}
Output:
banana
peach
I got called with fruit grape
This is exactly what you wanted or am I totally on the wrong track here?
这正是你想要的,还是我完全走错了?
2
One of my colleagues has implemented a tool to generate classes that do most (if not all) of what you want:
我的一位同事已经实现了一个工具来生成最能满足您需求的类(如果不是全部的话):
http://code.google.com/p/enumgen/
The current implementation is in Lisp, but do not hold that against him :-)
当前的实现是在Lisp中,但不要反对他:-)
1
There isn't an easy way to do that in C++, not least because the enumeration constants are not required to be unique or contiguous. The conversion from value to string is also non-trivial; the solutions I know of involve C/C++ Preprocessor hackery - and that is a pejorative use of the term hackery.
在C ++中没有一种简单的方法可以做到这一点,尤其是因为枚举常量不需要是唯一的或连续的。从值到字符串的转换也是非常重要的;我所知道的解决方案涉及C / C ++预处理器hackery - 这是对hackery一词的贬义用法。
I'm tempted to say "No"; I'm not certain that's correct, but it most certainly is non-trivial.
我很想说“不”;我不确定这是否正确,但它肯定是非平凡的。
1
you might take a look at the java enum (http://madbean.com/2004/mb2004-3/) and this idea: http://en.wikipedia.org/wiki/Curiously_Recurring_Template_Pattern
你可以看看java enum(http://madbean.com/2004/mb2004-3/)和这个想法:http://en.wikipedia.org/wiki/Curiously_Recurring_Template_Pattern
1
If you're interested in enumgen, I made a simple demo with your example. As already mentioned, I implemented it using common lisp, so the input file you write is lispy, but I tried really hard to make the syntax reasonable.
如果你对enumgen感兴趣,我用你的例子做了一个简单的演示。正如已经提到的,我使用常见的lisp实现它,所以你编写的输入文件是lispy,但我努力使语法合理。
Here it is:
这里是:
$ cat Fruit.enum
(def-enum "Fruit" (("apple")
("banana")
("cherry")
("peach")
("grape")
("INVALID_")))
$ enumgen Fruit.enum
Using clisp
;; Loading file /tmp/enumgen/enumgen.lisp ...
;; Loaded file /tmp/enumgen/enumgen.lisp
loading def file:
;; Loading file /tmp/enumgen/enumgen.def ...
;; Loaded file /tmp/enumgen/enumgen.def
generating output:
Fruit.cpp
Fruit.ipp
Fruit.hpp
DONE
To view the generated code, visit this url: http://code.google.com/p/enumgen/source/browse/#svn/trunk/demo
要查看生成的代码,请访问以下网址:http://code.google.com/p/enumgen/source/browse/#svn/trunk/demo
While it's pretty feature-rich as it is, there are a lot of things that can be tweaked as well, by setting variables in the input file or by specifying attributes of the enumerators.
虽然它的功能非常丰富,但通过在输入文件中设置变量或指定枚举器的属性,还可以调整很多东西。
For example, by default it represents the string names using std::string, but it can use char const * or any user-defined string class given a little effort.
例如,默认情况下,它使用std :: string表示字符串名称,但它可以使用char const *或任何用户定义的字符串类,只需花费一点力气。
You can have multiple names map to the same enum value, but must pick one to be the "primary" such that mapping the value to a string will result in this name (as opposed to the others.)
您可以将多个名称映射到相同的枚举值,但必须选择一个作为“主要”,以便将值映射到字符串将导致此名称(与其他名称相对)。
You can explicitly provide values to the enums, and they do not need to be unique. (Duplicates are implicit aliases for the previous enum with the same value.)
您可以明确地为枚举提供值,并且它们不必是唯一的。 (重复项是前一个枚举的隐含别名,具有相同的值。)
Further, you can iterate over all the unique values, and for each value over all its aliases, which is useful if you want to generate script-language "wrappers" for these, like we do using ruby.
此外,您可以遍历所有唯一值,并遍历其所有别名中的每个值,如果您要为这些值生成脚本语言“包装器”,这非常有用,就像我们使用ruby一样。
If you're interested in using this and have questions, feel free to contact me via email. (cuzdav at gmail).
如果您对使用此产品感兴趣并有疑问,请随时通过电子邮件与我联系。 (gz的cuzdav)。
Hope this helps. (There isn't a lot of documentation aside from the test suite and demo code, and the source if you care about that.)
希望这可以帮助。 (除了测试套件和演示代码之外,没有很多文档,如果你关心它,还有源代码。)
Chris
1
I wrote an enum_iterator
that does this, together with a ENUM
macro using Boost.Preprocessor:
我写了一个使用Boost.Preprocessor执行此操作的enum_iterator以及一个ENUM宏:
#include
#include "enum.hpp"
ENUM(FooEnum,
(N)
(A = 1)
(B = 2)
(C = 4)
(D = 8));
int main() {
litb::enum_iterator > i = N, end;
while(i != end) {
std::cout <
It declares the enum as plain old enum, so you may still use it for "normal" purposes. The iterator can be used for other normal enums too that have sequential values, that's why it has a second template parameter which defaults to litb::ConsequtiveRange<>
. It conforms to the bidirectional iterator requirements.
它将枚举声明为普通的枚举,因此您仍可将其用于“正常”目的。迭代器也可用于具有顺序值的其他常规枚举,这就是为什么它有第二个模板参数,默认为litb :: ConsequtiveRange <>。它符合双向迭代器要求。
The silly code can be downloaded from here
愚蠢的代码可以从这里下载
0
This article shows you how to generate the string version of an enumerated value, although it requires that you write the code to do so yourself. It also provides a preprocessor macro to very easily permit incrementing and decrementing enumerated variables, as long as your enum is continuous.
本文将向您展示如何生成枚举值的字符串版本,尽管它需要您自己编写代码来编写代码。它还提供了一个预处理器宏,只要您的枚举是连续的,就可以非常轻松地允许递增和递减枚举变量。
This library provides more flexible incrementing and decrementing.
该库提供更灵活的递增和递减。
The enum_rev4.6.zip library in the Boost Vault provides easy string conversions. It looks like it supports incrementing and decrementing using iterators (which is probably less convenient, but it works). It's basically undocumented, although the libs/test directory contains some good example code.
Boost Vault中的enum_rev4.6.zip库提供了简单的字符串转换。看起来它支持使用迭代器递增和递减(这可能不太方便,但它可以工作)。它基本上没有文档,尽管libs / test目录包含一些很好的示例代码。
0
It's unreleased software but it seems BOOST_ENUM from Frank Laub could fit the bill. The part I like about it is that you can define an enum within the scope of a class which most of the Macro based enums usually don't let you do. It is located in the Boost Vault at: http://www.boostpro.com/vault/index.php?action=downloadfile&filename=enum_rev4.6.zip&directory=& It hasn't seen any development since 2006 so I don't know how well it compiles with the new Boost releases. Look under libs/test for an example of usage. There is also Boost smart_enum (not released either). It does the iterator part of your question but not the output to a string. http://cryp.to/smart-enum/
它是未发布的软件,但似乎来自Frank Laub的BOOST_ENUM可以满足要求。我喜欢它的部分是你可以在一个类的范围内定义一个枚举,大多数基于宏的枚举通常不允许你这样做。它位于Boost Vault中:http://www.boostpro.com/vault/index.php?action = downloadfile&filename = enum_rev4.6.zip&directory =&它自2006年以来没有任何发展,所以我没有知道它如何与新的Boost版本一起编译。在libs / test下查看用法示例。还有Boost smart_enum(未发布)。它执行问题的迭代器部分,但不输出字符串。 http://cryp.to/smart-enum/