如何轻松地将 c 枚举映射到字符串

新手上路,请多包涵

我正在使用的一些库头文件中有一堆枚举类型,我想要一种将枚举值转换为用户字符串的方法 - 反之亦然。

RTTI 不会为我这样做,因为“用户字符串”需要比枚举更具可读性。

蛮力解决方案将是一堆这样的函数,但我觉得这有点太像 C 了。

 enum MyEnum {VAL1, VAL2,VAL3};

String getStringFromEnum(MyEnum e)
{
  switch e
  {
  case VAL1: return "Value 1";
  case VAL2: return "Value 2";
  case VAL1: return "Value 3";
  default: throw Exception("Bad MyEnum");
  }
}

我有一种直觉,认为有一个使用模板的优雅解决方案,但我还不能完全理解它。

更新: 感谢您的建议-我应该明确说明枚举是在第三方库标头中定义的,所以我不想更改它们的定义。

我现在的直觉是避免使用模板并执行以下操作:

 char * MyGetValue(int v, char *tmp); // implementation is trivial

#define ENUM_MAP(type, strings) char * getStringValue(const type &T) \
 { \
 return MyGetValue((int)T, strings); \
 }

; enum eee {AA,BB,CC}; - exists in library header file
; enum fff {DD,GG,HH};

ENUM_MAP(eee,"AA|BB|CC")
ENUM_MAP(fff,"DD|GG|HH")

// To use...

    eee e;
    fff f;
    std::cout<< getStringValue(e);
    std::cout<< getStringValue(f);

原文由 Roddy 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.3k
1 个回答

这是我的解决方案,我参考了其他一些设计,但我的更完整和使用更简单。

 // file: enum_with_string.h
#pragma once

#include <map>
#include <string>
#include <vector>

namespace EnumString {

template <typename T>
static inline void split_string_for_each(const std::string &str,
                                         const std::string &delimiter,
                                         const T &foreach_function,
                                         ssize_t max_number = -1) {
  ssize_t num = 0;
  std::string::size_type start;
  std::string::size_type end = -1;
  while (true) {
    start = str.find_first_not_of(delimiter, end + 1);
    if (start == std::string::npos) break;  // over

    end = str.find_first_of(delimiter, start + 1);

    if (end == std::string::npos) {
      foreach_function(num, str.substr(start));
      break;
    }
    foreach_function(num, str.substr(start, end - start));
    ++num;

    if (max_number > 0 && num == max_number) break;
  }
}

/**
 * Strip function, delete the specified characters on both sides of the string.
 */
inline std::string &strip(std::string &s,
                          const std::string &characters = " \t\r\n") {
  s.erase(0, s.find_first_not_of(characters));
  return s.erase(s.find_last_not_of(characters) + 1);
}

static inline std::map<int, std::string> ParserEnumDefine(
    const std::string &define_str) {
  int cur_num = 0;
  std::string cur_item_str;
  std::map<int, std::string> result_map;
  split_string_for_each(define_str, ",", [&](int num, const std::string &str) {
    split_string_for_each(
        str, "=",
        [&](int num, const std::string &str) {
          if (num == 0) cur_item_str = str;
          if (num == 1) cur_num = std::stoi(str);
        },
        2);
    result_map.emplace(cur_num, strip(cur_item_str));
    cur_num++;
  });
  return result_map;
}

}  // namespace EnumString

/**
 * Example:
 * @code
 * @endcode
 */
#define ENUM_WITH_STRING(Name, ...)                                     \
  enum class Name { __VA_ARGS__, __COUNT };                             \
  static inline const std::string &to_string(Name value) {              \
    static const auto map = EnumString::ParserEnumDefine(#__VA_ARGS__); \
    static const std::string cannot_converted =                         \
        "Cannot be converted to string";                                \
    int int_value = (int)value;                                         \
    if (map.count(int_value))                                           \
      return map.at(int_value);                                         \
    else                                                                \
      return cannot_converted;                                          \
  }

你可以像这样使用它:

 #include <iostream>
#include "enum_with_string.h"
ENUM_WITH_STRING(Animal, dog, cat, monkey = 50, fish, human = 100, duck)
int main() {
  std::cout << to_string(Animal::dog) << std::endl;
  std::cout << to_string(Animal::cat) << std::endl;
  std::cout << to_string(Animal::monkey) << std::endl;
  std::cout << to_string(Animal::fish) << std::endl;
  std::cout << to_string(Animal::human) << std::endl;
  std::cout << to_string(Animal::duck) << std::endl;
}

我有一个 github 要点

原文由 fengshun 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题