enum to string in modern C++11 / C++14 / C++17 and future C++20

后端 未结 28 2233
逝去的感伤
逝去的感伤 2020-11-22 16:57

Contrary to all other similar questions, this question is about using the new C++ features.

  • 2008 c Is there a simple way to convert C++ enum to string?
  • <
28条回答
  •  独厮守ぢ
    2020-11-22 17:18

    Just generate your enums. Writing a generator for that purpose is about five minutes' work.

    Generator code in java and python, super easy to port to any language you like, including C++.

    Also super easy to extend by whatever functionality you want.

    example input:

    First = 5
    Second
    Third = 7
    Fourth
    Fifth=11
    

    generated header:

    #include 
    
    enum class Hallo
    {
        First = 5,
        Second = 6,
        Third = 7,
        Fourth = 8,
        Fifth = 11
    };
    
    std::ostream & operator << (std::ostream &, const Hallo&);
    

    generated cpp file

    #include 
    
    #include "Hallo.h"
    
    std::ostream & operator << (std::ostream &out, const Hallo&value)
    {
        switch(value)
        {
        case Hallo::First:
            out << "First";
            break;
        case Hallo::Second:
            out << "Second";
            break;
        case Hallo::Third:
            out << "Third";
            break;
        case Hallo::Fourth:
            out << "Fourth";
            break;
        case Hallo::Fifth:
            out << "Fifth";
            break;
        default:
            out << "";
        }
    
        return out;
    }
    

    And the generator, in a very terse form as a template for porting and extension. This example code really tries to avoid overwriting any files but still use it at your own risk.

    package cppgen;
    
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.io.PrintWriter;
    import java.nio.charset.Charset;
    import java.util.LinkedHashMap;
    import java.util.Map;
    import java.util.Map.Entry;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    public class EnumGenerator
    {
        static void fail(String message)
        {
            System.err.println(message);
            System.exit(1);
        }
    
        static void run(String[] args)
        throws Exception
        {
            Pattern pattern = Pattern.compile("\\s*(\\w+)\\s*(?:=\\s*(\\d+))?\\s*", Pattern.UNICODE_CHARACTER_CLASS);
            Charset charset = Charset.forName("UTF8");
            String tab = "    ";
    
            if (args.length != 3)
            {
                fail("Required arguments:   ");
            }
    
            String enumName = args[0];
    
            File inputFile = new File(args[1]);
    
            if (inputFile.isFile() == false)
            {
                fail("Not a file: [" + inputFile.getCanonicalPath() + "]");
            }
    
            File outputDir = new File(args[2]);
    
            if (outputDir.isDirectory() == false)
            {
                fail("Not a directory: [" + outputDir.getCanonicalPath() + "]");
            }
    
            File headerFile = new File(outputDir, enumName + ".h");
            File codeFile = new File(outputDir, enumName + ".cpp");
    
            for (File file : new File[] { headerFile, codeFile })
            {
                if (file.exists())
                {
                    fail("Will not overwrite file [" + file.getCanonicalPath() + "]");
                }
            }
    
            int nextValue = 0;
    
            Map fields = new LinkedHashMap<>();
    
            try
            (
                BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(inputFile), charset));
            )
            {
                while (true)
                {
                    String line = reader.readLine();
    
                    if (line == null)
                    {
                        break;
                    }
    
                    if (line.trim().length() == 0)
                    {
                        continue;
                    }
    
                    Matcher matcher = pattern.matcher(line);
    
                    if (matcher.matches() == false)
                    {
                        fail("Syntax error: [" + line + "]");
                    }
    
                    String fieldName = matcher.group(1);
    
                    if (fields.containsKey(fieldName))
                    {
                        fail("Double fiend name: " + fieldName);
                    }
    
                    String valueString = matcher.group(2);
    
                    if (valueString != null)
                    {
                        int value = Integer.parseInt(valueString);
    
                        if (value < nextValue)
                        {
                            fail("Not a monotonous progression from " + nextValue + " to " + value + " for enum field " + fieldName);
                        }
    
                        nextValue = value;
                    }
    
                    fields.put(fieldName, nextValue);
    
                    ++nextValue;
                }
            }
    
            try
            (
                PrintWriter headerWriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(headerFile), charset));
                PrintWriter codeWriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(codeFile), charset));
            )
            {
                headerWriter.println();
                headerWriter.println("#include ");
                headerWriter.println();
                headerWriter.println("enum class " + enumName);
                headerWriter.println('{');
                boolean first = true;
                for (Entry entry : fields.entrySet())
                {
                    if (first == false)
                    {
                        headerWriter.println(",");
                    }
    
                    headerWriter.print(tab + entry.getKey() + " = " + entry.getValue());
    
                    first = false;
                }
                if (first == false)
                {
                    headerWriter.println();
                }
                headerWriter.println("};");
                headerWriter.println();
                headerWriter.println("std::ostream & operator << (std::ostream &, const " + enumName + "&);");
                headerWriter.println();
    
                codeWriter.println();
                codeWriter.println("#include ");
                codeWriter.println();
                codeWriter.println("#include \"" + enumName + ".h\"");
                codeWriter.println();
                codeWriter.println("std::ostream & operator << (std::ostream &out, const " + enumName + "&value)");
                codeWriter.println('{');
                codeWriter.println(tab + "switch(value)");
                codeWriter.println(tab + '{');
                first = true;
                for (Entry entry : fields.entrySet())
                {
                    codeWriter.println(tab + "case " + enumName + "::" + entry.getKey() + ':');
                    codeWriter.println(tab + tab + "out << \"" + entry.getKey() + "\";");
                    codeWriter.println(tab + tab + "break;");
    
                    first = false;
                }
                codeWriter.println(tab + "default:");
                codeWriter.println(tab + tab + "out << \"\";");
                codeWriter.println(tab + '}');
                codeWriter.println();
                codeWriter.println(tab + "return out;");
                codeWriter.println('}');
                codeWriter.println();
            }
        }
    
        public static void main(String[] args)
        {
            try
            {
                run(args);
            }
            catch(Exception exc)
            {
                exc.printStackTrace();
                System.exit(1);
            }
        }
    }
    

    And a port to Python 3.5 because different enough to be potentially helpful

    import re
    import collections
    import sys
    import io
    import os
    
    def fail(*args):
        print(*args)
        exit(1)
    
    pattern = re.compile(r'\s*(\w+)\s*(?:=\s*(\d+))?\s*')
    tab = "    "
    
    if len(sys.argv) != 4:
        n=0
        for arg in sys.argv:
            print("arg", n, ":", arg, " / ", sys.argv[n])
            n += 1
        fail("Required arguments:   ")
    
    enumName = sys.argv[1]
    
    inputFile = sys.argv[2]
    
    if not os.path.isfile(inputFile):
        fail("Not a file: [" + os.path.abspath(inputFile) + "]")
    
    outputDir = sys.argv[3]
    
    if not os.path.isdir(outputDir):
        fail("Not a directory: [" + os.path.abspath(outputDir) + "]")
    
    headerFile = os.path.join(outputDir, enumName + ".h")
    codeFile = os.path.join(outputDir, enumName + ".cpp")
    
    for file in [ headerFile, codeFile ]:
        if os.path.exists(file):
            fail("Will not overwrite file [" + os.path.abspath(file) + "]")
    
    nextValue = 0
    
    fields = collections.OrderedDict()
    
    for line in open(inputFile, 'r'):
        line = line.strip()
    
        if len(line) == 0:
            continue
    
        match = pattern.match(line)
    
        if match == None:
            fail("Syntax error: [" + line + "]")
    
        fieldName = match.group(1)
    
        if fieldName in fields:
            fail("Double field name: " + fieldName)
    
        valueString = match.group(2)
    
        if valueString != None:
            value = int(valueString)
    
            if value < nextValue:
                fail("Not a monotonous progression from " + nextValue + " to " + value + " for enum field " + fieldName)
    
            nextValue = value
    
        fields[fieldName] = nextValue
    
        nextValue += 1
    
    headerWriter = open(headerFile, 'w')
    codeWriter = open(codeFile, 'w')
    
    try:
        headerWriter.write("\n")
        headerWriter.write("#include \n")
        headerWriter.write("\n")
        headerWriter.write("enum class " + enumName + "\n")
        headerWriter.write("{\n")
        first = True
        for fieldName, fieldValue in fields.items():
            if not first:
                headerWriter.write(",\n")
    
            headerWriter.write(tab + fieldName + " = " + str(fieldValue))
    
            first = False
        if not first:
            headerWriter.write("\n")
        headerWriter.write("};\n")
        headerWriter.write("\n")
        headerWriter.write("std::ostream & operator << (std::ostream &, const " + enumName + "&);\n")
        headerWriter.write("\n")
    
        codeWriter.write("\n")
        codeWriter.write("#include \n")
        codeWriter.write("\n")
        codeWriter.write("#include \"" + enumName + ".h\"\n")
        codeWriter.write("\n")
        codeWriter.write("std::ostream & operator << (std::ostream &out, const " + enumName + "&value)\n")
        codeWriter.write("{\n")
        codeWriter.write(tab + "switch(value)\n")
        codeWriter.write(tab + "{\n")
        for fieldName in fields.keys():
            codeWriter.write(tab + "case " + enumName + "::" + fieldName + ":\n")
            codeWriter.write(tab + tab + "out << \"" + fieldName + "\";\n")
            codeWriter.write(tab + tab + "break;\n")
        codeWriter.write(tab + "default:\n")
        codeWriter.write(tab + tab + "out << \"\";\n")
        codeWriter.write(tab + "}\n")
        codeWriter.write("\n")
        codeWriter.write(tab + "return out;\n")
        codeWriter.write("}\n")
        codeWriter.write("\n")
    finally:
        headerWriter.close()
        codeWriter.close()
    

提交回复
热议问题