Вот когда точно статики инициализируются после main



Все-таки есть стопроцентный способ создать условия, чтобы этот эффект проявился.



Как вы знаете, есть 2 вида библиотек: статические и динамические. Код статических библиотек вставляется в основной код программы в то время, как код динамических библиотек подгружается в рантайме.



Так вот есть способы в любой момент исполнения программы руками подгрузить shared library и использовать ее символы, даже ничего не зная о ней на этапе линковки объектников!



На юниксах это системный вызов dlopen. Он принимает путь к библиотеки и возвращает ее хэндл. Через этот хэндл можно получать указатели на сущности из либы.



Естественно, что раз бинарник ничего не знал о сущностях библиотеки до ее explicit подгрузки, а библиотека просто лежала камнем в файловой системе, то буквально никакой код библиотеки не может быть выполнен до ее подгрузки. А значит, если мы открываем либу в main(), то только в этот момент начинается вся динамическая инициализация сущностей либы. Поэтому значение ее переменных со static storage duration устанавливается после входа в main()!



Минимальный пример:



// lib.cpp

struct CreationMomentShower {

CreationMomentShower(int num=0) : data{num} {

std::cout << "Created object with data " << num << std::endl;

}

int data;

};



struct Use {

static inline CreationMomentShower help{6};

};



// main.cpp

#include <iostream>

#include <dlfcn.h>



int main()

{

std::cout << "Main has already started" << std::endl;

void* libraryHandle = dlopen("libsource.so", RTLD_NOW);

if (libraryHandle == nullptr) {

std::cerr << dlerror() << std::endl;

return 1;

}

dlclose(libraryHandle);

}





Вывод:



Main has already started

Created object with data 6





Чтобы запустить это дело(на примере gcc), нужно:



1️⃣ Скомпилировать объектный файл из source.cpp g++ -c -fpic -std=c++17 source.cpp



2️⃣ Превратить его в библиотеку g++ -shared -o libsource.so source.o



3️⃣ Скомпилировать main.cpp g++ -o test main.cpp -std=c++17



4️⃣ Запустить ./test



Важно отметить, что о существовании библиотеки исполняемый файл test вообще не в курсе. Также не нужно добавлять путь до либы в какой-нибудь $LD_LIBRARY_PATH.



Если библиотеку сликовать с бинарем сразу же и подгружать ее неявно, то порядок инициализации будет снова неопределен в соотвествии с предыдущим постом. И скорее всего такого эффекта в этом случае не будет.



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



Dig deeper. Stay cool.



#compiler