Daniel Shawul wrote:Just say it plainly that strcpy() discussion was total garbage, and ofcourse you don't have to justify anything to me. What is puzzling is the deafening silence of the mods about the issue of pure programming discussions. I just recalled a perfect example of how I learned about effects of UB some 3 years ago with regard to order of destruction of objects, and how and where I asked questions, how I took my lectures... First my question is not asked here in a chess programming forum (so I do what I preach

) but in c++ codeguru forum, and it ended in like a page of discussion (so it ended very short as expected). I was actually looking for more discussion, but those guys does not let you ramble on. Infact I believe it needed a bit more clarification, but I gave up looking for it when one of the guys started lecturing me about not using global variables even though I mentioned use of global variables is necessary for the application. I guess that is a problem everywhere, i.e. getting lectures on programming languages/style, but sort of justified in a programming forum, but not in a chess_programming forum. Anyway you might find the actual UB discussion interesting so here
http://forums.codeguru.com/showthread.p ... ost1965764
Okay, now I understand your objection to the UB threads better.
About your global variables case from back then: I think the problem you ran into is commonly called
the static initialization order fiasco. The problem is that C++ guarantees to call the constructor and destructor for each global variable, but it doesn't promise to do them in any particular order. So any code you run "before" main (i.e. during one of those constructors of a global variable) or "after" main (i.e. during one of the destructors of a global variable) needs to be careful not to make use of any other global variable. All of the basic C types are safe, as are POD structs. But anything that does stuff in its ctor/dtor (such as STL containers, std::string, or any class that has an STL container or std::string as a member) can only be used after it has been properly constructed and before it has been destroyed. From the C++ point of view, that's the "lifetime" of the object; it doesn't really "exist" outside of those times, even if the raw storage space is still there. Constructors are invoked on some raw space and construct a usable object there. Destructors tear down that usable object, leaving just the raw space. So your "std::list dlist" got destroyed, and then the other class tried to access something in it and Bad Things(tm) ensued.
So how to make sure you only use globals that have been initialized and not destroyed yet? There are some tricks you can do to do a "lazy" initialization of the global, but they often come with a performance penalty (e.g. you replace it with a pointer and new/delete it yourself, but now you have to pay the cost of the pointer indirection every time you use it) and/or making them thread-safe can be a big challenge.
At my day job, we use a lot of "singleton" objects that would have this problem: they are global variables, and they need to be constructed and destructed. They often have complex dependencies between them, so we really need to control the order in which we initialize and destroy them. So what we always do is write our own Init and Destroy function for the program to initialize and destroy all of the singletons in the order that we want. We usually make some sort of "Singleton storer" template that reserves the proper size of space (and with proper alignment) for the singleton type, but doesn't have a constructor or destructor. (Its just raw space.) We call a method on this object, called CreateInstance or something, and that method uses placement new to construct the singleton in the space. Every place where we ever use this singleton, we call the GetInstance method which returns a reference to the proper object type. And when we are shutting down, we call DestroyInstance on it which invokes the singleton type's destructor.
So some variant of that might work for your case: make a template wrapper class that acts like the wrapped type except that it has no constructor or destructor, and it has methods to explicitly construct and destroy the wrapped type. Then initialize and destroy the wrappers yourself by calling those methods in a safe order when the program starts up or shuts down. Yes, this is kind of ugly... But the more globals of that sort that you have, the more important it is to be able to control the order yourself! And we use threads, but we make sure (during startup) not to start a worker thread before we have initialized all the singletons that it might try to access, and later (during shutdown) we wait for the thread to exit before we start tearing down those singletons.
A tricky part is that you want the wrapper to have the same alignment as the wrapped type; I'm not aware of a portable way to detect that alignment although the major compilers all support some extension for querying it. (Actually I think it can be deduced using some template metaprogramming, but that is real voodoo.) You could avoid that hassle by just aligning the wrapper to e.g. 16 since your wrapped type probably won't need more than 8 (for double or int64) or maybe 16 if it uses SIMD vector types.
Providing access to the wrapped "object" in a standard-compilant way is also tricky; you are probably casting a pointer to your raw storage into a pointer to the wrapped type, and the cast is probably illegal under the strict aliasing rule. At work we often have strict aliasing disabled anyway because we do plenty of type punning and nobody really understands how to do it safely AND efficiently across all our target compilers. In this case, I think even if the cast is technically undefined behavior, as long as you never try to access that storage using any _other_ type (such as whatever type you declared it with) then you should be OK, because the usual failure mode for strict-aliasing violations is the compiler believing that two memory accesses don't overlap because the types are different. So as long as you don't try to access the storage in some other way besides through that cast, it should be fine.