在C中使用枚举类型变量作为字符串的简单方法?
这就是我想要做的:
typedef enum { ONE, TWO, THREE } Numbers;
我正在尝试编写一个函数来执行类似于以下内容的 switch 情况:
char num_str[10];
int process_numbers_str(Numbers num) {
switch(num) {
case ONE:
case TWO:
case THREE:
{
strcpy(num_str, num); //some way to get the symbolic constant name in here?
} break;
default:
return 0; //no match
return 1;
}
有没有一种方法可以像我尝试做的那样使用枚举变量来设置它,而不是在每种情况下定义多于?
Here's what I am trying to do:
typedef enum { ONE, TWO, THREE } Numbers;
I am trying to write a function that would do a switch case similar to the following:
char num_str[10];
int process_numbers_str(Numbers num) {
switch(num) {
case ONE:
case TWO:
case THREE:
{
strcpy(num_str, num); //some way to get the symbolic constant name in here?
} break;
default:
return 0; //no match
return 1;
}
Instead of defining at every case, is there a way to set it using the enum variable like I am trying to do above?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(20)
这是使用具有以下功能的宏的解决方案:
仅将枚举的每个值写入一次,因此没有需要维护的双列表
不要将枚举值保存在稍后#included的单独文件中,这样我就可以将其写入任何我想要的地方
不要替换枚举本身,我仍然希望定义枚举类型,但除此之外我希望能够将每个枚举名称映射到相应的字符串(为了不影响遗留代码)
搜索应该很快,所以最好没有 switch-case,对于那些巨大的枚举
https://stackoverflow.com/a/20134475/1812866
Here is a solution using macros with the following features:
only write each value of the enum once, so there are no double lists to maintain
don't keep the enum values in a separate file that is later #included, so I can write it wherever I want
don't replace the enum itself, I still want to have the enum type defined, but in addition to it I want to be able to map every enum name to the corresponding string (to not affect legacy code)
the searching should be fast, so preferably no switch-case, for those huge enums
https://stackoverflow.com/a/20134475/1812866
吻。 您将使用枚举执行各种其他 switch/case 操作,那么为什么打印应该有所不同呢? 当您考虑到还有大约 100 个其他地方可能会忘记大小写时,在打印例程中忘记大小写并不是什么大问题。 只需编译 -Wall,它会警告非详尽的大小写匹配。 不要使用“默认”,因为这会使开关变得详尽,并且您不会收到警告。 相反,让开关退出并处理默认情况,如下所示......
KISS. You will be doing all sorts of other switch/case things with your enums so why should printing be different? Forgetting a case in your print routine isn't a huge deal when you consider there are about 100 other places you can forget a case. Just compile -Wall, which will warn of non-exhaustive case matches. Don't use "default" because that will make the switch exhaustive and you wont get warnings. Instead, let the switch exit and deal with the default case like so...
使用 boost::preprocessor 使之成为可能一个优雅的解决方案,如下所示:
步骤 1:包含头文件:
步骤 2:使用以下语法声明枚举对象:
步骤 3:使用数据:
获取元素数量:
获取关联字符串:
从中获取枚举值关联的字符串:
这看起来干净紧凑,没有额外的文件要包含。
我在 EnumUtilities.h 中编写的代码如下:
有一些限制,即 boost::preprocessor 的限制。 在这种情况下,常量列表不能超过 64 个元素。
遵循相同的逻辑,您还可以考虑创建稀疏枚举:
在这种情况下,语法为:
用法与上面类似(减去 eName##2Enum 函数,您可以尝试从之前的语法中推断)。
我在 Mac 和 Linux 上测试了它,但请注意 boost::preprocessor 可能不完全可移植。
The use of boost::preprocessor makes possible an elegant solution like the following:
Step 1: include the header file:
Step 2: declare the enumeration object with the following syntax:
Step 3: use your data:
Getting the number of elements:
Getting the associated string:
Getting the enum value from the associated string:
This looks clean and compact, with no extra files to include.
The code I wrote within EnumUtilities.h is the following:
There are some limitation, i.e. the ones of boost::preprocessor. In this case, the list of constants cannot be larger than 64 elements.
Following the same logic, you could also think to create sparse enum:
In this case, the syntax is:
Usage is similar as above (minus the eName##2Enum function, that you could try to extrapolate from the previous syntax).
I tested it on mac and linux, but be aware that boost::preprocessor may not be fully portable.
尝试将 C++ 枚举转换为字符串。 注释进行了改进,可以解决以下问题:枚举项具有任意值。
Try Converting C++ enums to strings. The comments have improvements that solve the problem when enum items have arbitrary values.
通过合并这里的一些技术,我想出了最简单的形式:
By merging some of the techniques over here I came up with the simplest form:
如果您使用 gcc,则可以使用:
然后只需调用例如
If you are using gcc, it's possible to use:
Then just call for instance
查看Mu Dynamics 研究实验室 - 博客存档<中的想法/a>. 我在今年早些时候发现了这一点 - 我忘记了我遇到它的确切上下文 - 并将其改编到此代码中。 我们可以讨论在前面添加 E 的优点; 它适用于所解决的具体问题,但不是通用解决方案的一部分。 我把它藏在我的“vignettes”文件夹中 - 我在那里保存有趣的代码片段,以备以后需要它们。 我很不好意思地说,我当时并没有记录这个想法的来源。
标题:paste1.h
示例来源:
不一定是世界上最干净的 C 预处理器使用 - 但它确实可以防止多次写出材料。
Check out the ideas at Mu Dynamics Research Labs - Blog Archive. I found this earlier this year - I forget the exact context where I came across it - and have adapted it into this code. We can debate the merits of adding an E at the front; it is applicable to the specific problem addressed, but not part of a general solution. I stashed this away in my 'vignettes' folder - where I keep interesting scraps of code in case I want them later. I'm embarrassed to say that I didn't keep a note of where this idea came from at the time.
Header: paste1.h
Example source:
Not necessarily the world's cleanest use of the C pre-processor - but it does prevent writing the material out multiple times.
如果枚举索引是从 0 开始的,则可以将名称放入 char* 数组中,并使用枚举值对它们进行索引。
If the enum index is 0-based, you can put the names in an array of char*, and index them with the enum value.
关于此方法的进一步讨论
针对新手的预处理器指令技巧
Further discussion on this method
Preprocessor directive tricks for newcomers
使某些内容既是 C 标识符又是字符串
Making something both a C identifier and a string
我创建了一个简单的模板类
streamable_enum
,它使用流运算符<<
和>>
并且基于std::map
:用法:
I have created a simple templated class
streamable_enum
that uses stream operators<<
and>>
and is based on thestd::map<Enum, std::string>
:Usage:
我认为像 Boost.Fusion 这样用于调整结构和类的解决方案会很好,他们甚至在某个时候拥有它,使用枚举作为融合序列。
所以我只做了一些小宏来生成打印枚举的代码。 这并不完美,与 Boost.Fusion 生成的样板代码无关,但可以像 Boost Fusion 宏一样使用。 我真的想生成 Boost.Fusion 所需的类型来集成到这个基础设施中,该基础设施允许打印结构成员的名称,但这将在稍后发生,目前这只是宏:
下面的旧答案非常糟糕,请不要不要用那个。 :)
旧答案:
我一直在寻找一种解决此问题的方法,而无需过多更改枚举声明语法。 我找到了一个解决方案,它使用预处理器从字符串化枚举声明中检索字符串。
我能够像这样定义非稀疏枚举:
并且我可以以不同的方式与它们交互:
基于以下定义:
当我需要稀疏枚举的支持时以及当我有更多时间时我会改进使用 boost::xpressive 实现 to_string 和 from_string ,但这会花费编译时间,因为执行了重要的模板,并且生成的可执行文件可能会更大。 但这的优点是它比这种丑陋的手动字符串操作代码更具可读性和可维护性。 :D
否则,我总是使用 boost::bimap 来执行枚举值和字符串之间的此类映射,但必须手动维护。
I thought that a solution like Boost.Fusion one for adapting structs and classes would be nice, they even had it at some point, to use enums as a fusion sequence.
So I made just some small macros to generate the code to print the enums. This is not perfect and has nothing to see with Boost.Fusion generated boilerplate code, but can be used like the Boost Fusion macros. I want to really do generate the types needed by Boost.Fusion to integrate in this infrastructure which allows to print names of struct members, but this will happen later, for now this is just macros :
The old answer below is pretty bad, please don't use that. :)
Old answer:
I've been searching a way which solves this problem without changing too much the enums declaration syntax. I came to a solution which uses the preprocessor to retrieve a string from a stringified enum declaration.
I'm able to define non-sparse enums like this :
And I can interact with them in different ways:
Based on the following definitions :
When I'll need support for sparse enum and when I'll have more time I'll improve the to_string and from_string implementations with boost::xpressive, but this will costs in compilation time because of the important templating performed and the executable generated is likely to be really bigger. But this has the advantage that it will be more readable and maintanable than this ugly manual string manipulation code. :D
Otherwise I always used boost::bimap to perform such mappings between enums value and string, but it has to be maintained manually.
因为出于所有常见原因我不喜欢使用宏,所以我使用了一个更有限的宏解决方案,它的优点是保持枚举声明宏自由。 缺点包括必须复制粘贴每个枚举的宏定义,以及在向枚举添加值时必须显式添加宏调用。
Because I prefer not to use macros for all the usual reasons, I used a more limited macro solution that has the advantage of keeping the enum declaration macro free. Disadvantages include having to copy paste the macro defintion for each enum, and having to explicitly add a macro invocation when adding values to the enum.
有一种更简单、更清晰的方法
该线程中缺少的内容:
唯一的缺点是最后一个单元格后面会加一个逗号,
尽管它似乎可以被 C/C++ 编译器接受。
There's a way simpler and imo sorta clearer approach
that was missing on this thread:
The only downside is that the last cell will be postceded by a comma,
though it appears to be acceptable by C/C++ compilers.
使某些东西既是 C 标识符又是字符串? 可以在这里使用。
与通常的预处理器内容一样,编写和理解预处理器部分可能很困难,包括将宏传递给其他宏并涉及使用 # 和 ## 运算符,但使用它非常简单。 我发现这种风格对于长枚举非常有用,因为两次维护相同的列表确实很麻烦。
工厂代码 - 仅键入一次,通常隐藏在标头中:
enumFactory.h:
工厂使用
someEnum.h:
someEnum.cpp:
该技术可以轻松扩展,以便 XX 宏接受更多参数,并且您还可以准备更多宏来根据不同的需求替换 XX,类似于我在本示例中提供的三个。
与使用 #include / #define / #undef 的 X-Macros 进行比较
虽然这与其他人提到的 X-Macros 类似,但我认为这个解决方案更优雅,因为它不需要 #undefing 任何东西,这允许您隐藏更多复杂的东西在工厂中的头文件中——当你需要定义一个新的枚举时,头文件是你根本不需要接触的东西,因此新的枚举定义要短得多、更干净。
The technique from Making something both a C identifier and a string? can be used here.
As usual with such preprocessor stuff, writing and understanding the preprocessor part can be hard, and includes passing macros to other macros and involves using # and ## operators, but using it is real easy. I find this style very useful for long enums, where maintaining the same list twice can be really troublesome.
Factory code - typed only once, usually hidden in the header:
enumFactory.h:
Factory used
someEnum.h:
someEnum.cpp:
The technique can be easily extended so that XX macros accepts more arguments, and you can also have prepared more macros to substitute for XX for different needs, similar to the three I have provided in this sample.
Comparison to X-Macros using #include / #define / #undef
While this is similar to X-Macros others have mentioned, I think this solution is more elegant in that it does not require #undefing anything, which allows you to hide more of the complicated stuff is in the factory the header file - the header file is something you are not touching at all when you need to define a new enum, therefore new enum definition is a lot shorter and cleaner.
C 或 C++ 不提供此功能,尽管我经常需要它。
以下代码可以工作,尽管它最适合非稀疏枚举。
我所说的非稀疏,是指不是这种形式,
因为其中存在巨大的差距。
这种方法的优点是它使枚举和字符串的定义彼此靠近; 在函数中使用 switch 语句将它们分开。 这意味着您不太可能只改变其中一个而不改变另一个。
C or C++ does not provide this functionality, although I've needed it often.
The following code works, although it's best suited for non-sparse enums.
By non-sparse, I mean not of the form
since that has huge gaps in it.
The advantage of this method is that it put the definitions of the enums and strings near each other; having a switch statement in a function spearates them. This means you're less likely to change one without the other.
没有内置的解决方案。 最简单的方法是使用 char* 数组,其中枚举的 int 值索引到包含该枚举的描述性名称的字符串。 如果您有一个稀疏的
enum
(不从 0 开始或编号中有间隙的枚举),其中某些int
映射足够高以构成数组 -基于映射不切实际,那么您可以使用哈希表代替。There's no built-in solution. The easiest way is with an array of
char*
where the enum's int value indexes to a string containing the descriptive name of that enum. If you have a sparseenum
(one that doesn't start at 0 or has gaps in the numbering) where some of theint
mappings are high enough to make an array-based mapping impractical then you could use a hash table instead.肯定有一种方法可以做到这一点 - 使用 X() 宏。 这些宏使用 C 预处理器从源数据列表构造枚举、数组和代码块。 您只需将新项目添加到包含 X() 宏的 #define 中。 switch 语句会自动扩展。
您的示例可以写成如下:
有更有效的方法(即使用 X 宏创建字符串数组和枚举索引),但这是最简单的演示。
There is definitely a way to do this -- use X() macros. These macros use the C preprocessor to construct enums, arrays and code blocks from a list of source data. You only need to add new items to the #define containing the X() macro. The switch statement would expand automatically.
Your example can be written as follows:
There are more efficient ways (i.e. using X Macros to create an string array and enum index), but this is the simplest demo.
我知道您有几个很好的可靠答案,但是您知道 C 预处理器中的 # 运算符吗?
它可以让你这样做:
I know you have a couple good solid answers, but do you know about the # operator in the C preprocessor?
It lets you do this: