Is there a simple way to convert C++ enum to string?

后端 未结 30 2247
我在风中等你
我在风中等你 2020-11-22 10:37

Suppose we have some named enums:

enum MyEnum {
      FOO,
      BAR = 0x50
};

What I googled for is a script (any language) that scans all

相关标签:
30条回答
  • 2020-11-22 10:48
    #include <stdarg.h>
    #include <algorithm>
    #include <string> 
    #include <vector>
    #include <sstream>
    #include <map>
    
    #define SMART_ENUM(EnumName, ...)                                   \
    class EnumName                                                      \
    {                                                                   \
    private:                                                            \
        static std::map<int, std::string> nameMap;                      \
    public:                                                             \
        enum {__VA_ARGS__};                                             \
    private:                                                            \
        static std::map<int, std::string> initMap()                     \
        {                                                               \
            using namespace std;                                        \
                                                                        \
            int val = 0;                                                \
            string buf_1, buf_2, str = #__VA_ARGS__;                    \
            replace(str.begin(), str.end(), '=', ' ');                  \
            stringstream stream(str);                                   \
            vector<string> strings;                                     \
            while (getline(stream, buf_1, ','))                         \
                strings.push_back(buf_1);                               \
            map<int, string> tmp;                                       \
            for(vector<string>::iterator it = strings.begin();          \
                                                   it != strings.end(); \
                                                   ++it)                \
            {                                                           \
                buf_1.clear(); buf_2.clear();                           \
                stringstream localStream(*it);                          \
                localStream>> buf_1 >> buf_2;                           \
                if(buf_2.size() > 0)                                    \
                    val = atoi(buf_2.c_str());                          \
                tmp[val++] = buf_1;                                     \
            }                                                           \
            return tmp;                                                 \
        }                                                               \
    public:                                                             \
        static std::string toString(int aInt)                           \
        {                                                               \
            return nameMap[aInt];                                       \
        }                                                               \
    };                                                                  \
    std::map<int, std::string>                                          \
    EnumName::nameMap = EnumName::initMap();
    

    Usage:

    SMART_ENUM(MyEnum, ONE=1, TWO, THREE, TEN=10, ELEVEN)
    cout<<MyEnum::toString(MyEnum::TWO);
    cout<<MyEnum::toString(10);
    
    0 讨论(0)
  • 2020-11-22 10:48

    I want to post this in case someone finds it useful.

    In my case, I simply need to generate ToString() and FromString() functions for a single C++11 enum from a single .hpp file.

    I wrote a python script that parses the header file containing the enum items and generates the functions in a new .cpp file.

    You can add this script in CMakeLists.txt with execute_process, or as a pre-build event in Visual Studio. The .cpp file will be automatically generated, without the need to manually update it each time a new enum item is added.

    generate_enum_strings.py

    # This script is used to generate strings from C++ enums
    
    import re
    import sys
    import os
    
    fileName = sys.argv[1]
    enumName = os.path.basename(os.path.splitext(fileName)[0])
    
    with open(fileName, 'r') as f:
        content = f.read().replace('\n', '')
    
    searchResult = re.search('enum(.*)\{(.*?)\};', content)
    tokens = searchResult.group(2)
    tokens = tokens.split(',')
    tokens = map(str.strip, tokens)
    tokens = map(lambda token: re.search('([a-zA-Z0-9_]*)', token).group(1), tokens)
    
    textOut = ''
    textOut += '\n#include "' + enumName + '.hpp"\n\n'
    textOut += 'namespace myns\n'
    textOut += '{\n'
    textOut += '    std::string ToString(ErrorCode errorCode)\n'
    textOut += '    {\n'
    textOut += '        switch (errorCode)\n'
    textOut += '        {\n'
    
    for token in tokens:
        textOut += '        case ' + enumName + '::' + token + ':\n'
        textOut += '            return "' + token + '";\n'
    
    textOut += '        default:\n'
    textOut += '            return "Last";\n'
    textOut += '        }\n'
    textOut += '    }\n'
    textOut += '\n'
    textOut += '    ' + enumName + ' FromString(const std::string &errorCode)\n'
    textOut += '    {\n'
    textOut += '        if ("' + tokens[0] + '" == errorCode)\n'
    textOut += '        {\n'
    textOut += '            return ' + enumName + '::' + tokens[0] + ';\n'
    textOut += '        }\n'
    
    for token in tokens[1:]:
        textOut += '        else if("' + token + '" == errorCode)\n'
        textOut += '        {\n'
        textOut += '            return ' + enumName + '::' + token + ';\n'
        textOut += '        }\n'
    
    textOut += '\n'
    textOut += '        return ' + enumName + '::Last;\n'
    textOut += '    }\n'
    textOut += '}\n'
    
    fileOut = open(enumName + '.cpp', 'w')
    fileOut.write(textOut)
    

    Example:

    ErrorCode.hpp

    #pragma once
    
    #include <string>
    #include <cstdint>
    
    namespace myns
    {
        enum class ErrorCode : uint32_t
        {
            OK = 0,
            OutOfSpace,
            ConnectionFailure,
            InvalidJson,
            DatabaseFailure,
            HttpError,
            FileSystemError,
            FailedToEncrypt,
            FailedToDecrypt,
            EndOfFile,
            FailedToOpenFileForRead,
            FailedToOpenFileForWrite,
            FailedToLaunchProcess,
    
            Last
        };
    
        std::string ToString(ErrorCode errorCode);
        ErrorCode FromString(const std::string &errorCode);
    }
    

    Run python generate_enum_strings.py ErrorCode.hpp

    Result:

    ErrorCode.cpp

    #include "ErrorCode.hpp"
    
    namespace myns
    {
        std::string ToString(ErrorCode errorCode)
        {
            switch (errorCode)
            {
            case ErrorCode::OK:
                return "OK";
            case ErrorCode::OutOfSpace:
                return "OutOfSpace";
            case ErrorCode::ConnectionFailure:
                return "ConnectionFailure";
            case ErrorCode::InvalidJson:
                return "InvalidJson";
            case ErrorCode::DatabaseFailure:
                return "DatabaseFailure";
            case ErrorCode::HttpError:
                return "HttpError";
            case ErrorCode::FileSystemError:
                return "FileSystemError";
            case ErrorCode::FailedToEncrypt:
                return "FailedToEncrypt";
            case ErrorCode::FailedToDecrypt:
                return "FailedToDecrypt";
            case ErrorCode::EndOfFile:
                return "EndOfFile";
            case ErrorCode::FailedToOpenFileForRead:
                return "FailedToOpenFileForRead";
            case ErrorCode::FailedToOpenFileForWrite:
                return "FailedToOpenFileForWrite";
            case ErrorCode::FailedToLaunchProcess:
                return "FailedToLaunchProcess";
            case ErrorCode::Last:
                return "Last";
            default:
                return "Last";
            }
        }
    
        ErrorCode FromString(const std::string &errorCode)
        {
            if ("OK" == errorCode)
            {
                return ErrorCode::OK;
            }
            else if("OutOfSpace" == errorCode)
            {
                return ErrorCode::OutOfSpace;
            }
            else if("ConnectionFailure" == errorCode)
            {
                return ErrorCode::ConnectionFailure;
            }
            else if("InvalidJson" == errorCode)
            {
                return ErrorCode::InvalidJson;
            }
            else if("DatabaseFailure" == errorCode)
            {
                return ErrorCode::DatabaseFailure;
            }
            else if("HttpError" == errorCode)
            {
                return ErrorCode::HttpError;
            }
            else if("FileSystemError" == errorCode)
            {
                return ErrorCode::FileSystemError;
            }
            else if("FailedToEncrypt" == errorCode)
            {
                return ErrorCode::FailedToEncrypt;
            }
            else if("FailedToDecrypt" == errorCode)
            {
                return ErrorCode::FailedToDecrypt;
            }
            else if("EndOfFile" == errorCode)
            {
                return ErrorCode::EndOfFile;
            }
            else if("FailedToOpenFileForRead" == errorCode)
            {
                return ErrorCode::FailedToOpenFileForRead;
            }
            else if("FailedToOpenFileForWrite" == errorCode)
            {
                return ErrorCode::FailedToOpenFileForWrite;
            }
            else if("FailedToLaunchProcess" == errorCode)
            {
                return ErrorCode::FailedToLaunchProcess;
            }
            else if("Last" == errorCode)
            {
                return ErrorCode::Last;
            }
    
            return ErrorCode::Last;
        }
    }
    
    0 讨论(0)
  • 2020-11-22 10:49

    X-macros are the best solution. Example:

    #include <iostream>
    
    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)
    

    However, I usually prefer the following method, so that it's possible to tweak the string a bit.

    #define X(a, b) a,
    #define X(a, b) b,
    
    X(Red, "red")
    X(Green, "green")
    // etc.
    
    0 讨论(0)
  • 2020-11-22 10:52

    You may want to check out GCCXML.

    Running GCCXML on your sample code produces:

    <GCC_XML>
      <Namespace id="_1" name="::" members="_3 " mangled="_Z2::"/>
      <Namespace id="_2" name="std" context="_1" members="" mangled="_Z3std"/>
      <Enumeration id="_3" name="MyEnum" context="_1" location="f0:1" file="f0" line="1">
        <EnumValue name="FOO" init="0"/>
        <EnumValue name="BAR" init="80"/>
      </Enumeration>
      <File id="f0" name="my_enum.h"/>
    </GCC_XML>
    

    You could use any language you prefer to pull out the Enumeration and EnumValue tags and generate your desired code.

    0 讨论(0)
  • 2020-11-22 10:52

    Another answer: in some contexts, it makes sense to define your enumeration in a non-code format, like a CSV, YAML, or XML file, and then generate both the C++ enumeration code and the to-string code from the definition. This approach may or may not be practical in your application, but it's something to keep in mind.

    0 讨论(0)
  • 2020-11-22 10:52

    You could use a reflection library, like Ponder. You register the enums and then you can convert them back and forth with the API.

    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"
    
    0 讨论(0)
提交回复
热议问题