用于创建字符串数组的 C++ 宏
C++ Macro to create a string array
有没有办法用预处理器宏创建 std::string
(或 char*
)数组?
像这样:
std::string myStrings[] = {MAGIC_MACRO(a, b, c)};
结果:
std::string myStrings[] = {"a", "b", "c"}
我知道它看起来毫无意义,但我需要在具有可变数量参数的更复杂的宏中使用它
简单的解决方案是为每个不同的计数设置一个单独的宏。使用 2 级 "stringify" 宏模式(阅读更多相关信息 here)你可以这样做:
#include <iostream>
#include <sstream>
#define XSTRINGIFY(s) #s
#define STRINGARRAY1(s0) { XSTRINGIFY(s0) }
#define STRINGARRAY2(s0, s1) { XSTRINGIFY(s0), XSTRINGIFY(s1) }
#define STRINGARRAY3(s0, s1, s2) { XSTRINGIFY(s0), XSTRINGIFY(s1), XSTRINGIFY(s2) }
using namespace std;
string dumpStrings(string *array, int count) {
stringstream ss;
if (count > 0) {
ss << '"' << array[0] << '"';
for(int i = 1; i < count; ++i) {
ss << ", \"" << array[i]<< '"';
}
}
return ss.str();
}
int main()
{
string strings1[1] = STRINGARRAY1(a);
string strings2[2] = STRINGARRAY2(a, b);
string strings3[3] = STRINGARRAY3(a, b, c);
cout << "strings1: " << dumpStrings(strings1, sizeof(strings1) / sizeof(strings1[0])) << endl;
cout << "strings2: " << dumpStrings(strings2, sizeof(strings2) / sizeof(strings2[0])) << endl;
cout << "strings3: " << dumpStrings(strings3, sizeof(strings3) / sizeof(strings3[0])) << endl;
}
输出:
strings1: "a"
strings2: "a", "b"
strings3: "a", "b", "c"
如果你只想要一个带有可变数量参数的宏,它会变得有点混乱,如其他答案所示。
也许有更有效的方法,但您可以简单地使用 Boost.PP:
#define MAGIC_MACRO_ELEM(r, data, i, elem) \
BOOST_PP_COMMA_IF(i) BOOST_PP_STRINGIZE(elem)
#define MAGIC_MACRO(...) \
BOOST_PP_SEQ_FOR_EACH_I(MAGIC_MACRO_ELEM, ~, BOOST_PP_TUPLE_TO_SEQ((__VA_ARGS__)))
我不确定这是否符合您要实现的目标,但我通常使用 this technique 生成字符串(或从字符串生成的其他列表,如枚举元素)。
例如,
#define FOREACH_APPLY(GENERATE) \
GENERATE(a) \
GENERATE(b) \
GENERATE(c)
#define GENERATE_STRING(STRING) #STRING,
std::string myStrings[] = {
FOREACH_APPLY(GENERATE_STRING)
};
下面的代码使用最多 1024 个参数和 不使用 使用额外的东西如 boost。它定义了一个 EVAL(...)
和一个 MAP(m, first, ...)
宏来进行递归,并在每次迭代中使用带有下一个参数 first
.
的宏 m
使用它,您的 MAGIC_MACRO(...)
看起来像:#define MAGIC_MACRO(...) EVAL(MAP(STRINGIZE, __VA_ARGS__))
。
大部分是抄自C Pre-Processor Magic. It is also great explained there. You can also download these helper macros like EVAL(...)
at this git repository,实际代码中也有很多解释。它是可变的,所以它需要你想要的参数数量。
但我更改了 FIRST
和 SECOND
宏,因为它使用 Gnu 扩展,就像我从中复制它的源代码中一样。
主要功能部分:
int main()
{
std::string myStrings[] = { MAGIC_MACRO(a, b, c) }; // Expands to: std::string myStrings[] = { "a" , "b" , "c" };
std::string myStrings[] = { MAGIC_MACRO(a, b, c, x, y, z) }; // Expands to: std::string myStrings[] = { "a" , "b" , "c", "x" , "y" , "z" };
}
宏定义:
#define FIRST_(a, ...) a
#define SECOND_(a, b, ...) b
#define FIRST(...) FIRST_(__VA_ARGS__,)
#define SECOND(...) SECOND_(__VA_ARGS__,)
#define EMPTY()
#define EVAL(...) EVAL1024(__VA_ARGS__)
#define EVAL1024(...) EVAL512(EVAL512(__VA_ARGS__))
#define EVAL512(...) EVAL256(EVAL256(__VA_ARGS__))
#define EVAL256(...) EVAL128(EVAL128(__VA_ARGS__))
#define EVAL128(...) EVAL64(EVAL64(__VA_ARGS__))
#define EVAL64(...) EVAL32(EVAL32(__VA_ARGS__))
#define EVAL32(...) EVAL16(EVAL16(__VA_ARGS__))
#define EVAL16(...) EVAL8(EVAL8(__VA_ARGS__))
#define EVAL8(...) EVAL4(EVAL4(__VA_ARGS__))
#define EVAL4(...) EVAL2(EVAL2(__VA_ARGS__))
#define EVAL2(...) EVAL1(EVAL1(__VA_ARGS__))
#define EVAL1(...) __VA_ARGS__
#define DEFER1(m) m EMPTY()
#define DEFER2(m) m EMPTY EMPTY()()
#define IS_PROBE(...) SECOND(__VA_ARGS__, 0)
#define PROBE() ~, 1
#define CAT(a,b) a ## b
#define NOT(x) IS_PROBE(CAT(_NOT_, x))
#define _NOT_0 PROBE()
#define BOOL(x) NOT(NOT(x))
#define IF_ELSE(condition) _IF_ELSE(BOOL(condition))
#define _IF_ELSE(condition) CAT(_IF_, condition)
#define _IF_1(...) __VA_ARGS__ _IF_1_ELSE
#define _IF_0(...) _IF_0_ELSE
#define _IF_1_ELSE(...)
#define _IF_0_ELSE(...) __VA_ARGS__
#define COMMA ,
#define HAS_ARGS(...) BOOL(FIRST(_END_OF_ARGUMENTS_ __VA_ARGS__)())
#define _END_OF_ARGUMENTS_() 0
#define MAP(m, first, ...) \
m(first) \
IF_ELSE(HAS_ARGS(__VA_ARGS__))( \
COMMA DEFER2(_MAP)()(m, __VA_ARGS__) \
)( \
/* Do nothing, just terminate */ \
)
#define _MAP() MAP
#define STRINGIZE(x) #x
#define MAGIC_MACRO(...) EVAL(MAP(STRINGIZE, __VA_ARGS__))
有没有办法用预处理器宏创建 std::string
(或 char*
)数组?
像这样:
std::string myStrings[] = {MAGIC_MACRO(a, b, c)};
结果:
std::string myStrings[] = {"a", "b", "c"}
我知道它看起来毫无意义,但我需要在具有可变数量参数的更复杂的宏中使用它
简单的解决方案是为每个不同的计数设置一个单独的宏。使用 2 级 "stringify" 宏模式(阅读更多相关信息 here)你可以这样做:
#include <iostream>
#include <sstream>
#define XSTRINGIFY(s) #s
#define STRINGARRAY1(s0) { XSTRINGIFY(s0) }
#define STRINGARRAY2(s0, s1) { XSTRINGIFY(s0), XSTRINGIFY(s1) }
#define STRINGARRAY3(s0, s1, s2) { XSTRINGIFY(s0), XSTRINGIFY(s1), XSTRINGIFY(s2) }
using namespace std;
string dumpStrings(string *array, int count) {
stringstream ss;
if (count > 0) {
ss << '"' << array[0] << '"';
for(int i = 1; i < count; ++i) {
ss << ", \"" << array[i]<< '"';
}
}
return ss.str();
}
int main()
{
string strings1[1] = STRINGARRAY1(a);
string strings2[2] = STRINGARRAY2(a, b);
string strings3[3] = STRINGARRAY3(a, b, c);
cout << "strings1: " << dumpStrings(strings1, sizeof(strings1) / sizeof(strings1[0])) << endl;
cout << "strings2: " << dumpStrings(strings2, sizeof(strings2) / sizeof(strings2[0])) << endl;
cout << "strings3: " << dumpStrings(strings3, sizeof(strings3) / sizeof(strings3[0])) << endl;
}
输出:
strings1: "a"
strings2: "a", "b"
strings3: "a", "b", "c"
如果你只想要一个带有可变数量参数的宏,它会变得有点混乱,如其他答案所示。
也许有更有效的方法,但您可以简单地使用 Boost.PP:
#define MAGIC_MACRO_ELEM(r, data, i, elem) \
BOOST_PP_COMMA_IF(i) BOOST_PP_STRINGIZE(elem)
#define MAGIC_MACRO(...) \
BOOST_PP_SEQ_FOR_EACH_I(MAGIC_MACRO_ELEM, ~, BOOST_PP_TUPLE_TO_SEQ((__VA_ARGS__)))
我不确定这是否符合您要实现的目标,但我通常使用 this technique 生成字符串(或从字符串生成的其他列表,如枚举元素)。
例如,
#define FOREACH_APPLY(GENERATE) \
GENERATE(a) \
GENERATE(b) \
GENERATE(c)
#define GENERATE_STRING(STRING) #STRING,
std::string myStrings[] = {
FOREACH_APPLY(GENERATE_STRING)
};
下面的代码使用最多 1024 个参数和 不使用 使用额外的东西如 boost。它定义了一个 EVAL(...)
和一个 MAP(m, first, ...)
宏来进行递归,并在每次迭代中使用带有下一个参数 first
.
m
使用它,您的 MAGIC_MACRO(...)
看起来像:#define MAGIC_MACRO(...) EVAL(MAP(STRINGIZE, __VA_ARGS__))
。
大部分是抄自C Pre-Processor Magic. It is also great explained there. You can also download these helper macros like EVAL(...)
at this git repository,实际代码中也有很多解释。它是可变的,所以它需要你想要的参数数量。
但我更改了 FIRST
和 SECOND
宏,因为它使用 Gnu 扩展,就像我从中复制它的源代码中一样。
主要功能部分:
int main()
{
std::string myStrings[] = { MAGIC_MACRO(a, b, c) }; // Expands to: std::string myStrings[] = { "a" , "b" , "c" };
std::string myStrings[] = { MAGIC_MACRO(a, b, c, x, y, z) }; // Expands to: std::string myStrings[] = { "a" , "b" , "c", "x" , "y" , "z" };
}
宏定义:
#define FIRST_(a, ...) a
#define SECOND_(a, b, ...) b
#define FIRST(...) FIRST_(__VA_ARGS__,)
#define SECOND(...) SECOND_(__VA_ARGS__,)
#define EMPTY()
#define EVAL(...) EVAL1024(__VA_ARGS__)
#define EVAL1024(...) EVAL512(EVAL512(__VA_ARGS__))
#define EVAL512(...) EVAL256(EVAL256(__VA_ARGS__))
#define EVAL256(...) EVAL128(EVAL128(__VA_ARGS__))
#define EVAL128(...) EVAL64(EVAL64(__VA_ARGS__))
#define EVAL64(...) EVAL32(EVAL32(__VA_ARGS__))
#define EVAL32(...) EVAL16(EVAL16(__VA_ARGS__))
#define EVAL16(...) EVAL8(EVAL8(__VA_ARGS__))
#define EVAL8(...) EVAL4(EVAL4(__VA_ARGS__))
#define EVAL4(...) EVAL2(EVAL2(__VA_ARGS__))
#define EVAL2(...) EVAL1(EVAL1(__VA_ARGS__))
#define EVAL1(...) __VA_ARGS__
#define DEFER1(m) m EMPTY()
#define DEFER2(m) m EMPTY EMPTY()()
#define IS_PROBE(...) SECOND(__VA_ARGS__, 0)
#define PROBE() ~, 1
#define CAT(a,b) a ## b
#define NOT(x) IS_PROBE(CAT(_NOT_, x))
#define _NOT_0 PROBE()
#define BOOL(x) NOT(NOT(x))
#define IF_ELSE(condition) _IF_ELSE(BOOL(condition))
#define _IF_ELSE(condition) CAT(_IF_, condition)
#define _IF_1(...) __VA_ARGS__ _IF_1_ELSE
#define _IF_0(...) _IF_0_ELSE
#define _IF_1_ELSE(...)
#define _IF_0_ELSE(...) __VA_ARGS__
#define COMMA ,
#define HAS_ARGS(...) BOOL(FIRST(_END_OF_ARGUMENTS_ __VA_ARGS__)())
#define _END_OF_ARGUMENTS_() 0
#define MAP(m, first, ...) \
m(first) \
IF_ELSE(HAS_ARGS(__VA_ARGS__))( \
COMMA DEFER2(_MAP)()(m, __VA_ARGS__) \
)( \
/* Do nothing, just terminate */ \
)
#define _MAP() MAP
#define STRINGIZE(x) #x
#define MAGIC_MACRO(...) EVAL(MAP(STRINGIZE, __VA_ARGS__))