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