简体   繁体   English

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

[英]How to easily map c++ enums to strings

I have a bunch of enum types in some library header files that I'm using, and I want to have a way of converting enum values to user strings - and vice-versa.我在使用的一些库头文件中有一堆枚举类型,我想有一种方法将枚举值转换为用户字符串 - 反之亦然。

RTTI won't do it for me, because the 'user strings' need to be a bit more readable than the enumerations. RTTI 不会为我做这件事,因为“用户字符串”需要比枚举更具可读性。

A brute force solution would be a bunch of functions like this, but I feel that's a bit too C-like.一个蛮力解决方案将是一堆这样的函数,但我觉得这有点太像 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");
  }
}

I have a gut feeling that there's an elegant solution using templates, but I can't quite get my head round it yet.我有一种直觉,即使用模板有一个优雅的解决方案,但我还不能完全理解它。

UPDATE: Thanks for suggestions - I should have made clear that the enums are defined in a third-party library header, so I don't want to have to change the definition of them.更新:感谢您的建议 - 我应该明确指出枚举是在第三方库头文件中定义的,所以我不想更改它们的定义。

My gut feeling now is to avoid templates and do something like this:我现在的直觉是避免使用模板并执行以下操作:

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

If you want the enum names themselves as strings, see this post .如果您希望枚举名称本身为字符串,请参阅此帖子 Otherwise, a std::map<MyEnum, char const*> will work nicely.否则, std::map<MyEnum, char const*>会很好地工作。 (No point in copying your string literals to std::strings in the map) (将字符串文字复制到地图中的 std::strings 没有意义)

For extra syntactic sugar, here's how to write a map_init class.对于额外的语法糖,这里是如何编写 map_init 类。 The goal is to allow目标是让

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

The function template <typename T> map_init(T&) returns a map_init_helper<T> .函数template <typename T> map_init(T&)返回一个map_init_helper<T> map_init_helper<T> stores a T&, and defines the trivial map_init_helper& operator()(typename T::key_type const&, typename T::value_type const&) . map_init_helper<T>存储一个 T&,并定义了简单的map_init_helper& operator()(typename T::key_type const&, typename T::value_type const&) (Returning *this from operator() allows the chaining of operator() , like operator<< on std::ostream s) (从operator()返回*this允许链接operator() ,就像std::ostreamoperator<<一样)

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

Since the function and helper class are templated, you can use them for any map, or map-like structure.由于函数和助手类是模板化的,您可以将它们用于任何地图或类似地图的结构。 Ie it can also add entries to std::unordered_map即它还可以向std::unordered_map添加条目

If you don't like writing these helpers, boost::assign offers the same functionality out of the box.如果您不喜欢编写这些帮助程序,boost::assign 提供了开箱即用的相同功能。

MSalters solution is a good one but basically re-implements boost::assign::map_list_of . MSalters 解决方案是一个很好的解决方案,但基本上重新实现了boost::assign::map_list_of If you have boost, you can use it directly:如果有boost,可以直接使用:

#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;
}

Auto-generate one form from another.从另一种形式自动生成一种形式。

Source:来源:

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

Generated:生成:

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

If enum values are large then a generated form could use unordered_map<> or templates as suggested by Constantin.如果枚举值很大,则生成的表单可以使用康斯坦丁建议的 unordered_map<> 或模板。

Source:来源:

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

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

Generated:生成:

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

Example:例子:

#include <iostream>

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

I remember having answered this elsewhere on StackOverflow.我记得在 StackOverflow 上的其他地方回答过这个问题。 Repeating it here.在这里重复一遍。 Basically it's a solution based on variadic macros, and is pretty easy to use:基本上它是基于可变参数宏的解决方案,并且非常易于使用:

#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;} 

To use it in your code, simply do:要在您的代码中使用它,只需执行以下操作:

AWESOME_MAKE_ENUM(Animal,
    DOG,
    CAT,
    HORSE
);
auto dog = Animal::DOG;
std::cout<<dog;

I suggest a mix of using X-macros are the best solution and the following template functions:我建议混合使用X-macros 是最好的解决方案和以下模板函数:

To borrow off marcinkoziukmyopenidcom and extended借用marcinkoziukmyopenidcom并扩展

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)

colour.def颜色文件

X(Red)
X(Green)
X(Blue)
X(Cyan)
X(Yellow)
X(Magenta)

I use this solution which I reproduce below:我使用我在下面重现的这个解决方案:

#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;
}

If you want to get string representations of MyEnum variables , then templates won't cut it.如果你想获得MyEnum variables 的字符串表示,那么模板不会削减它。 Template can be specialized on integral values known at compile-time.模板可以专门用于编译时已知的整数值。

However, if that's what you want then try:但是,如果这是您想要的,请尝试:

#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();
}

This is verbose, but will catch errors like the one you made in question - your case VAL1 is duplicated.这是冗长的,但会捕获像您所提出的错误一样的错误 - 您的case VAL1重复。

I have spent more time researching this topic that I'd like to admit.我花了更多时间研究这个我想承认的话题。 Luckily there are great open source solutions in the wild.幸运的是,有很多很棒的开源解决方案。

These are two great approaches, even if not well known enough (yet),这是两种很好的方法,即使还不够广为人知(还),

wise_enum明智的枚举

  • Standalone smart enum library for C++11/14/17. C++11/14/17 的独立智能枚举库。 It supports all of the standard functionality that you would expect from a smart enum class in C++.它支持您期望从 C++ 中的智能枚举类获得的所有标准功能。
  • Limitations: requires at least C++11.限制:至少需要 C++11。

Better Enums更好的枚举

  • Reflective compile-time enum library with clean syntax, in a single header file, and without dependencies.反射编译时枚举库,语法清晰,在单个头文件中,并且没有依赖项。
  • Limitations: based on macros, can't be used inside a class.限制:基于宏,不能在类中使用。

Your answers inspired me to write some macros myself.你的回答激励我自己写一些宏。 My requirements were the following:我的要求如下:

  1. only write each value of the enum once, so there are no double lists to maintain只写枚举的每个值一次,所以没有双重列表需要维护

  2. don't keep the enum values in a separate file that is later #included, so I can write it wherever I want不要将枚举值保存在以后 #included 的单独文件中,这样我就可以将它写在任何我想要的地方

  3. don't replace the enum itself, I still want to have the enum type defined, but in addition to it I want to be able to map every enum name to the corresponding string (to not affect legacy code)不要替换枚举本身,我仍然希望定义枚举类型,但除此之外,我希望能够将每个枚举名称映射到相应的字符串(以不影响遗留代码)

  4. the searching should be fast, so preferably no switch-case, for those huge enums对于那些巨大的枚举,搜索应该很快,所以最好不要切换案例

This code creates a classic enum with some values.这段代码创建了一个带有一些值的经典枚举。 In addition it creates as std::map which maps each enum value to it's name (ie map[E_SUNDAY] = "E_SUNDAY", etc.)此外,它创建为 std::map 将每个枚举值映射到它的名称(即 map[E_SUNDAY] = "E_SUNDAY" 等)

Ok, here is the code now:好的,这是现在的代码:

EnumUtilsImpl.h : EnumUtilsImpl.h :

map<int, string> & operator , (map<int, string> & dest, 
                               const pair<int, string> & keyValue) {
    dest[keyValue.first] = keyValue.second; 
    return dest;
}

#define ADD_TO_MAP(name, value) pair<int, string>(name, #name)

EnumUtils.h // this is the file you want to include whenever you need to do this stuff, you will use the macros from it: EnumUtils.h // 这是您在需要执行此操作时要包含的文件,您将使用其中的宏:

#include "EnumUtilsImpl.h"
#define ADD_TO_ENUM(name, value) \
    name value

#define MAKE_ENUM_MAP_GLOBAL(values, mapName) \
    int __makeMap##mapName() {mapName, values(ADD_TO_MAP); return 0;}  \
    int __makeMapTmp##mapName = __makeMap##mapName();

#define MAKE_ENUM_MAP(values, mapName) \
    mapName, values(ADD_TO_MAP);

MyProjectCodeFile.h // this is an example of how to use it to create a custom enum: MyProjectCodeFile.h // 这是如何使用它来创建自定义枚举的示例:

#include "EnumUtils.h*

#define MyEnumValues(ADD) \
    ADD(val1, ), \
    ADD(val2, ), \
    ADD(val3, = 100), \
    ADD(val4, )

enum MyEnum {
    MyEnumValues(ADD_TO_ENUM)
};

map<int, string> MyEnumStrings;
// this is how you initialize it outside any function
MAKE_ENUM_MAP_GLOBAL(MyEnumValues, MyEnumStrings); 

void MyInitializationMethod()
{ 
    // or you can initialize it inside one of your functions/methods
    MAKE_ENUM_MAP(MyEnumValues, MyEnumStrings); 
}

Cheers.干杯。

I'd be tempted to have a map m - and embedd this into the enum.我很想拥有一个地图 m - 并将其嵌入到枚举中。

setup with m[MyEnum.VAL1] = "Value 1";设置 m[MyEnum.VAL1] = "Value 1";

and all is done.一切都完成了。

Here is an attempt to get << and >> stream operators on enum automatically with an one line macro command only...这是尝试仅使用一行宏命令自动获取枚举上的 << 和 >> 流运算符...

Definitions:定义:

#include <string>
#include <iostream>
#include <stdexcept>
#include <algorithm>
#include <iterator>
#include <sstream>
#include <vector>

#define MAKE_STRING(str, ...) #str, MAKE_STRING1_(__VA_ARGS__)
#define MAKE_STRING1_(str, ...) #str, MAKE_STRING2_(__VA_ARGS__)
#define MAKE_STRING2_(str, ...) #str, MAKE_STRING3_(__VA_ARGS__)
#define MAKE_STRING3_(str, ...) #str, MAKE_STRING4_(__VA_ARGS__)
#define MAKE_STRING4_(str, ...) #str, MAKE_STRING5_(__VA_ARGS__)
#define MAKE_STRING5_(str, ...) #str, MAKE_STRING6_(__VA_ARGS__)
#define MAKE_STRING6_(str, ...) #str, MAKE_STRING7_(__VA_ARGS__)
#define MAKE_STRING7_(str, ...) #str, MAKE_STRING8_(__VA_ARGS__)
#define MAKE_STRING8_(str, ...) #str, MAKE_STRING9_(__VA_ARGS__)
#define MAKE_STRING9_(str, ...) #str, MAKE_STRING10_(__VA_ARGS__)
#define MAKE_STRING10_(str) #str

#define MAKE_ENUM(name, ...) MAKE_ENUM_(, name, __VA_ARGS__)
#define MAKE_CLASS_ENUM(name, ...) MAKE_ENUM_(friend, name, __VA_ARGS__)

#define MAKE_ENUM_(attribute, name, ...) name { __VA_ARGS__ }; \
    attribute std::istream& operator>>(std::istream& is, name& e) { \
        const char* name##Str[] = { MAKE_STRING(__VA_ARGS__) }; \
        std::string str; \
        std::istream& r = is >> str; \
        const size_t len = sizeof(name##Str)/sizeof(name##Str[0]); \
        const std::vector<std::string> enumStr(name##Str, name##Str + len); \
        const std::vector<std::string>::const_iterator it = std::find(enumStr.begin(), enumStr.end(), str); \
        if (it != enumStr.end())\
            e = name(it - enumStr.begin()); \
        else \
            throw std::runtime_error("Value \"" + str + "\" is not part of enum "#name); \
        return r; \
    }; \
    attribute std::ostream& operator<<(std::ostream& os, const name& e) { \
        const char* name##Str[] = { MAKE_STRING(__VA_ARGS__) }; \
        return (os << name##Str[e]); \
    }

Usage:用法:

// Declare global enum
enum MAKE_ENUM(Test3, Item13, Item23, Item33, Itdsdgem43);

class Essai {
public:
    // Declare enum inside class
    enum MAKE_CLASS_ENUM(Test, Item1, Item2, Item3, Itdsdgem4);

};

int main() {
    std::cout << Essai::Item1 << std::endl;

    Essai::Test ddd = Essai::Item1;
    std::cout << ddd << std::endl;

    std::istringstream strm("Item2");
    strm >> ddd;

    std::cout << (int) ddd << std::endl;
    std::cout << ddd << std::endl;
}

Not sure about the limitations of this scheme though... comments are welcome!虽然不确定这个方案的局限性......欢迎评论!

I've required this functionality several times for debugging/analyzing code from others.我多次需要此功能来调试/分析其他人的代码。 For this, I've written a Perl script which generates a class with several overloaded toString methods.为此,我编写了一个 Perl 脚本,它生成一个具有多个重载toString方法的类。 Each toString method takes an Enum as an argument and returns const char* .每个toString方法都将Enum作为参数并返回const char*

Of course, the script doesn't parse C++ for enums itself, but uses ctags for generating symbol table.当然,该脚本本身不会为枚举解析 C++,而是使用 ctags 来生成符号表。

The Perl script is here: http://heinitz-it.de/download/enum2string/enum2string.pl.html Perl 脚本在这里: http : //heinitz-it.de/download/enum2string/enum2string.pl.html

I know I'm late to party, but for everyone else who comes to visit this page, u could try this, it's easier than everything there and makes more sense:我知道我参加聚会迟到了,但是对于访问此页面的其他所有人,您可以尝试一下,它比那里的所有内容都更容易并且更有意义:

namespace texs {
    typedef std::string Type;
    Type apple = "apple";
    Type wood = "wood";
}

I just wanted to show this possible elegant solution using macros.我只是想使用宏来展示这个可能的优雅解决方案。 This doesn t solve the problem but I think it is a good way to rethik about the problem.这并不能解决问题,但我认为这是重新思考问题的好方法。

#define MY_LIST(X) X(value1), X(value2), X(value3)

enum eMyEnum
    {
    MY_LIST(PLAIN)
    };

const char *szMyEnum[] =
    {
    MY_LIST(STRINGY)
    };


int main(int argc, char *argv[])
{

std::cout << szMyEnum[value1] << value1 <<" " <<  szMyEnum[value2] << value2 << std::endl;

return 0;
}

---- EDIT ---- ---- 编辑 ----

After some internet research and some own experements I came to the following solution:经过一些互联网研究和一些自己的实验,我得出了以下解决方案:

//this is the enum definition
#define COLOR_LIST(X) \
  X( RED    ,=21)      \
  X( GREEN  )      \
  X( BLUE   )      \
  X( PURPLE , =242)      \
  X( ORANGE )      \
  X( YELLOW )

//these are the macros
#define enumfunc(enums,value) enums,
#define enumfunc2(enums,value) enums value,
#define ENUM2SWITCHCASE(enums) case(enums): return #enums;

#define AUTOENUM(enumname,listname) enum enumname{listname(enumfunc2)};
#define ENUM2STRTABLE(funname,listname) char* funname(int val) {switch(val) {listname(ENUM2SWITCHCASE) default: return "undef";}}
#define ENUM2STRUCTINFO(spacename,listname) namespace spacename { int values[] = {listname(enumfunc)};int N = sizeof(values)/sizeof(int);ENUM2STRTABLE(enum2str,listname)};

//here the enum and the string enum map table are generated
AUTOENUM(testenum,COLOR_LIST)
ENUM2STRTABLE(testfunenum,COLOR_LIST)
ENUM2STRUCTINFO(colorinfo,COLOR_LIST)//colorinfo structur {int values[]; int N; char * enum2str(int);}

//debug macros
#define str(a) #a
#define xstr(a) str(a)


int main( int argc, char** argv )
{
testenum x = YELLOW;
std::cout << testfunenum(GREEN) << "   " << testfunenum(PURPLE) << PURPLE << "  " << testfunenum(x);

for (int i=0;i< colorinfo::N;i++)
std::cout << std::endl << colorinfo::values[i] <<  "  "<< colorinfo::enum2str(colorinfo::values[i]);

  return EXIT_SUCCESS;
}

I just wanted to post it maybe someone could find this solution useful.我只是想发布它,也许有人会发现这个解决方案很有用。 There is no need of templates classes no need of c++11 and no need of boost so this could also be used for simple C.不需要模板类,不需要 c++11,也不需要 boost,所以这也可以用于简单的 C。

---- EDIT2 ---- ---- 编辑 2 ----

the information table can produce some problems when using more than 2 enums (compiler problem).当使用超过 2 个枚举时,信息表会产生一些问题(编译器问题)。 The following workaround worked:以下解决方法有效:

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

in the header:在标题中:

enum EFooOptions
 {
FooOptionsA = 0, EFooOptionsMin = 0,
FooOptionsB,
FooOptionsC,
FooOptionsD 
EFooOptionsMax
};
extern const wchar* FOO_OPTIONS[EFooOptionsMax];

in the .cpp file:在 .cpp 文件中:

const wchar* FOO_OPTIONS[] = {
    L"One",
    L"Two",
    L"Three",
    L"Four"
};

Caveat: Don't handle bad array index.警告:不要处理错误的数组索引。 :) But you can easily add a function to verify the enum before getting the string from the array. :) 但是您可以在从数组中获取字符串之前轻松添加一个函数来验证枚举。

typedef enum {
    ERR_CODE_OK = 0,
    ERR_CODE_SNAP,

    ERR_CODE_NUM
} ERR_CODE;

const char* g_err_msg[ERR_CODE_NUM] = {
    /* ERR_CODE_OK   */ "OK",
    /* ERR_CODE_SNAP */ "Oh, snap!",
};

Above is my simple solution.以上是我的简单解决方案。 One benefit of it is the 'NUM' which controls the size of the message array, it also prevents out of boundary access (if you use it wisely).它的一个好处是“NUM”,它控制消息数组的大小,它还可以防止越界访问(如果您明智地使用它)。

You can also define a function to get the string:您还可以定义一个函数来获取字符串:

const char* get_err_msg(ERR_CODE code) {
    return g_err_msg[code];
}

Further to my solution, I then found the following one quite interesting.在我的解决方案之后,我发现以下一个非常有趣。 It generally solved the sync problem of the above one.大体上解决了上面的同步问题。

Slides here: http://www.slideshare.net/arunksaha/touchless-enum-tostring-28684724幻灯片在这里: http : //www.slideshare.net/arunksaha/touchless-enum-tostring-28684724

Code here: https://github.com/arunksaha/enum_to_string代码在这里: https : //github.com/arunksaha/enum_to_string

By using designated array initializers your string array is independent of the order of elements in the enum:通过使用指定的数组初始值设定项,您的字符串数组与枚举中元素的顺序无关:

enum Values {
    Val1,
    Val2
};

constexpr string_view v_name[] = {
    [Val1] = "Value 1",
    [Val2] = "Value 2"
}

I recently had the same issue with a vendor library (Fincad).我最近在供应商库 (Fincad) 上遇到了同样的问题。 Fortunately, the vendor provided xml doucumentation for all the enums.幸运的是,供应商为所有枚举提供了 xml 文档。 I ended up generating a map for each enum type and providing a lookup function for each enum.我最终为每个枚举类型生成了一个映射,并为每个枚举提供了一个查找函数。 This technique also allows you to intercept a lookup outside the range of the enum.此技术还允许您拦截枚举范围之外的查找。

I'm sure swig could do something similar for you, but I'm happy to provide the code generation utils which are written in ruby.我确信 swig 可以为您做类似的事情,但我很高兴提供用 ruby​​ 编写的代码生成工具。

Here is a sample of the code:下面是代码示例:

std::map<std::string, switches::FCSW2::type> init_FCSW2_map() {
        std::map<std::string, switches::FCSW2::type> ans;
        ans["Act365Fixed"] = FCSW2::Act365Fixed;
        ans["actual/365 (fixed)"] = FCSW2::Act365Fixed;
        ans["Act360"] = FCSW2::Act360;
        ans["actual/360"] = FCSW2::Act360;
        ans["Act365Act"] = FCSW2::Act365Act;
        ans["actual/365 (actual)"] = FCSW2::Act365Act;
        ans["ISDA30360"] = FCSW2::ISDA30360;
        ans["30/360 (ISDA)"] = FCSW2::ISDA30360;
        ans["ISMA30E360"] = FCSW2::ISMA30E360;
        ans["30E/360 (30/360 ISMA)"] = FCSW2::ISMA30E360;
        return ans;
}
switches::FCSW2::type FCSW2_lookup(const char* fincad_switch) {
        static std::map<std::string, switches::FCSW2::type> switch_map = init_FCSW2_map();
        std::map<std::string, switches::FCSW2::type>::iterator it = switch_map.find(fincad_switch);
        if(it != switch_map.end()) {
                return it->second;
        } else {
                throw FCSwitchLookupError("Bad Match: FCSW2");
        }
}

Seems like you want to go the other way (enum to string, rather than string to enum), but this should be trivial to reverse.似乎您想走另一条路(枚举到字符串,而不是字符串到枚举),但这应该很容易反转。

-Whit -惠特

this right old mess is my effort based on bits and peices from SO.这个正确的旧烂摊子是我基于 SO 的点点滴滴所做的努力。 The for_each would have to be expanded to support more than 20 enum values. for_each 必须扩展以支持超过 20 个枚举值。 Tested it on visual studio 2019,clang and gcc.在 Visual Studio 2019、clang 和 gcc 上对其进行了测试。 c++11 C++11

#define _enum_expand(arg) arg
#define _enum_select_for_each(_,_0, _1, _2,_3,_4, _5, _6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,N, ...) N
#define _enum_for_each_0(_call, arg0,arg1,...)
#define _enum_for_each_1(_call, arg0,arg1) _call(arg0,arg1)
#define _enum_for_each_2(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_1(_call,arg0, __VA_ARGS__))
#define _enum_for_each_3(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_2(_call,arg0, __VA_ARGS__))
#define _enum_for_each_4(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_3(_call,arg0, __VA_ARGS__))
#define _enum_for_each_5(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_4(_call,arg0, __VA_ARGS__))
#define _enum_for_each_6(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_5(_call,arg0, __VA_ARGS__))
#define _enum_for_each_7(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_6(_call,arg0, __VA_ARGS__))
#define _enum_for_each_8(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_7(_call,arg0, __VA_ARGS__))
#define _enum_for_each_9(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_8(_call,arg0, __VA_ARGS__))
#define _enum_for_each_10(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_9(_call,arg0, __VA_ARGS__))
#define _enum_for_each_11(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_10(_call,arg0, __VA_ARGS__))
#define _enum_for_each_12(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_11(_call,arg0, __VA_ARGS__))
#define _enum_for_each_13(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_12(_call,arg0, __VA_ARGS__))
#define _enum_for_each_14(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_13(_call,arg0, __VA_ARGS__))
#define _enum_for_each_15(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_14(_call,arg0, __VA_ARGS__))
#define _enum_for_each_16(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_15(_call,arg0, __VA_ARGS__))
#define _enum_for_each_17(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_16(_call,arg0, __VA_ARGS__))
#define _enum_for_each_18(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_17(_call,arg0, __VA_ARGS__))
#define _enum_for_each_19(_call, arg0,arg1, ...) _call(arg) _enum_expand(_enum_for_each_18(_call,arg0, __VA_ARGS__))
#define _enum_for_each(arg, ...) \
    _enum_expand(_enum_select_for_each(_, ##__VA_ARGS__, \
    _enum_for_each_19, _enum_for_each_18, _enum_for_each_17, _enum_for_each_16, _enum_for_each_15, \
    _enum_for_each_14, _enum_for_each_13, _enum_for_each_12, _enum_for_each_11, _enum_for_each_10, \
    _enum_for_each_9,  _enum_for_each_8,  _enum_for_each_7,  _enum_for_each_6,  _enum_for_each_5,  \
    _enum_for_each_4,  _enum_for_each_3,  _enum_for_each_2,  _enum_for_each_1,  _enum_for_each_0)(arg, ##__VA_ARGS__))

#define _enum_strip_args_1(arg0) arg0
#define _enum_strip_args_2(arg0, arg1) arg0, arg1
#define _enum_make_args(...) (__VA_ARGS__)

#define _enum_elem_arity1_1(arg) arg,
#define _enum_elem_arity1( ...) _enum_expand(_enum_elem_arity1_1 __VA_ARGS__)
#define _enum_elem_arity2_1(arg0,arg1) arg0 = arg1,
#define _enum_elem_arity2( ...) _enum_expand(_enum_elem_arity2_1 __VA_ARGS__)

#define _enum_elem_select_arity_2(_0, _1, NAME,...) NAME
#define _enum_elem_select_arity_1(...) _enum_expand(_enum_elem_select_arity_2(__VA_ARGS__, _enum_elem_arity2,_enum_elem_arity1,_))
#define _enum_elem_select_arity(enum_type,...) _enum_expand(_enum_elem_select_arity_1 __VA_ARGS__)(__VA_ARGS__)

#define _enum_str_arity1_1(enum_type,arg) { enum_type::arg,#arg },
#define _enum_str_arity1(enum_type,...) _enum_expand(_enum_str_arity1_1 _enum_make_args( enum_type, _enum_expand(_enum_strip_args_1 __VA_ARGS__)))
#define _enum_str_arity2_1(enum_type,arg,value) { enum_type::arg,#arg },
#define _enum_str_arity2(enum_type, ...) _enum_expand(_enum_str_arity2_1 _enum_make_args( enum_type, _enum_expand(_enum_strip_args_2 __VA_ARGS__)))
#define _enum_str_select_arity_2(_0, _1, NAME,...) NAME
#define _enum_str_select_arity_1(...) _enum_expand(_enum_str_select_arity_2(__VA_ARGS__, _enum_str_arity2,_enum_str_arity1,_))
#define _enum_str_select_arity(enum_type,...) _enum_expand(_enum_str_select_arity_1 __VA_ARGS__)(enum_type,__VA_ARGS__)

#define error_code_enum(enum_type,...)  enum class enum_type {              \
    _enum_expand(_enum_for_each(_enum_elem_select_arity,enum_type, ##__VA_ARGS__))};  \
    namespace _ ## enum_type ## _detail { \
        template <typename> struct _ ## enum_type ## _error_code{ \
            static const std::map<enum_type, const char*> enum_type ## _map; \
        }; \
            template <typename T> \
            const std::map<enum_type, const char*> _ ## enum_type ## _error_code<T>::enum_type ## _map = { \
                _enum_expand(_enum_for_each(_enum_str_select_arity,enum_type,  ##__VA_ARGS__)) \
        }; \
    } \
    inline const char* get_error_code_name(const enum_type& value) { \
        return _ ## enum_type ## _detail::_ ## enum_type ## _error_code<enum_type>::enum_type ## _map.find(value)->second; \
    } 

error_code_enum(myenum,
    (one, 1),
    (two)
);

which produces the following code产生以下代码

enum class myenum { 
    one = 1,
    two,
};
namespace _myenum_detail {
    template <typename>
    struct _myenum_error_code {
        static const std::map<myenum, const char*> myenum_map;
    };
    template <typename T>
    const std::map<myenum, const char*> _myenum_error_code<T>::myenum_map = {
        { myenum::one, "one" }, 
        { myenum::two, "two" },
    };
}
inline const char* get_error_code_name(const myenum& value) { 
    return _myenum_detail::_myenum_error_code<myenum>::myenum_map.find(value)->second; 
}

Such a shame the hoops you have to jump though with the preprocessor to do this in one of the most used programming languages in the world...真是太可惜了,你必须用预处理器跳过篮球,才能用世界上最常用的编程语言之一来做到这一点......

#include <vector>
#include <string>

//Split one comma-separated value string to vector
std::vector<std::string> split(std::string csv, char separator){/*trivial*/}

//Initializer
#define ENUMIFY(name, ...)                                                                               \
  struct name                                                                                             \
  {                                                                                                       \
    enum Enum                                                                                       \
    {                                                                                                     \
      __VA_ARGS__                                                                                         \
    };                                                                                                    \
    static const std::vector<std::string>& Names()                                                        \
    {                                                                                                     \
      const static std::vector<std::string> _{split(#__VA_ARGS__, ',')}; \
      return _;                                                                                           \
    };                                                                                                    \
  };

Declaration:宣言:

ENUMIFY(States, INIT, ON, OFF, RUNNING)

Then all the enums are available, plus their strings are vectorized:然后所有枚举都可用,并且它们的字符串被向量化:

std::string enum_str = States::Names()[States::ON];

Another option is to use a static vector directly without the function wrapper.另一种选择是直接使用静态向量而不使用函数包装器。

And what about quite portable Enum class implementation ?那么相当便携的 Enum 类实现呢?
It is not much optimized for easy understanding.为了便于理解,它没有进行太多优化。

#define FOREACH_FRUIT(item) \
        item(apple)   \
        item(orange)  \
        item(grape, 5)   \
        item(banana)  \

No need to repeat or update copy of definition.无需重复或更新定义副本。

class EnumClass
{
#define GENERATE_ENUM(ENUM, ...) ENUM,
#define GENERATE_STRINGS(STRING, ...) { #STRING, ##__VA_ARGS__ },
#define GENERATE_SIZE(...) + 1
public:
    enum Enum {
        FOREACH_FRUIT(GENERATE_ENUM) // apple, orange, grape, banana,
    } _;
    EnumClass(Enum init)
    {
        _ = init; // grape(2)
        _EnumItem build[itemsNo] = { FOREACH_FRUIT(GENERATE_STRINGS) }; // _EnumItem build[itemsNo] = { { "apple"  }, { "orange"  }, { "grape",5 }, { "banana"  }, };
        int pos = 0;
        for (int i = 0; i < itemsNo; i++)
        {
            items[i].Name = build[i].Name;
            if (0 == build[i].No) {
                items[i].No = pos;
                for (int j = i; j--;)
                {
                    if (items[j].No == pos)
                        throw "Existing item # !";
                }
                pos++;
            }
            else {
                int destPos = build[i].No;
                if (destPos < pos) {
                    for (int j = 0; j < i; j++)
                    {
                        if (items[j].No == destPos)
                            throw "Existing item # !";
                    }
                }
                items[i].No = destPos;
                pos = destPos + 1;
            }
        }
    }
    operator int()
    {
        return items[_].No;
    }
    operator char*()
    {
        return items[_].Name;
    }
    EnumClass& operator ++(int)
    {
        if (_ == itemsNo - 1) {
            throw "Out of Enum options !";
        }
        _ = static_cast<EnumClass::Enum>(_ + 1);
        return *this;
    }
    EnumClass& operator --(int)
    {
        if (0 == _) {
            throw "Out of Enum options !";
        }
        _ = static_cast<EnumClass::Enum>(_ - 1);
        return *this;
    }
    EnumClass operator =(int right)
    {
        for (int i = 0; i < itemsNo; i++)
        {
            if (items[i].No == right)
            {
                _ = static_cast<EnumClass::Enum>(i);
                return *this;
            }
        }
        throw "Enum option does not exist !";
    }
    EnumClass operator =(char *right)
    {
        for (int i = 0; i < itemsNo; i++)
        {
            if (!strcmp(items[i].Name, right))
            {
                _ = static_cast<EnumClass::Enum>(i);
                return *this;
            }
        }
        throw "Enum option does not exist !";
    }
protected:
    static const int itemsNo = FOREACH_FRUIT(GENERATE_SIZE); // + 1 + 1 + 1 + 1; 
    struct _EnumItem {
        char *Name;
        int No;
    } items[itemsNo]; // { Name = "apple" No = 0 }, { Name = "orange" No = 1 } ,{ Name = "grape" No = 5 } ,{ Name = "banana" No = 6 }

#undef GENERATE_ENUM
#undef GENERATE_STRINGS
#undef GENERATE_SIZE
};

Now you can do any common operations + check definitions & runtime operations:现在您可以执行任何常见操作 + 检查定义和运行时操作:

int main()
{
    EnumClass ec(EnumClass::grape);
    ec = "banana"; // ec {_=banana (3)...}
    ec--; // ec {_=grape (2)...}
    char *name = ec;
    int val = ec; // 5
    printf("%s(%i)", name, val); // grape(5)
    return 0;
}

printf problem ... " The compiler does not know, technically, which type is required. " printf 问题……“ 从技术上讲,编译器不知道需要哪种类型。

This is my solution, I refer to some other designs, but mine is more complete and simple to use.这是我的解决方案,我参考了其他一些设计,但我的更完整且易于使用。

// 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;                                          \
  }

You can use it like this:你可以这样使用它:

#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;
}

I have a github gist .我有一个github gist

See if the following syntax suits you:看看以下语法是否适合您:

// WeekEnd enumeration
enum WeekEnd
{
    Sunday = 1,
    Saturday = 7
};

// String support for WeekEnd
Begin_Enum_String( WeekEnd )
{
    Enum_String( Sunday );
    Enum_String( Saturday );
}
End_Enum_String;

// Convert from WeekEnd to string
const std::string &str = EnumString<WeekEnd>::From( Saturday );
// str should now be "Saturday"

// Convert from string to WeekEnd
WeekEnd w;
EnumString<WeekEnd>::To( w, "Sunday" );
// w should now be Sunday

If it does, then you might want to check out this article:如果是这样,那么您可能想查看这篇文章:
http://www.gamedev.net/reference/snippets/features/cppstringizing/ http://www.gamedev.net/reference/snippets/features/cppstringizing/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM