Reputation: 273
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;
}
Upvotes: 8
Views: 5583
Reputation: 308206
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.
Upvotes: 14
Reputation: 473487
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.
Upvotes: 12