Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11 Calling Register Function on Link?

Tags:

c++

c++11

Is there any way to make a function get called just by linking its .o file?

For example:

foo.cpp:

extern int x;

void f() { x = 42; }

struct T { T() { f(); } } t; // we use constructor of global
                             // object to call f during initialization

bar.cpp:

#include <iostream>

int x;

int main()
{
    std::cout << x;
}

To compile/link/run:

$ g++ -c foo.cpp
$ g++ -c bar.cpp
$ g++ foo.o bar.o
$ ./a.out
42

This seems to work with gcc 4.7. It outputs 42 as expected. However I remember on some old compilers I had a problem with this pattern that because nothing was really "using" foo.o it was optimized out at link time. (perhaps this particular example is not representative of the problem for some reason)

What does the C++11 standard have to say about this pattern? Is it guaranteed to work?

like image 759
Andrew Tomazos Avatar asked Dec 10 '12 00:12

Andrew Tomazos


1 Answers

I believe you're not off the hook. The standard doesn't guarantee that your code works as intended, although many people rely on that behaviour for various "self-registering" constructions.

Your object t is dynamically initialized, which has the side effect of calling f. The standard has this to say about dynamic initialization of statically-stored objects (3.6.2/4, "Initialization of non-local variables"):

It is implementation-defined whether the dynamic initialization of a non-local variable with static storage duration is done before the first statement of main. If the initialization is deferred to some point in time after the first statement of main, it shall occur before the first odr-use (3.2) of any function or variable defined in the same translation unit as the variable to be initialized.

In your code, only x is odr-used, but x is defined in the main translation unit. No variable or function from the other TU is odr-used in your program, so technically there is no guarantee that t will ever be initialized. Technically, something from every TU must be referred to by the static control flow of your program in order for everything to be initialized.

As I said, there's lots of real-world code out there with "self-registering" translation units (which for example register a factory function pointer in a string-keyed map), so that by simply adding TUs to the final program you end up with more functionality. I'm told that most compilers will unconditionally initialize all global variables just because not doing so would break a lot of real-world code. But don't rely on it!

like image 70
Kerrek SB Avatar answered Oct 21 '22 10:10

Kerrek SB