psi*_*lia 110 c c++ preprocessor ansi-c
如何让printf显示枚举类型变量的值?例如:
typedef enum {Linux, Apple, Windows} OS_type;
OS_type myOS = Linux;
Run Code Online (Sandbox Code Playgroud)
而我需要的是类似的东西
printenum(OS_type, "My OS is %s", myOS);
Run Code Online (Sandbox Code Playgroud)
必须显示字符串"Linux",而不是整数.
我想,首先我必须创建一个值索引的字符串数组.但我不知道这是否是最美妙的方式.有可能吗?
Jam*_*lis 126
当然,天真的解决方案是为执行转换为字符串的每个枚举编写一个函数:
enum OS_type { Linux, Apple, Windows };
inline const char* ToString(OS_type v)
{
switch (v)
{
case Linux: return "Linux";
case Apple: return "Apple";
case Windows: return "Windows";
default: return "[Unknown OS_type]";
}
}
Run Code Online (Sandbox Code Playgroud)
然而,这是一场维护灾难.借助可与C和C++代码一起使用的Boost.Preprocessor库,您可以轻松利用预处理器并让它为您生成此功能.生成宏如下:
#include <boost/preprocessor.hpp>
#define X_DEFINE_ENUM_WITH_STRING_CONVERSIONS_TOSTRING_CASE(r, data, elem) \
case elem : return BOOST_PP_STRINGIZE(elem);
#define DEFINE_ENUM_WITH_STRING_CONVERSIONS(name, enumerators) \
enum name { \
BOOST_PP_SEQ_ENUM(enumerators) \
}; \
\
inline const char* ToString(name v) \
{ \
switch (v) \
{ \
BOOST_PP_SEQ_FOR_EACH( \
X_DEFINE_ENUM_WITH_STRING_CONVERSIONS_TOSTRING_CASE, \
name, \
enumerators \
) \
default: return "[Unknown " BOOST_PP_STRINGIZE(name) "]"; \
} \
}
Run Code Online (Sandbox Code Playgroud)
第一个宏(以...开头X_)在第二个内部使用.第二个宏首先生成枚举,然后生成一个ToString函数,该函数接受该类型的对象并将枚举器名称作为字符串返回(此实现,出于显而易见的原因,要求枚举器映射到唯一值).
在C++中,您可以将该ToString函数实现为operator<<重载,但我认为要求显式" ToString"将值转换为字符串形式更为清晰.
作为用法示例,您的OS_type枚举将定义如下:
DEFINE_ENUM_WITH_STRING_CONVERSIONS(OS_type, (Linux)(Apple)(Windows))
Run Code Online (Sandbox Code Playgroud)
虽然宏首先看起来像是很多工作,而OS_type外观的定义相当陌生,但请记住你必须编写一次宏,然后你可以将它用于每个枚举.您可以添加其他功能(例如,字符串形式到枚举转换)而不会有太多麻烦,它完全解决了维护问题,因为您只需在调用宏时提供一次名称.
然后可以使用枚举,就好像它是正常定义的:
#include <iostream>
int main()
{
OS_type t = Windows;
std::cout << ToString(t) << " " << ToString(Apple) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
从帖子开始,这篇帖子中的代码片段#include <boost/preprocessor.hpp>可以编译为发布以演示解决方案.
这个特殊的解决方案适用于C++,因为它使用C++特定的语法(例如,没有typedef enum)和函数重载,但是使用C也可以直接使用它.
Bo *_*son 63
真的没有这样做的美妙方式.只需设置一个由枚举索引的字符串数组.
如果你做了很多输出,你可以定义一个运算符<<,它接受一个枚举参数并为你做查找.
Ren*_*eno 26
这是预处理器块
#ifndef GENERATE_ENUM_STRINGS
#define DECL_ENUM_ELEMENT( element ) element
#define BEGIN_ENUM( ENUM_NAME ) typedef enum tag##ENUM_NAME
#define END_ENUM( ENUM_NAME ) ENUM_NAME; \
char* GetString##ENUM_NAME(enum tag##ENUM_NAME index);
#else
#define DECL_ENUM_ELEMENT( element ) #element
#define BEGIN_ENUM( ENUM_NAME ) char* gs_##ENUM_NAME [] =
#define END_ENUM( ENUM_NAME ) ; char* GetString##ENUM_NAME(enum \
tag##ENUM_NAME index){ return gs_##ENUM_NAME [index]; }
#endif
Run Code Online (Sandbox Code Playgroud)
枚举定义
BEGIN_ENUM(Os_type)
{
DECL_ENUM_ELEMENT(winblows),
DECL_ENUM_ELEMENT(hackintosh),
}
Run Code Online (Sandbox Code Playgroud)
打电话使用
GetStringOs_type(winblows);
Run Code Online (Sandbox Code Playgroud)
取自这里.多么酷啊 ?:)
There are lots of good answers already, but magic_enum is worth a look.
It describes itself as -
Static reflection for enums (to string, from string, iteration) for modern C++, work with any enum type without any macro or boilerplate code.
Header-only C++17 library provides static reflection for enums, work with any enum type without any macro or boilerplate code.
Example usage
enum Color { RED = 2, BLUE = 4, GREEN = 8 };
Color color = Color::RED;
auto color_name = magic_enum::enum_name(color);
// color_name -> "RED"
std::string color_name{"GREEN"};
auto color = magic_enum::enum_cast<Color>(color_name);
if (color.has_value()) {
// color.value() -> Color::GREEN
}
Run Code Online (Sandbox Code Playgroud)
C enums的问题在于它不是它自己的类型,就像它在C++中一样.C中的枚举是将标识符映射到整数值的一种方法.只是.这就是枚举值可以与整数值互换的原因.
正如您猜测的那样,一个好方法是在枚举值和字符串之间创建一个映射.例如:
char * OS_type_label[] = {
"Linux",
"Apple",
"Windows"
};
Run Code Online (Sandbox Code Playgroud)
小智 7
你试过这个吗:
#define stringify( name ) # name
enum enMyErrorValue
{
ERROR_INVALIDINPUT = 0,
ERROR_NULLINPUT,
ERROR_INPUTTOOMUCH,
ERROR_IAMBUSY
};
const char* enMyErrorValueNames[] =
{
stringify( ERROR_INVALIDINPUT ),
stringify( ERROR_NULLINPUT ),
stringify( ERROR_INPUTTOOMUCH ),
stringify( ERROR_IAMBUSY )
};
void vPrintError( enMyErrorValue enError )
{
cout << enMyErrorValueNames[ enError ] << endl;
}
int main()
{
vPrintError((enMyErrorValue)1);
}
Run Code Online (Sandbox Code Playgroud)
该stringify()宏可用于将代码中的任何文本转换为字符串,但只能是括号之间的确切文本。没有变量解引用或宏替换或任何其他类型的事情。
http://www.cplusplus.com/forum/general/2949/
我结合了James',Howard和Éder的解决方案,并创建了一个更通用的实现:
完整代码如下所示(使用"DEFINE_ENUM_CLASS_WITH_ToString_METHOD"来定义枚举)(在线演示).
#include <boost/preprocessor.hpp>
#include <iostream>
// ADD_PARENTHESES_FOR_EACH_TUPLE_IN_SEQ implementation is taken from:
// http://lists.boost.org/boost-users/2012/09/76055.php
//
// This macro do the following:
// input:
// (Element1, "Element 1 string repr", 2) (Element2) (Element3, "Element 3 string repr")
// output:
// ((Element1, "Element 1 string repr", 2)) ((Element2)) ((Element3, "Element 3 string repr"))
#define HELPER1(...) ((__VA_ARGS__)) HELPER2
#define HELPER2(...) ((__VA_ARGS__)) HELPER1
#define HELPER1_END
#define HELPER2_END
#define ADD_PARENTHESES_FOR_EACH_TUPLE_IN_SEQ(sequence) BOOST_PP_CAT(HELPER1 sequence,_END)
// CREATE_ENUM_ELEMENT_IMPL works in the following way:
// if (elementTuple.GetSize() == 4) {
// GENERATE: elementTuple.GetElement(0) = elementTuple.GetElement(2)),
// } else {
// GENERATE: elementTuple.GetElement(0),
// }
// Example 1:
// CREATE_ENUM_ELEMENT_IMPL((Element1, "Element 1 string repr", 2, _))
// generates:
// Element1 = 2,
//
// Example 2:
// CREATE_ENUM_ELEMENT_IMPL((Element2, _))
// generates:
// Element1,
#define CREATE_ENUM_ELEMENT_IMPL(elementTuple) \
BOOST_PP_IF(BOOST_PP_EQUAL(BOOST_PP_TUPLE_SIZE(elementTuple), 4), \
BOOST_PP_TUPLE_ELEM(0, elementTuple) = BOOST_PP_TUPLE_ELEM(2, elementTuple), \
BOOST_PP_TUPLE_ELEM(0, elementTuple) \
),
// we have to add a dummy element at the end of a tuple in order to make
// BOOST_PP_TUPLE_ELEM macro work in case an initial tuple has only one element.
// if we have a tuple (Element1), BOOST_PP_TUPLE_ELEM(2, (Element1)) macro won't compile.
// It requires that a tuple with only one element looked like (Element1,).
// Unfortunately I couldn't find a way to make this transformation, so
// I just use BOOST_PP_TUPLE_PUSH_BACK macro to add a dummy element at the end
// of a tuple, in this case the initial tuple will look like (Element1, _) what
// makes it compatible with BOOST_PP_TUPLE_ELEM macro
#define CREATE_ENUM_ELEMENT(r, data, elementTuple) \
CREATE_ENUM_ELEMENT_IMPL(BOOST_PP_TUPLE_PUSH_BACK(elementTuple, _))
#define DEFINE_CASE_HAVING_ONLY_ENUM_ELEMENT_NAME(enumName, element) \
case enumName::element : return BOOST_PP_STRINGIZE(element);
#define DEFINE_CASE_HAVING_STRING_REPRESENTATION_FOR_ENUM_ELEMENT(enumName, element, stringRepresentation) \
case enumName::element : return stringRepresentation;
// GENERATE_CASE_FOR_SWITCH macro generates case for switch operator.
// Algorithm of working is the following
// if (elementTuple.GetSize() == 1) {
// DEFINE_CASE_HAVING_ONLY_ENUM_ELEMENT_NAME(enumName, elementTuple.GetElement(0))
// } else {
// DEFINE_CASE_HAVING_STRING_REPRESENTATION_FOR_ENUM_ELEMENT(enumName, elementTuple.GetElement(0), elementTuple.GetElement(1))
// }
//
// Example 1:
// GENERATE_CASE_FOR_SWITCH(_, EnumName, (Element1, "Element 1 string repr", 2))
// generates:
// case EnumName::Element1 : return "Element 1 string repr";
//
// Example 2:
// GENERATE_CASE_FOR_SWITCH(_, EnumName, (Element2))
// generates:
// case EnumName::Element2 : return "Element2";
#define GENERATE_CASE_FOR_SWITCH(r, enumName, elementTuple) \
BOOST_PP_IF(BOOST_PP_EQUAL(BOOST_PP_TUPLE_SIZE(elementTuple), 1), \
DEFINE_CASE_HAVING_ONLY_ENUM_ELEMENT_NAME(enumName, BOOST_PP_TUPLE_ELEM(0, elementTuple)), \
DEFINE_CASE_HAVING_STRING_REPRESENTATION_FOR_ENUM_ELEMENT(enumName, BOOST_PP_TUPLE_ELEM(0, elementTuple), BOOST_PP_TUPLE_ELEM(1, elementTuple)) \
)
// DEFINE_ENUM_CLASS_WITH_ToString_METHOD final macro witch do the job
#define DEFINE_ENUM_CLASS_WITH_ToString_METHOD(enumName, enumElements) \
enum class enumName { \
BOOST_PP_SEQ_FOR_EACH( \
CREATE_ENUM_ELEMENT, \
0, \
ADD_PARENTHESES_FOR_EACH_TUPLE_IN_SEQ(enumElements) \
) \
}; \
inline const char* ToString(const enumName element) { \
switch (element) { \
BOOST_PP_SEQ_FOR_EACH( \
GENERATE_CASE_FOR_SWITCH, \
enumName, \
ADD_PARENTHESES_FOR_EACH_TUPLE_IN_SEQ(enumElements) \
) \
default: return "[Unknown " BOOST_PP_STRINGIZE(enumName) "]"; \
} \
}
DEFINE_ENUM_CLASS_WITH_ToString_METHOD(Elements,
(Element1)
(Element2, "string representation for Element2 ")
(Element3, "Element3 string representation", 1000)
(Element4, "Element 4 string repr")
(Element5, "Element5", 1005)
(Element6, "Element6 ")
(Element7)
)
// Generates the following:
// enum class Elements {
// Element1, Element2, Element3 = 1000, Element4, Element5 = 1005, Element6,
// };
// inline const char* ToString(const Elements element) {
// switch (element) {
// case Elements::Element1: return "Element1";
// case Elements::Element2: return "string representation for Element2 ";
// case Elements::Element3: return "Element3 string representation";
// case Elements::Element4: return "Element 4 string repr";
// case Elements::Element5: return "Element5";
// case Elements::Element6: return "Element6 ";
// case Elements::Element7: return "Element7";
// default: return "[Unknown " "Elements" "]";
// }
// }
int main() {
std::cout << ToString(Elements::Element1) << std::endl;
std::cout << ToString(Elements::Element2) << std::endl;
std::cout << ToString(Elements::Element3) << std::endl;
std::cout << ToString(Elements::Element4) << std::endl;
std::cout << ToString(Elements::Element5) << std::endl;
std::cout << ToString(Elements::Element6) << std::endl;
std::cout << ToString(Elements::Element7) << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
使用std::map<OS_type, std::string>枚举作为键并使用字符串表示作为值来填充它,然后您可以执行以下操作:
printf("My OS is %s", enumMap[myOS].c_str());
std::cout << enumMap[myOS] ;
Run Code Online (Sandbox Code Playgroud)
对于 C99,P99P99_DECLARE_ENUM中可以让您简单地声明如下:enum
P99_DECLARE_ENUM(color, red, green, blue);
Run Code Online (Sandbox Code Playgroud)
然后使用color_getname(A)获取带有颜色名称的字符串。
我自己的偏好是尽量减少重复输入和难以理解的宏,并避免将宏定义引入通用编译器空间。
所以,在头文件中:
enum Level{
/**
* zero reserved for internal use
*/
verbose = 1,
trace,
debug,
info,
warn,
fatal
};
static Level readLevel(const char *);
Run Code Online (Sandbox Code Playgroud)
cpp的实现是:
Logger::Level Logger::readLevel(const char *in) {
# define MATCH(x) if (strcmp(in,#x) ==0) return x;
MATCH(verbose);
MATCH(trace);
MATCH(debug);
MATCH(info);
MATCH(warn);
MATCH(fatal);
# undef MATCH
std::string s("No match for logging level ");
s += in;
throw new std::domain_error(s);
}
Run Code Online (Sandbox Code Playgroud)
一旦我们完成了它,请注意宏的#undef。
这里有很多很好的答案,但我认为有些人可能会发现我的有用。我喜欢它,因为用于定义宏的接口几乎可以实现。它也很方便,因为您不必包括任何额外的库-所有库都随C ++一起提供,甚至不需要真正的较新版本。我在网上从各个地方拉了一些东西,所以我不能一味地归功于它,但是我认为它的独特性足以保证一个新的答案。
首先创建一个头文件...将其命名为EnumMacros.h或类似的名称,并将其放入其中:
// Search and remove whitespace from both ends of the string
static std::string TrimEnumString(const std::string &s)
{
std::string::const_iterator it = s.begin();
while (it != s.end() && isspace(*it)) { it++; }
std::string::const_reverse_iterator rit = s.rbegin();
while (rit.base() != it && isspace(*rit)) { rit++; }
return std::string(it, rit.base());
}
static void SplitEnumArgs(const char* szArgs, std::string Array[], int nMax)
{
std::stringstream ss(szArgs);
std::string strSub;
int nIdx = 0;
while (ss.good() && (nIdx < nMax)) {
getline(ss, strSub, ',');
Array[nIdx] = TrimEnumString(strSub);
nIdx++;
}
};
// This will to define an enum that is wrapped in a namespace of the same name along with ToString(), FromString(), and COUNT
#define DECLARE_ENUM(ename, ...) \
namespace ename { \
enum ename { __VA_ARGS__, COUNT }; \
static std::string _Strings[COUNT]; \
static const char* ToString(ename e) { \
if (_Strings[0].empty()) { SplitEnumArgs(#__VA_ARGS__, _Strings, COUNT); } \
return _Strings[e].c_str(); \
} \
static ename FromString(const std::string& strEnum) { \
if (_Strings[0].empty()) { SplitEnumArgs(#__VA_ARGS__, _Strings, COUNT); } \
for (int i = 0; i < COUNT; i++) { if (_Strings[i] == strEnum) { return (ename)i; } } \
return COUNT; \
} \
}
Run Code Online (Sandbox Code Playgroud)
然后,在您的主程序中,您可以执行此操作...
#include "EnumMacros.h"
DECLARE_ENUM(OsType, Windows, Linux, Apple)
void main() {
OsType::OsType MyOs = OSType::Apple;
printf("The value of '%s' is: %d of %d\n", OsType::ToString(MyOs), (int)OsType::FromString("Apple"), OsType::COUNT);
}
Run Code Online (Sandbox Code Playgroud)
输出将为>>的值“ Apple”的值是:2/4
请享用!
这个简单的例子对我有用.希望这可以帮助.
#include <iostream>
#include <string>
#define ENUM_TO_STR(ENUM) std::string(#ENUM)
enum DIRECTION{NORTH, SOUTH, WEST, EAST};
int main()
{
std::cout << "Hello, " << ENUM_TO_STR(NORTH) << "!\n";
std::cout << "Hello, " << ENUM_TO_STR(SOUTH) << "!\n";
std::cout << "Hello, " << ENUM_TO_STR(EAST) << "!\n";
std::cout << "Hello, " << ENUM_TO_STR(WEST) << "!\n";
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
180787 次 |
| 最近记录: |