在 C++ 预处理器宏中扩展 __VA_ARGS__ 的 var 名称

问题描述

我正在尝试为 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,test3
};
const char *TestEnumStringArray[] = {"test1","test2,test3"};
const char *TestEnumToString(value) { return TestEnumStringArray[ value - offset]; }

因为我想要 "test2","test3" 而不是 "test2,test3"

有没有办法用逗号分隔扩展 #__VA_ARGS__ 名称?

干杯!

编辑:一个变通办法

这个问题似乎没有简单/优雅的解决方案。迄今为止最好的实现是由@h-walters 提出的,它提供了一个非常详细的实现分解(感谢您的时间!)。这是他为感兴趣的人分享的链接: H. Walters example

然而,即使这个实现似乎也必须限制您定义的枚举数量(在他的情况下为 9)。所以对我来说,解决方法是最终的答案。

与其费力寻找切片 __VA_ARGS__ 的方法,不如像我一样简单地将整个 __VA_ARGS__ 字符串化,并在每次我们想要获取特定枚举的名称时对获得的字符串进行切片。显然,这是一种较慢的方法,因为它每次都需要解析整个字符串,而不是直接使用枚举索引获取。

这个速度问题与我无关,因为枚举到字符串的转换旨在用于显示目的,否则枚举将直接用于耗时的例程。如果您在获得字符串翻译时仍然想要快速,您可以利用可以在您第一次调用 toString() 方法时构建的缓存(例如 std::vector<std::string> )。或者,您可以使用 @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,test3,Test_OVERFLOW
};
namespace TestEnumNamespace {
  const char *enumNamesAgregate = "test1,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");

希望它能帮助其他开发者:)

再次干杯!

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)