📜 ⬆️ ⬇️

Smart Listing Generator, EnumGenerator

Hello to all!

A few years ago, I began to be bothered by the issue of creating static (created and modified before the compilation process) enums. I wanted transfers not simple, which are implemented in C / C ++, but with a set of additional features, including associated data columns of arbitrary type, a kind of static database with access by a unique identifier.

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


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

EnumGenerator input, Color3 listing in Excel table:

')
Output EnumGenerator:
Enumeration of Color3 in the file 'Enums.hpp'
class Color3 { public: 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"; } private: static const char * const StringValues[ValueCount]; static const int IDInteger[ValueCount]; Value m_ValueCur; }; 


Enumeration of Color3 in the file 'Enums.cpp'
 const char * const Color3::StringValues[Color3::ValueCount]= { "Black", "Blue", "Green", "Invalid", "Red", "White" }; const int Color3::IDInteger[Color3::ValueCount] = { 0, 255, 65280, -1, 16711680, 16777215 }; 


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 minimal version of the lua interpreter and unpack it.
    2) Right-click on the file 'Enums.lua', click on "Open" or "Open with" and select the unpacked file 'lua.exe'.
  3. Ensure that the files 'Enums.hpp' and 'Enums.cpp' are generated in the directory. Done! :)

How to use EnumGenerator in your project?
  1. Try the EnumGenerator as described in the previous paragraph.
  2. Move the directory with the file 'EnumGenerator.lua' to a stable location.
    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, you need to do this once.
  4. Copy the files 'Enums.xls' and 'Enums.lua' into the directory of your project.
  5. Edit the 'Enums.xls' file in Excel or OpenOffice for your enumerations.
  6. Save the edited 'Enums.xls' file as is, additionally save it in csv format to the 'Enums.csv' file and close the editor.
  7. Run the Enums.lua file from the project directory of your project in the lua interpreter and verify that the generation was successful:
    • When launched in console mode, the script writes “Successfully completed!”.
    • When started from another program, the interpreter return code is 0.

The data flow scheme using EnumGenerator:


Here is an example that clearly demonstrates the basic uses of the generator, the Excel file.

Consider 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" in the same cell in the parentheses indicate the required and optional parameters of this part, 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 part (PartData) consists of the 0th column (leftmost), which determines the type of the entire row and the subsequent cells [0; ∞]. Three types are now implemented: Header - data title, Comment - comment, "" (empty line) - contains basic 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 feedback and tips for improvement, thank you!

Related Links:
String enum - string enum
Another Enums implementation for Python

Source: https://habr.com/ru/post/240537/


All Articles