Telegram Group & Telegram Channel
#madskillz

Как правильно выполнить любой код до и после main() 🏃

Функция int main() не является настоящей "точкой входа" в программе. До того, как зайти туда (и после него), программа выполняет много другого кода, в том числе юзерского.

Почти всегда этот код - инициализация статических объектов, которая выполняется до main(). Программа:
    std::vector<int> MakeVector() {
std::cout << "do MakeVector" << std::endl;
return {1, 2, 3};
}
std::vector<int> VECTOR = MakeVector();
int main() {
std::cout << "do main" << std::endl;
}
Выведет такое:
    do MakeVector
do main
Пример на godbolt

В этом коде можно делать что угодно (читать файлы, записывать в поток и т.д.), окружение там полностью готово. Я часто использую лямбды:
Пример на godbolt 1, godbolt 2.

😀 Есть крутой лонгрид (со схемами) про то, что происходит до и после main(): Linux x86 Program Start Up.
Но все описание там "примерное", потому что поведение зависит от компилятора и его версии 🖥
В моем случае некоторые действия находятся в другом положении схемы (увидел по gdb), и еще есть дополнительная вложенность - есть функции для отдельных .cpp-файлов, а там внутри уже вызовы для инициализации переменных.

⌨️ Для того, чтобы "вызвать функцию до main()", можно пометить ее специальным атрибутом.
А именно __attribute__((constructor)) или менее вырвиглазно [[gnu::constructor]].

Здесь слова "constructor" и "destructor" не относятся к классам в C++, это исторические названия еще до C++.

Пример на godbolt с ctor-ами и dtor-ами

Однако здесь есть типичный прикол с std::cout 😁
👍 Пример по ссылке выше, скомпилированный на x86-64 clang trunk по состоянию на 13.06.2023 работает нормально.
Более старый релиз x86-64 clang 16.0.0 дает сегфолт в кторах и будет работать только если поменять там на printf - ссылка на godbolt.
😠 Это связано с тем, что std::cout и подобные объекты инициализируются в конструкторе статического объекта std::ios_base::Init (объявление этого объекта находится в хедере <iostream>).
А компилятор данной версии так скомпилировал, что кторы выполняются ДО НЕГО и пытаются обратиться к неинициализированному объекту.
Функция printf таких спецэффектов не имеет, там просто перенаправление в нужный системный вызов (syscall) без регистрации и СМС.
Мораль думайте сами 👍

Для чего может быть полезна такая штука? Лучше описать как "вопрос-ответ"

1️⃣
Вопрос: В чем принципиальное отличие этого кода:
    [[gnu::constructor]] void foo() {
// приколы
}
от этого:
    struct Dummy {
Dummy() { /* приколы */ }
};
Dummy dummy;
или более укуренного этого:
    const auto dummy = []{
// приколы
return nullptr;
}();
Ответ: Никакого, кроме более удобной записи.

2️⃣
Вопрос: Зачем это все надо, почему нельзя этот кусок кода поместить в int main()???
Ответ: Иногда нужно выполнить кусок кода, только если мы линкуем какую-то библиотеку или подключаем хедер. Например, если в библиотеке хэширования есть расчет хитрых данных для хэшей на старте программы в [[gnu::constructor]], то не надо париться с тем, как и что вызвать в main(), можно просто линковать библиотеку и заработает.

Да точно это же и происходит для std::cout, как мы только что увидели выше!

3️⃣
Вопрос: Какие жесткие проблемы решает эта запись?
Ответ: В кторах и дторах можно указывать приоритет 😐 Чем он выше, тем позже выполнится ктор (и соответственно раньше дтор).
Пример на godbolt.
Это решает артиллерийский выстрел в ногу под названием Static Initialization Order Fiasco. Очередность выполнения инициализаций обычно зависит от порядка линковки, а с приоритетом стало можно это нормально разруливать! 😎

В остальном проблемы линковки все еще остаются легендарными - можно почитать пример особо опасной проблемы, о которой писал раньше: https://www.group-telegram.com/sg/cxx95.com/76
Please open Telegram to view this post
VIEW IN TELEGRAM



group-telegram.com/cxx95/109
Create:
Last Update:

#madskillz

Как правильно выполнить любой код до и после main() 🏃

Функция int main() не является настоящей "точкой входа" в программе. До того, как зайти туда (и после него), программа выполняет много другого кода, в том числе юзерского.

Почти всегда этот код - инициализация статических объектов, которая выполняется до main(). Программа:

    std::vector<int> MakeVector() {
std::cout << "do MakeVector" << std::endl;
return {1, 2, 3};
}
std::vector<int> VECTOR = MakeVector();
int main() {
std::cout << "do main" << std::endl;
}
Выведет такое:
    do MakeVector
do main
Пример на godbolt

В этом коде можно делать что угодно (читать файлы, записывать в поток и т.д.), окружение там полностью готово. Я часто использую лямбды:
Пример на godbolt 1, godbolt 2.

😀 Есть крутой лонгрид (со схемами) про то, что происходит до и после main(): Linux x86 Program Start Up.
Но все описание там "примерное", потому что поведение зависит от компилятора и его версии 🖥
В моем случае некоторые действия находятся в другом положении схемы (увидел по gdb), и еще есть дополнительная вложенность - есть функции для отдельных .cpp-файлов, а там внутри уже вызовы для инициализации переменных.

⌨️ Для того, чтобы "вызвать функцию до main()", можно пометить ее специальным атрибутом.
А именно __attribute__((constructor)) или менее вырвиглазно [[gnu::constructor]].

Здесь слова "constructor" и "destructor" не относятся к классам в C++, это исторические названия еще до C++.

Пример на godbolt с ctor-ами и dtor-ами

Однако здесь есть типичный прикол с std::cout 😁
👍 Пример по ссылке выше, скомпилированный на x86-64 clang trunk по состоянию на 13.06.2023 работает нормально.
Более старый релиз x86-64 clang 16.0.0 дает сегфолт в кторах и будет работать только если поменять там на printf - ссылка на godbolt.
😠 Это связано с тем, что std::cout и подобные объекты инициализируются в конструкторе статического объекта std::ios_base::Init (объявление этого объекта находится в хедере <iostream>).
А компилятор данной версии так скомпилировал, что кторы выполняются ДО НЕГО и пытаются обратиться к неинициализированному объекту.
Функция printf таких спецэффектов не имеет, там просто перенаправление в нужный системный вызов (syscall) без регистрации и СМС.
Мораль думайте сами 👍

Для чего может быть полезна такая штука? Лучше описать как "вопрос-ответ"

1️⃣
Вопрос: В чем принципиальное отличие этого кода:
    [[gnu::constructor]] void foo() {
// приколы
}
от этого:
    struct Dummy {
Dummy() { /* приколы */ }
};
Dummy dummy;
или более укуренного этого:
    const auto dummy = []{
// приколы
return nullptr;
}();
Ответ: Никакого, кроме более удобной записи.

2️⃣
Вопрос: Зачем это все надо, почему нельзя этот кусок кода поместить в int main()???
Ответ: Иногда нужно выполнить кусок кода, только если мы линкуем какую-то библиотеку или подключаем хедер. Например, если в библиотеке хэширования есть расчет хитрых данных для хэшей на старте программы в [[gnu::constructor]], то не надо париться с тем, как и что вызвать в main(), можно просто линковать библиотеку и заработает.

Да точно это же и происходит для std::cout, как мы только что увидели выше!

3️⃣
Вопрос: Какие жесткие проблемы решает эта запись?
Ответ: В кторах и дторах можно указывать приоритет 😐 Чем он выше, тем позже выполнится ктор (и соответственно раньше дтор).
Пример на godbolt.
Это решает артиллерийский выстрел в ногу под названием Static Initialization Order Fiasco. Очередность выполнения инициализаций обычно зависит от порядка линковки, а с приоритетом стало можно это нормально разруливать! 😎

В остальном проблемы линковки все еще остаются легендарными - можно почитать пример особо опасной проблемы, о которой писал раньше: https://www.group-telegram.com/sg/cxx95.com/76

BY C++95


Warning: Undefined variable $i in /var/www/group-telegram/post.php on line 260

Share with your friend now:
group-telegram.com/cxx95/109

View MORE
Open in Telegram


Telegram | DID YOU KNOW?

Date: |

On December 23rd, 2020, Pavel Durov posted to his channel that the company would need to start generating revenue. In early 2021, he added that any advertising on the platform would not use user data for targeting, and that it would be focused on “large one-to-many channels.” He pledged that ads would be “non-intrusive” and that most users would simply not notice any change. The original Telegram channel has expanded into a web of accounts for different locations, including specific pages made for individual Russian cities. There's also an English-language website, which states it is owned by the people who run the Telegram channels. One thing that Telegram now offers to all users is the ability to “disappear” messages or set remote deletion deadlines. That enables users to have much more control over how long people can access what you’re sending them. Given that Russian law enforcement officials are reportedly (via Insider) stopping people in the street and demanding to read their text messages, this could be vital to protect individuals from reprisals. Oleksandra Matviichuk, a Kyiv-based lawyer and head of the Center for Civil Liberties, called Durov’s position "very weak," and urged concrete improvements. The channel appears to be part of the broader information war that has developed following Russia's invasion of Ukraine. The Kremlin has paid Russian TikTok influencers to push propaganda, according to a Vice News investigation, while ProPublica found that fake Russian fact check videos had been viewed over a million times on Telegram.
from sg


Telegram C++95
FROM American