与其他类似的问题不同,这个问题是关于如何使用c++的新特性。

2008 c Is there a simple way to convert C++ enum to string? 2008 c Easy way to use variables of enum types as string in C? 2008 c++ How to easily map c++ enums to strings 2008 c++ Making something both a C identifier and a string? 2008 c++ Is there a simple script to convert C++ enum to string? 2009 c++ How to use enums as flags in C++? 2011 c++ How to convert an enum type variable to a string? 2011 c++ Enum to String C++ 2011 c++ How to convert an enum type variable to a string? 2012 c How to convert enum names to string in c 2013 c Stringifying an conditionally compiled enum in C

看了很多答案后,我还没有找到:

优雅的方式使用c++ 11、c++ 14或c++ 17的新特性 或者在Boost中使用一些现成的东西 还有一些东西计划在c++ 20中实现

例子

举例往往比冗长的解释更好。 您可以在Coliru上编译和运行这个代码片段。 (另一个前面的例子也可用)

#include <map>
#include <iostream>

struct MyClass
{
    enum class MyEnum : char {
        AAA = -8,
        BBB = '8',
        CCC = AAA + BBB
    };
};

// Replace magic() by some faster compile-time generated code
// (you're allowed to replace the return type with std::string
// if that's easier for you)
const char* magic (MyClass::MyEnum e)
{
    const std::map<MyClass::MyEnum,const char*> MyEnumStrings {
        { MyClass::MyEnum::AAA, "MyClass::MyEnum::AAA" },
        { MyClass::MyEnum::BBB, "MyClass::MyEnum::BBB" },
        { MyClass::MyEnum::CCC, "MyClass::MyEnum::CCC" }
    };
    auto   it  = MyEnumStrings.find(e);
    return it == MyEnumStrings.end() ? "Out of range" : it->second;
}

int main()
{
   std::cout << magic(MyClass::MyEnum::AAA) <<'\n';
   std::cout << magic(MyClass::MyEnum::BBB) <<'\n';
   std::cout << magic(MyClass::MyEnum::CCC) <<'\n';
}

约束

请不要无价值的重复其他答案或基本链接。 请避免基于宏的臃肿答案,或尽量减少#define开销。 请不要手动enum ->字符串映射。

很高兴有

支持从不同于零的数字开始的enum值 支持负enum值 支持碎片enum值 支持类枚举(c++ 11) 支持类枚举:<类型>有任何允许的<类型> (c++ 11) 编译时(不是运行时)到字符串的转换, 或者至少在运行时快速执行(例如std::map不是一个好主意…) constexpr (c++ 11,然后在c++ 14/17/20中放松) noexcept (C + + 11) c++ 17/ c++ 20友好的代码片段

一个可能的想法是使用c++编译器功能,在编译时使用基于可变参数模板类和constexpr函数的元编程技巧来生成c++代码……


当前回答

你可以使用一个反射库,比如Ponder:

enum class MyEnum
{
    Zero = 0,
    One  = 1,
    Two  = 2
};

ponder::Enum::declare<MyEnum>()
    .value("Zero", MyEnum::Zero)
    .value("One",  MyEnum::One)
    .value("Two",  MyEnum::Two);

ponder::EnumObject zero(MyEnum::Zero);

zero.name(); // -> "Zero"

其他回答

我的解决方案是不使用宏。

优点:

你知道你在做什么 访问是通过哈希映射进行的,因此适用于许多有值枚举 不需要考虑顺序值或非连续值 既enum到字符串和字符串到enum转换,而添加的enum值必须添加在一个额外的地方

缺点:

您需要将所有枚举值复制为文本 哈希映射中的访问必须考虑字符串大小写 维护如果添加值是痛苦的-必须添加在enum和直接翻译映射

所以…直到c++实现c# Enum。解析功能,我将坚持这个:

            #include <unordered_map>

            enum class Language
            { unknown, 
                Chinese, 
                English, 
                French, 
                German
                // etc etc
            };

            class Enumerations
            {
            public:
                static void fnInit(void);

                static std::unordered_map <std::wstring, Language> m_Language;
                static std::unordered_map <Language, std::wstring> m_invLanguage;

            private:
                static void fnClear();
                static void fnSetValues(void);
                static void fnInvertValues(void);

                static bool m_init_done;
            };

            std::unordered_map <std::wstring, Language> Enumerations::m_Language = std::unordered_map <std::wstring, Language>();
            std::unordered_map <Language, std::wstring> Enumerations::m_invLanguage = std::unordered_map <Language, std::wstring>();

            void Enumerations::fnInit()
            {
                fnClear();
                fnSetValues();
                fnInvertValues();
            }

            void Enumerations::fnClear()
            {
                m_Language.clear();
                m_invLanguage.clear();
            }

            void Enumerations::fnSetValues(void)
            {   
                m_Language[L"unknown"] = Language::unknown;
                m_Language[L"Chinese"] = Language::Chinese;
                m_Language[L"English"] = Language::English;
                m_Language[L"French"] = Language::French;
                m_Language[L"German"] = Language::German;
                // and more etc etc
            }

            void Enumerations::fnInvertValues(void)
            {
                for (auto it = m_Language.begin(); it != m_Language.end(); it++)
                {
                    m_invLanguage[it->second] = it->first;
                }
            }

            // usage -
            //Language aLanguage = Language::English;
            //wstring sLanguage = Enumerations::m_invLanguage[aLanguage];

            //wstring sLanguage = L"French" ;
            //Language aLanguage = Enumerations::m_Language[sLanguage];

在类/struct (struct默认为public成员)和重载操作符中使用enum的解决方案:

struct Color
{
    enum Enum { RED, GREEN, BLUE };
    Enum e;

    Color() {}
    Color(Enum e) : e(e) {}

    Color operator=(Enum o) { e = o; return *this; }
    Color operator=(Color o) { e = o.e; return *this; }
    bool operator==(Enum o) { return e == o; }
    bool operator==(Color o) { return e == o.e; }
    operator Enum() const { return e; }

    std::string toString() const
    {
        switch (e)
        {
        case Color::RED:
            return "red";
        case Color::GREEN:
            return "green";
        case Color::BLUE:
            return "blue";
        default:
            return "unknown";
        }
    }
};

从外部看,它几乎完全像一个类枚举:

Color red;
red = Color::RED;
Color blue = Color::BLUE;

cout << red.toString() << " " << Color::GREEN << " " << blue << endl;

这将输出“red 12”。你可以重载<<使蓝色输出成为一个字符串(尽管这可能会导致歧义,所以不可能),但它不会与Color::GREEN一起工作,因为它不会自动转换为Color。

隐式转换为Enum(隐式转换为int或给定类型)的目的是能够做到:

Color color;
switch (color) ...

这是可行的,但这也意味着这也是可行的:

int i = color;

对于枚举类,它不会编译。 如果重载两个函数,接受枚举和整数,或者删除隐式转换…

另一个解决方案将涉及使用实际的枚举类和静态成员:

struct Color
{
    enum class Enum { RED, GREEN, BLUE };
    static const Enum RED = Enum::RED, GREEN = Enum::GREEN, BLUE = Enum::BLUE;

    //same as previous...
};

它可能会占用更多的空间,并且花费更长的时间,但会导致隐式int转换的编译错误。我就会用这个!

虽然这样做肯定有开销,但我认为它比我见过的其他代码更简单,看起来更好。还可以添加功能,这些功能都可以在类中进行范围限定。

编辑:这是有效的,大多数可以在执行前编译:

class Color
{
public:
    enum class Enum { RED, GREEN, BLUE };
    static const Enum RED = Enum::RED, GREEN = Enum::GREEN, BLUE = Enum::BLUE;

    constexpr Color() : e(Enum::RED) {}
    constexpr Color(Enum e) : e(e) {}

    constexpr bool operator==(Enum o) const { return e == o; }
    constexpr bool operator==(Color o) const { return e == o.e; }
    constexpr operator Enum() const { return e; }

    Color& operator=(Enum o) { const_cast<Enum>(this->e) = o; return *this; }
    Color& operator=(Color o) { const_cast<Enum>(this->e) = o.e; return *this; }

    std::string toString() const
    {
        switch (e)
        {
        case Enum::RED:
            return "red";
        case Enum::GREEN:
            return "green";
        case Enum::BLUE:
            return "blue";
        default:
            return "unknown";
        }
    }
private:
    const Enum e;
};

你可以滥用用户定义的文字来达到想要的结果:

enum
{
  AAA = "AAA"_h8,
  BB = "BB"_h8,
};
   
std::cout << h8::to_string(AAA) << std::endl;
std::cout << h8::to_string(BB) << std::endl;

这将一个字符串打包成一个整数,这是可逆的。请看这里的例子。

对于c++ 17 c++ 20,您将对反思研究小组(SG7)的工作感兴趣。还有一系列平行的论文,包括措辞(P0194)和基本原理、设计和进化(P0385)。(链接解析为每个系列的最新论文。)

从P0194r2(2016-10-15)开始,该语法将使用建议的reflexpr关键字:

meta::get_base_name_v<
  meta::get_element_m<
    meta::get_enumerators_m<reflexpr(MyEnum)>,
    0>
  >

例如(改编自Matus Choclik的reflexpr clang分支):

#include <reflexpr>
#include <iostream>

enum MyEnum { AAA = 1, BBB, CCC = 99 };

int main()
{
  auto name_of_MyEnum_0 = 
    std::meta::get_base_name_v<
      std::meta::get_element_m<
        std::meta::get_enumerators_m<reflexpr(MyEnum)>,
        0>
    >;

  // prints "AAA"
  std::cout << name_of_MyEnum_0 << std::endl;
}

静态反射未能进入c++ 17(更确切地说,进入了2016年11月在Issaquah举行的标准会议上提出的可能是最终草案),但有信心它将进入c++ 20;摘自赫布·萨特的旅行报告:

特别是,反射研究小组审查了最新合并的静态反射提案,并发现它准备在我们的下一次会议上进入主要的进化小组,开始考虑TS或下一个标准的统一静态反射提案。

几天前我也遇到了同样的问题。没有一些奇怪的宏魔法,我找不到任何c++解决方案,所以我决定写一个CMake代码生成器来生成简单的开关case语句。

用法:

enum2str_generate(
  PATH          <path to place the files in>
  CLASS_NAME    <name of the class (also prefix for the files)>
  FUNC_NAME     <name of the (static) member function>
  NAMESPACE     <the class will be inside this namespace>
  INCLUDES      <LIST of files where the enums are defined>
  ENUMS         <LIST of enums to process>
  BLACKLIST     <LIST of constants to ignore>
  USE_CONSTEXPR <whether to use constexpr or not (default: off)>
  USE_C_STRINGS <whether to use c strings instead of std::string or not (default: off)>
)

该函数搜索文件系统中的include文件(使用include_directories命令提供的include目录),读取它们并执行一些regex来生成类和函数。

注意:constexpr在c++中意味着内联,所以使用USE_CONSTEXPR选项将只生成一个头类!

例子:

- includes a。h:

enum AAA : char { A1, A2 };

typedef enum {
   VAL1          = 0,
   VAL2          = 1,
   VAL3          = 2,
   VAL_FIRST     = VAL1,    // Ignored
   VAL_LAST      = VAL3,    // Ignored
   VAL_DUPLICATE = 1,       // Ignored
   VAL_STRANGE   = VAL2 + 1 // Must be blacklisted
} BBB;

/ CMakeLists.txt:

include_directories( ${PROJECT_SOURCE_DIR}/includes ...)

enum2str_generate(
   PATH       "${PROJECT_SOURCE_DIR}"
   CLASS_NAME "enum2Str"
   NAMESPACE  "abc"
   FUNC_NAME  "toStr"
   INCLUDES   "a.h" # WITHOUT directory
   ENUMS      "AAA" "BBB"
   BLACKLIST  "VAL_STRANGE")

生成:

/ enum2Str.hpp:

/*!
  * \file enum2Str.hpp
  * \warning This is an automatically generated file!
  */

#ifndef ENUM2STR_HPP
#define ENUM2STR_HPP

#include <string>
#include <a.h>

namespace abc {

class enum2Str {
 public:
   static std::string toStr( AAA _var ) noexcept;
   static std::string toStr( BBB _var ) noexcept;
};

}

#endif // ENUM2STR_HPP

/ enum2Str.cpp:

/*!
  * \file enum2Str.cpp
  * \warning This is an automatically generated file!
  */

#include "enum2Str.hpp"

namespace abc {

/*!
 * \brief Converts the enum AAA to a std::string
 * \param _var The enum value to convert
 * \returns _var converted to a std::string
 */
std::string enum2Str::toStr( AAA _var ) noexcept {
   switch ( _var ) {
      case A1: return "A1";
      case A2: return "A2";
      default: return "<UNKNOWN>";
   }
}

/*!
 * \brief Converts the enum BBB to a std::string
 * \param _var The enum value to convert
 * \returns _var converted to a std::string
 */
std::string enum2Str::toStr( BBB _var ) noexcept {
   switch ( _var ) {
      case VAL1: return "VAL1";
      case VAL2: return "VAL2";
      case VAL3: return "VAL3";
      default: return "<UNKNOWN>";
   }
}
}

更新:

脚本现在还支持作用域枚举(枚举类|struct)和 我将它与一些我经常使用的其他脚本一起移动到一个单独的repo: https://github.com/mensinda/cmakeBuildTools