Smart Enumerator Generator, EnumGenerator

    Hello everybody!

    A few years ago, I began to worry about creating static (created and modified before the compilation process) enumerations. I didn’t want the simple transfers that are implemented in C / C ++, but with a set of additional features, including associated data columns of an arbitrary type, a kind of static database with access by a unique identifier.

    Then, in my understanding, three types of objects that could be a unique identifier were clearly distinguished: a numerical index, a numerical identifier, and a symbolic identifier. When trying to apply each of them to solve the problem of switching from a key to a value, their main disadvantages and advantages formed:

    • The numerical index is a unique integer value, an element of a sequential array, for C / C ++ it is the range [0; n), where n is the size of the array. If we see index 5, then this implies that there are also indices [0; 4]. Examples: classic C-array index, hash table, physical memory cell address. Brief conclusion: the processing speed is maximum, the accompaniment is minimal.
    • A numeric identifier is a unique integer value that does not have the obligation to be consistent. Examples: a handle to an arbitrary (file, socket, device) object, a thread identifier, the address of some variable or function in the process. Brief conclusion: processing speed is high, average accompaniment.
    • A character identifier is a unique string value, which, unlike numbers, is itself endowed with some logical meaning. Examples: preprocessor definition using #define, classic enum enumeration, variable name in program, key in json object, value in XML format. Brief conclusion: processing speed is minimal, maximum accompaniment.

    The main objective of the EnumGenerator project is to generate an enumeration that conveniently, safely, and efficiently integrates these identifiers into a single construct. So that one value can be accessed in three ways:
    1. Very fast on a numerical index. For the main use in the text of the program. Example: regular listing.
    2. Fast and flexible with a numerical identifier. To save values ​​to non-volatile storage and safely restore from it. To exchange data with other programs that may have an older or newer version of the same listing. Example: database, network communication protocol.
    3. Conveniently and clearly by symbolic identifier. To save values ​​to configuration files that can be edited by humans, and safely restore from it. Example: configuration file * .ini, * .json, * .yaml, *. Xml.

    EnumGenerator input, enumeration of Color3 in an Excel table:

    EnumGenerator output:
    Enumeration Color3 in the file 'Enums.hpp'
    class Color3
        enum Value
            Black = 0, //! men in black
            Blue = 1, //! blue ocean
            Green = 2, //! green forest
            Invalid = 3,
            Red = 4, //! lady in red
            White = 5 //! white snow
        static const int ValueCount = 6;
        static const Value ValueInvalid = Invalid;
        Color3(Value val = ValueInvalid) : m_ValueCur(val) {}
        Color3(const Color3 &other) : m_ValueCur(other.m_ValueCur) {}
        explicit Color3(int val) : m_ValueCur(ValueInvalid)
            int index = NS_JDSoft::NS_EnumGenerator::ValueFindLinear(IDInteger, ValueCount, val);
            if (index >= 0) m_ValueCur = Value(index);
        explicit Color3(const char * val) : m_ValueCur(ValueInvalid)
            int index = NS_JDSoft::NS_EnumGenerator::StringFindBinary(StringValues, ValueCount, val);
            if (index >= 0) m_ValueCur = Value(index);
        Color3 &operator =(Value val) { m_ValueCur = val; return *this; }
        Color3 &operator =(const Color3 &other) { m_ValueCur = other.m_ValueCur; return *this; }
        bool operator ==(Value val) const { return m_ValueCur == val; }
        bool operator ==(const Color3 &other) const { return m_ValueCur == other.m_ValueCur; }
        bool isValid() const { return m_ValueCur != ValueInvalid; }
        Value toValue() const { return m_ValueCur; }
        int toInt() const { return IDInteger[m_ValueCur]; }
        const char * toString() const { return StringValues[m_ValueCur]; }
        static const char * enumName() { return "Color3"; }
        static const char * const StringValues[ValueCount];
        static const int IDInteger[ValueCount];
        Value m_ValueCur;

    Enumeration Color3 in the file 'Enums.cpp'
    const char * const Color3::StringValues[Color3::ValueCount]=
    const int Color3::IDInteger[Color3::ValueCount] =

    An example of using the Color3 enumeration in a Qt test project:
    #include "Enums.hpp"
    // Создание объекта перечисления и проверка невалидности его значения
    Color3 colorPen;
    QCOMPARE(colorPen.isValid(), false); // isValid()
    QVERIFY(colorPen == Color3::Invalid); // operator ==(Value val)
    // Проверка правильности основных преобразований
    QCOMPARE(colorPen.toValue(), Color3::Invalid); // toValue()
    QCOMPARE(colorPen.toInt(), -1); // toInt()
    QCOMPARE(colorPen.toString(), "Invalid"); // toString()
    // Задание валидного значения
    colorPen = Color3::Red; // operator =(Value val)
    QCOMPARE(colorPen.isValid(), true);
    // Проверка правильности основных преобразований
    QCOMPARE(colorPen.toValue(), Color3::Red);
    QCOMPARE(colorPen.toInt(), 0xFF0000);
    QCOMPARE(colorPen.toString(), "Red");
    // Создание объекта с известным значением
    QCOMPARE(Color3(Color3::Green).toString(), "Green");
    QCOMPARE(Color3(0x00FF00).toString(), "Green");
    QCOMPARE(Color3("Green").toString(), "Green");
    // Сравнение объектов
    QVERIFY(Color3(0x0000FF) == Color3("Blue")); // operator ==(const Color3 &other)

    How to try EnumGenerator?
    1. Download the project to your computer, Download zip and unzip it.
    2. Run the Enums.lua file in the lua interpreter.
      The easiest way for windows users:
      1) Download the minimum version of the lua interpreter and unzip it.
      2) Right-click on the file 'Enums.lua', click on 'Open' or 'Open with' and select the unpacked file 'lua.exe'.
    3. Make sure that the files 'Enums.hpp' and 'Enums.cpp' were generated in the directory. Done! :)

    How to use EnumGenerator in your project?
    1. Try EnumGenerator as described in the previous paragraph.
    2. Move the directory with the file 'EnumGenerator.lua' to a stable place.
      The path to the directory with the file 'EnumGenerator.lua' will be called 'ENUMGENERATOR_PATH'.
    3. Open the file 'Enums.lua' and edit it so that the EnumGenerator variable contains the full path to the generator.
      For this and subsequent projects this needs to be done once.
    4. Copy the files 'Enums.xls' and 'Enums.lua' into the directory of your project.
    5. Edit the file 'Enums.xls' in Excel or OpenOffice for your enumerations.
    6. Save the edited file 'Enums.xls' as is, additionally save it in csv format to the file 'Enums.csv' and close the editor.
    7. Run the Enums.lua file from the directory of your project in the lua interpreter and make sure that the generation was successful:
      • When launched in console mode, the script writes “Successfully completed!”.
      • When launched from another program, the interpreter return code is 0.

    Data flow chart when using EnumGenerator:

    Here is an example that clearly demonstrates the main features of using a generator, an Excel file.

    Let's consider a part of this file in more detail:

    Input file structure:
    1. The file consists of independent block parts, [0; ∞]. Each Part begins with the word “PartBegin” and ends with the word “PartEnd” in the leftmost cell. After “PartBegin”, the required and optional parameters of this part are indicated in parentheses in the same cell, for example: PartBegin (Type = Enum, Name = Country). All lines between "PartBegin" and "PartEnd" are the body of the part (PartData). The sequence of parts is taken into account.
    2. The body of the part (PartData) consists of the 0th column (the leftmost), which determines the type of the entire row and subsequent cells [0; ∞]. Now 3 types are implemented: Header - data title, Comment - comment, "" (empty line) - contains the main data in accordance with the title.
    3. The data header (Header) describes the required column identifier and possible additional parameters.

    I hope this looks clear, simple and logical.

    I will be glad to reviews and tips for improvement, thanks!

    Related links:
    String enum - string enum
    Another implementation of Enums for Python

    Also popular now: