C++ 中的枚举 就像 Ada 中的枚举一样?

发布于 2024-07-09 17:19:12 字数 1694 浏览 7 评论 0原文

有一次,我考虑过在 C++ 中实现一个类/模板,该类/模板将支持 Enum,其行为与 Ada 中的行为类似。 自从我思考这个问题以来已经有一段时间了,我想知道是否有人解决过这个问题?

编辑:

抱歉,我应该澄清我认为在 Enum 的 Ada 实现中有用的功能。 鉴于枚举

type fruit is (apple, banana, cherry, peach, grape);

我们知道水果是列出的水果之一:苹果、香蕉、樱桃、桃子、葡萄。 与 C++ 没有什么不同。

非常有用的是以下功能,您可以通过 Ada 中的每个枚举获得这些功能,而无需任何额外的工作:

  • 打印出枚举值 生成字符串版本
  • 您可以递增枚举变量
  • 您可以递减枚举变量

我希望这可以解决问题多一点。


从评论中添加的注释

有用的功能Ada 枚举 枚举

  • 中的第一个值是 fruit'first,它给出 apple
  • 枚举中的最后一个值是fruit'last,它给出grape
  • 增量操作是fruit'succ(apple),它给出banana
  • 递减操作是fruit'pred(cherry),它也给出banana
  • 从枚举到整数的转换是 fruit'pos(cherry) ,它返回 2 因为 Ada 使用从 0 开始的枚举。
  • 从整数到枚举的转换是 fruit'val(2),它返回 cherry
  • 从枚举到字符串的转换是 fruit'Image(apple),它返回(大写)字符串 "APPLE"
  • 从字符串到枚举的转换是 fruit'Value("apple"),它返回值 apple

另请参阅相关的 SO 问题:

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?

EDIT:

My apologies, I should clarify what functionality I thought were useful in the Ada implementation of the Enum. Given the enumeration

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++.

What is very useful are the following pieces of functionality that you get with every enum in Ada without any additional work:

  • printing out an enumerated value generates the string version
  • you can increment the enumerated variable
  • you can decrement the enumerated variable

I hope this defines the problem a bit more.


Notes added from comments:

Useful features of Ada enumerations

  • The first value in the enumeration is fruit'first which gives apple.
  • The last value in the enumeration is fruit'last which gives grape.
  • The increment operation is fruit'succ(apple) which gives banana.
  • The decrement operation is fruit'pred(cherry) which also gives banana.
  • The conversion from enumeration to integer is fruit'pos(cherry) which returns 2 because Ada uses 0-based enumerations.
  • The conversion from integer to enumeration is fruit'val(2) which returns cherry.
  • The conversion from enumeration to string is fruit'Image(apple) which returns the (upper-case) string "APPLE".
  • The conversion from string to enumeration is fruit'Value("apple") which returns the value apple.

See also related SO questions:

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

笑忘罢 2024-07-16 17:19:12

好吧,让我们暂时把 C++ 放在一边。 C++ 只是 C 的超集(这意味着 C 中可以完成的所有操作也可以在 C++ 中完成)。 因此,让我们专注于纯 C(因为这是我熟悉的语言)。 C 有枚举:

enum fruit { apple, banana, cherry, peach, grape };

这是完全合法的 C,并且值是连续的,apple 的值为 0,banana 的值为 apple + 1。您可以创建带孔的枚举,但前提是您显式地打孔 就像这样

enum  fruit { apple = 0, banana, cherry = 20, peach, grape };

,苹果为 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");

这将打印 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");

这将打印 NO,并且 myFruit 的值与任何枚举常量都不相同。

顺便说一句,为了避免您必须说“枚举水果 myFruit”,您可以使用 typedef 来避免枚举。 只需使用“typedef enum Fruit Fruit;” 在自己的线路上。 现在你可以说“fruit myFruit”,前面不用枚举。 它通常在定义枚举时直接完成:

typedef enum fruit { apple = 0, banana, cherry, peach, grape } fruit;

fruit myFruit;

缺点是你不再知道fruit是一个枚举,它可能是一个对象、结构或其他任何东西。 我通常会避免使用这些类型的 typedef,如果是枚举,我宁愿在前面写枚举,如果是结构,我宁愿在前面写枚举(我将在这里使用它们,因为它看起来更好)。

无法获取字符串值。 在运行时,枚举只是一个数字。 这意味着,如果您不知道那是什么类型的枚举,则不可能(因为 0 可能是 apple,但它也可能是不同枚举集的不同事物)。 但是,如果您知道它是一种水果,那么编写一个函数来为您做这件事就很容易了。 预处理器是你的朋友:-)

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;
}

输出:

banana
peach
I got called with fruit grape

这正是你想要的,还是我完全走错了路?

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:

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

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:

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:

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.

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:

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).

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 :-)

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?

手长情犹 2024-07-16 17:19:12

我的一位同事实现了一种工具来生成类,该类可以完成您想要的大部分(如果不是全部)功能:

http://code.google.com/p/enumgen/

当前的实现是用 Lisp 实现的,但不要因此反对他:-)

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 :-)

纸短情长 2024-07-16 17:19:12

我编写了一个 enum_iterator 来执行此操作,以及使用 Boost.Preprocessor 的 ENUM 宏:

#include <iostream>
#include "enum.hpp"

ENUM(FooEnum, 
  (N)
  (A = 1)
  (B = 2)
  (C = 4)
  (D = 8));

int main() {
  litb::enum_iterator< FooEnum, litb::SparseRange<FooEnum> > i = N, end;
  while(i != end) {
    std::cout << i.to_string() << ": " << *i << std::endl;
    ++i;
  }
}

它将枚举声明为普通的旧枚举,因此您仍然可以将它用于“正常” ”的目的。 迭代器也可用于具有顺序值的其他普通枚举,这就是为什么它有第二个模板参数,默认为 litb::ConsequtiveRange。 它符合双向迭代器的要求。

愚蠢的代码可以从这里下载

I wrote an enum_iterator that does this, together with a ENUM macro using Boost.Preprocessor:

#include <iostream>
#include "enum.hpp"

ENUM(FooEnum, 
  (N)
  (A = 1)
  (B = 2)
  (C = 4)
  (D = 8));

int main() {
  litb::enum_iterator< FooEnum, litb::SparseRange<FooEnum> > i = N, end;
  while(i != end) {
    std::cout << i.to_string() << ": " << *i << std::endl;
    ++i;
  }
}

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.

The silly code can be downloaded from here

三生池水覆流年 2024-07-16 17:19:12

在 C++ 中没有一种简单的方法可以做到这一点,尤其是因为枚举常量不需要是唯一的或连续的。 从值到字符串的转换也很重要; 我所知道的解决方案涉及 C/C++ 预处理器 hackery - 这是对 hackery 一词的贬义用法。

我很想说“不”; 我不确定这是正确的,但它肯定是不平凡的。

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.

I'm tempted to say "No"; I'm not certain that's correct, but it most certainly is non-trivial.

追风人 2024-07-16 17:19:12

如果您对 enumgen 感兴趣,我做了一个简单的演示
你的例子。 正如已经提到的,我使用它来实现它
common lisp,所以你写的输入文件是lispy,但我
非常努力地使语法合理。

这是:

$ 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

要查看生成的代码,请访问以下网址:
http://code.google.com/p/enumgen /source/browse/#svn/trunk/demo

虽然它的功能相当丰富,但还有很多东西
也可以通过在输入文件中设置变量来调整
或者通过指定枚举数的属性。

例如,默认情况下它表示使用的字符串名称
std::string,但它可以使用 char const * 或任何用户定义的字符串
班级付出了一点努力。

您可以将多个名称映射到同一枚举值,但必须
选择一个作为“主要”,以便将值映射到字符串
将产生此名称(与其他名称相对)。

您可以显式向枚举提供值,但它们不会
需要是独一无二的。 (重复项是前一个的隐式别名
具有相同值的枚举。)

此外,您可以迭代所有唯一值,并且对于每个
值超过其所有别名,如果您想要的话,这很有用
为它们生成脚本语言“包装器”,就像我们使用 ruby​​ 一样。

如果您有兴趣使用此功能并有疑问,请随时
通过电子邮件与我联系。 (gmail 的 cuzdav)。

希望这可以帮助。 (除了
测试套件和演示代码,以及源代码(如果您关心的话)。)

Chris

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.

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

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.

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.

If you're interested in using this and have questions, feel free to
contact me via email. (cuzdav at gmail).

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

游魂 2024-07-16 17:19:12

本文向您展示如何生成枚举值的字符串版本,尽管它需要您自己编写代码来执行此操作。 它还提供了一个预处理器宏,只要您的枚举是连续的,就可以轻松地允许递增和递减枚举变量。

该库提供更灵活的递增和递减。

Boost Vault 中的 enum_rev4.6.zip 库提供了简单的字符串转换。 看起来它支持使用迭代器进行递增和递减(这可能不太方便,但它有效)。 尽管 libs/test 目录包含一些很好的示例代码,但它基本上没有文档记录。

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.

顾冷 2024-07-16 17:19:12

这是未发布的软件,但 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/

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/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文