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/vn/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/vn/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: |

The War on Fakes channel has repeatedly attempted to push conspiracies that footage from Ukraine is somehow being falsified. One post on the channel from February 24 claimed without evidence that a widely viewed photo of a Ukrainian woman injured in an airstrike in the city of Chuhuiv was doctored and that the woman was seen in a different photo days later without injuries. The post, which has over 600,000 views, also baselessly claimed that the woman's blood was actually makeup or grape juice. Two days after Russia invaded Ukraine, an account on the Telegram messaging platform posing as President Volodymyr Zelenskiy urged his armed forces to surrender. In a statement, the regulator said the search and seizure operation was carried out against seven individuals and one corporate entity at multiple locations in Ahmedabad and Bhavnagar in Gujarat, Neemuch in Madhya Pradesh, Delhi, and Mumbai. Multiple pro-Kremlin media figures circulated the post's false claims, including prominent Russian journalist Vladimir Soloviev and the state-controlled Russian outlet RT, according to the DFR Lab's report. Pavel Durov, a billionaire who embraces an all-black wardrobe and is often compared to the character Neo from "the Matrix," funds Telegram through his personal wealth and debt financing. And despite being one of the world's most popular tech companies, Telegram reportedly has only about 30 employees who defer to Durov for most major decisions about the platform.
from vn


Telegram C++95
FROM American