pip_cmg and picodeinfo.h doc
This commit is contained in:
297
doc/pages/code_model.md
Normal file
297
doc/pages/code_model.md
Normal file
@@ -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<include_dir> - добавить папку включения (например, -I.. -I../some_dir -I/usr/include);
|
||||
* -D<define> - добавить макрос, PICODE всегда объявлен (например, -DMY_DEFINE добавит макрос MY_DEFINE).
|
||||
|
||||
Параметры создания:
|
||||
* -A - создать всё;
|
||||
* -M - создать метаинформацию (имена и типы всех членов, иерархия включения);
|
||||
* -E - создать перечисления (списки перечислений);
|
||||
* -S - создать операторы де/сериализации;
|
||||
* -G - создать методы получения значений переменных по именам;
|
||||
* -o <output_file> - имя файлов модели без расширения (например, "ccm" - создадутся файлы "ccm.h" и "ccm.cpp")
|
||||
|
||||
# CMake
|
||||
|
||||
Для автоматизации кодогенерации существует CMake макрос pip_code_model, который сам вызывает pip_cmg и
|
||||
следит за актуальностью модели.
|
||||
|
||||
Формат вызова макроса:
|
||||
\code{.cmake}
|
||||
pip_code_model(<out_var> 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
|
||||
@@ -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<PICodeInfo::TypeFlag> TypeFlags;
|
||||
@@ -52,49 +59,145 @@ typedef PIMap<PIString, PIString> 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<PICodeInfo::TypeInfo> 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<PIConstChars> parents;
|
||||
|
||||
//! \~english Variables
|
||||
//! \~russian Переменные
|
||||
PIVector<PICodeInfo::TypeInfo> variables;
|
||||
|
||||
//! \~english Methods
|
||||
//! \~russian Методы
|
||||
PIVector<PICodeInfo::FunctionInfo> functions;
|
||||
|
||||
//! \~english Subclass list
|
||||
//! \~russian Список наследников
|
||||
PIVector<PICodeInfo::ClassInfo * > 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<PICodeInfo::EnumeratorInfo> 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<PIConstChars, PICodeInfo::ClassInfo * > * classesInfo;
|
||||
|
||||
//! \~english Pointer to single storage of PICodeInfo::EnumInfo, access by name
|
||||
//! \~russian Указатель на единое хренилище PICodeInfo::EnumInfo, доступ по имени
|
||||
extern PIP_EXPORT PIMap<PIConstChars, PICodeInfo::EnumInfo * > * enumsInfo;
|
||||
|
||||
extern PIP_EXPORT PIMap<PIConstChars, PICodeInfo::AccessValueFunction> * accessValueFunctions;
|
||||
|
||||
extern PIP_EXPORT PIMap<PIConstChars, PICodeInfo::AccessTypeFunction> * accessTypeFunctions;
|
||||
|
||||
inline PIByteArray getMemberValue(const void * p, const char * class_name, const char * member_name) {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user