TGViewer
C++95 C++95 @cxx95 · 1.5K subscribers
Post #76 1.71K
#creepy #compiler

Самое мерзкое правило в C++ для модульных программ и как его обойти 🤢

Недавно я в своем pet project снова столкнулся с тем, что могу назвать самым мерзким правилом C++ в своем опыте, по крайней мере для модульных программ. Оно связано с особенностями работы линковщика и требует всяких тайных знания для решения.

В больших модульных проектах для скорости разработки иногда используется такая схема - каждому модулю бизнес-логики соответствует статическая библиотека (static library).

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

Есть файл module.h с такими методами (упрощенно)
    struct IModule { virtual void Do() = 0; };
void AddModule(std::unique_ptr<IModule> module);
const std::vector<std::unique_ptr<IModule>>& GetModules();

Файл main.cpp должен использовать GetModules(), а модуль должен зарегистрировать сам себя через AddModule.

Единственный способ, которым это можно адекватно сделать - добавить код, который должен вызываться на старте программы. Это делается через статическую инициализацию объектов в конструкторе объекта. Где-то в одном из .cpp-файлов модуля должно быть такое:
    struct Dummy {
Dummy() {
AddModule(std::make_shared<MyCoolModule>());
}
};
static Dummy dummy;

Дальше начинается кино. Переменная dummy и код для ее инициализации попадает в статическую библиотеку libcoolmodule.a (можно проверить через objdump), но при линковке бинарника эта переменная выбрасывается линкером как неиспользуемая. В итоге модуль не зарегистрируется.

Процесс решения проблемы зависит от системы сборки, операционной системы и многих других вещей. Общепринятого решения проблемы нет. Как эту проблему решают в разных случаях:

1️⃣ Windows - указать на переменные, которых нельзя выбросить - ссылка
__pragma comment(linker,"/include:?variable_name@")

2️⃣ Помещение переменных в отдельные секции и пометка этих секций как невыбрасываемых - ссылка
    static Var_t g_DumbVar __attribute__((__used__, section(".var_section.g_DumbVar"))) = (const Var_t) X_MARKER;
static Var_t* g_DumbVarGuard[] __attribute__((__used__, section(".guard"))) = { &g_DumbVar };

3️⃣ Linux - также указать на невыбрасываемые переменные через параметр командной строки - ссылка1, ссылка2, переменная не должна быть static и volatile.

4️⃣ Linux - сделать link-скрипт, который указывает что и как надо линковать - ссылка

Через время поисков я нашел ответ, почему так работает линкер.
Когда собираешь бинарник, то индивидуальные объектные файлы (.o) в команде к линкеру линкуются по порядку и ничто не выбрасывается.

С библиотеками (.a, архив из .o) есть "оптимизация": .o-файл из библиотеки линкуется только если в нем находится определение какого-нибудь undefined symbol, который требуется в уже слинкованных прежде .o-файлах. В противном случае считается, что этот .o-файл не нужен и в бинарник он не попадает.

В системе сборки CMake есть метод, который позволит обойти это правило. Надо заменить такую строку:
    add_library(enum_serializer STATIC module.cpp helper.cpp)
на такую:
    add_library(enum_serializer OBJECT module.cpp helper.cpp)
И тогда, если какой-то бинарник зависит от enum_serializer, он будет линковать не libenum_serializer.a, а module.o и helper.o.
Поэтому "регистрация модуля" сработает и проблема будет решена 😁
  • 👍 20
  • 🖕 1
More from @cxx95
  1. Sep 21, 2026#offtop Великое Мидревью 💼 В последнее время темы про C++ накапливаются медленно, и то та…
  2. Jun 17, 2026#jostik Обзор жостиков C++ 💼 (номер 5️⃣) В последние несколько месяцев совсем ничего не п…
  3. Jun 17, 2026document post
  4. Jan 7, 2026#compiler #ai На фото из марта 2024 - мерч с работы, это кофта и набор нашивок, которые мо…
  5. Jan 7, 2026document post
  6. Aug 18, 2025#books Текст в окружности - первые слова в опере (услышать) 🎤 📕 Обзор на книгу про памят…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →