使用预处理器从C ++中的字符串中获取枚举

时间:2015-07-24 09:41:56

标签: c++ enums c-preprocessor

有很多解决方案如何使用C ++ proprocessor(例如this topic)将枚举的文本表示形式化为字符串。

但是,我想在相反的方向进行转换,以模仿字符串上的开关。

所以例如 - 给出以下枚举:

typedef enum { RIGHT, LEFT, FORWARD, BACKWARD} direction;

对于小枚举,可以定义字符串数组并使用枚举值来获取相应的字符串,但这对于维护是不利的。

我想要一些宏来定义一个函数"方向FromString(char * str)"如果我有一个字符串" RIGHT"将返回RIGHT。 / p>

其他编程语言有很多解决方案,例如C#Java,所以我认为这不是一种糟糕的做法。

我可以使用similar question,但不使用预处理器。

我目前的溶剂(基于one of the answers to another question)如下所示:

#include <boost/preprocessor.hpp>

#define X_DEFINE_ENUM_WITH_STRING_CONVERSIONS_TOENUM(r, data, elem)          \
    if (!strcmp(s, BOOST_PP_STRINGIZE( elem ))) return elem;

#define DEFINE_ENUM_WITH_STRING_CONVERSIONS(name, enumerators)               \
    enum name {                                                              \
        BOOST_PP_SEQ_ENUM(enumerators)                                       \
    };                                                                       \
    inline const name FromString(char * s) {                                 \
         BOOST_PP_SEQ_FOR_EACH(                                              \
             X_DEFINE_ENUM_WITH_STRING_CONVERSIONS_TOENUM,                   \
             name,                                                           \
             enumerators                                                     \
         )                                                                   \
      return ERR;                                                            \
  }

可以通过以下方式使用它:

   DEFINE_ENUM_WITH_STRING_CONVERSIONS(direction, (RIGHT)(LEFT)(...)(ERR))

缺点是当字符串与任何枚举名称都不匹配时,必须包含ERR来处理这种情况。怎么避免这个?

我在想,可能会使用更优雅的溶剂 Boost Bimap

5 个答案:

答案 0 :(得分:1)

如果使用X-macros定义枚举,则可以轻松生成实现切换的函数:

#include <string>

#define MOUSE_BUTTONS \
X(LeftButton, 1)   \
X(MiddleButton, 2) \
X(RightButton, 4)

enum MouseButton {
    None = 0
#define X(name, value) ,name = value
MOUSE_BUTTONS
#undef X
};

static MouseButton stringAsMouseButton( const std::string &s )
{
#define X(name, value) if ( s == #name ) return name;
MOUSE_BUTTONS
#undef X
    return None;
}

答案 1 :(得分:1)

你可以

#define stringify(name) # name

direction FromString(char * s) {
    if (!strcmp(s, stringify(RIGHT)) return RIGHT;
    if (!strcmp(s, stringify(LEFT)) return LEFT;
    if (!strcmp(s, stringify(FORWARD)) return FORWARD;
    if (!strcmp(s, stringify(BACKWARD)) return BACKWARD;
    return -1;
}

答案 2 :(得分:1)

您可以尝试使用此库(免责声明 - 我是作者):https://github.com/aantron/better-enums

相关方法是_from_string(链接到文档)。如果出现错误,该方法会引发异常。还有另一种returns an option方法,类似于boost::optional

如果您想要实现自己的版本,请在Stack Overflow上的this answer中说明该方法。该答案中的代码包含_from_string的实现。

答案 3 :(得分:0)

您可以创建一个地图,如下所示:

#define RIGHT_DIR "RIGHT"
#define LEFT_DIR "LEFT"

_directionActionMap[RIGHT_DIR] = RIGHT;
_directionActionMap[LEFT_DIR] = LEFT;
...

然后当你想从字符串

中检索枚举时
int GetDirectionENUMFromString(const string& str)
{
  int directionEnum;

  std::map<string,int>::iterator iter;

  iter = _directionActionMap.find(str);

  if (iter != _directionActionMap.end())
  {
      directionEnum = (*iter).second;
  }
  else
  {
     directionEnum = -1;
  }

  return directionEnum; 
}

用法:

int directionEnum = GetDirectionENUMFromString("RIGHT");

switch( directionEnum )
{
  case RIGHT:
   break;
  case LEFT:
   break;
  ...
}

答案 4 :(得分:0)

我通常使用数组来存储字符串(请求C ++ 11)

enum colors : int
{
    RED,
    GREEN,
    BLUE,
    MAX // MAX must be always at the end
};

static const char* const colorsString[] =
{
    "RED",
    "GREEN",
    "BLUE",
};

// Ensure your two "lists" size are matching
static_assert(MAX == sizeof(colorsString) / sizeof(colorsString[0]), "Colors must have the same number of elements.");