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?
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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With