Executing code automatically at program start without violating ODR

Viewed 82

I try to code a simple way to execute code at start of the program automatically (without using not portable attribute()).

I wrote the following code, and I'm asking if it does violate the ODR if the code written before main() is put in header files. Or, does the inline function and variable prevent that? Would there be a case where the function would be called twice if the ODR is violated?

#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;
}

Thank you in advance

1 Answers

Your solution should be ODR-safe. The C++ standard requires that there is only one instance of static data member auto_init_ for each distinct instantiation of execute_at_start<> class template and that it is initialized once.

Itanium C++ ABI (most compilers comply with it, apart from MSVC) requires guard variables for globals (auto_init_ here) to make sure they are initialized once:

If a function-scope static variable or a static data member with vague linkage (i.e., a static data member of a class template) is dynamically initialized, then there is an associated guard variable which is used to guarantee that construction occurs only once.

See Itanium C++ ABI §2.8 Initialization Guard Variables for more details.


Another standard ODR-safe solution for this problem that doesn't rely on guard variables is Schwarz Counter. It has been used to initialize the standard streams (std::cout and friends) since the dawn of C++. In this solution counter is an explicit guard variable that has external linkage and it makes sure only the first invocation of execute_at_start constructor calls 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() {}
Related