2

Let's say I have class A with only static members. One of its members is of class B

class A { 
  //...
  static B b;
}

In the A.cpp file I initialize all static members, using their constructors.

A.cpp:

B A::b(/*constructor arguments*/);

Now, nowhere in my whole project do I ever use the variable A::b.

Does that mean that the construction of that variable may totally be omitted from the final binary file by the compiler or the linker?

Even if I never use that variable, its construction has impact to the system configuration. (It may affect peripheral registers for example).

I am not using makefiles. I am using Atmel studio for AVR uControllers.

How can I be sure that the constructor of that variable will be executed during the globals and statics initialization phase of my program?

And, in general, how can I control that? (There may be a case that I need the opposite: to make sure it doesn't get initialized unless needed somewhere)

I have the impression that the initialization code may be omitted by the linker if there is no reference of that name anywhere.

Is that true?

Does it make a difference if other static variables from the file A.cpp are used? Maybe it has to do with the automated Makefile creation by the IDE, maybe it omits the whole cpp file if nothing is referenced from within

*The A.cpp belongs to a static library which I include into the project as a .a file

Thanks!

  • 1
    If your program cannot observe the difference, why does it matter? – n. m. could be an AI Mar 23 '19 at 18:16
  • 1
    @n.m. The constructor of `B` seems to have side effects: *"its construction has impact to the system configuration. (It may affect peripheral registers for example)."* – HolyBlackCat Mar 23 '19 at 18:16
  • I have actually seen that in some cases it was omitted. I would expect it to always be initialized. Let me also point out an important detail: The A.cpp belongs to a static library, which I include to the project as precompiled. Maybe that has an impact? – user8839561 Mar 23 '19 at 18:18
  • @user8839561 *"A.cpp belongs to a static library"* You should edit that info into the question. – HolyBlackCat Mar 23 '19 at 18:27
  • I would ask myself if I can do what I need without relying on initialization of a static variable (or its initializatino order, which is more or less unpredictable to me). Good queston anyway. – Gian Paolo Mar 23 '19 at 18:33
  • @user8839561 Are you finally asking about _singleton class instatiation_ guarantees at all? If none of your code interacts with the Singleton code, the compiler is likely to optimize it away. – πάντα ῥεῖ Mar 23 '19 at 18:38
  • Inside the constructor of B, important things happen. It interracts with system registers that have high impact, even if the rest of the code doesn't not explicitly interact with that object. How can I make sure this is preserved? – user8839561 Mar 23 '19 at 18:47
  • @πάνταῥεῖ above – user8839561 Mar 23 '19 at 19:03
  • @user8839561 _"How can I make sure this is preserved?"_ You instantiate it in some of your translation units. You can't rely on any instances made outsides of your _static class_ though. – πάντα ῥεῖ Mar 23 '19 at 19:06
  • @πάνταῥεῖ I do instantiate it in the A.cpp translation unit . I don't get your last sentence. A has only static members, no instances – user8839561 Mar 23 '19 at 19:11

1 Answers1

3

It's a bit complicated, because it depends also on the linker.

If you are building static libraries lib*.a then it's possible that the object will not be pulled into an executable during linking. In this case it will not be run. see GCC C++ Linker errors: Undefined reference to 'vtable for XXX', Undefined reference to 'ClassName::ClassName()'

If you use shared libraries or just build an executable then the code will be in the binary. Now comes the question of will it be executed.

Whether a static storage duration object is initialized depends a tiny bit on the implementation (there is some leeway to allow for optimization). BUT I think most implementations usually do it before main(). But a static storage duration object must be initialized before any variable or function defined in the same translation unit are used. See 6.8.3.3 of the standard for exact legal definition. see https://stackoverflow.com/a/1273257/14065

Now there is also another thing you have to consider. All static storage duration objects in a translation unit are constructed in the same order that they are declared within the translation unit (that happen during the dynamic initialization phase). Note: the order across translation units is undefined. see https://stackoverflow.com/a/211307/14065

Note: There is also a "static initialization" phase that happens prior to "dynamic initialization" phase where all static storage duration objects are zero initialized or are initialized using constant expressions.

But because of "Initialization Order Fiasco" (hate that name, it's not a fiasco its well defined you just need to know the rules, but that's the name you should google). You should avoid static storage duration objects at file scope (especially if they reference other static storage variables at file scope).

But there is workaround: Create a static storage duration in a function and return a reference. Use the function to get access to the object and that will solve your initialization order issues.

Using functions rather than globals: Is access to a static function variable slower than access to a global variable?
Solving the order of initialization: https://stackoverflow.com/a/335746/14065

Martin York
  • 257,169
  • 86
  • 333
  • 562
  • I am using a static lib*.a library. The object is in the global scope and not in another namespace. I understand the issue about timing. I have taken care that all my statics across translation units do not depend to each other. What do you mean 'it may not be pulled into the exevutable' It depends on the linker? Is it undefined by the standard? – user8839561 Mar 23 '19 at 18:28
  • May problem is not an initialization order issue at all. I am talking about complete omittance of the initialization code – user8839561 Mar 23 '19 at 18:57
  • @user8839561 It may be "defferred" if no other variable or function in the same translation unit is used. So put the declaration into a translation unit that you know will be used. – Martin York Mar 23 '19 at 19:00
  • @MartinYork Can you please elaborate on that? 'may', meaning it is undefined? Also any link to the concept of 'deferring' ? Also, why does it have to do with the translation unit as a whole? It is precompiled for sure since it belongs to a library – user8839561 Mar 23 '19 at 19:08
  • The implementation (the compiler) may defer the initialization of static storage duration object till after `main()`. This means the compiler may have optimizations that change the behavior. So the compiler is allowed to initialize your object after `main()` is called. – Martin York Mar 23 '19 at 19:24
  • The standard says the object must be defined before any functions or variables in the same translation units are accessed. So if the initialization was deferred then it will be completed before these functions or variables are used. – Martin York Mar 23 '19 at 19:27
  • `meaning it is undefined` No. Meaning it is implementation defined. I should think the implementation also needs to document this. But that is usually hard to find unless you are really into compilers and how they work. – Martin York Mar 23 '19 at 19:28