• Forum
  • Lounge
  • Guaranteed order of global initializatio

 
Guaranteed order of global initialization

Pages: 12
For a long time now I have wanted to make a design whereby classes would call functions by initializing global variables so as to add themselves to a global factory of constructible classes, thus avoiding writing and maintaining a large list of classes manually - it would all be automatic as the classes could self-register.

However for the longest time I could not solve the problem of the global factory possibly not being initialized before any of the classes try to register themselves. This is an inherent problem with global variables, and just one of many reasons they are avoided.

Then I found out that static variables in functions are initialized the first time the function is called, and an idea struck - no matter what order the classes register in, if the global factory is a static variable in a function that returns it, it will always be initialized before it needs to be used. Problem solved!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct GlobalFactory
{
    using COnstructor_t = std::function<std::unique_ptr<Base> (Blah, Meh)>;
    using Factory_t = std::map<std::string /*ID*/, Constructor_t>;
    using Registration_t = Factory_t::const_iterator;

    static Registration_t Register(std::string ID, Constructor_t constructor)
    {
        Factory()[ID] = constructor;
        return Factory().find(ID);
    }

private:
    static Factory_t &Factory()
    {
        static Factory_t factory;
        return factory;
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct ClassA : Base
{
    //...
private:
    static GlobalFactory::Registration_t registration;
};

//cpp file:

GlobalFactory::Registration_t ClassA::registration = GlobalFactory::Register
(
    "MyCode::ClassA",
    [](Blah b, Meh m) -> std::unique_ptr<Base>
    {
        //I don't think I can use make_unique here
        return std::unique_ptr<Base>{new ClassA{b, m}};
    }
);


I haven't tested this code, but after fixing any syntax errors it should work, even with ClassB, ClassC, ClassD, etc...

The whole point of this is to avoid maintaining a list of all your classes - just compile them into the project or load them from a DLL at runtime, and BAM they're in the global factory. It works well with template instantiations too, otherwise you would have to list all template instantiations (and you wouldn't notice if you had extra).
Last edited on
I did this a while ago as well.

The problem is it didn't work if you statically link (or dynamically link... I forget which) to the library containing the self-registering objects, as the objects appear "unused" and are apparently optimized out.

ie: If my registration is defined in "foo.cpp", and I statically (?or dynamically?) link... its ctor wasn't getting run unless I called some function which was defined in foo.cpp.

Or at least that was the case when I tried it last (years ago). And maybe it was an issue with overaggressive optimization in the linker I was using. Regardless it's worth testing in your implementation.
I know that MSVC has an option to remove unused code, but I generally never use it because it can change behavior like you describe. When I have time I will investigate whether it's static, dynamic, both, or neither variant of linking that causes that behavior. I know for a fact in Java that behavior is the default (no static initialization until first use).
closed account (S6k9GNh0)
There are special instructions on any given platform to perform pre-main() initialization, including global variable construction. For a more proper example, SFML does this with TLS which is why there is no explicit resource allocation.
Last edited on
All I can find about TLS is Transport Layer Security, which, in the context of your post, doesn't make sense. What TLS are you referring to?

Also I think you forgot to backspace some text...
closed account (S6k9GNh0)
thread local storage - in this case, helps solves threading issues
Last edited on
Couldn't the language be extended to fix this problem by having the compiler generate a sort of dependency hierarchy of global objects that tells the runtime to initialise them in dependency order? Obviously there could still be circular dependencies and such, but that would be an error that the programmer would have to resolve, and it would be no worse than the present situation. And I know that it's generally preferred to keep the core language small and extend the standard library instead, but honestly, I think it would be better done in the language.
Determining the dependency hierarchy would not be straightforward.
LB wrote:
if the global factory is a static variable in a function that returns it, it will always be initialized before it needs to be used.

Hey, you independently reinvented the Meyers Singleton, good job! Can you do Schwartz Counter? (that's what makes std::cout safe to use in any constructor as long as <iostream> was included...)
To be honest, I have no idea how to replicate the behavior of the global streams within the confines of C++, I always thought implementation magic was used. Looking at explanations for it, I'm still confused as to how it works. :)

But yeah, I have heard of Meyers Singletons but never looked up how to actually implement them - I thought they were just a style of singleton. I guessed that my idea was reinventing someone that already had been discovered, I just didn't know how to search for it :)
I'm a little confused by the Schwartz Counter as well.

The examples I've found illustrate how it works with static members (which can be initialized from outside code), which make sense.

But how can you use it to ensure that the object's ctor was been called and the non-static members are initialized? IE: how does it guarantee that cout has been constructed?


EDIT:

Hmmm... actually... why do you need the counter at all? How about something like this?

1
2
3
// header.h

static Foo& cout;

1
2
3
4
5
6
7
8
9
10
11
// source.cpp

#include "header.h"

Foo& createFoo()
{
    static Foo theRealCout;
    return theRealCout;
}

Foo& cout( createFoo() );


Wouldn't that work without having to create a separate class? Or does the "statics initialize first" rule only apply to integral types? Or am I misunderstanding a critical point?


EDIT: Blarg this wouldn't work because you can't put the initialization of a static global in a separate cpp file.

Bljaslkrlksjdlslk
Last edited on
@LB and Disch
This helped me understand them: http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Nifty_Counter
@Script Coder: that's the one I found that confused me, lol
http://www.cplusplus.com/forum/general/110827/#msg605483

Why is this topic in the lounge? You would get far better mileage if you place programming questions in a section related to programming.
It's in the lounge because I was showing an untested proof-of concept, not asking a question.

And thanks, I think I understand now :)
closed account (S6k9GNh0)
I still don't understand the point of the counter. What advantage does it bring over just creating a global? EDIT: Also, how is it threadsafe?
Last edited on
computerquip wrote:
I still don't understand the point of the counter. What advantage does it bring over just creating a global?

It's reference counting, it makes sure the static members get destroyed when (and only when) there are no objects left.

[edit] Also, does this mean that when helios told me it was undefined behaviour to use std::cout in a static object's constructor, he was wrong, because std::cout is guaranteed to be constructed?
Last edited on
@Script Coder:

Yeah I saw that... but that only illustrates how to initialize static members:

1
2
3
4
5
6
7
StreamInitializer::StreamInitializer ()
{
  if (0 == nifty_counter++)
  {
    // Initialize Stream object's static members.
  }
}


Note the comment.

This does not illustrate how it guarantees that cout (a global object) has had its constructor called.

You can't explicitly call a constructor for a global object from within that if statement.
Last edited on
closed account (o1vk4iN6)
I think that only guarantees it for use outside of global initialization. If you try and use that stream object in the constructor of another global object then you have no guarantee that it will be constructed.

e: ok i understand how it works now...

It guarantees it had it's constructor called as there it defines an object in every source file it's used in:

1
2
3
4
// in stream.h
static class StreamInitializer {
// ...
} initializer; //Note object here in the header. 


That at least doesn't solve the problem cause the global initializer stream is referenced in side the stream constructor. While the Register object is not referenced anywhere, the constructor is just used as a means to run code before main is called.

I know Doom 3 does what you are trying to do LB to register object names, though all those objects are in an external library, idlib i think they call it. So they aren't optimized out.
Last edited on
It guarantees it had it's constructor called as there it defines an object in every source file it's used in:


That ensures the constructor for the initializer object has been called.
It does not ensure the constructor for the stream object has been called.

In this analogy... cout is a global object of type Stream. It is not 'initializer' -- that object is separate and is somehow being used to initialize cout. My question is how can it initialize cout? How can it ensure that the ctor for another object has been run?

I'm assuming cout is not a static object, so you can't use the same mechanism for ensuring its construction as the one we use to ensure 'initializer' has been constructed.
Pages: 12