5

I have a function which calls an initialization routine statically. This is needed for a pluginsystem where the plugin is either dynamically loaded (which works fine) or can also be statically linked. Since the static plugin is not known to the main application it must register itself, so that the main app knows about it (just like it were a dynamically loaded plugin).

The problem now is that the initalization is never called. However, when I add a dummy function and call it from the main app, then the initializer is suddenly called. So this looks to me as if the initalization is "optimized" away from gcc.

static bool registerPlugins(void)
{
    std::cout << "Registering CSV static plugin ... " << std::endl;
    PluginManager::registerStaticPlugin(&PluginInfo);

    return true;
}
static bool gCSVRegistered = registerPlugins();

The text "Registering..." is never printed, but adding the dummy function

void helper(void)
{
    std::cout << "Registered: "  << gCSVRegistered << std::endl;
}

... and call it dfrom the main app, then all the exptected text is printed.

So how can I force the static initalizer to not get thrown away???

I'm using Mingw32 with gcc 4.9.2 (just for the record :)).

Important update: The relevant code is in a static library. In this case it doesn't get triggered, only when the module is linked directly to the main application the initalizer is called.

SSCCE

main.cpp:

#include <iostream>

const char *gData = NULL;

int main()
{
    if(gData)
        std::cout << "Registration: " << gData << std::endl;
    else
        std::cout << "Registration: NULL" << std::endl;

    return 0;
}

void registered(const char *pData)
{
    gData = pData;
}

static1.cpp

void registered(const char *pData);

static bool registration(void)
{
    registered("Static initializer 1");
    return true;
}

static bool reg = registration();

static2.cpp

void registered(const char *pData);

static bool registration(void)
{
    registered("Static initializer 2");
    return true;
}

static bool reg = registration();

static library: lib_main.cpp

void registered(const char *pData);

static bool registration(void)
{
    registered("Static initializer library");
    return true;
}

static bool reg = registration();
Community
  • 1
  • 1
Devolus
  • 21,661
  • 13
  • 66
  • 113
  • 8
    Objects with static storage duration at namespace scope are only required to be initialized before the first function in the TU in which they appear is called. – Kerrek SB Sep 15 '15 at 11:06
  • Hm, it appears to me that if the initializer is called on access, it should still work properly. – Bartek Banachewicz Sep 15 '15 at 11:07
  • Oh! The original function is a class object, so this means I should make it a static function like I posted here? Thought this wouldn't be relevant, but it seems it is... – Devolus Sep 15 '15 at 11:09
  • A good compiler does not link into the executable that which isn't used. So if you don't use that static anywhere it will be thrown away. As a wild guess as to what you try to accomplish: I think you're looking for what I know as the factory-pattern. – Nathilion Sep 15 '15 at 11:11
  • I cannot reproduce the problem with the code (minus the register call) in your question, even if in different translation units with gcc 4.8.5. Have you tested the posted version? – dhke Sep 15 '15 at 11:12
  • So I have to artificially use it? – Devolus Sep 15 '15 at 11:12
  • 3
    Relying on static initialization order is a bad idea. You may also want to read https://isocpp.org/wiki/faq/ctors#static-init-order and https://isocpp.org/wiki/faq/ctors#static-init-order-on-first-use to prevent you from going to static initialization hell. – Jens Sep 15 '15 at 11:13
  • So how can I solve the problem to register the plugin? – Devolus Sep 15 '15 at 11:14
  • @Devolus: Magic self-registration is not strictly supported by C++. The only standard-guaranteed way is to call the registration function from your `main` function (or containing TU). – Kerrek SB Sep 15 '15 at 11:16
  • 1
    @KerrekSB The standard says "A non-local variable with static storage duration having initialization with side-effects must be initialized even if it is not odr-used". Isn't the `cout` a side-effect and thus it must be initialized at some point? – Jens Sep 15 '15 at 11:16
  • @Jens: cf. 3.6.2/4 in the WD. Compilers/linkers may offer an "always link" option or some other kind of annotation that forces initializers to run, but that's platform-specific. – Kerrek SB Sep 15 '15 at 11:18
  • @KerrekSB 3.6.2/4 has a footnote, which contains the text I quoted. Also, 1.9/12 defines all calls to I/O functions as side effects. I think if this is reproducible, it should be a compiler bug. – Jens Sep 15 '15 at 11:21
  • 1
    @Jens: Footnotes aren't normative, are they? – Kerrek SB Sep 15 '15 at 11:23
  • @KerrekSB I don't know. Anybody here with more standard knowledge? I couldn't reproduce it anyway, but I would like to have an answer. – Jens Sep 15 '15 at 11:27
  • I wrote a SSCCE and there it gets called, which is strange. It crashes because of std::cout but that is not a problem and only fore debugging anyway. – Devolus Sep 15 '15 at 11:29
  • @Devolus The cout may be the important part because it is a visible side-effect. – Jens Sep 15 '15 at 11:31
  • @Devolus Can you post a complete, minimal example? I think some people tried to reproduce it, but at least my platform behaves as expected. – Jens Sep 15 '15 at 12:09
  • @Jens, that is the problem. I have written an SSCCE and there everything works. It just doesn't work in my main code, even though I do the same. The only difference is that the module is linked via a library, which might mean that it gets optimized away before the final linker sees that it is needed. – Devolus Sep 15 '15 at 12:11
  • At least I can now reproduce it. it happens only when the static intialization is inside a libray (which is the case in my project). I wrote an SSCCE and there happens the same. Only the initializer from the main project is triggered, and the one from the library is gone. – Devolus Sep 15 '15 at 12:19
  • @Devolus There is no guarantee about the order of initialization of two different translation units. You cannot rely on this. Read isocpp.org/wiki/faq/ctors#static-init-order and isocpp.org/wiki/faq/ctors#static-init-order-on-first-use. – Jens Sep 15 '15 at 12:20
  • @Jens, the order doesn't matter to me. In my SSCCE I added a second module to main and both get called, only the one from the library not. So I guess I have to rewrite my CMake project for static linking. :( – Devolus Sep 15 '15 at 12:26
  • Added the SSCCE for more information. – Devolus Sep 15 '15 at 12:29
  • @Devolus I am confused. If you create a static library, you are already using static linking. Are you using a shared library? How do you know that both modules get called? You are only printed the final result of gData. – Jens Sep 15 '15 at 12:51
  • @Devolus And given that reg is not used anywhere in the translation unit, I think the compiler legally optimizes it out. – Jens Sep 15 '15 at 12:54
  • @Jens, I set a breakpoint so I could see which ones were called. cout crashes in this example, so I couldn't print it. Anyway, your comment lead me to the right track and I have solved the problem now using ` -Wl,--whole-archive` I will put an answer, or if you write it, I would gladly accept it. – Devolus Sep 15 '15 at 12:59
  • 1
    @Devolus I think you can just refer to http://stackoverflow.com/questions/805555/ld-linker-question-the-whole-archive-option. But I still think that the compiler would be allowed to optimize the calls away, at least in the simple example where the registered function has no side effects. This may happen when you use a more recent gcc version (> 4.8) and link-time optimizations. – Jens Sep 15 '15 at 13:05
  • @KerrekSB Footnotes are not normative: http://stackoverflow.com/questions/21364398/are-notes-and-examples-in-the-core-language-specification-of-the-c-standard-no. – Jens Sep 15 '15 at 13:07

3 Answers3

3

So because of the comments on the question that finally led me into the right direction. The problem is that the function is in a static library and there the code is not called if it is not used in the main application, because the linker doesn't even add the code to the executable.

For a more detailed description I found this question: Static initialization and destruction of a static library's globals not happening with g++

In order to force the code being called, I have to either move the registration into a module that will be needed for sure (and thus gets linked in), or use the linker option -Wl,--whole-archive.

ld linker question: the --whole-archive option

Community
  • 1
  • 1
Devolus
  • 21,661
  • 13
  • 66
  • 113
  • 1
    For what it's worth, the concept of a "library" is not part of C++, so you are automatically in platform-specific territory here. Note that if you *do* odr-use any part of the library, the standard guarantee would in fact be effective (since the linker would no longer be allowed to remove the dependency). – Kerrek SB Sep 15 '15 at 13:28
2

I can't reproduce the error, but volatile could do the trick:

static volatile bool gCSVRegistered = registerPlugins();
alain
  • 11,939
  • 2
  • 31
  • 51
1

Because you don't use the static variable it gets optimised away. To prevent that you can tell the compiler it is needed using the "used" attribute:

[[gnu::used]] const bool gCSVRegistered = registerPlugins();

(I also changed static to const because presumably you don't want to modify the variable after it is initialized, and namespace-scope const objects are static by default anyway).

For C++03 you can use the non-standard GNU attribute syntax:

__attribute__((used)) const bool gCSVRegistered = registerPlugins();
T.C.
  • 133,968
  • 17
  • 288
  • 421
Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
  • 1
    I don't think that the compiler is allowed to optimize it away. The function calls cout, which is part of the observable behavior and thus it would be a violation of the as-if rule. – Jens Sep 15 '15 at 11:28
  • The attribute should still solve the OP's problem though. – Jonathan Wakely Sep 15 '15 at 11:31
  • 1
    @T.C. `__attribute` and `__attribute__` are equivalent, so your edit was redundant. – Jonathan Wakely Sep 15 '15 at 11:32
  • @JonathanWakely TIL. I've never seen the former form :) – T.C. Sep 15 '15 at 11:36
  • @JonathanWakely Is there something in the documentation about the first form? I can't find it. – T.C. Sep 15 '15 at 11:39
  • 1
    Neither volatile nor this used attribute works. It compiles but it doesn't change anything. – Devolus Sep 15 '15 at 11:41
  • 1
    @T.C. hmm, it seems to be undocumented, so I suppose preferring `__` before and after is better, as it's documented. It works for other extensions too, `__typeof` etc. – Jonathan Wakely Sep 15 '15 at 11:59
  • This is basically equivalent to alain's `volatile` solution (writes to `volatile` are observable, cannot be removed, and are therefore odr-used) but alain's solution is portable. – MSalters Sep 15 '15 at 13:37