在 C++ 预处理器宏中扩展 __VA_ARGS__ 的 var 名称
Expanding var names of __VA_ARGS__ in C++ preprocessor macro
我正在尝试实现一个适当的 enum to str 宏,它将在代码中自动关联枚举值和它们的名称。
例如,我想像这样定义一个名为“Test”的新宏:
ENUM_STR_DECLARE(Test, 1, test1, test2, test3)
这样,通过调用 TestEnumToString(test2)
我可以访问字符串 "test2"
。目前,我目前对这个 ENUM_STR_DECLARE
的实现是这样完成的:
#define ENUM_STR_DECLARE(enumName, intOffset, v1, ...)\
enum enumName { v1 = intOffset, __VA_ARGS__};\
const char *enumName##EnumStringArray[] = { #v1 ,#__VA_ARGS__};\
const char *enumName##EnumToString(value) { return enumName##EnumStringArray[ value - offset ]; }
虽然当我这样做时,__VA_ARGS__
处理的枚举没有得到正确的名称分隔:
enum Test {
test1 = 1, test2, test3
};
const char *TestEnumStringArray[] = {"test1", "test2, test3"};
const char *TestEnumToString(value) { return TestEnumStringArray[ value - offset]; }
因为我想要 "test2", "test3"
而不是 "test2, test3"
。
有什么方法可以用逗号分隔扩展 #__VA_ARGS__
名称吗?
干杯!
编辑:解决方案
这个问题似乎没有 easy/elegant 解决方案。迄今为止最好的实现是由@h-walters 提出的,它提供了一个非常详细的实现分解(感谢您的时间!)。这是他为感兴趣的人分享的link:
H. Walters example.
然而,即使是这个实现似乎也必须限制您定义的枚举数量(在他的例子中是 9 个)。所以解决方法是我的最终答案。
与其苦苦寻找切片方式 __VA_ARGS__
,不如像我一样简单地对整个 __VA_ARGS__
进行字符串化,然后在每次我们想要获取特定名称时对获得的字符串进行切片枚举。显然,这是一种较慢的方法,因为它需要每次都解析整个字符串,而不是直接使用枚举索引获取。
这个速度问题与我无关,因为枚举到字符串的转换旨在用于显示目的,否则枚举将直接用于耗时的例程。如果您仍然希望在获取字符串翻译时更快,您可以利用缓存(例如 std::vector<std::string>
),它可以在您第一次调用 toString() 方法时构建。或者你可以选择@h-walters 解决方案,这是最快的,知道它的局限性。
说得够多了,这是我最终得到的解决方案:
#define VA_TO_STR(...) #__VA_ARGS__
#define ENUM_STR_DECLARE(enumName_, intOffset_, v1_, ...)\
enum enumName_ { v1_ = intOffset_, __VA_ARGS__, enumName_##_OVERFLOW };\
namespace enumName_##EnumNamespace{\
const char *enumNamesAgregate = VA_TO_STR(v1_, __VA_ARGS__); \
int enumOffSet = intOffset_; \
std::string toString(int enumValue_) {\
int commaCounter = 0; std::string outStr;\
for( unsigned long iChar = 0 ; iChar < strlen(enumNamesAgregate) ; iChar++ ){ \
if( enumNamesAgregate[iChar] == ',' ){\
if( not outStr.empty() ){ return outStr; } /* found! return */\
else{ commaCounter++; iChar += 2; } /* not yet found, next */ \
}\
if( commaCounter == enumValue_ ){ outStr += enumNamesAgregate[iChar]; }\
}\
return outStr;\
}\
std::string toString(enumName_ enumValue_){ return enumName_##EnumNamespace::toString(static_cast<int>(enumValue_)); }\
int toEnumInt(const std::string& enumStr_){\
for( int enumIndex = intOffset_ ; enumIndex < enumName_::enumName_##_OVERFLOW ; enumIndex++ ){ \
if( enumName_##EnumNamespace::toString(enumIndex) == enumStr_ ){ return enumIndex; } \
}\
return intOffset_ - 1; /* returns invalid value */\
}\
enumName_ toEnum(const std::string& enumStr_){ return static_cast<enumName_>(enumName_##EnumNamespace::toEnumInt(enumStr_)); }\
}
因此,我上面给出的简单示例将展开为:
enum Test {
test1 = 1, test2, test3, Test_OVERFLOW
};
namespace TestEnumNamespace {
const char *enumNamesAgregate = "test1, test2, test3";
int enumOffSet = 1;
std::string toString(int enumValue_) {
int commaCounter = 0;
std::string outStr;
for (unsigned long iChar = 0; iChar < strlen(enumNamesAgregate); iChar++) {
if (enumNamesAgregate[iChar] == ',') {
if (notoutStr.empty()) { return outStr; }
else {
commaCounter++;
iChar += 2;
}
}
if (commaCounter == enumValue_) { outStr += enumNamesAgregate[iChar]; }
}
return outStr;
}
std::string toString(Test enumValue_) { return TestEnumNamespace::toString(static_cast<int>(enumValue_)); }
int toEnumInt(const std::string &enumStr_) {
for (int enumIndex = 1; enumIndex < Test::Test_OVERFLOW; enumIndex++) {
if (TestEnumNamespace::toString(enumIndex) == enumStr_) { return enumIndex; }
}
return 1 - 1;
}
Test toEnum(const std::string &enumStr_) { return static_cast<Test>(TestEnumNamespace::toEnumInt(enumStr_)); }
}
假设,如果您想获取枚举“test1”的字符串:
TestEnumNamespace::toString(Test::test1);
TestEnumNamespace::toEnum("test1");
希望它能帮助其他开发者:)。
再次欢呼!
#include <boost/preprocessor/tuple.hpp>
#include <boost/preprocessor/seq.hpp>
#include <boost/preprocessor/stringize.hpp>
#define MACRO_DELIM(r, data, elem) BOOST_PP_STRINGIZE(elem) ,
#define DELIMITED_STRINGIZE(...) \
BOOST_PP_SEQ_FOR_EACH(MACRO_DELIM, _, \
BOOST_PP_TUPLE_TO_SEQ((__VA_ARGS__)))
#define ENUM_STR_DECLARE(enumName, intOffset, v1, ...)\
enum enumName { v1 = intOffset, __VA_ARGS__};\
const char *enumName##EnumStringArray[] = { DELIMITED_STRINGIZE(v1, __VA_ARGS__)};\
const char *enumName##EnumToString(value) { return enumName##EnumStringArray[ value - offset ]; }
ENUM_STR_DECLARE(Test, 1, test1, test2, test3)
演示:
不完全是解决方案,而是解决方法,缺点是您必须释放每个枚举:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const char** explode(const char* src, size_t & out_size);
#define CAT(...) #__VA_ARGS__
#define ENUM_STR_DECLARE(enumName, intOffset, v1, ...)\
enum enumName { v1 = intOffset, __VA_ARGS__};\
const char *enumName##EnumString = CAT(v1, __VA_ARGS__);\
size_t enumName##EnumStringArraySize = 0;\
const char **enumName##EnumStringArray = explode(enumName##EnumString, enumName##EnumStringArraySize);\
const char *enumName##EnumToString(size_t value) { return enumName##EnumStringArray[ value - intOffset ]; }\
void free##enumName() {\
for (size_t i = 0; i < enumName##EnumStringArraySize; ++i) {\
free((char *)enumName##EnumStringArray[i]);\
}\
free(enumName##EnumStringArray);\
}
ENUM_STR_DECLARE(Test, 3, test1, test2, test3, test4, test5, test6)
int main() {
printf("test1: %s, test3: %s, test6: %s", TestEnumToString(test1), TestEnumToString(test3), TestEnumToString(test6));
freeTest();
return 0;
}
const char** explode(const char* src, size_t & out_size)
{
size_t size = strlen(src);
if (!size) return NULL;
int i = 0;
char* psrc = (char*)src;
for (; psrc[i]; (psrc[i] == ',' ? i++ : *psrc++));
char** dest = (char**)malloc(sizeof(char*) * ++i);
out_size = i;
char* srcCopy = (char*)malloc(size + 1);
memcpy(srcCopy, src, size);
srcCopy[size] = '[=10=]';
const char * d = ", [=10=]";
char* tok = strtok(srcCopy, d);
if (tok == NULL) {
// only one item
dest[0] = (char*)malloc(size + 1);
memcpy(dest[0], src, size);
dest[0][size] = '[=10=]';
}
else {
// first item
size = strlen(tok);
dest[0] = (char*)malloc(size + 1);
memcpy(dest[0], tok, size);
dest[0][size] = '[=10=]';
// rest
i = 1;
while ((tok = strtok(NULL, d)) != NULL) {
size = strlen(tok);
dest[i] = (char*)malloc(size + 1);
memcpy(dest[i], tok, size);
dest[i][size] = '[=10=]';
++i;
}
}
free(srcCopy);
return (const char**)dest;
}
我正在尝试实现一个适当的 enum to str 宏,它将在代码中自动关联枚举值和它们的名称。
例如,我想像这样定义一个名为“Test”的新宏:
ENUM_STR_DECLARE(Test, 1, test1, test2, test3)
这样,通过调用 TestEnumToString(test2)
我可以访问字符串 "test2"
。目前,我目前对这个 ENUM_STR_DECLARE
的实现是这样完成的:
#define ENUM_STR_DECLARE(enumName, intOffset, v1, ...)\
enum enumName { v1 = intOffset, __VA_ARGS__};\
const char *enumName##EnumStringArray[] = { #v1 ,#__VA_ARGS__};\
const char *enumName##EnumToString(value) { return enumName##EnumStringArray[ value - offset ]; }
虽然当我这样做时,__VA_ARGS__
处理的枚举没有得到正确的名称分隔:
enum Test {
test1 = 1, test2, test3
};
const char *TestEnumStringArray[] = {"test1", "test2, test3"};
const char *TestEnumToString(value) { return TestEnumStringArray[ value - offset]; }
因为我想要 "test2", "test3"
而不是 "test2, test3"
。
有什么方法可以用逗号分隔扩展 #__VA_ARGS__
名称吗?
干杯!
编辑:解决方案
这个问题似乎没有 easy/elegant 解决方案。迄今为止最好的实现是由@h-walters 提出的,它提供了一个非常详细的实现分解(感谢您的时间!)。这是他为感兴趣的人分享的link: H. Walters example.
然而,即使是这个实现似乎也必须限制您定义的枚举数量(在他的例子中是 9 个)。所以解决方法是我的最终答案。
与其苦苦寻找切片方式 __VA_ARGS__
,不如像我一样简单地对整个 __VA_ARGS__
进行字符串化,然后在每次我们想要获取特定名称时对获得的字符串进行切片枚举。显然,这是一种较慢的方法,因为它需要每次都解析整个字符串,而不是直接使用枚举索引获取。
这个速度问题与我无关,因为枚举到字符串的转换旨在用于显示目的,否则枚举将直接用于耗时的例程。如果您仍然希望在获取字符串翻译时更快,您可以利用缓存(例如 std::vector<std::string>
),它可以在您第一次调用 toString() 方法时构建。或者你可以选择@h-walters 解决方案,这是最快的,知道它的局限性。
说得够多了,这是我最终得到的解决方案:
#define VA_TO_STR(...) #__VA_ARGS__
#define ENUM_STR_DECLARE(enumName_, intOffset_, v1_, ...)\
enum enumName_ { v1_ = intOffset_, __VA_ARGS__, enumName_##_OVERFLOW };\
namespace enumName_##EnumNamespace{\
const char *enumNamesAgregate = VA_TO_STR(v1_, __VA_ARGS__); \
int enumOffSet = intOffset_; \
std::string toString(int enumValue_) {\
int commaCounter = 0; std::string outStr;\
for( unsigned long iChar = 0 ; iChar < strlen(enumNamesAgregate) ; iChar++ ){ \
if( enumNamesAgregate[iChar] == ',' ){\
if( not outStr.empty() ){ return outStr; } /* found! return */\
else{ commaCounter++; iChar += 2; } /* not yet found, next */ \
}\
if( commaCounter == enumValue_ ){ outStr += enumNamesAgregate[iChar]; }\
}\
return outStr;\
}\
std::string toString(enumName_ enumValue_){ return enumName_##EnumNamespace::toString(static_cast<int>(enumValue_)); }\
int toEnumInt(const std::string& enumStr_){\
for( int enumIndex = intOffset_ ; enumIndex < enumName_::enumName_##_OVERFLOW ; enumIndex++ ){ \
if( enumName_##EnumNamespace::toString(enumIndex) == enumStr_ ){ return enumIndex; } \
}\
return intOffset_ - 1; /* returns invalid value */\
}\
enumName_ toEnum(const std::string& enumStr_){ return static_cast<enumName_>(enumName_##EnumNamespace::toEnumInt(enumStr_)); }\
}
因此,我上面给出的简单示例将展开为:
enum Test {
test1 = 1, test2, test3, Test_OVERFLOW
};
namespace TestEnumNamespace {
const char *enumNamesAgregate = "test1, test2, test3";
int enumOffSet = 1;
std::string toString(int enumValue_) {
int commaCounter = 0;
std::string outStr;
for (unsigned long iChar = 0; iChar < strlen(enumNamesAgregate); iChar++) {
if (enumNamesAgregate[iChar] == ',') {
if (notoutStr.empty()) { return outStr; }
else {
commaCounter++;
iChar += 2;
}
}
if (commaCounter == enumValue_) { outStr += enumNamesAgregate[iChar]; }
}
return outStr;
}
std::string toString(Test enumValue_) { return TestEnumNamespace::toString(static_cast<int>(enumValue_)); }
int toEnumInt(const std::string &enumStr_) {
for (int enumIndex = 1; enumIndex < Test::Test_OVERFLOW; enumIndex++) {
if (TestEnumNamespace::toString(enumIndex) == enumStr_) { return enumIndex; }
}
return 1 - 1;
}
Test toEnum(const std::string &enumStr_) { return static_cast<Test>(TestEnumNamespace::toEnumInt(enumStr_)); }
}
假设,如果您想获取枚举“test1”的字符串:
TestEnumNamespace::toString(Test::test1);
TestEnumNamespace::toEnum("test1");
希望它能帮助其他开发者:)。
再次欢呼!
#include <boost/preprocessor/tuple.hpp>
#include <boost/preprocessor/seq.hpp>
#include <boost/preprocessor/stringize.hpp>
#define MACRO_DELIM(r, data, elem) BOOST_PP_STRINGIZE(elem) ,
#define DELIMITED_STRINGIZE(...) \
BOOST_PP_SEQ_FOR_EACH(MACRO_DELIM, _, \
BOOST_PP_TUPLE_TO_SEQ((__VA_ARGS__)))
#define ENUM_STR_DECLARE(enumName, intOffset, v1, ...)\
enum enumName { v1 = intOffset, __VA_ARGS__};\
const char *enumName##EnumStringArray[] = { DELIMITED_STRINGIZE(v1, __VA_ARGS__)};\
const char *enumName##EnumToString(value) { return enumName##EnumStringArray[ value - offset ]; }
ENUM_STR_DECLARE(Test, 1, test1, test2, test3)
演示:
不完全是解决方案,而是解决方法,缺点是您必须释放每个枚举:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const char** explode(const char* src, size_t & out_size);
#define CAT(...) #__VA_ARGS__
#define ENUM_STR_DECLARE(enumName, intOffset, v1, ...)\
enum enumName { v1 = intOffset, __VA_ARGS__};\
const char *enumName##EnumString = CAT(v1, __VA_ARGS__);\
size_t enumName##EnumStringArraySize = 0;\
const char **enumName##EnumStringArray = explode(enumName##EnumString, enumName##EnumStringArraySize);\
const char *enumName##EnumToString(size_t value) { return enumName##EnumStringArray[ value - intOffset ]; }\
void free##enumName() {\
for (size_t i = 0; i < enumName##EnumStringArraySize; ++i) {\
free((char *)enumName##EnumStringArray[i]);\
}\
free(enumName##EnumStringArray);\
}
ENUM_STR_DECLARE(Test, 3, test1, test2, test3, test4, test5, test6)
int main() {
printf("test1: %s, test3: %s, test6: %s", TestEnumToString(test1), TestEnumToString(test3), TestEnumToString(test6));
freeTest();
return 0;
}
const char** explode(const char* src, size_t & out_size)
{
size_t size = strlen(src);
if (!size) return NULL;
int i = 0;
char* psrc = (char*)src;
for (; psrc[i]; (psrc[i] == ',' ? i++ : *psrc++));
char** dest = (char**)malloc(sizeof(char*) * ++i);
out_size = i;
char* srcCopy = (char*)malloc(size + 1);
memcpy(srcCopy, src, size);
srcCopy[size] = '[=10=]';
const char * d = ", [=10=]";
char* tok = strtok(srcCopy, d);
if (tok == NULL) {
// only one item
dest[0] = (char*)malloc(size + 1);
memcpy(dest[0], src, size);
dest[0][size] = '[=10=]';
}
else {
// first item
size = strlen(tok);
dest[0] = (char*)malloc(size + 1);
memcpy(dest[0], tok, size);
dest[0][size] = '[=10=]';
// rest
i = 1;
while ((tok = strtok(NULL, d)) != NULL) {
size = strlen(tok);
dest[i] = (char*)malloc(size + 1);
memcpy(dest[i], tok, size);
dest[i][size] = '[=10=]';
++i;
}
}
free(srcCopy);
return (const char**)dest;
}