Детерминированный порядок вызова конструкторов, с зависимостями.
От: fk0 Россия https://fk0.name
Дата: 21.02.14 12:50
Оценка:
Немедленно нажми на RESET, All!

Предположим, в терминах C++, что есть ряд глобальных объектов. Которые к тому же взаимодействуют между собой без определённого порядка (нет чёткой иерархии, нельзя часть из них сделать вложенными в другие). Чтобы вся система работала нужно чтоб не было ситуации использования объекта до вызова его конструктора. Если бы можно было исключить вызов в конструкторе методов других объектов (классов), то нет проблем. Но де-факто это сделать сложно и всё равно потребуется механизм о котором речь пойдёт ниже. Нужен, стало быть, определённый порядок вызова конструкторов. Например, GCC предлагает такой вариант такой вариант.

Неудобство в том, что нужно вручную назначать эти цифры приоритета и где-то на бумажке записать шпаргалку, чтоб не ошибиться. Аналогичная проблема существует в init-скриптах unix-подобных систем. Там тоже запуск части сервисов (демонов, служб) привязан к другим которые должны быть запущены раньше. в SysV init это решается так же -- цифрами. Есть и более простой механизм а-ля BSD 3-й версии -- просто вручную вызвать что нужно из вручную написанного /etc/rc.init (вручную вызвать нужные функции из main()...) Этот простой механизм применительно к программированию на C/C++ неудобен необходимостью ручной поддержки этого списка, и в частности, нельзя из одного набора исходников получать разные бинарники просто путём включения/исключения ненужных *.o файлов -- что совсем уж неудобно.

Речь вообще не о C++, а о plain C, но сущности это не меняет, просто так трудней с терминологией. Такой механизм, подобный init_priority в GCC или SysV init и сделан. Но он этими цифрами и неудобен. Хотелось бы автоматического разрешения зависимостей, кто от кого зависит, и соответственно определённого порядка запуска. Как бы эту задачу разрешить в рамках C-компилятора? Сейчас в в рамках C++ разрешается через конструкторы, вызываемые до main, в рамках C похожим образом через ручной их вызов из main по списку с приоритетами, формируемому в момент компиляции -- адреса "конструкторов" кладутся в специальную секцию линкером (по которой потом пробегается функция вызванная из main() и вызывает в порядке приоритета). Но хотелось бы избавиться от ручного назначения цифр.

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

Как описывать зависимости? Описателем зависимостей может являться ссылка на список указателей конструкторов классов от которых зависит каждый конкретный класс. Т.е. в специальную секцию помещаются не список указателей на конструкторы с их приоритетом (как сейчас):
static const struct {
    int (*init_func)(void);
    unsigned priority;
} anonymous#__LINE__ __attribute__((section("myinit")));


А что-то вроде:
struct _s_constructor {
    int (*init_func)(void);
    const struct _s_constructor *depend[];
};

const /* NOT STATIC! */ struct _s_constructor classname __attribute__((section("myinit"))) = {
   .init_func = module_name_init,
   .depend = {
        otherclass1,
        otherclass2,
        NULL
   }
};

Можно даже static объекты делать, не видимые за пределами модуля, с именем anonymous#__LINE__, но от них ничего зависеть не должно, они должны быть только зависимые.

Может как-то по другому можно?
Re: Детерминированный порядок вызова конструкторов, с зависимостями.
От: Stanislav V. Zudin Россия  
Дата: 21.02.14 13:22
Оценка:
Здравствуйте, fk0, Вы писали:

fk0> Предположим, в терминах C++, что есть ряд глобальных объектов. Которые к тому же взаимодействуют между собой без определённого порядка (нет чёткой иерархии, нельзя часть из них сделать вложенными в другие). Чтобы вся система работала нужно чтоб не было ситуации использования объекта до вызова его конструктора. Если бы можно было исключить вызов в конструкторе методов других объектов (классов), то нет проблем.


Если проблема только в конструкторах, то синглетон мейерса спасет положение.
Порядка вызова деструкторов он, ессно, не гарантирует.
_____________________
С уважением,
Stanislav V. Zudin
Re: Детерминированный порядок вызова конструкторов, с зависимостями.
От: Mr.Delphist  
Дата: 21.02.14 13:38
Оценка: 3 (1)
Здравствуйте, fk0, Вы писали:

fk0> Чтобы вся система работала нужно чтоб не было ситуации использования объекта до вызова его конструктора.

google://two phase initialization c++
Если вкратце — то сначала мы конструируем всё, затем проставляем взаимные ссылки и стартуем. На крайняк — lazy-обёртки, создающие объект при первом реальном обращении к нему.

fk0> Как описывать зависимости?

google://dependency injection c++
Если вкратце — описываем набор зависимостей между сущностями, порядок инстанциирования будет выведен автоматически
Re[2]: Детерминированный порядок вызова конструкторов, с зависимостями.
От: trophim Россия  
Дата: 22.02.14 15:49
Оценка:
Здравствуйте, Mr.Delphist, Вы писали:

fk0>> Как описывать зависимости?

MD>google://dependency injection c++
MD>Если вкратце — описываем набор зависимостей между сущностями, порядок инстанциирования будет выведен автоматически

А, кстати, что есть на C++ для IoC/DI уже готового, чтоб не велосипедить?
Я нашел
http://code.google.com/p/infectorpp/
... << RSDN@Home 1.2.0 alpha 5 rev. 1495>>
Let it be! — Давайте есть пчелу!
Re: Детерминированный порядок вызова конструкторов, с зависимостями.
От: Vzhyk  
Дата: 22.02.14 17:13
Оценка: +1
2/21/2014 3:50 PM, fk0 пишет:

> Предположим, в терминах C++, что есть ряд глобальных объектов. Которые к

> тому же взаимодействуют между собой без определённого порядка (нет
> чёткой иерархии, нельзя часть из них сделать вложенными в другие). Чтобы
> вся система работала нужно чтоб не было ситуации использования объекта
> до вызова его конструктора.
Зависит от твоих целей.
Если сделать код как можно круче и показать всем, какой ты крутой, то
двигайся в стороны "паттернов", как тебе уже накидали гору советов.
Если же, чтобы работало, то запихать всю эту помойку в один класс, там
сделать инициализацию в нужном тебе порядке. А затем рефакторить код,
чтобы избавиться от этого дерьма.
Posted via RSDN NNTP Server 2.1 beta
Re: Детерминированный порядок вызова конструкторов, с зависимостями.
От: Шахтер Интернет  
Дата: 23.02.14 11:52
Оценка:
Здравствуйте, fk0, Вы писали:

fk0>Может как-то по другому можно?


В CCore я использую для этих целей подсистему PlantInit. Вот пример её использования для инициализации хипа.

namespace Private_MemBase {

class MainHeap : public HeapEngine<RadixHeap<MemPage> >
 {
  public:
   
   MainHeap() : HeapEngine<RadixHeap<MemPage> >("!MainHeap") {}
   
   ~MainHeap() {}
   
   static const char * GetTag() { return "MemBase"; }
 };
 
PlanInitObject<MainHeap,PlanInitReq<GetPlanInitNode_Exception>
                       ,PlanInitReq<GetPlanInitNode_Task> 
                       > Object CCORE_INITPRI_1 ;


} // namespace Private_MemBase

PlanInitNode * GetPlanInitNode_MemBase() { return &Object; }


Т.е. мы создаём в cpp файле объект и декларируем некоторую функцию, которая может быть использована для определения зависимостей.
В данном случая, сам объект зависит от GetPlanInitNode_Exception и GetPlanInitNode_Task.

Дальше при старте программы все таким образам определённые объекты создаются до вызова конструкторов обычных глобальных объектов с учётом зависимостей.
Если возникает циклическая зависимость, происходит аборт.

В реализации используются расширения gcc -- инициализация с приоритетом. Для нужд PlanInit зарезервированы три приоритета.

#define CCORE_INITPRI_0 __attribute__((init_priority(101)))
#define CCORE_INITPRI_1 __attribute__((init_priority(102)))
#define CCORE_INITPRI_2 __attribute__((init_priority(103)))
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.