5

According to information on other sources C++ distinguish two kinds of initialization of static variables:

  • static - if variable is initialized by putting it within initial value to special section in executable file.

  • dynamic - if initial value of static variable has too be computed

There is many discussion regarding order of dynamic initialization constructors calls. But I didn't found info how to wait until all dynamic initialization of all static variables in executable finishes. Or from other side how to call by hands this initialization in the indirect/generic way.

I use static variables initialization for loose coupling of kind of plugin architecture. I have plugin1.c, plugin2.c ... and static variable inside of plugin1.c static bool installed = plugin1_install();

But in main I need to wait until all plugins installed.

The same thing I use was suggested here 1. As the answer to following question

I wanted to write a shared library for my program. However, I need the library to have some self initialization routines before anyother functions in the library are called...

Answer:

C++ itself supports global initializations of things. You can do things like:

int global_variable=some_global_function();

This would be illegal in C, but is legal in C++.

Can I implement feature I need with help of __CTOR_LIST__?

Community
  • 1
  • 1
V_V
  • 612
  • 9
  • 23

4 Answers4

1

There is no need to wait. In C++ program startup is singly-threaded so once you get to the first instruction of main all global static storage duration variables will have been already initialized.

The problem you read about is however that there are little guarantees about what is the order of initialization during that startup phase. In other words if the initialization of a static object requires the use of another static object then you're possibly in trouble.

My suggestion is try to avoid doing complex processing during the startup phase... and specifically avoid doing anything that could possibly fail. The reason is that during that period (and during its dual at shutdown time) the system is not yet (or not any more) 100% functional and debugging is especially hard. For example in windows systems a segfault during shutdown is often silenced and with some environments debugging is not working properly before the start of main.

If your initialization phase is just however plugin registration then this can be made safe by using a lazy singleton pattern for the registry:

struct Plugin
{
    Plugin(const std::string& name);
    ...
};

std::map<const char *, Plugin *>& registered_plugins()
{
    static std::map<const char *, Plugin *> directory;
    return directory;
}

Plugin::Plugin(const char * name)
{
    registered_plugins()[name] = this;
}

...

struct MyPlugin1 : Plugin
{
    MyPlugin1() : Plugin("Plugin-1")
    ...
} MyPlugin_instance;

In the above code MyPlugin_instance variable will be created during startup (before main) but the plugin registry is known to have been already correctly constructed because it's not a global but a function static, and those variables are initialized the first time their scope is entered.

Declaring instead the global plugin directory as a static duration global would be problematic because if plugins and the directory are not in the same compilation unit then there is no guarantee about the order of initialization; therefore it could be possible that the plugin gets constructed and tries to register itself in a map that is not yet constructed.

What could still be dangerous with this approach is however accessing the singleton in the destructor of a static duration object because - like for construction - the risk is that someone will try to use the registry after it has been already destroyed.

My suggestion is anyway to try to keep this kind of pre-main processing at a minimum; anything non trivial can be a source of big and hard to debug problems. Startup and shutdown, if possible, should be IMO kept under clear control and singly threaded.

6502
  • 112,025
  • 15
  • 165
  • 265
  • "In the above code MyPlugin_instance variable will be created during startup (before main)". According to this answer [1](http://stackoverflow.com/questions/6234791/how-to-wait-until-dynamic-initialization-of-static-variables-finishes/6235448#6235448) it is not necessery true if `main` and `MyPlugin_instance` are in different translation units. – V_V Jun 04 '11 at 08:41
  • Like I wrote as a comment in that answer, while this is formally true this is false AFAIK in every existing C++ implementation (and an implementation not doing that would have many problems with a lot of existing C++ software where this technique is used). By the way the standard in 3.6.2/4 [34] explicitly says that a non-local variable with static storage duration with side effects must be initialized even if it's not used. Saying that it could be initialized after the start of main but before first use is clearly nonsense (there are no uses!). – 6502 Jun 04 '11 at 09:08
  • @V_V: That is not true. If `MyPlugin_instance` is in the global namespace. Then it will be fully initialized before main is entered. Translation unit has nothing to do with preventing initialization (the order of of SSDO initialization is affected). – Martin York Jun 04 '11 at 09:16
  • "...non-local variable with static storage duration with side effects must be initialized even if it's not used. Saying that it could be initialized after the start of main but before first use is clearly nonsense (there are no uses!)." No, it makes perfect sense: Standard does **not** say "before first use of *that variable*", standard says "before first use of **any variable or function defined in the same translation unit**" – Serge Dundich Jun 04 '11 at 15:14
1

You can't do it the way you are doing it. I've done this before in C++ and there are a few problems due to the way C++ works that you don't have in other languages.

First of all, there is no guarantee of order of initialization. Someone already copied and pasted the statement from the standard, and this is basically what the copied paragraph means.

So the basic approach I took was to use a prototype pattern. For purposes of following the open-closed principle, you don't want to have a piece of code that you have to keep modifying each time you add a plugin. So the plugins need to either register themselves (which is hard) or you have some startup class that loads them from shared libraries (which is quite a bit easier). The approach you take depends on what your requirements are. If you are familiar with the prototype design pattern, the rest of this might make some sense.

It looks like you're leaning towards them registering themselves since your making your plugins as part of your code. I would highly suggest taking the approach of putting each plugin in a shared library rather than do it this way (will explain why in a moment). Using shared libraries, you can have one class load shared libraries from a directory list and probe them for your plugins. If this is done on initialization, then all the plugins are loaded before any part of your program uses them.

To make plugins register with the prototype manager upon program startup, the prototype manager needs to be a singleton. You can make a class that performs the registration for you, and each plugin class' file can define an instance of that registration class. When you add more plugins, they automatically register as long as you make an instance of that registration class as a global variable.

Now here is the hard part. If you don't have an explicit reference to an instance of any of your plugins, it may be optimized out of the code on compile when compiling an application (depends on the compiler). The reference to it when registering with the prototype manager is not enough; there is no direct call to an instance of your plugin, so the linker will not link code that is not called. I worked around this by containing all the plugins and the prototype manager in a shared library that was linked to the application. If it is in a shared library, the linker can't optimize it out because it doesn't know if a particular class will be referenced by code using that shared library. (Thus the suggestion to go to a shared library per plugin.)

There may be a way to force a reference to each plugin through compiler settings. I had to do this for an iPhone app in Objective-C. I don't like this because it means you have to remember to add the reference in the compiler settings every time you add a new plugin. In my opinion, this does not follow the open-closed principle even though you're not modifying code.

This would work with function pointers too (since you might be doing that for some reason). It would take a bit of modification to the way the prototype pattern is created.

I'm sure this is clear as mud. =) Hopefully you go with the one-shared-library-per-plugin approach and make it easy on yourself.

  • Note: There are guarantees about the order. Variables in the same translation unit (ie file) are guaranteed to be constructed in the order of declaration. – Martin York Jun 04 '11 at 09:17
  • @Martin: "Variables in the same translation unit (ie file) are guaranteed to be constructed in the order of declaration" No. In the order of **definition** (within same translation unit). – Serge Dundich Jun 04 '11 at 13:16
0

According to ISO14882-2003 (3.6.2):

  • Dynamic initialization of static duration objects of namespace scope (including global namespace scope) is performed in order of objects definition in a single translation unit. Order of initialization of objects from different translation units is unspecified.

  • Some particular dynamic initialization of namespace scope object may be performed either before the first statement of main function or it may be delayed after the first statement of main. If it is delayed then it is guaranteed that the initialization is performed before the first use of any function or object defined in the same translation unit as the object being initialized.

ADD:

Obviously to make sure that some of your static duration objects of namespace scope is initialized you just have to either call any function or use any object defined in the same translation unit (as the object you want initialized) in function main or in any function directly or indirectly called from the function main.

Serge Dundich
  • 4,221
  • 2
  • 21
  • 16
  • I remember discussing this specific point many years ago. IIRC this has been placed in the standard just to allow using of DLLs in C++ programs. AFAIK there are no implementations in which a module that is not dynamically loaded gets a delayed initialization (and such an implementation would have a lot of problems with a lot of C++ existing software where dynamic initialization is often used for updating directories). – 6502 Jun 04 '11 at 08:07
  • Note: The first statement is true. – Martin York Jun 04 '11 at 09:15
  • @Martin: "course you are wrong (in your comment below)" No I am not. See my answer to your comment below. "the first part is incomplet and thus misleading" Please explain. What is incomplete and how it is misleading? What kind of wrong conclusion the reader can make? – Serge Dundich Jun 04 '11 at 12:28
  • @Martin: Both of my statements are part of the section I'm mentioning part of which you have quoted in your answer (in less formal manner). – Serge Dundich Jun 04 '11 at 12:33
  • @6502: "AFAIK there are no implementations in which a module that is not dynamically loaded gets a delayed initialization" I believe that making assumptions about behavior of any implementation not based on documentation of any kind (standard or implementation's docs/help) is a very bad and dangerous practice. Some new version of the compiler may perform some additional optimization and break such assumptions. – Serge Dundich Jun 04 '11 at 12:54
  • @6502: Actually I don't think that there is so much software that would break if initialization is performed not before `main` but before first usage of the same translation entity. – Serge Dundich Jun 04 '11 at 13:14
-1

Unfortunately the language does not support those semantics.

static storage duration objects SSDO (both static and dynamically initialized) are only guaranteed to be initialized before they are used. The standard has a description of the ordering constraints and guarantees in [basic.start.init].

The following describeshow dynamic initialization may be deferred until after main.

(n2723) 3.6.2 Initialization of non-local objects [basic.start.init]

paragraph 4

It is implementation-defined whether or not the dynamic initialization (8.5, 9.4, 12.1, 12.6.1) of an object of namespace scope 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 use of any function or object defined in the same translation unit as the object to be initialized.

In practice, as long as your object have no side affects, then you can write your code as if they were all initialized before main (as an attempt to access them will cause their initialization (no explicit action is required by your code)). If they have side affects then don't rely on the side affects happening.

If you rely on the side-effects then you can take advantage of main()s location.
Just declare all SSDO objects in the same file as main(). Because they are in the same translation unit they must now all be initialized before main is entered.

It is written this way (or one of the reasons) to allow the usage of dynamically loading shared libraries (dll) without explicitly having the concept of shared libraries written into the standard. As a dynamically loaded shared library is not present in the executable before main() is entered it can not have its SSDO initialized before main(). The above part of the standard allows for this fact. Though it does guarantee that any SSDO will be initialized before they are used.

Shared Libraries

Of course nothing about shared libraries (dll) is defined in the standard as these components are not tied directly to the language but are a feature provided by the OS.

That being said. Most OS's when they load a shared library (through the standard shared library loading mechanism ie. dlopen()) will initialize all SSDO before the function call to load the shared library returns. In situations where SSDO initialization is deferred it will obey the rule defined above in [basic.start.init].

Order of Initialization:

The order of initialization is not usually important.

Unless you use a SSDO during the initialization (or destruction) of another SSDO. Then order does become important as you need to make sure that the used object has been created/initialized before it is used (otherwise its data is garbage).

You have a couple of options. If the SSDO are in the same translation unit, you can define there initialization order by the order they are declared in the translation unit:

// File OneFile.cpp
myClass2 gv_2   = some_othere_global_function();
myClass1 gv_1   = some_global_function(); // If this uses gv_2 it must be declared first

If they are in different translation units then you have no guarantees about the order:

// File 1.cpp
myClass1 gv_1   = some_global_function(); // If this uses gv_2 you may have a problem.

// File 2.cpp
myClass2 gv_2   = some_othere_global_function();

The order problem can be solved by using a function:

// File 1.cpp
myClass1& get_gv_1()
{
    static myClass1 gv1 = some_global_function();  // This function can only access gv2
                                                   // by calling get_gv_2(). This will of
                                                   // force dynamic creation of the object
                                                   // this it is guaranteed to be initialized.
    return gv1;
}

// File 2.cpp
myClass2& get_gv_2()
{
    static myClass2 gv2 = some_othere_global_function();
 // ^^^^^^
 //    Notice the use of static here.
 //    This means the object is created the first time the function
 //    is called (and destroyed on application exit).
 //
 //    All subsequent calls will just use the same value.
 //    Note the return type. We return a reference to the object.
    return gv2;
}

Also see:

Community
  • 1
  • 1
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • Thanks for the prompt answer. But what do you mean behind `namespace` here? If you mean C++ `namespace` than there is something called from this namespaces in my case but still dynamic initialization not always happens. – V_V Jun 04 '11 at 07:06
  • Namespaces has nothing to do with initialization order. – Serge Dundich Jun 04 '11 at 07:21
  • 1
    "Thus all SSDO in the global namespace will be fully initialized before main() is entered" This is not true. – Serge Dundich Jun 04 '11 at 07:35
  • 2
    In general, "at namespace scope" simply means "not in a function/class." Based solely on that quote, I'm not reaching the conclusion that variables in the global namespace have any more of a guarantee than variables in any other namespace. And i definitely dont see anything to suggest the entire namespace is initialized, rather than just the single translation unit. I don't have my copy of the standard handy, so maybe I'm missing something from the context. – Dennis Zickefoose Jun 04 '11 at 12:30
  • @Martin: If you think that global namespace is somehow different from any other namespace then please quote part of the standard your point is based on. – Serge Dundich Jun 04 '11 at 12:36
  • @Neil Butterworth: "How is it not true?" Martin even quoted the part of the standard that explicitly states that it is not true (though it seems he didn't understand that). – Serge Dundich Jun 04 '11 at 15:41
  • @Martin: It seems to me that you don't get that global namespace scope is also namespace scope. Other types of scopes are statement block scope (i.e. function local scope) and class scope. But global scope has exactly the same properties as any other namespace scope. If you think that global namespace is somehow different from any other namespace then please quote part of the standard your opinion is based on. – Serge Dundich Jun 04 '11 at 15:44
  • @Neil Butterworth: "I quoted nothing. You are confused." Right. I'm sorry. I fixed my comments (I thought you were Martin). – Serge Dundich Jun 04 '11 at 15:47
  • @Surge. Refactored. No I was not thinging global namespace was any different. Just that main() is in the global namespace. I have re-read and come to the same conclusion as you. – Martin York Jun 04 '11 at 16:21
  • @V_V: Don;t worry about it too much. The standard guarantees that before you try and use it an SSDO will exist and be initialized (with the limitations I spelled out in the section Order of Initialization). – Martin York Jun 04 '11 at 16:23
  • @Dennis Zickefoose: Thanks. Based on your comments made me read the quote more carefully. – Martin York Jun 04 '11 at 16:24