From 54cc6c55b26533d2351b9e741c3d7af83e6f7ab8 Mon Sep 17 00:00:00 2001 From: peri4 Date: Wed, 3 Aug 2022 14:14:24 +0300 Subject: [PATCH] pip_cmg and picodeinfo.h doc --- doc/pages/code_model.md | 297 ++++++++++++++++++++++++++++++++++ libs/main/code/picodeinfo.h | 129 +++++++++++++-- libs/main/code/picodemodule.h | 2 + 3 files changed, 419 insertions(+), 9 deletions(-) create mode 100644 doc/pages/code_model.md diff --git a/doc/pages/code_model.md b/doc/pages/code_model.md new file mode 100644 index 00000000..47426be4 --- /dev/null +++ b/doc/pages/code_model.md @@ -0,0 +1,297 @@ +\~english \page code_model Code generation +\~russian \page code_model Генерация кода + +\~english + +\~russian + +# Введение + +Кодогенерация помогает в случаях, когда нужен доступ к строковому представлению +сущностей (классов, перечислений, ...), либо автоматизированная де/сериализация +структур и классов. + +Например, необходимо для создания интерфейса получить в готовом виде список +пар "имя" = "значение" от какого-либо перечисления, либо обойти список +вложенных в класс структур с дополнительными метками. Или просто описать +структуру любой сложности, пометить поля номерами и получить готовые операторы +де/сериализации для PIBinaryStream с возможностью будущих изменений и сохранением +обратной совместимости. + +# pip_cmg + +PIP предоставляет утилиту, которая берет на вход файлы исходного кода, пути +включения, параметры и макросы, и на выходе создает h/cpp пару файлов с +необходимым функционалом. В зависимости от параметров, в этих файлах будут +присутствовать секции: + * метаинформации о сущностях; + * операторы де/сериализации; + * возможность получить PIVariant любого члена структуры по имени. + +Параметры обработки: + * -s - не следовать "#include" внутри файлов; + * -I - добавить папку включения (например, -I.. -I../some_dir -I/usr/include); + * -D - добавить макрос, PICODE всегда объявлен (например, -DMY_DEFINE добавит макрос MY_DEFINE). + +Параметры создания: + * -A - создать всё; + * -M - создать метаинформацию (имена и типы всех членов, иерархия включения); + * -E - создать перечисления (списки перечислений); + * -S - создать операторы де/сериализации; + * -G - создать методы получения значений переменных по именам; + * -o - имя файлов модели без расширения (например, "ccm" - создадутся файлы "ccm.h" и "ccm.cpp") + +# CMake + +Для автоматизации кодогенерации существует CMake макрос pip_code_model, который сам вызывает pip_cmg и +следит за актуальностью модели. + +Формат вызова макроса: +\code{.cmake} +pip_code_model( file0 [file1 ...] [OPTIONS opt0 [opt1 ...] ] [NAME name]) +\endcode + +Параметры: + * out_var - имя переменной, куда будут записаны абсолютные пути сгенерённых файлов; + * file... - файлы для генерации, допускаются относительные или абсолютные пути; + * OPTIONS - передаваемые в pip_cmg параметры, например, "-Es"; + * NAME - базовое имя файлов модели, если не указано, то используется "ccm_${PROJECT_NAME}". + +Этот макрос сам включает все пути для PIP. + +Для получения актуальных параметров pip_cmg можно вызывать "pip_cmg -v". + +# Подробности + +## Метаинформация + +Метаинформация - это текстовое представление всех членов и методов структуры или класса C++. +Для доступа к ним используется PICodeInfo::classesInfo->value("name"), который возвращает +указатель на структуру PICodeInfo::ClassInfo, содержащую всю информацию о сущности. + +В любой структуре PICodeInfo есть поле "MetaMap meta", содержащее произвольные +данные, видимые для кодогенератора, но невидимые для компилятора. +Для этого используется макрос PIMETA(), который необходимо вписать после объявления +переменной, метода либо сущности, например: +\code{.cpp} +struct MyStruct: Header PIMETA(type=in,port=5005) { + ushort calcChecksum() const PIMETA(show=true); + bool checkChecksum() const; + void setChecksum(); + uchar block_id PIMETA(type=int) = 0; +}; +enum FOV { // Поле зрения + fovWide PIMETA(label="Широкое",angle=90), + fovNormal PIMETA(label="Нормальное",angle=60), + fovNarrow PIMETA(label="Узкое",angle=30) +}; +\endcode +В этом примере в каждом месте, где указана PIMETA, её можно будет получить через "MetaMap meta". + +## Перечисления + +Перечисления записываются отдельно, для доступа к ним используется PICodeInfo::enumsInfo->value("name"), +который возвращает указатель на структуру PICodeInfo::EnumInfo, содержащую всю информацию о перечеслении. + +## Операторы де/сериализации + +Эти операторы создаются в h файле для всех сутрктур и классов, в которых есть хотя бы один член, +доступный для работы. Операторы работают с PIBinaryStream в двух вариантах - простом или через PIChunkStream. + +Для каждой структуры можно указать режим де/сериализации с помощью фиксированного поля в PIMETA: + * нет указаний - работа через PIChunkStream; + * simple-stream - работа просто через PIBinaryStream; + * no-stream - не создавать операторы. + +Например, для структуры +\code{.cpp} +struct DateTime { + uchar seconds; + uchar minutes; + uchar hours; + uchar days; + uchar months; + uchar years; +}; +\endcode +создадутся операторы +\code{.cpp} +BINARY_STREAM_WRITE(DateTime) { + PIChunkStream cs; + cs << cs.chunk(1, v.seconds); + cs << cs.chunk(2, v.minutes); + cs << cs.chunk(3, v.hours); + cs << cs.chunk(4, v.days); + cs << cs.chunk(5, v.months); + cs << cs.chunk(6, v.years); + s << cs.data(); + return s; +} +BINARY_STREAM_READ (DateTime) { + PIByteArray csba; s >> csba; + PIChunkStream cs(csba); + while (!cs.atEnd()) { + switch (cs.read()) { + case 1: cs.get(v.seconds); break; + case 2: cs.get(v.minutes); break; + case 3: cs.get(v.hours); break; + case 4: cs.get(v.days); break; + case 5: cs.get(v.months); break; + case 6: cs.get(v.years); break; + } + } + return s; +} +\endcode +, где порядок id последовательнен. + +Для структуры +\code{.cpp} +struct DateTime PIMETA(simple-stream) { + uchar seconds; + uchar minutes; + uchar hours; + uchar days; + uchar months; + uchar years; +}; +\endcode +создадутся операторы +\code{.cpp} +BINARY_STREAM_WRITE(DateTime) { + s << v.seconds; + s << v.minutes; + s << v.hours; + s << v.days; + s << v.months; + s << v.years; + return s; +} +BINARY_STREAM_READ (DateTime) { + s >> v.seconds; + s >> v.minutes; + s >> v.hours; + s >> v.days; + s >> v.months; + s >> v.years; + return s; +} +\endcode + +Для структуры +\code{.cpp} +struct DateTime PIMETA(no-stream) { + uchar seconds; + uchar minutes; + uchar hours; + uchar days; + uchar months; + uchar years; +}; +\endcode +не создадутся операторы + +В режиме работы через PIChunkStream также можно указать индивидуальные id, +что очень полезно для сохранения обратной совместимости структур разных версий: +Для структуры +\code{.cpp} +struct DateTime { + PIMETA(id=10) uchar seconds; + PIMETA(id=11) uchar minutes; + PIMETA(id=12) uchar hours; + PIMETA(id=20) uchar days; + PIMETA(id=21) uchar months; + PIMETA(id=22) uchar years; +}; +\endcode +\code{.cpp} +BINARY_STREAM_WRITE(DateTime) { + PIChunkStream cs; + cs << cs.chunk(10, v.seconds); + cs << cs.chunk(11, v.minutes); + cs << cs.chunk(12, v.hours); + cs << cs.chunk(20, v.days); + cs << cs.chunk(21, v.months); + cs << cs.chunk(22, v.years); + s << cs.data(); + return s; +} +BINARY_STREAM_READ (DateTime) { + PIByteArray csba; s >> csba; + PIChunkStream cs(csba); + while (!cs.atEnd()) { + switch (cs.read()) { + case 10: cs.get(v.seconds); break; + case 11: cs.get(v.minutes); break; + case 12: cs.get(v.hours); break; + case 20: cs.get(v.days); break; + case 21: cs.get(v.months); break; + case 22: cs.get(v.years); break; + } + } + return s; +} +\endcode + +Если в этом режиме какую-либо переменную надо проигнорировать, то вместо +числа id можно указать "-": +\code{.cpp} +struct DateTime { + PIMETA(id=10) uchar seconds; + PIMETA(id=11) uchar minutes; + PIMETA(id=-) uchar hours; + PIMETA(id=20) uchar days; + PIMETA(id=21) uchar months; + PIMETA(id=-) uchar years; +}; +\endcode +\code{.cpp} +BINARY_STREAM_WRITE(DateTime) { + PIChunkStream cs; + cs << cs.chunk(10, v.seconds); + cs << cs.chunk(11, v.minutes); + cs << cs.chunk(20, v.days); + cs << cs.chunk(21, v.months); + s << cs.data(); + return s; +} +BINARY_STREAM_READ (DateTime) { + PIByteArray csba; s >> csba; + PIChunkStream cs(csba); + while (!cs.atEnd()) { + switch (cs.read()) { + case 10: cs.get(v.seconds); break; + case 11: cs.get(v.minutes); break; + case 20: cs.get(v.days); break; + case 21: cs.get(v.months); break; + } + } + return s; +} +\endcode + +# Интеграция в проект + +При использовании CMake достаточно включить содержимое переменной out_var в приложение +или библиотеку, и включить через "#include" сгенерированный заголовочный файл в нужном месте. +После этого перечисления и метаинформация будут загружены в момент запуска, до int main(), +а операторы станут доступны через заголовочный файл. + +CMakeLists.txt: +\code{.cmake} +project(myapp) +pip_code_model(CCM "structures.h" OPTIONS "-EMs") +add_executable(${PROJECT_NAME} ${CPPS} ${CCM}) +\endcode + +C++: +\code{.cpp} +#include "ccm_myapp.h" + +... + +PICodeInfo::EnumInfo * ei = PICodeInfo::enumsInfo->value("MyEnum", 0); +if (ei) { + ei->members.forEach([](const PICodeInfo::EnumeratorInfo & e){piCout << e.name << "=" << e.value;}); +} +\endcode diff --git a/libs/main/code/picodeinfo.h b/libs/main/code/picodeinfo.h index 7e576c40..584d6973 100644 --- a/libs/main/code/picodeinfo.h +++ b/libs/main/code/picodeinfo.h @@ -1,8 +1,8 @@ /*! \file picodeinfo.h * \ingroup Code * \~\brief - * \~english C++ code info structs - * \~russian Структуры для C++ кода + * \~english C++ code info structs. See \ref code_model. + * \~russian Структуры для C++ кода. Подробнее \ref code_model. */ /* PIP - Platform Independent Primitives @@ -34,17 +34,24 @@ class PIVariant; +//! \~english Namespace contains structures for code generation. See \ref code_model. +//! \~russian Пространство имен содержит структуры для кодогенерации. Подробнее \ref code_model. namespace PICodeInfo { + +//! \~english +//! Type modifiers +//! \~russian +//! Модификаторы типа enum TypeFlag { NoFlag, - Const = 0x01, - Static = 0x02, - Mutable = 0x04, - Volatile = 0x08, - Inline = 0x10, - Virtual = 0x20, - Extern = 0x40 + Const /** const */ = 0x01, + Static /** static */ = 0x02, + Mutable /** mutable */ = 0x04, + Volatile /** volatile */ = 0x08, + Inline /** inline */ = 0x10, + Virtual /** virtual */ = 0x20, + Extern /** extern */ = 0x40 }; typedef PIFlags TypeFlags; @@ -52,49 +59,145 @@ typedef PIMap MetaMap; typedef PIByteArray(*AccessValueFunction)(const void *, const char *); typedef const char*(*AccessTypeFunction)(const char *); + +//! \~english Type information +//! \~russian Информация о типе struct PIP_EXPORT TypeInfo { TypeInfo(const PIConstChars & n = PIConstChars(), const PIConstChars & t = PIConstChars(), PICodeInfo::TypeFlags f = 0, int b = -1) {name = n; type = t; flags = f; bits = b;} + + //! \~english Returns if variable if bitfield + //! \~russian Возвращает битовым ли полем является переменная bool isBitfield() const {return bits > 0;} + + //! \~english Custom PIMETA content + //! \~russian Произвольное содержимое PIMETA MetaMap meta; + + //! \~english Name + //! \~russian Имя PIConstChars name; + + //! \~english Type + //! \~russian Тип PIConstChars type; + + //! \~english Modifiers + //! \~russian Модификаторы PICodeInfo::TypeFlags flags; + + //! \~english Bitfield variable bit count + //! \~russian Количество бит битового поля int bits; }; + +//! \~english Method information +//! \~russian Информация о методе struct PIP_EXPORT FunctionInfo { + + //! \~english Custom PIMETA content + //! \~russian Произвольное содержимое PIMETA MetaMap meta; + + //! \~english Name + //! \~russian Имя PIConstChars name; + + //! \~english Return type + //! \~russian Возвращаемые тип TypeInfo return_type; + + //! \~english Arguments types + //! \~russian Типы аргументов PIVector arguments; }; + +//! \~english Class or struct information +//! \~russian Информация о классе или структуре struct PIP_EXPORT ClassInfo { ClassInfo() {has_name = true;} + + //! \~english Custom PIMETA content + //! \~russian Произвольное содержимое PIMETA MetaMap meta; + + //! \~english Has name or not + //! \~russian Имеет или нет имя bool has_name; + + //! \~english Type + //! \~russian Тип PIConstChars type; + + //! \~english Name + //! \~russian Имя PIConstChars name; + + //! \~english Parent names + //! \~russian Имена родителей PIVector parents; + + //! \~english Variables + //! \~russian Переменные PIVector variables; + + //! \~english Methods + //! \~russian Методы PIVector functions; + + //! \~english Subclass list + //! \~russian Список наследников PIVector children_info; }; + +//! \~english Enumerator information +//! \~russian Информация об элементе перечисления struct PIP_EXPORT EnumeratorInfo { EnumeratorInfo(const PIConstChars & n = PIConstChars(), int v = 0) {name = n; value = v;} PIVariantTypes::Enumerator toPIVariantEnumerator() {return PIVariantTypes::Enumerator(value, name.toString());} + + //! \~english Custom PIMETA content + //! \~russian Произвольное содержимое PIMETA MetaMap meta; + + //! \~english Name + //! \~russian Имя PIConstChars name; + + //! \~english Value + //! \~russian Значение int value; }; + +//! \~english Enum information +//! \~russian Информация о перечислении struct PIP_EXPORT EnumInfo { + + //! \~english Returns member name with value "value" + //! \~russian Возвращает имя элемента со значением "value" PIString memberName(int value) const; + + //! \~english Returns member value with name "name" + //! \~russian Возвращает значение элемента с именем "name" int memberValue(const PIString & name) const; + + //! \~english Returns as PIVariantTypes::Enum + //! \~russian Возвращает как PIVariantTypes::Enum PIVariantTypes::Enum toPIVariantEnum(); + + //! \~english Custom PIMETA content + //! \~russian Произвольное содержимое PIMETA MetaMap meta; + + //! \~english Name + //! \~russian Имя PIConstChars name; + + //! \~english Members + //! \~russian Элементы PIVector members; }; @@ -161,9 +264,17 @@ inline PICout operator <<(PICout s, const PICodeInfo::EnumInfo & v) { return s; } + +//! \~english Pointer to single storage of PICodeInfo::ClassInfo, access by name +//! \~russian Указатель на единое хренилище PICodeInfo::ClassInfo, доступ по имени extern PIP_EXPORT PIMap * classesInfo; + +//! \~english Pointer to single storage of PICodeInfo::EnumInfo, access by name +//! \~russian Указатель на единое хренилище PICodeInfo::EnumInfo, доступ по имени extern PIP_EXPORT PIMap * enumsInfo; + extern PIP_EXPORT PIMap * accessValueFunctions; + extern PIP_EXPORT PIMap * accessTypeFunctions; inline PIByteArray getMemberValue(const void * p, const char * class_name, const char * member_name) { diff --git a/libs/main/code/picodemodule.h b/libs/main/code/picodemodule.h index 97a3bb30..207d3310 100644 --- a/libs/main/code/picodemodule.h +++ b/libs/main/code/picodemodule.h @@ -35,9 +35,11 @@ //! //! \~english //! These files provides parsing C++ code and storage to use results of \a pip_cmg utility. +//! See \ref code_model. //! //! \~russian //! Эти файлы обеспечивают разбор C++ кода и хранение результатов работы утилиты \a pip_cmg. +//! Подробнее \ref code_model. //! //! \~\authors //! \~english