it-workshop/UniSched

Пересмотр модульной подсистемы

Closed this issue · 10 comments

Пересматриваю свойства модульной подсистемы.

Что мы имеем сейчас:
  • Класс модуля AbstractBackend;
  • Класс модуля пользовательского интерфейса UI::AbstractUI (наследует AbstractBackend);
  • Класс модуля базы данных (пишу сейчас) Storage::AbstractDataBase (наследует AbstractBackend);
  • Свойства любого модуля:
    • Должен содержать реализацию не абстрактного класса, наследующего один из вышеперечисленных классов;
    • Должен содержать статический объект этого класса;
    • Должен быть скомпилирован как SharedObject;
  • Подключить модуль к основному исполняемому файлу его следует загрузить в тоже виртуальное адресное пространство. Сделать это можно двумя способами:
    • Прилинковать модуль к raspisator на этапе сборки, тогда модуль будет загружаться вместе с самой программой;
    • Загрузить модуль непосредственно перед запуском программы с помощью переменной окружения LD_PRELOAD (обращаю внимание, что данный способ не будет работать под Windows, даже если нам удастся все-таки собрать для неё бинарники, однако, помнится, аналогичный способ загрузки .dll там был, хоть и довольно не тривиальный);
  • Ограничения:
    • Только один модуль каждого типа может работать в каждый конкретный момент времени;
    • Возможности сменить используемый модуль, не перезапуская программу (даже если он загружен в память), нет;
    • Не предусмотрен способ выбора какой модуль использовать для работы, используется первый найденный подходящий;
Вопрос:

Стоит ли работать над снятием первых двух ограничений? О том, что надо снимать последнее, даже не спрашиваю, просто надо.

Как это работает.

Рассмотрим, как работает модуль класса SomeUI (унаследован от UI::AbstractUI). пусть он будет скомпилирован в libsomeui.so.

Мы запускаем программу и загружаем в её адресное пространство модуль.
$ LD_PRELOAD=libsomeui.so raspisator
Конструкторы статических объектов вызываются до вызова main(), поэтому в определенный момент времени будет вызван конструктор по умолчанию объекта класса SomeUI.
Для простоты предположим, что конструктор объекта пустой. Однако даже если он не пустой, до начала исполнения тела конструктора будет передано управление а конструктор UI::AbstractUI.
Вызывается конструктор AbstractBackend, с параметром AbstractBackend::UI, чтобы установить тип модуля.
Сам конструктор AbstractBackend добавляет указатель this на объект в статический вектор.
После конструкции всех статических объектов в этом векторе будут указатели на объекты всех загруженных модулей.

Для начала хочу заметить, что интерфейс пользователя - это фронтенд, тогда как хранилище данных это бэкенд.

Поясни вот этот пункт:

Свойства любого модуля:
* Должен содержать статический объект этого класса;

Подключить модуль к основному исполняемому файлу его следует загрузить в тоже виртуальное адресное пространство. Сделать это можно двумя способами:

  • Прилинковать модуль к raspisator на этапе сборки, тогда модуль будет загружаться вместе с самой программой

Для начала мы будем использовать линковку на стадии сборки. В дальнейшем можно рассмотреть использование кросс-платформенных технологий (например, реализованных в Qt::Core).

Стоит ли работать над снятием первых двух ограничений? О том, что надо снимать последнее, даже не спрашиваю, просто надо.
Согласен, что реализация последнего пункта присутствует в наших планах (часть изначальной концепции, все-таки).
По-поводу 1-го пункта, интерфейс пользователя пусть будет один из, тогда как работу с файлами надо сделать совместимой с работой с базой. Пусть, наверное, у файлов будет особый статус (они как бы часть Core как раз).
Второй пункт можно не реализовывать пока что (если будет супер-идея, тогда уж поговорим, пока что ради этого подгонять архитуктуру не стоит).

Сорри что несколькими письмами отвечаю.

Конструктор статического объекта вызывается до main, самый дальний предок занесет указатель на этот объект в общий для всей программы вектор, а значит собственно в самой программе мы уже можем выбрать любой объект из этого вектора.
Просто иначе программа ничего не узнает о модуле. Другой вариант был - раскуривать динамическую загрузку .so, но это гораздо сложнее. кстати подобный способ предложил еще Страуструп.

Поясни вот этот пункт:

Свойства любого модуля:

  • Должен содержать статический объект этого класса;

Работа с файлами у нас происходит на несколько другом уровне. Мы же определились, что включаем это в Core, а вот баз, которых может быть несколько (вспоминаем твой пример, в котором ты предложил подключиться к удаленной базе и внести изменения туда), вопрос в том делаем ли мы это?

Пока для меня понятно только то, что модуль базы может вовсе не быть загружен. А дальше много вариантов как делать. Делать ли возможным одновременную работу больше чем одного варианта базы? Если да, что делать в случае конфликтов данных? делать ли модули базы активными/неактивными в каждый конкретный момент времени?

Согласен, что реализация последнего пункта присутствует в наших планах (часть изначальной концепции, все-таки).
По-поводу 1-го пункта, интерфейс пользователя пусть будет один из, тогда как работу с файлами надо сделать совместимой с работой с базой. Пусть, наверное, у файлов будет особый статус (они как бы часть Core как раз).
Второй пункт можно не реализовывать пока что (если будет супер-идея, тогда уж поговорим, пока что ради этого подгонять архитуктуру не стоит).

Стоит ли работать над снятием первых двух ограничений? О том, что надо снимать последнее, даже не спрашиваю, просто надо.

Да, по поводу того, что UI это фронтенд я знаю, думаю о том, что надо переименовывать вопрос даже не стоит.

Тогда так и примем, модуль базы возможно загрузить, но не обязательно.

Теперь вопрос, какие параметры командной строки мы вводим для выбора модуля. Предлагаю следующий формат:
raspisator --ui uimodulename --db dbmodulename
при этом если запрошенный модуль не найден или он просто не задан, выбирается первый подходящий.

Предлагаю сменить терминологию... Переименовать UI::AbstractUI и (гипотетический Storage::AbstractStorage) в Frontend и Backend соответственно.

Также ввиду того, что это единственные классы (и даже правильнее сказать интерфейсы, потому что они чисто абстрактные), являются единственным содержимым пространств имен UI и Storage соответственно, предлагаю перенести их, а также Module, который вообще сейчас в глобальном пространстве имен в Core.

Тогда у нас явно выделяется монолитное ядро проекта в виде Core, которое полностью самодостаточно и набор модулей.

Что даст переименование? Да, UI::AbstractUI является с точки зрения архитектуры Frontend-ом, а Storage::AbstractStorage -- Backend-ом. Но есть ли причины все перекраивать? Насчет пространств имен хочу заметить, что в данный момент они действительно не идеально организованы, так что если поймешь что тут менять то будет здорово. Но вообще судя по тому, как идет эволюция пространств имен в Расписаторе, ключевым словом namespace мы злоупотребляем. Имей ввиду, что часто столь строгое обособление никогда не пригождается (большинство классов сами-себе пространства имен).

Про монолитность я бы тоже хотел заметить. Рано штамповать код, переживающий нормальный этап метаморфоз такими сильными тэгами. Если что-то в нем и меняется, то это должно иметь смысл. Кстати, система компактного ядра, окруженного модулями, если на то пошло, это скорее микроядро. Если считать при этом само ядро не максимально упрощенным, то тогда это уже скорее гибридное ядро. Хотя повторюсь, что данная терминалогия пока для нас неприменима.

Гм. То, что у нас AbstractUI выделен в отдельный нэймспэйс начинает мешать - при внесении изменений в соответствии с #27 и без того не очевидная реализация двунаправленной связи Manager <-> Object становится еще менее очевидной, благодаря тому, что AbstractUI и Object находятся в разных нэймспейсах. Как хотите, а я переношу его в Core, потому что:

  1. держать отдельный нэймспэйс для одного класса не разумно;
  2. собирать один класс в отдельной библиотеке не разумно;
  3. отметать неотъемлимую часть кода в отдельную библиотеку не разумно
  4. это затрудняет реализацию
  5. могу придумать еще, но не буду, просто сделаю.

Да, вопрос о переименовании AbstractUI в Frontend считаю открытым.

Про пространства имен - да, про переименовывание классов - нет, т. к. ты предлагаешь переименовать нож и вилку в столовый прибор а и столовый прибор б, что не имеет смысла.

сделано в 0.0.2