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

问题描述 投票:114回答:19

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

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");
  }
}

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

UPDATE:谢谢您的建议-我应该明确指出枚举是在第三方库头中定义的,因此我不想更改它们的定义。

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

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);
c++ templates enums
19个回答
59
投票

如果要枚举名称本身为字符串,请参见this post。否则,std::map<MyEnum, char const*>会很好地工作。 (将字符串文字复制到映射中的std :: strings没有意义)

对于额外的语法糖,这是编写map_init类的方法。目标是允许

std::map<MyEnum, const char*> MyMap;
map_init(MyMap)
    (eValue1, "A")
    (eValue2, "B")
    (eValue3, "C")
;

函数template <typename T> map_init(T&)返回一个map_init_helper<T>map_init_helper<T>存储T&,并定义平凡的map_init_helper& operator()(typename T::key_type const&, typename T::value_type const&)。 (从*this返回operator()可以链接operator(),就像operator<<上的std::ostream一样)

template<typename T> struct map_init_helper
{
    T& data;
    map_init_helper(T& d) : data(d) {}
    map_init_helper& operator() (typename T::key_type const& key, typename T::mapped_type const& value)
    {
        data[key] = value;
        return *this;
    }
};

template<typename T> map_init_helper<T> map_init(T& item)
{
    return map_init_helper<T>(item);
}

由于函数和帮助程序类是模板化的,因此您可以将它们用于任何地图或类似地图的结构。即它也可以将条目添加到std::unordered_map

如果您不喜欢编写这些帮助器,则boost :: assign提供了相同的功能。


2
投票

我多次使用此功能来调试/分析其他人的代码。为此,我编写了一个Perl脚本,该脚本生成带有多个重载Better Enums方法的类。每个toString方法都将toString作为参数并返回Enum


2
投票

您的回答启发了我自己编写一些宏。我的要求如下:


2
投票

这里是仅使用单行宏命令尝试自动使枚举中的<>流运算符...


1
投票

我只是想展示使用宏的这种可能的优雅解决方案。这不能解决问题,但我认为这是对问题进行反思的好方法。


1
投票
#define ENUM2STRUCTINFO(spacename,listname) namespace spacename { int spacename##_##values[] = {listname(enumfunc)};int spacename##_##N = sizeof(spacename##_##values)/sizeof(int);ENUM2STRTABLE(spacename##_##enum2str,listname)};

0
投票

在标题中:


0
投票

我最近在供应商库(Fincad)中遇到了相同的问题。幸运的是,供应商为所有枚举提供了xml双重注释。我最终为每种枚举类型生成了一个映射,并为每种枚举提供了查找功能。该技术还允许您拦截枚举范围之外的查找。


0
投票

查看以下语法是否适合您:


0
投票

这个旧的烂摊子是我根据SO的一点点努力做出的努力。 for_each将必须扩展为支持20个以上的枚举值。在Visual Studio 2019,clang和gcc上进行了测试。 c ++ 11


0
投票

我知道我要参加聚会很晚,但是对于其他所有访问此页面的人,您都可以尝试一下,它比那里的一切都容易,并且更有意义:


31
投票

MSalters解决方案是一个很好的解决方案,但基本上是重新实现的std::unordered_map。如果您有增强功能,则可以直接使用它:

boost::assign::map_list_of

19
投票

从另一种自动生成一种形式。

来源:

#include <boost/assign/list_of.hpp>
#include <boost/unordered_map.hpp>
#include <iostream>

using boost::assign::map_list_of;

enum eee { AA,BB,CC };

const boost::unordered_map<eee,const char*> eeeToString = map_list_of
    (AA, "AA")
    (BB, "BB")
    (CC, "CC");

int main()
{
    std::cout << " enum AA = " << eeeToString.at(AA) << std::endl;
    return 0;
}

生成:

enum {
  VALUE1, /* value 1 */
  VALUE2, /* value 2 */
};

如果枚举值很大,则生成的表单可以使用unordered_map <>或康斯坦丁建议的模板。

来源:

const char* enum2str[] = {
  "value 1", /* VALUE1 */
  "value 2", /* VALUE2 */
};

生成:

enum State{
  state0 = 0, /* state 0 */
  state1 = 1, /* state 1 */
  state2 = 2, /* state 2 */
  state3 = 4, /* state 3 */

  state16 = 0x10000, /* state 16 */
};

示例:

template <State n> struct enum2str { static const char * const value; };
template <State n> const char * const enum2str<n>::value = "error";

template <> struct enum2str<state0> { static const char * const value; };
const char * const enum2str<state0>::value = "state 0";

11
投票

我记得在StackOverflow的其他地方回答过此问题。在这里重复。基本上,这是基于可变参数宏的解决方案,并且非常易于使用:

#include <iostream>

int main()
{
  std::cout << enum2str<state16>::value << std::endl;
  return 0;
}

要在您的代码中使用它,只需执行:

#define AWESOME_MAKE_ENUM(name, ...) enum class name { __VA_ARGS__, __COUNT}; \
inline std::ostream& operator<<(std::ostream& os, name value) { \
std::string enumName = #name; \
std::string str = #__VA_ARGS__; \
int len = str.length(); \
std::vector<std::string> strings; \
std::ostringstream temp; \
for(int i = 0; i < len; i ++) { \
if(isspace(str[i])) continue; \
        else if(str[i] == ',') { \
        strings.push_back(temp.str()); \
        temp.str(std::string());\
        } \
        else temp<< str[i]; \
} \
strings.push_back(temp.str()); \
os << enumName << "::" << strings[static_cast<int>(value)]; \
return os;} 

10
投票

我建议同时使用AWESOME_MAKE_ENUM(Animal, DOG, CAT, HORSE ); auto dog = Animal::DOG; std::cout<<dog; 和以下模板函数:

借用X-macros are the best solution并扩展

marcinkoziukmyopenidcom

colour.def

enum Colours {
#   define X(a) a,
#   include "colours.def"
#   undef X
    ColoursCount
};

char const* const colours_str[] = {
#   define X(a) #a,
#   include "colours.def"
#   undef X
    0
};

template <class T> T str2enum( const char* );
template <class T> const char* enum2str( T );

#define STR2ENUM(TYPE,ARRAY) \
template <> \
TYPE str2enum<TYPE>( const char* str ) \
    { \
    for( int i = 0; i < (sizeof(ARRAY)/sizeof(ARRAY[0])); i++ ) \
        if( !strcmp( ARRAY[i], str ) ) \
            return TYPE(i); \
    return TYPE(0); \
    }

#define ENUM2STR(TYPE,ARRAY) \
template <> \
const char* enum2str<TYPE>( TYPE v ) \
    { \
    return ARRAY[v]; \
    }

#define ENUMANDSTR(TYPE,ARRAY)\
    STR2ENUM(TYPE,ARRAY) \
    ENUM2STR(TYPE,ARRAY)

ENUMANDSTR(Colours,colours_str)

5
投票

我使用下面复制的X(Red) X(Green) X(Blue) X(Cyan) X(Yellow) X(Magenta) 解决方案:

this

4
投票

如果要获取#define MACROSTR(k) #k #define X_NUMBERS \ X(kZero ) \ X(kOne ) \ X(kTwo ) \ X(kThree ) \ X(kFour ) \ X(kMax ) enum { #define X(Enum) Enum, X_NUMBERS #undef X } kConst; static char *kConstStr[] = { #define X(String) MACROSTR(String), X_NUMBERS #undef X }; int main(void) { int k; printf("Hello World!\n\n"); for (k = 0; k < kMax; k++) { printf("%s\n", kConstStr[k]); } return 0; } 变量的字符串表示形式,则模板不会将其剪切。模板可以专门处理编译时已知的整数值。

但是,如果那是您想要的,请尝试:

MyEnum

这很冗长,但会捕获与您所犯的错误类似的错误-您的#include <iostream> enum MyEnum { VAL1, VAL2 }; template<MyEnum n> struct StrMyEnum { static char const* name() { return "Unknown"; } }; #define STRENUM(val, str) \ template<> struct StrMyEnum<val> { \ static char const* name() { return str; }}; STRENUM(VAL1, "Value 1"); STRENUM(VAL2, "Value 2"); int main() { std::cout << StrMyEnum<VAL2>::name(); } 已重复。


3
投票

我花了更多时间研究我想承认的主题。幸运的是,在野外有很棒的开源解决方案。

这是两个很好的方法,即使还不为人所知(])>

case VAL1

  • 用于C ++ 11/14/17的独立智能枚举库。它支持C ++中的智能枚举类所期望的所有标准功能。
  • 限制:至少需要C ++ 11。
  • wise_enum

  • 具有纯净语法的反射式编译时枚举库,在单个头文件中,没有依赖性。
  • 限制:基于宏,不能在类内部使用。

2
投票

我很想拥有一张地图m-并将其嵌入到枚举中。

© www.soinside.com 2019 - 2024. All rights reserved.