Автоматическое выполнение кода при запуске программы без нарушения ODR - PullRequest
2 голосов
/ 19 июня 2020

Я пытаюсь создать простой способ автоматического выполнения кода при запуске программы (без использования непереносимого атрибута ()).

Я написал следующий код и спрашиваю, не нарушает ли он ODR, если код, написанный до main (), помещен в файлы заголовков. Или это предотвращает встроенная функция и переменная? Будет ли функция вызываться дважды при нарушении ODR?

#include <iostream>

//-----

template <void(*init_function)()>
class execute_at_start
{
    struct dummy final {};
    ~execute_at_start() = delete;
    inline static dummy execute_() { init_function(); return dummy{}; }
    inline static dummy auto_init_ = execute_();
};

//-----

inline void echo(){ std::cout << "echo" << std::endl; }

template class execute_at_start<&echo>;

int main()
{
    std::cout << "EXIT SUCCESS" << std::endl;
    return EXIT_SUCCESS;
}

Заранее спасибо

1 Ответ

2 голосов
/ 19 июня 2020

Ваше решение должно быть ODR-безопасным. Стандарт C ++ требует, чтобы был только один экземпляр stati c data member auto_init_ для каждого отдельного экземпляра шаблона класса execute_at_start<> и чтобы он инициализировался один раз.

Itanium C ++ ABI (большинство компиляторов соответствуют с ним, помимо MSV C), требуются защитные переменные для глобальных объектов (здесь auto_init_), чтобы убедиться, что они инициализированы один раз:

Если переменная stati c области видимости функции или stati c элемент данных с неопределенной связью (т.е. член данных stati c шаблона класса ) динамически инициализируется, затем существует связанная защитная переменная, которая используется для гарантии того, что построение происходит только один раз.

Подробнее см. Itanium C ++ ABI §2.8 Переменные защиты инициализации .


Еще одно стандартное ODR-безопасное решение этой проблемы, которое не Не полагаться на охранные переменные - это счетчик Шварца . Он использовался для инициализации стандартных потоков (std::cout и других) с самого начала C ++. В этом решении counter - это явная защитная переменная, имеющая внешнюю связь, и она гарантирует, что только первый вызов execute_at_start вызывает конструктор init_function:

// Header begin.
#include <iostream>

template<void(*init_function)()>
class execute_at_start {
    static inline int counter = 0;
public:
    execute_at_start() {
        if(!counter++)
            init_function();
    }
};

inline void echo() { std::cout << "echo" << std::endl; }

execute_at_start<echo> const execute_at_start_echo; // A copy in each translation unit.

// Header end.

int main() {}
...