当前位置:  开发笔记 > 编程语言 > 正文

有没有一种简单的方法将C++枚举转换为字符串?

如何解决《有没有一种简单的方法将C++枚举转换为字符串?》经验,为你挑选了10个好方法。

假设我们有一些命名的枚举:

enum MyEnum {
      FOO,
      BAR = 0x50
};

我搜索的是一个脚本(任何语言),它扫描我项目中的所有标题,并生成一个标题,每个枚举一个函数.

char* enum_to_string(MyEnum t);

以及类似这样的实现:

char* enum_to_string(MyEnum t){
      switch(t){
         case FOO:
            return "FOO";
         case BAR:
            return "BAR";
         default:
            return "INVALID ENUM";
      }
 }

这个问题确实与typedefed枚举和未命名的C风格枚举有关.有人知道这个吗?

编辑:解决方案不应该修改我的源,除了生成的函数.枚举是在API中,因此使用迄今为止提出的解决方案不是一种选择.



1> Marcin Koziu..:

X宏是最好的解决方案.例:

#include 

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

std::ostream& operator<<(std::ostream& os, enum Colours c)
{
    if (c >= ColoursCount || c < 0) return os << "???";
    return os << colours_str[c];
}

int main()
{
    std::cout << Red << Blue << Green << Cyan << Yellow << Magenta << std::endl;
}

colours.def:

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

但是,我通常更喜欢以下方法,因此可以稍微调整字符串.

#define X(a, b) a,
#define X(a, b) b,

X(Red, "red")
X(Green, "green")
// etc.


我不确定"最佳"解决方案!
漂亮,虽然我不喜欢额外的文件
只需确保您的构建过程不会在每个包含文件之前添加#pragma(一次)...

2> Avdi..:

您可能想要查看GCCXML.

在示例代码上运行GCCXML会产生:


  
  
  
    
    
  
  

您可以使用您喜欢的任何语言来提取Enumeration和EnumValue标记并生成所需的代码.


+1,GCCXML看起来非常好看!(虽然我差不多了,因为我最初误读了这个建议,使用上面详细的XML语法来编码你的枚举 - 一个充满过度工程的解决方案!)

3> Jasper Bekke..:

@hydroo:没有额外的文件:

#define SOME_ENUM(DO) \
    DO(Foo) \
    DO(Bar) \
    DO(Baz)

#define MAKE_ENUM(VAR) VAR,
enum MetaSyntacticVariable{
    SOME_ENUM(MAKE_ENUM)
};

#define MAKE_STRINGS(VAR) #VAR,
const char* const MetaSyntacticVariableNames[] = {
    SOME_ENUM(MAKE_STRINGS)
};



4> gbjbaanb..:

我倾向于创建一个C数组,其名称与枚举值的顺序和位置相同.

例如.

enum colours { red, green, blue };
const char *colour_names[] = { "red", "green", "blue" };

然后你可以在你想要人类可读值的地方使用数组,例如

colours mycolour = red;
cout << "the colour is" << colour_names[mycolour];

您可以尝试使用字符串化运算符(请参阅预处理器参考中的#),在某些情况下可以执行您想要的操作 - 例如:

#define printword(XX) cout << #XX;
printword(red);

将打印"红色"到标准输出.不幸的是,它不适用于变量(因为你将打印出变量名称)


仅当您不为枚举条目设置特殊数值时才有效.

5> 小智..:

我有一个非常简单易用的宏,以完全干燥的方式做到这一点.它涉及可变参数宏和一些简单的解析魔法.开始:

#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 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(value)]; \
return os;} 

要在代码中使用它,只需执行以下操作:

AWESOME_MAKE_ENUM(Animal,
    DOG,
    CAT,
    HORSE
);



6> Ronny Brende..:

QT能够拉动(感谢元对象编译器):link



7> Mark Ransom..:

我今天刚刚重新发明了这个轮子,并且认为我会分享它.

此实现执行要求对定义的常量,它可以是枚举或代码进行任何更改#defineS或其他任何转予整数-在我的情况,我在其他符号的定义的符号.它也适用于稀疏值.它甚至允许多个名称用于相同的值,始终返回第一个名称.唯一的缺点是它需要你创建一个常量表,例如,当添加新的常量时,它可能会变得过时.

struct IdAndName
{
   int          id;
   const char * name;
   bool operator<(const IdAndName &rhs) const { return id < rhs.id; }
};
#define ID_AND_NAME(x) { x, #x }

const char * IdToName(int id, IdAndName *table_begin, IdAndName *table_end)
{
   if ((table_end - table_begin) > 1 && table_begin[0].id > table_begin[1].id)
      std::stable_sort(table_begin, table_end);

   IdAndName searchee = { id, NULL };
   IdAndName *p = std::lower_bound(table_begin, table_end, searchee);
   return (p == table_end || p->id != id) ? NULL : p->name;
}

template
const char * IdToName(int id, IdAndName (&table)[N])
{
   return IdToName(id, &table[0], &table[N]);
}

您如何使用它的一个示例:

static IdAndName WindowsErrorTable[] =
{
   ID_AND_NAME(INT_MAX),               // flag value to indicate unsorted table
   ID_AND_NAME(NO_ERROR),
   ID_AND_NAME(ERROR_INVALID_FUNCTION),
   ID_AND_NAME(ERROR_FILE_NOT_FOUND),
   ID_AND_NAME(ERROR_PATH_NOT_FOUND),
   ID_AND_NAME(ERROR_TOO_MANY_OPEN_FILES),
   ID_AND_NAME(ERROR_ACCESS_DENIED),
   ID_AND_NAME(ERROR_INVALID_HANDLE),
   ID_AND_NAME(ERROR_ARENA_TRASHED),
   ID_AND_NAME(ERROR_NOT_ENOUGH_MEMORY),
   ID_AND_NAME(ERROR_INVALID_BLOCK),
   ID_AND_NAME(ERROR_BAD_ENVIRONMENT),
   ID_AND_NAME(ERROR_BAD_FORMAT),
   ID_AND_NAME(ERROR_INVALID_ACCESS),
   ID_AND_NAME(ERROR_INVALID_DATA),
   ID_AND_NAME(ERROR_INVALID_DRIVE),
   ID_AND_NAME(ERROR_CURRENT_DIRECTORY),
   ID_AND_NAME(ERROR_NOT_SAME_DEVICE),
   ID_AND_NAME(ERROR_NO_MORE_FILES)
};

const char * error_name = IdToName(GetLastError(), WindowsErrorTable);

IdToName函数依赖于std::lower_bound快速查找,这需要对表进行排序.如果表中的前两个条目出现故障,该函数将自动对其进行排序.

编辑:评论让我想到了使用相同原理的另一种方式.宏可以简化大型switch语句的生成.

#define ID_AND_NAME(x) case x: return #x

const char * WindowsErrorToName(int id)
{
    switch(id)
    {
        ID_AND_NAME(ERROR_INVALID_FUNCTION);
        ID_AND_NAME(ERROR_FILE_NOT_FOUND);
        ID_AND_NAME(ERROR_PATH_NOT_FOUND);
        ID_AND_NAME(ERROR_TOO_MANY_OPEN_FILES);
        ID_AND_NAME(ERROR_ACCESS_DENIED);
        ID_AND_NAME(ERROR_INVALID_HANDLE);
        ID_AND_NAME(ERROR_ARENA_TRASHED);
        ID_AND_NAME(ERROR_NOT_ENOUGH_MEMORY);
        ID_AND_NAME(ERROR_INVALID_BLOCK);
        ID_AND_NAME(ERROR_BAD_ENVIRONMENT);
        ID_AND_NAME(ERROR_BAD_FORMAT);
        ID_AND_NAME(ERROR_INVALID_ACCESS);
        ID_AND_NAME(ERROR_INVALID_DATA);
        ID_AND_NAME(ERROR_INVALID_DRIVE);
        ID_AND_NAME(ERROR_CURRENT_DIRECTORY);
        ID_AND_NAME(ERROR_NOT_SAME_DEVICE);
        ID_AND_NAME(ERROR_NO_MORE_FILES);
        default: return NULL;
    }
}



8> serge..:

这可以在C ++ 11中完成

#include 
enum MyEnum { AA, BB, CC, DD };

static std::map< MyEnum, const char * > info = {
   {AA, "This is an apple"},
   {BB, "This is a book"},
   {CC, "This is a coffee"},
   {DD, "This is a door"}
};

void main()
{
    std::cout << info[AA] << endl
              << info[BB] << endl
              << info[CC] << endl
              << info[DD] << endl;
}



9> Ben..:
#define stringify( name ) # name

enum MyEnum {
    ENUMVAL1
};
...stuff...

stringify(EnumName::ENUMVAL1);  // Returns MyEnum::ENUMVAL1

进一步讨论这种方法

新手的预处理器指令技巧


实际上这是相当无用的,因为stringify方法是在编译时并且非常直观.如果你说变量中的枚举类型有问题,尝试对变量进行字符串化只会给你变量名,而不是枚举类型名.

10> Carl..:

有趣的是看多少种方法.这是我很久以前用过的一个:

在文件myenummap.h中:

#include 
#include 
enum test{ one, two, three, five=5, six, seven };
struct mymap : std::map
{
  mymap()
  {
    this->operator[]( one ) = "ONE";
    this->operator[]( two ) = "TWO";
    this->operator[]( three ) = "THREE";
    this->operator[]( five ) = "FIVE";
    this->operator[]( six ) = "SIX";
    this->operator[]( seven ) = "SEVEN";
  };
  ~mymap(){};
};

在main.cpp中

#include "myenummap.h"

...
mymap nummap;
std::cout<< nummap[ one ] << std::endl;

它不是常数,但它方便.

这是使用C++ 11功能的另一种方式.这是const,不继承STL容器并且有点整洁:

#include 
#include 
#include 
#include 

//These stay together and must be modified together
enum test{ one, two, three, five=5, six, seven };
std::string enum_to_str(test const& e)
{
    typedef std::pair mapping;
    auto m = [](test const& e,std::string const& s){return mapping(static_cast(e),s);}; 
    std::vector const nummap = 
    { 
        m(one,"one"), 
        m(two,"two"), 
        m(three,"three"),
        m(five,"five"),
        m(six,"six"),
        m(seven,"seven"),
    };
    for(auto i  : nummap)
    {
        if(i.first==static_cast(e))
        {
            return i.second;
        }
    }
    return "";
}

int main()
{
//  std::cout<< enum_to_str( 46 ) << std::endl; //compilation will fail
    std::cout<< "Invalid enum to string : [" << enum_to_str( test(46) ) << "]"<


继承stl容器不是一个好主意
推荐阅读
echo7111436
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有