8

I am confused by the result i am getting from this code. In one dll the counter is incremented when the static variable is initialized. Then when main is executed i read this counter but i get 0 instead of 1. Can anybody explain this to me?

In my dynamic library project:

// Header file class Foo { int i_ = 0; Foo(const Foo&) = delete; Foo& operator= (Foo) = delete; Foo() { } public: void inc() { ++i_; } int geti() { return i_; } static Foo& get() { static Foo instance_; return instance_; } Foo( Foo&&) = default; Foo& operator= (Foo&&) = default; }; int initialize() { Foo::get().inc(); return 10; } class Bar { static int b_; }; // cpp file #include "ClassLocalStatic.h" int Bar::b_ = initialize(); 

In my application project

// main.cpp #include <iostream> #include "ClassLocalstatic.h" int main(int argc, const char * argv[]) { std::cout << Foo::get().geti(); return 0; } 
10
  • 1
    Try looking up Static order initialization fiasco Commented Aug 15, 2012 at 1:19
  • I just tried this and it gives me 1. Maybe you should not depend on static initialization order... Commented Aug 15, 2012 at 1:23
  • @Antimony I don't think this is the case. Because the order in defined in this cas... When Bar::b_ get initialized it will instantiate Foo and increment the counter. This will happen before main. Which it does, but the problem is that in main Foo::get() actually create a new Foo, different then the one created in initialize(). That is what i don't understand. Also take note that if all this code is the same library the output is 1. Commented Aug 15, 2012 at 1:26
  • @Henry Hu i don't depend on the initialization order. it is defined in this case. Are you sure you tested with the code in a lib and main in another project? I am maybe suspecting a bug in Xcode, but I really doubt it... Commented Aug 15, 2012 at 1:27
  • @monamimani I compiled the first .h and .cpp into a .so, and then compiled the second into an executable linked with the library. Commented Aug 15, 2012 at 1:29

2 Answers 2

14

The executable and DLL are both going to get their own copy of Foo::get(), each with their own copy of the static variable. Because they're in separate linker outputs, the linker can't consolidate them as it would normally.

To expand on this further:

The C++ specification allows an inline function to be defined in multiple translation units as long as they all have the same body; putting the function in a header file is perfectly OK because it ensures each copy will be identical. See https://stackoverflow.com/a/4193698/5987 . If there are static variables within the inline function, the compiler and linker need to work together to make sure that only one copy gets used between all of them. I'm not sure of the exact mechanics but it does not matter, the standard requires it. Unfortunately the reach of the linker stops after it has produced the output executable or DLL and it is unable to tell that the function exists in both places.

The fix is to move the body of Foo::get() out of the header and put it in a source file that's only in the DLL.

Sign up to request clarification or add additional context in comments.

12 Comments

But that doesn't make sens. if you have a singleton in one lib and you change its state from the same lib then the other lib wouldn't see those new state.
@monamimani: The problem is that your functions are inlined. So each executable has their own separate copy. You need to take the function definitions out of the header.
If this answer is correct, then why does the linker/loader not complaint about a clash?
@NicolBolas Really? Let say i use a singleton that declare it's instance as a local static variable, that mean that each call to getInstance() would get a fresh copy of the singleton. I doubt this is the case.
@Walter, inline functions are explicitly allowed to have multiple definitions as long as they're identical. See my update.
|
12

The rules of C++ state that inline function definitions will work correctly with static local variables. That is, if you inline function definitions, any local static variables will all be referring to the same variable.

However, there is one thing C++ does not define: DLLs.

The C++ specification is completely ignorant of DLLs; it has no idea how to handle them. C++ is defined in terms of static linkage, not dynamic.

As such, this means that the specification no longer applies when dealing with DLL boundaries. And that's where your problem comes from.

While C++ requires that inline functions with local static variables still work, C++ having no knowledge of DLLs means that everything is up to what the compiler decides to do.

It is perfectly legitimate compiler behavior for inline functions split across DLL boundaries to not have local static variables work as expected. It's such an outlier case that I seriously doubt any compiler developers spent time coding for such an eventuality.

The most reasonable thing for a compiler to do is exactly what it would do if you declared a extern global variable in a DLL header: each DLL and executable gets a separate one. That's why you need special syntax to say that a definition should be defined a by this executable/DLL (__declspec(dllexport)) and what will come from some other executable/DLL (__declspec(dllimport)).

You must always be careful of what you put across DLL boundaries. In general, don't inline things across DLL boundaries like this.

1 Comment

Is this behavior really compliant or merely reasonable? The C++ Standard doesn't address DLLs, but that doesn't mean DLLs are a blank check. If the Standard says static locals work for inline functions, a compliant implementation must abide.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.