Привет всем!
Несколько лет назад меня начал беспокоить вопрос создания статических (создаваемых и изменяемых до процесса компиляции) перечислений. Перечислений я хотел не простых, которые реализованы в С/С++, а с набором дополнительных возможностей, в том числе и ассоциированными столбцами данных произвольного типа, своего рода статическая база данных с доступом по уникальному идентификатору.
Тогда в моем понимании ясно выделились три типа объектов, которые могут быть уникальным идентификатором: числовой индекс, числовой идентификатор, символьный идентификатор. При попытке применить каждый из них для решения задачи перехода от ключа к значению сформировались основные их недостатки и преимущества:
Основная задача проекта EnumGenerator — сгенерировать перечисление, которое удобно, безопасно и эффективно объединяет эти идентификаторы в единую конструкцию. Чтобы к одному значению можно было обратиться тремя способами:
Входные данные EnumGenerator, перечисление Color3 в таблице Excel:

Выходные данные EnumGenerator:
Пример использования перечисления Color3 в тестовом проекте Qt:
Как попробовать EnumGenerator?
Как использовать EnumGenerator в своем проекте?
Схема прохождения данных при использовании EnumGenerator:

Вот пример, который наглядно демонстрирует основные возможности использования генератора, файл Excel.
Рассмотрим часть этого файла более подробно:

Структура входного файла:
Надеюсь, это выглядит наглядным, простым и логичным.
Буду рад отзывам и советам по улучшению, спасибо!
Связанные ссылки:
String enum — строковые enum
Ещё одна реализация Enums для Python
Несколько лет назад меня начал беспокоить вопрос создания статических (создаваемых и изменяемых до процесса компиляции) перечислений. Перечислений я хотел не простых, которые реализованы в С/С++, а с набором дополнительных возможностей, в том числе и ассоциированными столбцами данных произвольного типа, своего рода статическая база данных с доступом по уникальному идентификатору.
Тогда в моем понимании ясно выделились три типа объектов, которые могут быть уникальным идентификатором: числовой индекс, числовой идентификатор, символьный идентификатор. При попытке применить каждый из них для решения задачи перехода от ключа к значению сформировались основные их недостатки и преимущества:
- Числовой индекс — это уникальное целочисленное значение, элемент последовательного массива, для С/С++ это диапазон [0;n), где n — размер массива. Если мы видим индекс 5, то это подразумевает, что обязательно есть и индексы [0;4]. Примеры: индекс классического С-массива, хеш-таблица, адрес ячейки физической памяти. Краткий вывод: скорость обработки максимальная, сопровождаемость минимальная.
- Числовой идентификатор — это уникальное целочисленное значение, которое лишено обязанности быть последовательным. Примеры: дескриптор произвольного (файл, сокет, устройство) объекта, идентификатор потока, адрес какой-то переменной или функции в процессе. Краткий вывод: скоро��ть обработки высокая, сопровождаемость средняя.
- Символьный идентификатор — это уникальное строковое значение, которое, в отличие от чисел, само по себе наделено некоторым логическим смыслом. Примеры: препроцессорное определение с помощью #define, классическое перечисление с помощью enum, название переменной в программе, ключ в объекте json, значение в формате XML. Краткий вывод: скорость обработки минимальная, сопровождаемость максимальная.
Основная задача проекта EnumGenerator — сгенерировать перечисление, которое удобно, безопасно и эффективно объединяет эти идентификаторы в единую конструкцию. Чтобы к одному значению можно было обратиться тремя способами:
- Очень быстро по числовому индексу. Для основного использования в тексте программы. Пример: обычное перечисление.
- Быстро и гибко по числовому идентификатору. Для сохранения значения в энергонезависимое хранилища и безопасное восстановление из него. Для обмена данными с другими программами, которые могут иметь более старую или более новую версию этого же перечисления. Пример: база данных, протокол сетевого взаимодействия.
- Удобно и наглядно по символьному идентификатору. Для сохранения значения в конфигурационный файлы, который может редактироваться человеком, и безопасное восстановление из него. Пример: файл конфигурации *.ini, *.json, *.yaml,*.xml.
Входные данные EnumGenerator, перечисление Color3 в таблице Excel:

Выходные данные EnumGenerator:
Перечисление Color3 в файле '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; };
Перечисление Color3 в файле '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 };
Пример использования перечисления Color3 в тестовом проекте Qt:
#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)
Как попробовать EnumGenerator?
- Скачать проект на свой компьютер, Download zip и распаковать его.
- Запустить файл Enums.lua в интерпретаторе lua.
Самый простой способ для пользователей windows:
1) Скачать минимальную версию интерпретатора lua и распаковать его.
2) Щелкнуть правой кнопкой мыши по файлу 'Enums.lua', кликнуть «Открыть» или «Открыть с помощью» и выбрать распакованный файл 'lua.exe'.
- Убедиться, что в директории сгенерировались файлы 'Enums.hpp' и 'Enums.cpp'. Готово! :)
Как использовать EnumGenerator в своем проекте?
- Попробовать EnumGenerator как описано в предыдущем пункте.
- Переместить директорию с файлом 'EnumGenerator.lua' в стабильное место.
Путь к директории с файлом 'EnumGenerator.lua' будем называть 'ENUMGENERATOR_PATH'. - Открыть файл 'Enums.lua' и отредактировать его так, чтобы переменная EnumGenerator содержала полный путь к генератору.
Для этого и последующих проектов это нужно сделать один раз.
- Скопировать файлы 'Enums.xls' и 'Enums.lua' в директорию своего проекта.
- Отредактировать файл 'Enums.xls' в Excel или OpenOffice для своих перечислений.
- Сохранить отредактированный файл 'Enums.xls' как есть, дополнительно сохранить его в формате csv в файл 'Enums.csv' и закрыть редактор.
- Запустить файл Enums.lua из директории своего проекта в интерпретаторе lua и убедиться, что генерация прошла успешно:
- При запуске в консольном режиме скрипт пишет «Successfully completed!».
- При запуске из другой программы код возврата интерпретатора равен 0.
Схема прохождения данных при использовании EnumGenerator:

Вот пример, который наглядно демонстрирует основные возможности использования генератора, файл Excel.
Рассмотрим часть этого файла более подробно:

Структура входного файла:
- Файл состоит из независимых частей-блоков, [0;∞]. Каждая часть (Part) начинается словом «PartBegin» и заканчивается словом «PartEnd» в самой левой ячейке. После «PartBegin» в этой же ячейке в круглых скобках указываются обязательные и опциональные параметры этой части, пример: PartBegin(Type=Enum, Name=Country). Все строки между «PartBegin» и «PartEnd» являются телом части (PartData). Последовательность частей учитывается.
- Тело части (PartData) состоит из 0-го столбца (самый левый), который определяет тип всей строки и последующих ячеек [0;∞]. Сейчас реализовано 3 типа: Header — заглавие данных, Comment — комментарий, ""(пустая строка) — содержит основные данные в соответствии с заглавием.
- Заглавие данных (Header) описывает обязательный идентификатор столбца и возможные дополнительные параметры.
Надеюсь, это выглядит наглядным, простым и логичным.
Буду рад отзывам и советам по улучшению, спасибо!
Связанные ссылки:
String enum — строковые enum
Ещё одна реализация Enums для Python
