如何将枚举类型变量转换为字符串?
如何使 printf 显示枚举类型变量的值?例如:
typedef enum {Linux, Apple, Windows} OS_type;
OS_type myOS = Linux;
我需要的是类似的东西
printenum(OS_type, "My OS is %s", myOS);
,必须显示字符串“Linux”,而不是整数。
我想,首先我必须创建一个值索引的字符串数组。但我不知道这是否是最好的方式。有可能吗?
How to make printf to show the values of variables which are of an enum type? For instance:
typedef enum {Linux, Apple, Windows} OS_type;
OS_type myOS = Linux;
and what I need is something like
printenum(OS_type, "My OS is %s", myOS);
which must show a string "Linux", not an integer.
I suppose, first I have to create a value-indexed array of strings. But I don't know if that is the most beautiful way to do it. Is it possible at all?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(30)
我需要这个在两个方向上工作,并且我经常将枚举嵌入到包含类中,因此我从 James McNellis 的解决方案开始,位于这些答案的顶部,但我提出了这个解决方案。另请注意,我更喜欢枚举类而不仅仅是枚举,这使答案有些复杂。
要在类中使用它,您可以执行以下操作:
我编写了一个 CppUnit 测试,它演示了如何使用它:
您必须选择要使用的宏,DEFINE_ENUMERATION 或 DEFINE_ENUMERATION_INSIDE_CLASS。您会看到我在定义 ComponentStatus::Status 时使用了后者,但在定义 Status 时使用了前者。区别很简单。在类中,我将 to/from 方法前缀为“static”,如果不在类中,我使用“inline”。差异虽小,但却是必要的。
不幸的是,我认为没有一种干净的方法可以避免这样做:
尽管您可以在类定义之后手动创建一个内联方法,该方法简单地链接到类方法,例如:
I needed this to work in both directions AND I frequently embed my enums inside a containing class, and so I started with the solution by James McNellis way, way at the top of these answers, but I made this solution. Note also I prefer enum class rather than just enum, which complicates the answer somewhat.
To use it inside a class, you could do something like this:
And I wrote a CppUnit test, which demonstrates how to use it:
You have to pick which macro to use, either DEFINE_ENUMERATION or DEFINE_ENUMERATION_INSIDE_CLASS. You'll see I used the latter when defining ComponentStatus::Status but I used the former when just defining Status. The difference is simple. Inside a class, I prefix the to/from methods as "static" and if not in a class, I use "inline". Trivial differences, but necessary.
Unfortunately, I don't think there's a clean way to avoid having to do this:
although you could manually create an inline method after your class definition that simply chains to the class method, something like:
对此还有很多其他答案,但我认为更好的方法是使用 C++17 功能并使用 constexpr,以便在编译时完成翻译。这是类型安全的,我们不需要弄乱宏。见下文:
然后可以轻松地使用它,以便在编译时检测到字符串键错误:
该代码比其他一些解决方案更详细,但我们可以轻松地在编译时进行枚举到字符串的转换和字符串到枚举的转换并检测类型错误。借助未来的一些 C++20 功能,这可能可以进一步简化。
There are many other answers to this but I think a better way is to use C++17 features and to use constexpr so that translations are done at compile time. This is type safe and we do not need to mess with macros. See below:
This can then easily be used so that string key errors are detected at compile time:
The code is more verbose than some other solutions but we can easily do Enum to String conversion and String to Enum conversion at compile time and detect type errors. With some of the future C++20 features this can probably be simplified a bit more.
就我个人而言,我会选择简单的方法并使用运算符来完成此操作。
考虑以下枚举:
我们可以创建一个运算符来将结果输出到
std::ostream
中。与本线程中介绍的其他一些方法相比,样板代码确实相当大。尽管如此,它的优点是非常简单且易于使用。
Personally, I would go for something simple and use an operator to do so.
Considering the following enum:
We can create an operator to output the result in an
std::ostream
.The boilerplate code is indeed pretty big compared to some other methods presented in this thread. Still, it has the avantage of being pretty straightforward and easy to use.
这是仅使用 C 预处理器的 Old Skool 方法(曾经在 gcc 中广泛使用)。如果您生成离散数据结构但需要保持它们之间的顺序一致,则非常有用。 mylist.tbl 中的条目当然可以扩展到更复杂的东西。
test.cpp:
然后是 mylist.tbl:
Here's the Old Skool method (used to be used extensively in gcc) using just the C pre-processor. Useful if you're generating discrete data structures but need to keep the order consistent between them. The entries in mylist.tbl can of course be extended to something much more complex.
test.cpp:
And then mylist.tbl:
为了扩展詹姆斯的答案,有人想要一些示例代码来支持使用 int 值定义枚举,我也有这个要求,所以这是我的方法:
第一个是内部使用宏,由 FOR_EACH 使用:
并且,这里是定义宏:
所以在使用它的时候,你可能会这样写:
它将扩展为:
基本思想是定义一个SEQ,其中每个元素都是一个TUPLE,这样我们就可以为枚举成员添加加值。在FOR_EACH循环中,检查TUPLE项的大小,如果大小为2,则将代码扩展为KEY = VALUE,否则只保留TUPLE的第一个元素。
因为输入 SEQ 实际上是 TUPLE,所以如果你想定义 STRINGIZE 函数,你可能需要先预处理输入枚举器,这里是完成这项工作的宏:
宏
DEFINE_ENUM_WITH_STRING_CONVERSIONS_FIRST_ELEM_SEQ
只会保留每个TUPLE中的第一个元素,然后转换为SEQ,现在修改James的代码,你将拥有完整的能力。我的实现可能不是最简单的,所以如果您没有找到任何干净的代码,我的代码供您参考。
To extend James' answer, someone want some example code to support enum define with int value, I also have this requirement, so here is my way:
First one the is internal use macro, which is used by FOR_EACH:
And, here is the define macro:
So when using it, you may like to write like this:
which will expand to:
The basic idea is to define a SEQ, which every element is a TUPLE, so we can put addition value for enum member. In FOR_EACH loop, check the item TUPLE size, if the size is 2, expand the code to KEY = VALUE, else just keep the first element of TUPLE.
Because the input SEQ is actually TUPLEs, so if you want to define STRINGIZE functions, you may need to pre-process the input enumerators first, here is the macro to do the job:
The macro
DEFINE_ENUM_WITH_STRING_CONVERSIONS_FIRST_ELEM_SEQ
will only keep the first element in every TUPLE, and later convert to SEQ, now modify James' code, you will have the full power.My implementation maybe not the simplest one, so if you do not find any clean code, mine for your reference.
纯标准 C 中干净、安全的解决方案:
输出
基本原理
在解决核心问题“具有相应字符串的枚举常量”时,明智的程序员会提出以下要求:
第一个要求,也许还有第二个要求,可以通过各种混乱的宏解决方案来满足,例如臭名昭著的“x宏”技巧或其他形式的宏魔法。这种解决方案的问题在于,它们给您留下了一堆完全不可读的神秘宏——它们不满足上面的第三个要求。
这里唯一需要的实际上是有一个字符串查找表,我们可以通过使用枚举变量作为索引来访问它。这样的表自然必须直接对应于枚举,反之亦然。当其中一个更新时,另一个也必须更新,否则将无法工作。
代码说明
假设我们有一个像
这样的枚举,可以将其更改为
优点是这些宏常量现在可以在其他地方使用,例如生成字符串查找表。将预处理器常量转换为字符串可以使用“stringify”宏来完成:
就是这样。通过使用
hello
,我们得到值为0的枚举常量。通过使用test_str[hello]
,我们得到字符串“hello”。为了使枚举和查找表直接对应,我们必须确保它们包含相同数量的项目。如果有人维护代码并且只更改枚举,而不更改查找表,反之亦然,则此方法将不起作用。
解决方案是让枚举告诉您它包含多少个项目。为此,有一个常用的 C 技巧,只需在末尾添加一个项目,这仅起到告知枚举有多少个项目的目的:
现在我们可以在编译时检查枚举中的项目数为与查找表中的项目数量一样多,最好使用 C11 静态断言:(
如果有人坚持使用恐龙编译器,那么在旧版本的 C 标准中也有丑陋但功能齐全的方法来创建静态断言。至于C++,它也支持静态断言。)
顺便说一句,在C11中我们还可以通过更改stringify宏来实现更高的类型安全性:(
int
因为枚举常量实际上是类型int
,而不是test_t
)这将阻止诸如
STRINGIFY(random_stuff)
之类的代码编译。Clean, safe solution in pure standard C:
Output
Rationale
When solving the core problem "have enum constants with corresponding strings", a sensible programmer will come up with the following requirements:
The first requirement, and maybe also the second, can be fulfilled with various messy macro solutions such as the infamous "x macro" trick, or other forms of macro magic. The problem with such solutions is that they leave you with a completely unreadable mess of mysterious macros - they don't meet the third requirement above.
The only thing needed here is actually to have a string look-up table, which we can access by using the enum variable as index. Such a table must naturally correspond directly to the enum and vice versa. When one of them is updated, the other has to be updated too, or it will not work.
Explanation of the code
Suppose we have an enum like
This can be changed to
With the advantage that these macro constants can now be used elsewhere, to for example generate a string look-up table. Converting a pre-processor constant to a string can be done with a "stringify" macro:
And that's it. By using
hello
, we get the enum constant with value 0. By usingtest_str[hello]
we get the string "hello".To make the enum and look-up table correspond directly, we have to ensure that they contain the very same amount of items. If someone would maintain the code and only change the enum, and not the look-up table, or vice versa, this method won't work.
The solution is to have the enum to tell you how many items it contains. There is a commonly-used C trick for this, simply add an item at the end, which only fills the purpose of telling how many items the enum has:
Now we can check at compile time that the number of items in the enum is as many as the number of items in the look-up table, preferably with a C11 static assert:
(There are ugly but fully-functional ways to create static asserts in older versions of the C standard too, if someone insists on using dinosaur compilers. As for C++, it supports static asserts too.)
As a side note, in C11 we can also achieve higher type safety by changing the stringify macro:
(
int
because enumeration constants are actually of typeint
, nottest_t
)This will prevent code like
STRINGIFY(random_stuff)
from compiling.我自己的答案,不使用 boost - 使用我自己的方法,没有大量的定义魔法,并且这个解决方案有一个限制,即无法定义特定的枚举值。
最新版本可以在 github 上找到:
https://github。 com/tapika/cppscriptcore/blob/master/SolutionProjectModel/EnumReflect.h
My own answer, not using boost - using my own approach without heavy define magic, and this solution has a limitation of not be able to define specific enum value.
Latest version can be found on github in here:
https://github.com/tapika/cppscriptcore/blob/master/SolutionProjectModel/EnumReflect.h
XMACROS
xmacros.h:
代码:
XMACROS
xmacros.h:
code:
在 C++ 中是这样的:
In c++ like this:
来自 http://www.codeproject。 com/Articles/42035/Enum-to-String-and-Vice-Versa-in-C 以及
插入
后如果枚举中的值不重复,则可以正常工作。
将枚举值转换为字符串的示例代码:
相反的示例代码:
from http://www.codeproject.com/Articles/42035/Enum-to-String-and-Vice-Versa-in-C and after
insert
Works fine if values in the enum are not duplicate.
Sample code for converting an enum value to string:
Sample code for just the opposite:
感谢詹姆斯的建议。它非常有用,所以我以相反的方式实现了以某种方式做出贡献。
Thanks James for your suggestion. It was very useful so I implemented the other way around to contribute in some way.
我所做的结合了我在这里看到的内容以及本网站上类似问题的内容。我用的是 Visual Studio 2013 制作的。我没有用其他编译器测试过。
首先,我定义了一组可以实现这些技巧的宏。
接下来定义一个宏,它将创建枚举类和获取字符串的函数。
现在定义一个枚举类型并为其添加字符串变得非常容易。您需要做的就是:
可以使用以下几行来测试它。
这将输出:
我相信它非常干净且易于使用。有一些限制:
如果你能解决这个问题。我认为,尤其是如何使用它,这是很好而且精益的。优点:
What I made is a combination of what I have seen here and in similar questions on this site. I made this is Visual Studio 2013. I have not tested it with other compilers.
First of all I define a set of macros that will do the tricks.
Next define a single macro that will create the enum class and the functions to get the strings.
Now defining an enum type and have strings for it becomes really easy. All you need to do is:
The following lines can be used to test it.
This will output:
I believe it is very clean and easy to use. There are some limitations:
If you can work around this. I think, especially how to use it, this is nice and lean. Advantages:
这个问题的一个干净的解决方案是:
这个解决方案的好处是它很简单,并且可以通过复制和替换轻松地构建函数。请注意,如果您要进行大量转换并且枚举有太多可能的值,则此解决方案可能会占用 CPU 资源。
A clean solution to this problem would be:
The good thing about this solution is that it is simple and also constructing the function can be done easily via copy and replace. Note that if you are going to do a lot of conversions and your enum has too many possible values, this solution might become CPU intensive.
当然,最简单的解决方案是为每个枚举编写一个函数来执行到字符串的转换:
然而,这是一个维护灾难。借助可与 C 和 C++ 代码一起使用的 Boost.Preprocessor 库,您可以轻松利用预处理器并让它为您生成此函数。生成宏如下:
第一个宏(以
X_
开头)由第二个宏内部使用。第二个宏首先生成枚举,然后生成一个 ToString 函数,该函数接受该类型的对象并将枚举器名称作为字符串返回(出于显而易见的原因,此实现要求枚举器映射到唯一的值)。在 C++ 中,您可以将
ToString
函数实现为operator<<
重载,但我认为需要显式的“ToString” 将值转换为字符串形式。
作为一个用法示例,您的
OS_type
枚举将定义如下:虽然该宏乍一看似乎需要大量工作,并且
OS_type
的定义看起来相当陌生,请记住,您必须编写一次宏,然后您可以将其用于每个枚举。您可以向它添加附加功能(例如,字符串形式到枚举的转换),而不会太麻烦,并且它完全解决了维护问题,因为当您调用宏时,您只需提供一次名称。然后可以像正常定义一样使用枚举:
本文中的代码片段(从
#include
行开始)可以按照发布的方式进行编译,以演示解决方案。这个特定的解决方案适用于 C++,因为它使用 C++ 特定的语法(例如,没有
typedef enum
)和函数重载,但也可以直接在 C 中使用。The naive solution, of course, is to write a function for each enumeration that performs the conversion to string:
This, however, is a maintenance disaster. With the help of the Boost.Preprocessor library, which can be used with both C and C++ code, you can easily take advantage of the preprocessor and let it generate this function for you. The generation macro is as follows:
The first macro (beginning with
X_
) is used internally by the second. The second macro first generates the enumeration, then generates aToString
function that takes an object of that type and returns the enumerator name as a string (this implementation, for obvious reasons, requires that the enumerators map to unique values).In C++ you could implement the
ToString
function as anoperator<<
overload instead, but I think it's a bit cleaner to require an explicit "ToString
" to convert the value to string form.As a usage example, your
OS_type
enumeration would be defined as follows:While the macro looks at first like it is a lot of work, and the definition of
OS_type
looks rather foreign, remember that you have to write the macro once, then you can use it for every enumeration. You can add additional functionality to it (e.g., a string-form to enum conversion) without too much trouble, and it completely solves the maintenance problem, since you only have to provide the names once, when you invoke the macro.The enumeration can then be used as if it were defined normally:
The code snippets in this post, beginning with the
#include <boost/preprocessor.hpp>
line, can be compiled as posted to demonstrate the solution.This particular solution is for C++ as it uses C++-specific syntax (e.g., no
typedef enum
) and function overloading, but it would be straightforward to make this work with C as well.确实没有什么好的方法可以做到这一点。只需设置一个由枚举索引的字符串数组即可。
如果要做很多输出,可以定义一个operator<<它接受一个枚举参数并为您进行查找。
There really is no beautiful way of doing this. Just set up an array of strings indexed by the enum.
If you do a lot of output, you can define an operator<< that takes an enum parameter and does the lookup for you.
这是预处理器块
Enum 定义
使用 getStringOsType(WINBLOWS) 调用
;
摘自此处。那有多酷? :)
This is the pre processor block
Enum definition
Call using
getStringOsType(WINBLOWS);
Taken from here. How cool is that ? :)
已经有很多好的答案,但是 magic_enum 值得一看。
它将自己描述为——
用法示例
There are lots of good answers already, but magic_enum is worth a look.
It describes itself as -
Example usage
我合并了 James'、Howard 的 和 Éder 的 解决方案并创建了更通用的实现:
完整的代码如下所示(使用“DEFINE_ENUM_CLASS_WITH_ToString_METHOD”来定义枚举)(在线演示)。
I have combined the James', Howard's and Éder's solutions and created a more generic implementation:
The full code is written bellow (use "DEFINE_ENUM_CLASS_WITH_ToString_METHOD" for defining an enum) (online demo).
您是否尝试过:
stringify()
宏可用于将代码中的任何文本转换为字符串,但仅限于括号之间的确切文本。没有变量取消引用或宏替换或任何其他类型的事情完成。http://www.cplusplus.com/forum/general/2949/
Did you try this:
The
stringify()
macro can be used to turn any text in your code into a string, but only the exact text between the parentheses. There are no variable dereferencing or macro substitutions or any other sort of thing done.http://www.cplusplus.com/forum/general/2949/
使用 std::map并使用枚举作为键、字符串表示作为值来填充它,然后您可以执行以下操作:
Use
std::map<OS_type, std::string>
and populate it with enum as key, and string representation as values, then you can do these:C 枚举的问题在于它不是它自己的类型,就像 C++ 中的那样。 C 中的枚举是一种将标识符映射到整数值的方法。就这样。这就是为什么枚举值可以与整数值互换。
正如您猜对的那样,一个好方法是在枚举值和字符串之间创建映射。例如:
The problem with C enums is that it's not a type of it's own, like it is in C++. An enum in C is a way to map identifiers to integral values. Just that. That's why an enum value is interchangeable with integer values.
As you guess correctly, a good way is to create a mapping between the enum value and a string. For example:
对于 C99,P99 中有
P99_DECLARE_ENUM
,让您只需像这样声明enum
:然后使用
color_getname(A)
获取带有颜色名称的字符串。For C99 there is
P99_DECLARE_ENUM
in P99 that lets you simply declareenum
like this:and then use
color_getname(A)
to obtain a string with the color name.我自己的偏好是尽量减少重复输入和难以理解的宏,并避免将宏定义引入通用编译器空间。
因此,在头文件中:
cpp 实现是:
一旦我们完成了它,请注意宏的#undef。
My own preference is to minimize both repetitive typing and hard to understand macros and to avoid introducing macro definitions into the general compiler space.
So, in the header file:
and the cpp implementation is:
Note the #undef of the macro as soon we're done with it.
这里有很多很好的答案,但我想有些人可能会发现我的答案很有用。我喜欢它,因为用于定义宏的界面非常简单。它也很方便,因为您不必包含任何额外的库 - 它全部带有 C++,甚至不需要真正最新的版本。我从网上的各个地方收集了一些片段,所以我不能把所有的归功于它,但我认为它足够独特,足以保证一个新的答案。
首先创建一个头文件...将其命名为 EnumMacros.h 或类似的文件,然后将其放入其中:
然后,在主程序中,您可以执行此操作...
其中输出将是 >> “Apple”的值为: 2 of 4
享受!
There are a lot of good answers here, but I thought some people might find mine useful. I like it because the interface that you use to define the macro is about as simple as it can get. It's also handy because you don't have to include any extra libraries - it all comes with C++ and it doesn't even require a really late version. I pulled pieces from various places online so I can't take credit for all of it, but I think it's unique enough to warrant a new answer.
First make a header file... call it EnumMacros.h or something like that, and put this in it:
Then, in your main program you can do this...
Where the output would be >> The value of 'Apple' is: 2 of 4
Enjoy!
这个简单的例子对我有用。希望这有帮助。
This simple example worked for me. Hope this helps.
假设您的枚举已经定义,您可以创建一个对数组:
现在,您可以创建一个映射:
现在,您可以使用该映射。如果您的枚举发生更改,您必须从数组pairs[]中添加/删除pair。我认为这是从 C++ 中的枚举获取字符串的最优雅的方法。
Assuming that your enum is already defined, you can create an array of pairs:
Now, you can create a map:
Now, you can use the map. If your enum is changed, you have to add/remove pair from array pairs[]. I thinkk that it is the most elegant way to obtain a string from enum in C++.
这是我的 C++ 代码:
Here is my C++ code:
我的解决方案,不使用 boost:
以下是如何使用它
My solution, not using boost:
And here is how to use it
虽然有点晚了,但这是我的 C++11 解决方案:
A little late to the party, but here's my C++11 solution:
另一个迟到的聚会,使用预处理器:(
我只是输入行号,这样更容易谈论。)
第 1-4 行是您编辑的内容,用于定义枚举的元素。
(我称其为“列表宏”,因为它是一个列出事物列表的宏。@Lundin 告诉我这些是一种称为 X 宏的众所周知的技术。)
第 7 行定义了内部宏,以便填充第 8-11 行中的实际枚举声明。
第 12 行取消定义内部宏(只是为了消除编译器警告)。
第 14 行定义了内部宏,以便创建枚举元素名称的字符串版本。
然后第 15-18 行生成一个数组,可以将枚举值转换为相应的字符串。
第 21-27 行生成一个函数,该函数将字符串转换为枚举值,或者如果字符串不匹配则返回 NULL。
这在处理第 0 个元素的方式上有点麻烦。
我过去实际上已经解决过这个问题。
我承认这种技术让那些不想认为预处理器本身可以被编程来为您编写代码的人感到困扰。
我认为它强烈地说明了可读性和可维护性之间的区别。
代码很难阅读,
但如果枚举有几百个元素,您可以添加、删除或重新排列元素,并且仍然确保生成的代码没有错误。
Another late to the party, using the preprocessor:
(I just put in line numbers so it's easier to talk about.)
Lines 1-4 are what you edit to define the elements of the enum.
(I have called it a "list macro", because it's a macro that makes a list of things. @Lundin informs me these are a well-known technique called X-macros.)
Line 7 defines the inner macro so as to fill in the actual enum declaration in lines 8-11.
Line 12 undefines the inner macro (just to silence the compiler warning).
Line 14 defines the inner macro so as to create a string version of the enum element name.
Then lines 15-18 generate an array that can convert an enum value to the corresponding string.
Lines 21-27 generate a function that converts a string to the enum value, or returns NULL if the string doesn't match any.
This is a little cumbersome in the way it handles the 0th element.
I've actually worked around that in the past.
I admit this technique bothers people who don't want to think the preprocessor itself can be programmed to write code for you.
I think it strongly illustrates the difference between readability and maintainability.
The code is difficult to read,
but if the enum has a few hundred elements, you can add, remove, or rearrange elements and still be sure the generated code has no errors.