Трохи рефлексії для С++. Частина третя: документационная



Дана стаття є третьою і завершальною в циклі про розробку бібліотеки cpp runtime, яка додає можливість додавати трохи метаінформації про класи З++ і далі використовувати цю інформацію під час виконання програми.

Дана стаття розповідає про те, як користувач може взаємодіяти з бібліотекою і демонструє чистової результат роботи, описаної у перших двох статтях циклу.

Посилання на всі статті циклу1. Про розробку
2. Про підготовку до публікації
3. Про результат




Посилання на репозиторій бібліотеки
Те, що викладається в даній статті, вірно для коміта e9c34bb.

Структура проекту— Документація ---
readme.md
Рідмі-файл. Замислювався як файл, з якого починається ознайомлення з проектом.

README.cmake
Файл, який розповідає, як зібрати за допомогою CMake бібліотеку та/або постачаються разом з нею додаткові цілі збірки.

/docs
Папка з документацією… на Жаль, єдине, що в неї зараз є – це посилання на даний цикл статей. Я помилився, відклав документацію наостанок, і мені не вистачило духу зайнятися нею після тритижневої роботи без вихідних над цим циклом статей.

— Файли бібліотеки cpprt ---
/include
Програмний інтерфейс для доступу до API бібліотеки cpprt.

/src
Папка з вихідним кодом бібліотеки.

— Складання для популярних систем ---
build
Готові проекти для деяких популярних тулчейнов і IDE.

/lib
Готові збірки бібліотеки для деяких популярних компіляторів.

— Додаткові проекти ---
/examples
Папка з вихідним кодом для прикладів… В даний момент, для одного простого приклад, що демонструє принципи використання бібліотеки.

/tools
Папка з вихідним кодом для тулзов, що йдуть в комплекті з бібліотекою.

— Система складання ---
build
Пуста папка, в якій рекомендується виконувати збирання бібліотеки і/або додаткових цілей складання.

CMakeLists.txt
Файл із описом конфігурації CMake.

— Інші файли ---
LICENCE.txt
Файл ліцензії, під якою розповсюджується проект (MIT-ліцензія).

copying.txt
Файл з шаблоном шапки для файлів вихідного коду, в якій міститься інформація про ліцензії.


Збірка бібліотеки та супровідних матеріалів

Про те, як створювалася система для складання проекту, можна почитати у другій статті циклу (посилання).

Рекомендований спосіб складання самої бібліотеки і супровідних матеріалів передбачає використання CMake. Якщо у вас не встановлений CMake, можете завантажити його звідси.

Для складання через командний рядок потрібно виконати наступні дії:

1. Створити де-небудь, де вам зручно, папку, в рамках якої ви будете здійснювати збірку. Перейти в цю папку:
mkdir {your-build-folder}
cd {your-build-folder}
{your-build-folder} — папка, в якій ви будете виконувати збирання.

2. Викликати команду для генерації конфігурації:
cmake -G "{generator-name}" {options} {cpprt-folder-path}
{generator-name} — це ім'я генератора для вашого тучлейна.
{options} — перелік опцій для вказівки того, що саме ви бажаєте збирати.
Детальніше про опціїНезалежно від обраних опцій, згенерованих конфігах (файлах IDE) завжди буде принаймні одна мета збірки — для статичної складання самої бібліотеки cpprt. Для того, щоб згенерувати конфіги для складання додатково супровідних проектів, ви можете використовувати наступні опції (першою вказується значення опції за умовчанням):

-DBUILD_ALL={OFF/ON}
Якщо ця опція виставлена значення ON, то будуть сгенерированны конфігурації для всіх поставляються разом бібліотекою супровідних проектів. При цьому якщо опції для складання конкретних цілей складання.

-DBUILD_TOOLS={OFF/ON}
Якщо дана опція виставлена значення ON, то будуть сгенерированны конфігурації для складання інструментів, що поставляються разом з бібліотекою. В даний момент є один інструмент — console (про неї ще буде далі).

-DBUILD_EXAMPLES={OFF/ON}
Якщо дана опція виставлена значення ON, то будуть сгенерированны конфігурації для складання прикладів, що поставляються разом з бібліотекою. В даний момент є один приклад (simple_example) в якому описується класичний для ООП приклад ієрархії класів тварин.

Додаткові опції:
-DCONSOLE_WITH_EXAMPLE={ON/OFF}
Якщо дана опція виставлена в ON (за замовчуванням виставлена), то разом з вихідним кодом інструменту console будуть зібрані файли з описом тестових ієрархій класів.


{cpprt-folder-path} — папка, яка є коренем клону репозиторію cpprt.

Після виклику в папці {your-build-folder} утворюються конфігураційні файли для вашого тулчейна (або для вашої IDE). Далі нічого налаштовувати не потрібно. Достатньо просто запустити збірку потрібних цілей складання (проектів для IDE).

Можливості бібліотеки

З допомогою API, що надається бібліотекою cpprt, клас може бути зареєстрований. Якщо клас зареєстрований, то для нього відкриваються такі можливості:
1. Створення об'єкта класу під час виконання програми на підставі імені класу в строковому поданні.
2. Доступ до інформації про зареєстрованих спадкоємців і предків класу (в міру переданої при реєстрації інформації).
3. Можливість отримувати інформацію про абстрактності класів.
4. Можливість отримувати рядковий ім'я класу через методи його об'єктів.

Нижче представлений користувальницький інтерфейс для отримання метаінформації про класах. Тут опущені приватні члени класу, реалізації методів і деяка інша лушпиння. Тільки інтерфейс, через який відбувається взаємодія з метаданими зареєстрованих класів:

Інтерфейс бібліотеки cpprt
// Клас, що реалізує загальне управління метаданими cpprt.
// Об'єкт-одинак даного класу використовується для більшої
// частини дій з метаданими класів.
class CPPRTRuntime {
public:

//----------------------------------------------------------------
// ТИПИ

// Клас, об'єкти якого зберігають метаінформацію
// для всіх реєстрованих класів. Практично не
// має самостійного поведінки, тільки аксессоры
// для доступу до даних. Ініціалізацією і більшої
// частиною дій по реєстрації об'єктів даного
// класу займається CPPRTRuntime.
class ClassData {
public:

// Геттери для отримання імені класу.
// Повне рядковий ім'я класу відповідає
// запису повного імені в межах вихідного коду
// З++. Неймспейсы поділяються двома двокрапками
// (ns - скорочення від namespace). Умовно
// описати наступним чином відношення між
// маєтком і повним ім'ям класів.
// <fullName> = <ns>::<ns>::<ns>...<ns>::<name>
const char *fullName() const;
const char *name() const;

// Геттер, через який можна дізнатися, чи є
// клас абстрактним і, відповідно, можливо
// створювати об'єкти цього класу.
bool isInterface();

// Створення об'єкта класу, якому відповідає
// даний об'єкт ClassData.
ICPPRTManagedClass *createObject();
};

// Нумератор, з допомогою якого конфігуруються
// особливості отримання інформації про спадкоємців класу
// через метод CPPRTRuntime::observeChildren(...). Прапори ставлять
// наступні правила пошуку метаданих про спадкоємців:
enum ObservingFlag {

// Якщо виставлений цей прапор, то в перелік одержуваних
// метаданих не передаються метеданные для абстрактних
// класів.
ObservingFlagWithoutInterface = 1 << 0,

// Даний прапор вимагає виконувати пошук метаданих
// для всіх спадкоємців рекурсивно (перебір спадкоємців
// спадкоємців, і далі по древу спадкування).
ObservingFlagRecursive = 1 << 1,

// Якщо виставлений цей прапор, то в перелік одержуваних
// метаданих не передаються метадані класу, для якого
// виконувався пошук спадкоємців (аргумент inBaseRegistry
// методу CPPRTRuntime::observeChildren(...)
ObservingFlagIgnoreBase = 1 << 2
};

// Змінна, через яку передаються прапори при запиті
// інформації про спадкування класів. Прапори передають так,
// через побитное АБО (загальноприйнятий прийом, приклади
// далі покажуть, як конкретно це працює)
typedef char ObservingFlags;

//----------------------------------------------------------------
// МЕТОДИ

// Публічний метод, через який можна отримати інформацію
// про спадкоємців класу inBaseRegistry з урахуванням правил, які
// передаються через прапори inFlags (див. вище). Всі знайдені метадані,
// підходящі за критеріями, які ставлять прапори, додаються
// масив outRegistries.
// Якщо в якості inBaseRegistry передати NULL, то пошук буде
// виконуватися, умовно кажучи, починаючи з якогось умовного
// класу, від якого успадковуються будь-які реєструвалися
// користувачем класи, що не мають предків.
void observeChildren(ClassData *inBaseRegistry,
std::vector<ClassData *> &outRegistries,
ObservingFlags inInheritFlags = 0);

// Публічний метод для отримання метаінформації про класі
// через його повне рядковий ім'я.
ClassData *getClassData(const char *inClassName);

// Публічний метод, що дозволяє створити об'єкт по строковому
// імені його класу.
ICPPRTManagedClass *createObject(const char *inClassName);
};

// Функція для доступу до глобального менеджера метаданих класу.
CPPRTRuntime &cppRuntime();

// Завдання синоніми типу для зберігання метаданих. Користувачу бібліотеки
// рекомендується використовувати саме цей синонім у випадку, якщо потрібно
// описувати тип даного класу.
typedef CPPRTRuntime::ClassData CPPRTClassData;



Приклади використання API для взаємодії з метаданими:

1.Створення об'єкта по строковому імені його класу:
ICPPRTManagedClass *theTestClassObject = cppRuntime().createObject("TestClass");


2. Роздруківка інформації про абстрактності класу за вказівником на об'єкт базового класу:
void printObjectClassInformation(ICPPRTManagedClass *inObject) {
const CPPRTRuntime::ClassData &theClassData = *inObject->getClassDataRT();
std::cout << "Class" << theClassData.fullName() << " is "
<< theClassData.isInterface() ? "abstract" : "concrete" << std::endl;
}


3. Роздруківка імен всіх класів-спадкоємців класу TestClass. При цьому вибираються і спадкоємці спадкоємців рекурсивно, фільтруються абстрактні класи і одержуваний перелік не додається метаінформація для для самого класу TestClass:
std::vector<CPPRTClassData *> theChildData;
cppRuntime().observeChildren(TestClass::gClassData, theChildData,
ObservingFlagWithoutInterface |
ObservingFlagRecursive |
ObservingFlagIgnoreBase);

for (size_t theIndex = 0, theSize = theChildData.size(); theIndex < theSize; ++theIndex) {
std::cout << theChildData[theIndex]->fullName() << std::endl;
}


4. Роздруківка імен всіх класів, що не мають предків:
std::vector<CPPRTClassData *> theBasesData;
cppRuntime().observeChildren NULL, theBasesData);

for (size_t theIndex = 0, theSize = theBasesData.size(); theIndex < theSize; ++theIndex) {
std::cout << theBasesData [theIndex]->fullName() << std::endl;
}


Реєстрація класів

Тепер розберемося з тим, що необхідно для реєстрації класів:

1. Зареєстрований клас повинен мати у предків клас ICPPRTManagedClass; достатньо щоб базовий клас користувальницької ієрархії успадковував ICPPRTManagedClass. Приклад:
// UserClasses.h

// Базовий клас ієрархії об'єктів користувача.
// Успадковує ICPPRTManagedClass.
class UserBaseClass : public ICPPRTManagedClass {
. . . // <- Декларація класу, про неї далі
};

// Так як UserBaseClass успадковує ICPPRTManagedClass,
// UserInheritedClass не повинен успадковувати ICPPRTManagedClass.
class UserInheritedClass : public UserBaseClass { 
. . . // <- Декларація класу, про неї далі
};


2. Зареєстрований клас повинен володіти методами і полями, які використовуються бібліотекою cpprt для роботи. Щоб не писати їх вручну, краще скористатися макросами для реєстрації. Є дві групи макросів: для декларації необхідних полів і методів, і для реалізації.

Декларація. CPPRT_DECLARATION – макрос для декларації. Приймає один аргумент – ім'я класу, який реєструється (не рядок з ім'ям, а саме ім'я). Код, в який розкривається макрос, закінчується специфікатором доступу protected. Це зроблено для більш простого включення макросу в існуючий код, адже якщо використовувати макрос на початку декларації класу, то доступ для всіх наступних декларованих членів класу залишиться таким же, як без використання макросу (так як в С++ до оголошення іншого специфікатора доступу декларовані члени класу мають protected доступ). Приклад:
// UserClasses.h

class UserBaseClass : public ICPPRTManagedClass {
// Що з макросом, що без, всі наступні
// декларовані члени класу мають
// специфікатором доступу protected.
CPPRT_DECLARATION(UserBaseClass)

. . . // <- інші декларації членів класу
};

class UserInheritedClass : public UserBaseClass {
// Декларації для спадкоємця не потрібно
// вказувати базовий клас – тільки ім'я
// даного класу
CPPRT_DECLARATION(UserInheritedClass)

. . . // <- інші декларації членів класу
};


Реалізація. Основна частина метаінформації про класі передається в рамках макросу реалізації. Ці макроси діляться на групи в залежності від кількості базових класів.

CPPRT_CLASS_IMPLEMENTATION_BASE_{N}(C, B1, ..., BN)
CPPRT_INTERFACE_IMPLEMENTATION_BASE_{N}( C, B1, ..., BN)
N – кількість базових класів.
C – повне ім'я класу, для якого описується реалізація.
B1BN – повні імена базових класів. Ці класи мають бути зареєстровані у рамках cpprt.

Макрос CLASS використовується для реєстрації класів, об'єкти яких можна створювати, а з INTERFACE – для реєстрації абстрактних класів. Приклад:
// UserClasses.cpp

CPPRT_CLASS_IMPLEMENTATION_BASE_0(UserBaseClass);
CPPRT_CLASS_IMPLEMENTATION_BASE_1(UserInheritedClass, UserBaseClass);


Власне, все. Описаних речей досить для реєстрації класу в рамках cpprt. Живий приклад ви можете подивитися в рамках репозиторію (мета складання simple_example, опція -DBUILD_EXAMPLES={OFF/ON} для генерації проекту через CMake).

Тут теж наведу приклад для реєстрації більш складної ієрархії класів, ієрархія наслідування класів для якого-небудь типового шутера:

WeaponClasses.h
// Abstract classes
// Bases
class IWeapon : public ICPPRTManagedClass {
CPPRT_DECLARATION(IWeapon)
. . .
};

class IAntiAir {
CPPRT_DECLARATION(IAntiAir)
. . .
};

// Classification by weapon type
class IMachineGunWeapon : public IWeapon {
CPPRT_DECLARATION(IMachineGunWeapon)
. . .
};

class IRocketWeapon : public IWeapon {
CPPRT_DECLARATION(IRocketWeapon)
. . .
};

// Concrete classes
// Machine guns
class M16 : public IMachineGunWeapon {
CPPRT_DECLARATION(M16)
. . .
};

class Abakan : public IMachineGunWeapon {
. CPPRT_DECLARATION(Abakan)
. . .
};

class Uzi : public IMachineGunWeapon {
CPPRT_DECLARATION(Uzi)
. . .
};

// Rocket weapons
class Stinger : public IRocketWeapon, public IAntiAir {
CPPRT_DECLARATION(Stinger)
. . .
};

namespace IraqEdition {
class Bazuka : public IRocketWeapon {
CPPRT_DECLARATION(Bazuka)
. . .
};
}

// Tank inner classes
class Tank {
public:
class MachineGunTurret : public Abakan, public IAntiAir {
CPPRT_DECLARATION(MachineGunTurret)
. . .
};

class RocketTurret : public IRocketWeapon {
CPPRT_DECLARATION(RocketTurret)
. . .
};
};



WeaponClasses.cpp
// Abstract classes
// Bases
CPPRT_INTERFACE_IMPLEMENTATION_BASE_0(IWeapon)
CPPRT_INTERFACE_IMPLEMENTATION_BASE_0(IAntiAir)

// Classification by weapon type
CPPRT_INTERFACE_IMPLEMENTATION_BASE_1(IMachineGunWeapon, IWeapon)
CPPRT_INTERFACE_IMPLEMENTATION_BASE_1(IRocketWeapon, IWeapon)

// Concrete classes
// Machine guns
CPPRT_CLASS_IMPLEMENTATION_BASE_1(M16, IMachineGunWeapon)
CPPRT_CLASS_IMPLEMENTATION_BASE_1(Abakan, IMachineGunWeapon)
CPPRT_CLASS_IMPLEMENTATION_BASE_1(Uzi, IMachineGunWeapon)

// Rocket weapons
CPPRT_CLASS_IMPLEMENTATION_BASE_2(Stinger, IRocketWeapon, IAntiAir)
CPPRT_CLASS_IMPLEMENTATION_BASE_1(IraqEdition::Bazuka, IRocketWeapon)

// Tank inner classes
CPPRT_CLASS_IMPLEMENTATION_BASE_2(Tank::MachineGunTurret, IRocketWeapon, IAntiAir)

CPPRT_CLASS_IMPLEMENTATION_BASE_1(Tank::RocketTurret, IRocketWeapon)



Player.h
class Player {
private:
IWeapon *_weapon; // Set with NULL value in constructor
. . .

public:
// It's possible to set player's weapon by its class name.
void setWeapon(const char *inName) {
if (NULL != _weapon) delete _weapon;
_weapon = cppRuntime().createObject(inName);
}
. . .
};



Testdrive.cpp
int main() {
Player thePlayer;
thePlayer1.setWeapon("Abakan");
return 0;
}



Інші можливості

В рамках cpprt не обов'язково реєструвати всі класи і можна не вказувати всіх спадкоємців для класів. Це може бути корисно, наприклад, для приховування частини службового інтерфейсу спадкування класів. Наприклад:
// Dog.h

class Dog : public Mammal, public ISerializable {
CPPRT_DECLARATION(Dog)
. . .
};

// Dog.cpp

// ISerializable не вказується серед базових класів для Dog
// під час реєстрації. Це може бути службовий інтерфейс,
// про який немає сенсу знати при оперуванні з метаданими
// класів. У такому разі ISerializable взагалі не має сенсу
// реєструвати.
CPPRT_CLASS_IMPLEMENTATION_BASE_1(Dog, Mammal)


Console tool

Разом з бібліотекою, крім прикладу, у постачанні йде інструмент консоль (console). Він надає кілька команд для створення об'єктів по строковому імені їх класів (об'єкти зберігаються в тестовому масиві), а також для перегляду ієрархії успадкування для реєстрованих класів. Короткий опис команд, підтримуються консоллю:

help
Команда, при натисненні якої роздруковується коротка інформація про всіх доступних для консолі командах.

print (-c) ({full-class-name})
Якщо ця команда викликається без аргументів, то вона роздруковує перелік усіх створених в даний момент об'єктів із зазначенням їх класів.
Якщо переданий прапор -c (необов'язковий прапор), але при цьому не передано ім'я класу (необов'язковий параметр {full-class-name}), то цей виклик роздрукує дерева зареєстрованих класів-спадкоємців для всіх зареєстрованих класів, які не мають базових класів (кореневих класів).
Якщо крім прапора -c переданий параметр {full-class-name}, що задає ім'я класу, то ця команда роздрукує дерево зареєстрованих класів-спадкоємців для класу з переданим ім'ям.

create {class-name} {object-name}
Команда, яка створює об'єкт зареєстрованого класу {class-name} з ім'ям {object-name} в тестовому масиві об'єктів.

delete {object-name}
Видаляє об'єкт тестового масиву об'єктів по його імені.

exit
Команда для виходу з консолі. Завершує виконання програми консолі.

Мабуть, я розповів все про можливості використання бібліотеки. Якщо хочеться дізнатися як це все реалізовано — про більшої частини прийомів і трюків, які були використані в бібліотеці, я докладно розповів у першій статті циклу (посилання). Її прочитання достатньо для розуміння принципів роботи бібліотеки.

Плани на майбутнє

1. Реалізувати можливість обходу дерева спадкування не тільки від предків до спадкоємців, але і навпаки.

Прототип
std::vector<ClassData *> theParentsData;
cppRuntime().observeParents(TestClass::gClassData, theParentsData,
ObservingFlagWithoutInterface |
ObservingFlagRecursive);



Ця фіча реалізована елементарно в рамках поточної реалізації – потрібно зробити internal-методи для обходу спадкоємців шаблонними, з булевими аргументом ObserveChildren, в залежності від значення true/false якого вибирати метадані спадкоємців базових класів для переданих метаданих.

2. Налагодити нормальну інкапсуляцію даних для класів бібліотеки. При поточному дизайні бібліотеки назовні стирчать багато зайвих типів і методів (наприклад, у того ж класу CPPRTRuntime) які захаращують користувальницький інтерфейс бібліотеки.

3. Зробити ітератори для можливості обходити дерево метаінформації для класів. Це дозволить економити використання даних при рекурсивном обході дерева спадкоємців і дасть користувачу самому виконувати обхід дерева успадкування класів без порушення інкапсуляції.

Прототип
CPPRTRuntime::iterator theIterator = cppRuntime().iteratorForClassData(TestClass::gClassData);

// Обхід метаданих класів-спадкоємців класу TestClass до тих пір,
// поки не буде знайдений спадкоємець з ім'ям ClassToFind.
CPPRTRuntime::ClassData *theData = NULL;
while((theData = theIterator.nextChild()) != NULL) {
if (0 == strcmp(theData->name(), "ClassToFind")) {
// Якщо метадані для класу-спадкоємця з ім'ям знайдені – перевести ітератор на нього
theIterator.moveToChild();
}
}



В якості альтернативи ітератора можна використовувати шаблон проектування visitor (наприклад, як це зроблено в clang, ось великий приклад використання). Цей підхід взагалі часто використовується для обходу деревоподібних структур даних.

4. Зробити можливість додавання інформації до класів – щоб можна було фільтрувати класи за цими даними при запиті.

Прототип
// Classes.cpp

CPPRT_CLASS_IMPLEMENTATION_BASE_1(Car, IVehicle)
CPPRT_CLASS_IMPLEMENTATION_BASE_1(Bus, IVehicle)

CPPRT_CLASS_IMPLEMENTATION_BASE_1(BigBus, IVehicle).
// Додавання мітки "bold_in_editor" метаданих
// класу BigBus.
addTag("bold_in_editor");

// Using.cpp

// Отримання і роздруківка метаданих класів, у яких
// виставлена мітка "bold_in_editor"
std::vector<ClassData *> theChildData;
cppRuntime().observeChildren(TestClass::gClassData, theChildData,
ObservingFlagWithoutInterface |
ObservingFlagRecursive |
ObservingFlagIgnoreBase, "bold_in_editor");

for (size_t theIndex = 0, theSize = theChildData.size(); theIndex < theSize; ++theIndex) {
std::cout << theChildData[theIndex]->fullName() << std::endl;
}



4. Зробити підтримку шаблонів. Ось це вельми нетривіальне завдання. Потрібно добре думати з приводу того, чи дійсно потрібно (швидше за все, потрібно) і як взагалі можна описати API для використання шаблонозависимых класів.

5. Подумати про можливості роботи з метаданими класів в runtime. Наприклад, реєстрація класу в runtime, або навпаки – видалення класів зі списку реєстрованих.

Прототип
cppRuntime().createClassData("MyRTClass", new CPPRTRuntime::Fabric<MyRTClass>() );
. . .
// Use registered class
. . .
cppRuntime().removeClassData("MyRTClass");



В принципі, подібний функціонал доступний вже зараз, але це, швидше, баг дизайну, ніж фіча… Взагалі, ймовірно, ця фіча небезпечна і варто добре подумати, чи потрібна вона взагалі.

6. Місце для ваших пропозицій. Пропонуйте, що ще може бути корисним. Обговоримо.

Висновок статті і закінчення циклу

На цьому, мабуть, можна поставити жирну крапку. Я розповів все, що хотів, про бібліотеку cpprt. Сподіваюся, дорогий читачу, ти витягнув що-небудь корисне з даного циклу статей. Я був би радий ще більше, якби ти прямо зараз взяв якусь пылящуюся без діла на домашньому репозиторії бібліотеку, оформив як слід і опублікував би її. Розкажи про неї людям в якійсь своїй статті!

Ну і буде непогано, якщо хто-небудь, зацікавившись, внесе свій внесок в розвиток бібліотеки cpprt.

Про таємничий основний проектОтже, цікаво, що за «основний проект»?.. Якщо ви це читаєте — значить план вдався.

Основний проект – це бібліотека, що дозволяє використовувати дані про стан об'єкта, передані для серіалізації, з метою генерування метаінформації про даному об'єкті і подальшого використання цієї метаінформації.
Зауважу, даний підхід не новий – його часто використовують для ігрових проектів, так і деякі із згаданих у першій статті аналогів бібліотеки cpprt дозволяють користувачеві задавати подібну інформацію для полів класів в момент реєстрації класів. Перерахую тут реалізовані та заплановані особливості проекту, з-за яких, на мій погляд, варто їм займатися:
1. У бібліотеки виконується не тільки збір метаданих про стани об'єктів, але зібрані дані також використовуються для генерації GUI, через який можна впливати на стан об'єктів. За рахунок цього користувачеві бібліотеки достатньо описати методи save і load для об'єкта, щоб отримати автоматично генерований для нього GUI object inspector. У даний момент GUI зроблений на Qt, але код проектувався з прицілом на підтримку різних GUI (у тому числі GUI користувача, яке можна буде підключити, реалізувавши інтерфейс). У планах до денця спробувати можливості, що відкриваються за рахунок такого погляду на генерування GUI.
2. Я завжди фанатів від концепції middleware. Мене вражала можливість знайти загальне в ряді інструментів, які мають одне призначення, винести це щось спільне в інтерфейс і далі підсовувати різні інструменти під цей інтерфейс, маючи можливість вибору реалізації інтерфейсу… Так от, задумано як слід реалізувати концепцію middleware. За рахунок цього хочеться домогтися максимальної простоти інтеграції користувачем бібліотеки в свій проект, аж до передачі системи типів користувача шаблонні класи бібліотеки.

У даний момент бібліотека написана до стану базового робочого прототипу, але вона вимагає розширення і поглиблення функціоналу, і потребує також приведення структури проекту в публикабельный вид (додавання документації, прикладів, тестів, інструментів, і т. д.).

Я сподівався сам пиляти цю бібліотеку, але в якийсь момент зрозумів, що зашиваюсь… Приєднуйтеся, станете співзасновниками проекту. Якщо вас у якій-небудь мірі зацікавило все, що тут написано – пишіть в лічку, розповім докладніше.

Наведемо разом бібліотеку в належний вигляд, опублікуємо, зробимо цикл статей для піару… хіба Мало – раптом злетить, і коли-небудь ми будемо згадувати, як закладали разом початок нової віхи для створення GUI в рамках мови С++?


Спасибі що дочитали все це! Сподіваюся, мені вдалося розповісти хоч що-небудь корисне!

Автор заздалегідь дякує читачам за вказівки на помилки в статті, конструктивні поради та побажання.

ТитриРедагування статті: Сергій Семенякин


Джерело: Хабрахабр
  • avatar
  • 0

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.