Детерминированный порядок вызова конструкторов, с зависимостями.
От: 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__, но от них ничего зависеть не должно, они должны быть только зависимые.

Может как-то по другому можно?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.