从 C++ 宏创建字符串列表和枚举列表
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5530248/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Creating a string list and an enum list from a C++ macro
提问by Tiago
In order to make my code shorter and easier to change I want to replace something like
为了使我的代码更短更容易更改,我想替换类似的东西
enum{ E_AAA, E_BBB, E_CCC };
static const char *strings{"AAA", "BBB", "CCC" };
With a macro, like INIT(AAA, BBB, CCC); but when I try doing a macro with variable arguments, and stringification I get an error as the arguments are not declared.
使用宏,如 INIT(AAA, BBB, CCC); 但是当我尝试使用可变参数和字符串化做一个宏时,我得到一个错误,因为没有声明参数。
Any idea on how to do this?
关于如何做到这一点的任何想法?
回答by Dr Beco
Here a solution I learned a few days ago. The simplifiedversion that attends your question is:
这是我几天前学到的解决方案。参加您的问题的简化版本是:
#define ENUM_MACRO(name, v1, v2, v3, v4, v5, v6, v7)\
enum name { v1, v2, v3, v4, v5, v6, v7};\
const char *name##Strings[] = { #v1, #v2, #v3, #v4, #v5, #v6, #v7};
ENUM_MACRO(Week, Sun, Mon, Tue, Wed, Thu, Fri, Sat);
But you can have an improved version, with a function call, like this:
但是你可以有一个改进的版本,带有一个函数调用,像这样:
#define ENUM_MACRO(name, v1, v2, v3, v4, v5, v6, v7)\
enum name { v1, v2, v3, v4, v5, v6, v7};\
const char *name##Strings[] = { #v1, #v2, #v3, #v4, #v5, #v6, #v7};\
const char *name##ToString(value) { return name##Strings[value]; }
ENUM_MACRO(Week, Sun, Mon, Tue, Wed, Thu, Fri, Sat);
This will grow to be:
这将增长为:
enum Week { Sun, Mon, Tue, Wed, Thu, Fri, Sat};
const char *WeekStrings[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
const char *WeekToString(value) { return WeekStrings[value]; };
You can even use an offsetfor the first element, like this one:
您甚至可以为第一个元素使用偏移量,如下所示:
#define ENUM_MACRO(name, offset, v1, v2, v3, v4, v5, v6, v7)\
enum name { v1 = offset, v2, v3, v4, v5, v6, v7};\
const char *name##Strings[] = { #v1, #v2, #v3, #v4, #v5, #v6, #v7};\
const char *name##ToString(value) { return name##Strings[value - offset ]; }
ENUM_MACRO(Week, 1, Sun, Mon, Tue, Wed, Thu, Fri, Sat);
I hope this helps.
我希望这有帮助。
Take care, Beco
保重,贝科
Reference:
参考:
Print the month question, by Kush, answer by Danny Varod
打印月问题,由 Kush,由 Danny Varod 回答
回答by Scott Smedley
You can do it with a bit of macro magic:
你可以用一点宏魔法来做到这一点:
#define FRUITS \
etype(Unknown), \
etype(Apple), \
etype(Orange), \
etype(Banana), \
etype(Apricot), \
etype(Mango)
#define etype(x) F_##x
typedef enum { FRUITS } Fruit;
#undef etype
#define etype(x) #x
static const char *strFruit[] = { FRUITS };
Here is a test program:
这是一个测试程序:
#include <iostream>
#include <exception>
#include <vector>
#define FRUITS \
etype(Unknown), \
etype(Apple), \
etype(Orange), \
etype(Banana), \
etype(Apricot), \
etype(Mango)
#define etype(x) F_##x
typedef enum { FRUITS } Fruit;
#undef etype
#define etype(x) #x
static const char *strFruit[] = { FRUITS };
const char *enum2str (Fruit f)
{
return strFruit[static_cast<int>(f)];
}
Fruit str2enum (const char *f)
{
const int n = sizeof(strFruit) / sizeof(strFruit[0]);
for (int i = 0; i < n; ++i)
{
if (strcmp(strFruit[i], f) == 0)
return (Fruit) i;
}
return F_Unknown;
}
int main (int argc, char *argv[])
{
std::cout << "I like " << enum2str(F_Mango) << std::endl;
std::cout << "I do not like " << enum2str(F_Banana) << std::endl;
std::vector<char *> v;
v.push_back("Apple");
v.push_back("Mango");
v.push_back("Tomato");
for (int i = 0; i < v.size(); ++i)
{
const Fruit f = str2enum(v[i]);
if (f == F_Unknown)
std::cout << "Is " << v[i] << " a fruit?" << std::endl;
else
std::cout << v[i] << " is a fruit" << std::endl;
}
return 0;
}
It outputs:
它输出:
I like Mango
I do not like Banana
Apple is a fruit
Mango is a fruit
Is Tomato a fruit?
回答by indigo
Here is my solution:
这是我的解决方案:
#define FRUITS(fruit) \
fruit(Apple) \
fruit(Orange) \
fruit(Banana)
#define CREATE_ENUM(name) \
F_##name,
#define CREATE_STRINGS(name) \
#name,
The trick is that 'fruit' is an argument of the macro 'FRUITS' and will be replaced by what ever you pass to. For example:
诀窍是'fruit'是宏'FRUITS'的一个参数,它将被你传递的任何东西替换。例如:
FRUITS(CREATE_ENUM)
will expand to this:
将扩展到这个:
F_Apple, F_Orange, F_Banana,
Lets create the enum and the string array:
让我们创建枚举和字符串数组:
enum fruit {
FRUITS(CREATE_ENUM)
};
const char* fruit_names[] = {
FRUITS(CREATE_STRINGS)
};
回答by Begemoth
Here is a solution with Boost.Preprocessor:
这是 Boost.Preprocessor 的解决方案:
#include <boost/preprocessor.hpp>
#define DEFINE_ENUM_DECL_VAL(r, name, val) BOOST_PP_CAT(name, BOOST_PP_CAT(_, val))
#define DEFINE_ENUM_VAL_STR(r, name, val) BOOST_PP_STRINGIZE(val)
#define DEFINE_ENUM(name, val_seq) \
enum name { \
BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(DEFINE_ENUM_DECL_VAL, name, val_seq)) \
}; \
static const char* BOOST_PP_CAT(name, _strings[] = ) { \
BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(DEFINE_ENUM_VAL_STR, name, val_seq)) \
};
DEFINE_ENUM(E, (AAA)(BBB)(CCC))
(AAA)(BBB)(CCC)
is a Boost.Preprocessor sequence of tree elements AAA, BBB and CCC; the macro append the enum name to it's modalities:
(AAA)(BBB)(CCC)
是树元素 AAA、BBB 和 CCC 的 Boost.Preprocessor 序列;宏将枚举名称附加到它的模态:
enum E { E_AAA, E_BBB, E_CCC };
static const char* E_strings[] = { "AAA", "BBB", "CCC" };
回答by Chris Pitman
One way to do this is with X-Macros, which are basically a way to define a macro which is then used for generating more complex structures than a simple macro easily allows. Here is an exampleof doing exactly what you are asking.
一种方法是使用X-Macros,它基本上是一种定义宏的方法,然后用于生成比简单宏容易允许的更复杂的结构。 这是一个完全按照您的要求执行的示例。
回答by gg99
I am a bit late to the party but here is another suggestion.
It creates a strongly typed enum class, say MyEnumName
and a companion static helper class Enumator<MyEnumName>
.
It's bigger than previous answers as it has more features, e.g. stream operators for conversion from/to string.
Note that it relies on c++ 14 standard due to the use of index sequence.
我参加聚会有点晚了,但这里有另一个建议。
它创建了一个强类型枚举类,比如MyEnumName
和一个伴随的静态帮助器类Enumator<MyEnumName>
。
它比以前的答案更大,因为它具有更多功能,例如用于从/到字符串转换的流运算符。
请注意,由于使用索引序列,它依赖于 c++ 14 标准。
Usage:
用法:
/* One line definition - no redundant info */
ENUM_DEFINE(WeekDay /*first item is enum name*/,
Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday);
/* works seemlessly with streams (good for logging) */
auto dayOne = WeekDay::Sunday;
std::cout << "day of week is: " << day_of_week;
/* explicit construction from string using WeekDay_enum companion class*/
auto dayTwo = Enumator<WeekDay>::fromString("Tuesday");
/*Iterate over all enum values using Enumator<WeekDay> companion class*/
std::cout << "Days of the week are:\n"
for (auto enumVal : Enumator<WeekDay>::getValues()) {
std::cout << enumVal << "\n";
}
Source:
来源:
#include <array>
#include <string>
#include <sstream>
#include <stdexcept>
template<typename E>
using isEnum = typename std::enable_if<std::is_enum<E>::value>::type;
template<typename E, typename = isEnum<E>>
constexpr static int enumSize() {
return 0;
}
template<typename E, typename = isEnum<E>>
inline static std::string getEnumStringValues() {
return "";
}
/*Enum companion class to hold the methods that can't be declared in an enum*/
template<typename EnumType, isEnum<EnumType>* = nullptr>
class Enumator
{
Enumator() = delete; /* prevents instantiation */
public:
constexpr static int size() {
return enumSize<EnumType>();
}
/* list of all enum values a string */
static auto const& getValuesStr()
{
static std::array<std::string, size()> values;
if (values[0].empty()) {
std::string valuesStr = getEnumStringValues<EnumType>();
std::stringstream ss(valuesStr);
for (auto& value : values) {
std::getline(ss, value, ',');
}
}
return values;
};
/* list of all enum values */
static auto const& getValues()
{
static std::array<EnumType, size()> values{ make_array(std::make_index_sequence<size()>()) };
return values;
};
/* To/from string conversion */
constexpr static std::string const& toString(EnumType arg) {
return getValuesStr()[static_cast<unsigned>(arg)];
}
static EnumType fromString(std::string const& val)
{
/* Attempt at converting from string value */
auto const& strValues = getValuesStr();
for (unsigned int i = 0; i < strValues.size(); i++)
{
if (val == strValues[i])
{
return static_cast<EnumType>(i);
}
}
throw std::runtime_error("No matching enum value found for token: " + val);
}
private:
/* Helper method to initialize array of enum values */
template<std::size_t...Idx>
static auto make_array(std::index_sequence<Idx...>)
{
return std::array<EnumType, size()>{{static_cast<EnumType>(Idx)...}};
}
};
template<typename EnumType, isEnum<EnumType>* = nullptr>
inline std::istream& operator>> (std::istream& input, EnumType& arg)
{
std::string val;
input >> val;
arg = Enumator<EnumType>::fromString(val);
return input;
}
template<typename EnumType, isEnum<EnumType>* = nullptr>
inline std::ostream& operator<< (std::ostream& output, const EnumType& arg)
{
return output << Enumator<EnumType>::toString(arg);
}
#define ENUM_DEFINE(EnumName,...)\
\
enum class EnumName;\
\
template<>\
constexpr int enumSize<EnumName>() {\
/*Trick to get the number of enum members:*/\
/*dump all the enum values in an array and compute its size */\
enum EnumName { __VA_ARGS__ }; \
EnumName enumArray[]{ __VA_ARGS__ }; \
return sizeof(enumArray) / sizeof(enumArray[0]); \
}\
\
template<>\
inline std::string getEnumStringValues<EnumName>() { return #__VA_ARGS__; }\
\
enum class EnumName : int { __VA_ARGS__ }
回答by Lindydancer
One way to handle this is to define a list macro, i.e. something that expands to another macro that is left for the user to define. For example:
处理这个问题的一种方法是定义一个列表宏,即扩展到另一个留给用户定义的宏的东西。例如:
#define MY_LIST MY_ENTRY(AAA) MY_ENTRY(BBB) MY_ENTRY(CCC)
To define the enum
:
定义enum
:
#define MY_ENTRY(x) E_##x,
enum name
{
MY_LIST
NUMBER_OF_ELEMENTS /* Needed to eat trailing comma (not needed in C99, but in C++) */
};
#undef MY_ENTRY
To define the string:
定义字符串:
#define MY_ENTRY(x) #x,
static const char *strings[] = { MY_LIST };
#undef MY_ENTRY
Personally, I find this much easier to work with than the X
macro, as this does not rely in include-file magic.
就个人而言,我发现这比X
宏更容易使用,因为它不依赖于包含文件魔法。
回答by Josh Kelley
For a simple solution, I'd recommend something like X-Macros.
对于一个简单的解决方案,我会推荐类似X-Macros 的东西。
For a more complex solution that adds several other features (like range checking, enhanced type safety, optional associated data, etc.), there's a proposed (but never finalized) Boost.Enum library.
对于添加其他一些功能(如范围检查、增强的类型安全、可选的关联数据等)的更复杂的解决方案,有一个提议的(但从未最终确定的)Boost.Enum 库。