Notice: file_put_contents(): Write of 7129 bytes failed with errno=28 No space left on device in /var/www/tgoop/post.php on line 50

Warning: file_put_contents(): Only 16384 of 23513 bytes written, possibly out of free disk space in /var/www/tgoop/post.php on line 50
C++95@cxx95 P.76
CXX95 Telegram 76
#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.
Поэтому "регистрация модуля" сработает и проблема будет решена 😁
Please open Telegram to view this post
VIEW IN TELEGRAM
👍20🖕1



tgoop.com/cxx95/76
Create:
Last Update:

#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.
Поэтому "регистрация модуля" сработает и проблема будет решена 😁

BY C++95


Share with your friend now:
tgoop.com/cxx95/76

View MORE
Open in Telegram


Telegram News

Date: |

For crypto enthusiasts, there was the “gm” app, a self-described “meme app” which only allowed users to greet each other with “gm,” or “good morning,” a common acronym thrown around on Crypto Twitter and Discord. But the gm app was shut down back in September after a hacker reportedly gained access to user data. As the broader market downturn continues, yelling online has become the crypto trader’s latest coping mechanism after the rise of Goblintown Ethereum NFTs at the end of May and beginning of June, where holders made incoherent groaning sounds and role-played as urine-loving goblin creatures in late-night Twitter Spaces. How to create a business channel on Telegram? (Tutorial) 2How to set up a Telegram channel? (A step-by-step tutorial) Done! Now you’re the proud owner of a Telegram channel. The next step is to set up and customize your channel.
from us


Telegram C++95
FROM American