Unsolved Static Member variable and static member function resides in ????
-
@Kent-Dorfman Knowing memory layout can be REALLY important. The lack of that knowledge can introduce bugs. Like the static initialization order problems. It should also be noted that static/global vars are initialized by the OS before main executes. While this init may not occur on an embedded system with no OS. So it can affect portability of code.
-
In c++ a design that is sensitive to static order initialization is itself suspect, and shouldn't pass a reasonable code review. It's not the OS that initializes anything in a c++ elf or a.out image. it is typically a small linked in object called crt0.o and since the order of global/static initialization in c++ is undefined, it should never be relied upon, even if a particular platform is observed to behave a certain way.
The well defined and verifiable work-around is the "constuct on first use idiom". None of which requires internal knowledge of program memory layout in c++.
These kinds of discussions remind me of things like editing vtables in c++. There should never be a reason to do so, and in fact, the language specification doesn't even require their existence.
-
@Kent-Dorfman said:
These kinds of discussions remind me of things like editing vtables in c++. There should never be a reason to do so
I disagree. It's not like "if it's not in C++ standard it doesn't exist". The hardware is hardware and doesn't care for high level languages. OSes rarely have C++ API only, so to leverage some fine grained features of these you sometimes need to rely on hardware/OS guarantees, not those of C++ standard.
Sure, I'm not saying you should rely on static initialization order because there's just no guarantee at all, C++ or otherwise, but that's not what OP was asking and I don't see anything wrong in wanting to know how stuff works. I see that as a positive. Curiosity in programming is a good trait in my view, even if you don't necessarily immediately apply everything you're curious about. I think saying "you shouldn't be asking that" is a trait of bad teacher, not bad student.As an example - I wouldn't automatically take a question about undefined behavior as a sign that someone wants to exploit it, but rather someone trying to avoid it. Same here - asking about statics can lead to better usage of them and avoiding some of their traps.
-
@fcarney said in Static Member variable and static member function resides in ????:
@Kent-Dorfman Knowing memory layout can be REALLY important. The lack of that knowledge can introduce bugs. Like the static initialization order problems.
Since this question is still going on: How does knowing anything about memory layout address anything about the static initialization order problem you linked to?
-
@JonB I kinda lumped it into knowing when and where things are happening. That problem in particular may not be all that relevant. I found it interesting because I found out that the OS does initialize the global memory for the app at startup ("and is zeroed out by the OS when loading the program"). This zero init does not happen on systems without an OS so may provide challenges for portability of code. I found this out when researching why I was having init problems of static/global variables that accessed the same data structure from different compilation units. This lead to a series of investigations to find out if I really did need to init some variables or not. There was a constructor that would overwrite things when it wasn't really needed. That code is legacy and needs to be reworked, but unfortunately it is production code at the moment. So yeah, not directly related, but definitely in a similar realm of knowledge in my mind. To me, at times, it has been important to have some kind of idea of memory layout when coding. Of course this is more important when doing things system wise like firmware development or microcontrollers. Desktop apps, not so much, unless memory is highly constrained for some reason. Then it might be important.
-
@fcarney Just a note: you should always initialize all your variables and not trust OS/framework/whatever to do it for you :-)
-
@jsulm I'd say you should usually initialize your variables. There are a lot of C style APIs out there that, for example, take a reference to a data structure and fill it out. Initializing the members beforehand is just wasteful and compiler can't always optimize redundant writes like that, especially on function boundaries. I've seen a case with code like
Foo array_of_foos[COUNT]; ... Initialize(array_of_foos);
where someone innocently enough did a static analysis pass and the tool suggested
Foo array_of_foos[COUNT] {};
This started to show up badly in profiles. Generally I would avoid words like "always" and "never" in C++. "Most of the time" or "rarely" seem to fit a lot better with reality.
-
I'm a touch confused about one thing which is being said. (Yes, I could go look it up somewhere, but I prefer to ask here.)
Earlier @fcarney said in Static Member variable and static member function resides in ????:
It should also be noted that static/global vars are initialized by the OS before main executes. While this init may not occur on an embedded system with no OS. So it can affect portability of code.
int fred[100]; void func() { static char jim[100]; }
In my day both
fred
&jim
were guaranteed initialized to 0s (zeroes), and AFAIR that was specified in the C language. Are you saying that an OS implementation does not have to do that, so it's not a feature of the language? -
I am going by what cppreference says. It says the OS initializes some things. I have no idea if the OS "has" to or not. My guess it is supposed to. On an embedded system, without an OS, then there is no OS to init this memory.
-
@fcarney said:
I am going by what cppreference says. It says the OS initializes some things. I have no idea if the OS "has" to or not. My guess it is supposed to.
C++ standard says those are initialized. It does not say an OS should or shouldn't do that. It has no notion of an OS. How this initialization is done is implementation defined and a conforming toolchain will do whatever is required to achieve that effect on given platform. In some cases it may be a compile time initialization stored in the executable image, for some the OS loader may take care of that at load time and for other cases (like no OS) the compiler might generate explicit code run before main() to initialize those variables. The point is that C++ standard does not define how a given effect is to be achieved, only that it is. This is intentional, as it does not impose artificial requirements on platforms and is left for toolchain implementers to do whatever is the best option for their platform.
-
These discussions are so cool! :)
Edit:
@Chris-Kawa said in Static Member variable and static member function resides in ????:How this initialization is done is implementation defined
With major note that the standard dictates trivial initializations happen before non-trivial ones. And this is a behaviour one can rely on.
-
@Chris-Kawa said in Static Member variable and static member function resides in ????:
left for toolchain implementers to do whatever is the best option for their platform
So I did test this on an embedded platform. It was an Arduino IDE based platform (Teensy 4 in this case). It does indeed init arrays declared in the global/static region of the code. For testing I created an array of structs. Looping through I was able to confirm that everything was zeroed out. Thanks for the clarification.
-
@fcarney said in Static Member variable and static member function resides in ????:
For testing I created an array of structs. Looping through I was able to confirm that everything was zeroed out. Thanks for the clarification.
That's not a valid test. initailize with a unique bit pattern. Power off, then restart the board and verify the bit patterns after reinitialization. Also read the standard regarding object/variable initialization. The rules are here:
https://en.cppreference.com/w/cpp/language/default_initializationWhat is not defined is the order of static initialization. Thats why it is important to initialize static members as part of the main() startup: "construct on first use".
-
@Kent-Dorfman said in Static Member variable and static member function resides in ????:
Thats why it is important to initialize static members as part of the main() startup: "construct on first use".
No, that's not how it works. Global static variables are initialized before
main()
. They might have side effects which need to be visible before the variable itself is used. Heck, there might even not be anymain()
in your program. For example MFC framework has just a static global variable that runs event loop from its constructor. There usually is nomain()
in such app and yet it still runs. -
Further to @Chris-Kawa's (correct) observation above about "Global static variables".
Turning to non-global scope static variables. A few months we had an interesting discussion in this forum about when these get initialized, but I don't have the reference to the thread. We were talking either about
static
s declared in a function, or in a class, or both, I don't recall.We found these are indeed only initialized on "first use" of the function/class/whatever. We looked at the code generated to accomplish this. It turns out that the compiler generated a "hidden guard variable/memory location" for these. Effectively like a boolean initialized to 0/false to say "not yet called for initialization". On being called/created, the code tests this variable and does the initialization once at this instant if the boolean is still false. Then it sets the boolean to true, so that initialization will not occur again.
This allows e.g. a static initialization somewhere to reference, say, another class/object which itself is not ready to be called at start up time; it will not evaluate the reference until the calling code is actually first executed, thereby allowing the referenced object to have been initialized itself.
I at least found the "hidden guard" code implementation interesting :)
-
Yeah, function local statics are scoped, so they behave a little different. This case of static init was further complicated in C++11. Before that C++ standard had no notion of threads, so initialization of static variable in a function called simultaneously from different threads was undefined - it could be initialized once, twice, at all, partially or crash your app entirely. In C++11 this is guaranteed to be initialized exactly once. It's not specified from which thread that init occurs, only that the observed behavior is that on first use in any thread the variable is fully initialized. This got nicknamed as "magic statics". It's important to note that because of the synchronization needed function statics initialization in C++11 became kinda costly operation, so it's something to keep in mind. This also lead to a new storage specifier -
thread_local
. -
@Chris-Kawa said in Static Member variable and static member function resides in ????:
It's important to note that because of the synchronization needed function statics initialization in C++11 became kinda costly operation, so it's something to keep in mind.
Sort of, but on the bright side function static initialization happens on first use only (threads notwithstanding), so if you never call the method that inits it, it is never initialized (which is not true for class's statics that are the same as globals).
-
@Chris-Kawa said in Static Member variable and static member function resides in ????:
No, that's not how it works. Global static variables are initialized before main(). They might have side effects which need to be visible before the variable itself is used. Heck, there might even not be any main() in your program. For example MFC framework has just a static global variable that runs event loop from its constructor. There usually is no main() in such app and yet it still runs.
Referring to a 25 year old framework in a discussion of modern C++ best practices really doesn't help your case though, and even humble MFC had a main(), they just didn't call it that. I think it was _tmain() or wmain().
See https://stackoverflow.com/questions/895827/what-is-the-difference-between-tmain-and-main-in-cThe best practice in the regulated field I work in is to use the "construct on first use" paradigm. Global (static) objects are bad when they have interdependence upon each other. that's why it is often better to use global static pointers to those object and construct them in a well-defined order in main().
I suspect we'll continue to agree to disagree. LOL
-
Referring to a 25 year old framework in a discussion of modern C++ best practices really doesn't help your case though
Why? Things haven't changed that much really. Some new stuff came along but the old is very much still there. There's still a lot of software using MFC today. I'm not saying you should use it in new projects or anything like that, but it's still there, still works and the rules of how it works didn't change, so I think it's a perfectly valid point in this discussion. I'm not talking about some obscure tech that fell out of use. It's (sadly) very much alive.
and even humble MFC had a main(), they just didn't call it that. I think it was _tmain() or wmain().
Right, well, it's, again, a little bit more complicated. Yes, that framework provides a
_tWinMain
that calledAfxWinMain
and that's one way to make an MFC app, but you can also skip that and start the event pump another way. Anyways, I don't want to get into MFC discussion, that wasn't the point :P The point was you don't need amain()
or any other function to initialize global statics and run some code in their constructors. Another example is global statics in dynamically loaded libraries. I hope this one is less controversial :)The best practice in the regulated field I work in is to use the "construct on first use" paradigm.
I think we have a misunderstanding coming from that name, but the thing it names is a perfectly valid strategy, I agree.
Global (static) objects are bad when they have interdependence upon each other.
I absolutely agree. If I seemed to advocate for using static globals let me be very clear - I'm not. They have their uses, sure, but also the order of initialization problem you mentioned, so that's something to take into account in your app design. If it matters - don't do it, if it doesn't - go ahead. I was merely trying to convey how they work on a technical level.
that's why it is often better to use global static pointers to those object and construct them in a well-defined order in main().
See, I think this is the point of confusion we had. If you use global pointers then those pointers get initialized (zeroed) in the way I was taking about. The pointers, not the objects that they later point to and you can obviously assign to them in
main()
or wherever else in a controlled order that you like. The pointers themselves though are originally initialized for you in an unspecified order. It doesn't really matter in that scenario of course as you overwrite them later in a way you see fit. The gist of it is that the pointers are the global static objects we're talking about here, not the things they later point to.I suspect we'll continue to agree to disagree. LOL
I think we actually agree. We just misunderstood each other on some technicalities ;)