So I have a static library (MacOS, .a library). It's written in C++ and has static initializers in it's code like that:
//myclass.hpp
class MyClass {
...
static MyClass *defaultValue_;
static MyClass *newInitialDefaultValue();
...
}
...
//myclass.cpp
MyClass *MyClass::defaultValue_ = newInitialDefaultValue();
...
I'm linking my .dylib library against this .a lib. Unfortunately, when my .dylib file is loaded, no MyClass::newInitialDefaultValue() is get called.
What could be the reasons and how to deal with this?
I've tried -all_load and -force_load linker flags with no luck.
The all_load and force_load linker flags only ensure that the code is linked into the binary. I don't think these flags will help you.
The only guarantee that I think you can count on is that your initializer in myclass.cpp will be called before any other code in the same cpp file is called. You need to expose access to your default via a function. This would be similar to the Singleton pattern. For example:
//myclass.hpp
class MyClass {
...
static MyClass *getDefaultValue();
...
}
...
//myclass.cpp
static MyClass* defaultValue_; // Note that this is not a member variable
MyClass* MyClass::getDefaultValue() {
if (defaultValue_ == nullptr) {
defaultValue_ = newInitialDefaultValue();
}
return defaultValue_;
}
...
NOTE: I made no attempt to make this thread safe or handle errors.
Now, the defaultValue_ still will not be automatically initialized when the library is loaded. But since the only access callers have is through getDefaultValue(), it is guaranteed to be initialized when they access it.
Related
First, just to avoid XY problem: this issue comes from https://github.com/cnjinhao/nana/issues/445#issuecomment-502080177. The library code should probably not do such thing (reliance on construction of unused global object) but the question is more about whether it's valid LTO behaviour rather than code quality issues.
Minimal code that showcases the same problem (untested, just to make example smaller):
// main.cpp
#include <lib/font.hpp>
int main()
{
lib::font f;
}
// lib/font.hpp
namespace lib
{
struct font
{
font();
int font_id;
};
}
// lib/font.cpp
#include <lib/font.hpp>
#include <lib/font_abstraction.hpp>
namespace lib
{
font::font()
{
font_id = get_default_font_id();
}
}
// lib/font_abstraction.hpp
namespace lib
{
int get_default_font_id();
void initialize_font();
}
// lib/font_abstraction.cpp
#include <lib/font_abstraction.hpp>
namespace lib
{
static int* default_font_id;
int get_default_font_id()
{
return *default_font_id;
}
void initialize_font()
{
default_font_id = new int(1);
}
}
// lib/platform_abstraction.hpp
namespace lib
{
struct platform_abstraction
{
platform_abstraction();
};
}
// lib/platform_abstraction.cpp
#include <lib/platform_abstraction.hpp>
#include <lib/font_abstraction.hpp>
namespace lib
{
platform_abstraction::platform_abstraction()
{
initialize_font();
}
static platform_abstraction object;
}
The construction of font object in main.cpp relies on the initialization of the pointer. The only thing that initializes the pointer is global object object but it's unsued - in the case of linked issue that object was removed by LTO. Is such optimization allowed? (See C++ draft 6.6.5.1.2)
Some notes:
The library was build as a static library and linked with main file using -flto -fno-fat-lto-objects and dynamic C++ standard library.
This example can be build without compiling lib/platform_abstraction.cpp at all - in such scenario the pointer will not be initialized for sure.
VTT's answer gives a GCC answer, but the question is tagged language-lawyer.
The ISO C++ reason is that objects defined in a Translation must be initialized before the first call to a function defined in the same Translation Unit. That means platform_abstraction::object must be initialized before platform_abstraction::platform_abstraction() is called. As the linker correctly figured out, there are no other platform_abstraction objects, so platform_abstraction::platform_abstraction is never called, so object's initialization can be postponed indefinitely. A conforming program cannot detect this.
Since you never reference object from static library in the main executable it is not going to exist unless you link that static library with -Wl,--whole-archive. It is not a good idea to rely on construction of some global objects to perform initialization anyway. So you should just invoke initialize_font explicitly prior to using other functions from that library.
Additional explanation for question tagged language-lawyer:
static platform_abstraction object; can not be eliminated in any circumstances according to
6.6.4.1 Static storage duration [basic.stc.static]
2 If a variable with static storage duration has initialization or a destructor with side effects, it shall not be eliminated even if it appears to be unused, except that a class object or its copy/move may be eliminated as
specified in 15.8.
So what is going on here? When linking static library (archive of object files) by default linker will only pick objects files required to fill undefined symbols and since stuff from platform_abstraction.cpp is not used anywhere else linker will completely omit this translation unit. --whole-archive option alters this default behavior by forcing linker to link all the object files from the static library.
Don't have global static variables.
The order of initialization is undefined (in the general case).
Put static objects inside funcations as static objects then you can gurantee they are created before use.
namespace lib
{
static int* default_font_id;
int get_default_font_id()
{
return *default_font_id;
}
void initialize_font()
{
default_font_id = new int(1);
}
}
// Change this too:
namespace lib
{
int get_default_font_id()
{
// This new is guaranteed to only ever be called once.
static std::unique_ptr<int> default_font_id = new int(1);
return *default_font_id;
}
void initialize_font()
{
// Don't need this ever.
}
}
I have a static library and it has a class like below in its header file:
namespace MyNameSpace
{
class MyClass
{
public:
void Something();
};
}
I linked above static library with my another dynamic link library project, and now I can call above function like below:
int Main()
{
MyNameSpace::MyClass A;
A.Something();
}
Although above works fine, I like to use my static library function like below, instead declaring classes:
int Main()
{
MyNameSpace::Something();
}
But I am wondering about the correct way to do this.
I tried it in static library like:
namespace MyNameSpace
{
void Something();
}
and after linking static library to the dynamic library, tried to use the function in it like:
MyNameSpace::Something();
But, IntelliSense doesn't see it and I already included my static library's header file and linked it properly.
What I want to do is declare a function in a static library outside classes and make it visible to another projects whose it linked into. In this case, to dynamic library.
Thanks in Advance.
I was finally able to do what I want by declaring the function like below:
In Static Library:
namespace MyNameSpace
{
void Something();
}
In Dynamic Link Library:
MeNameSpace::Something();
Now the function is visible even outside file scope.
I've a .h file that declare 3 different/similar class:
#ifndef _ICUSTOMCONTROLS_
#define _ICUSTOMCONTROLS_
class ICustomKnob1 : public IKnobControl
{
private:
// ..
public:
// ..
};
class ICustomKnob2 : public IControl
{
private:
// ..
public:
// ..
};
class ICustomButton : public IButtonControl
{
private:
// ..
public:
// ..
};
#endif // !_ICUSTOMCONTROLS_
I've a cpp file that define these class. I include the .h in the cpp and also in other .h/.cpp files within the project (which is a DLL).
At this point, in the CPP, I'd like to use a common/global struct (IText) for all the 3 classes. If I add the declaration in the .h:
IText gTextCustomControl;
in fact it "define" it, so I got a LNK1169 one or more multiply defined symbols found error message on compiling (as I said, I add this .h many times).
I could add it (with real definition) in the .cpp file:
IText gTextCustomControl = IText(12, &COLOR_WHITE, "Arial", IText::kStyleBold, IText::kAlignCenter, 0, IText::kQualityDefault);
But I will never sure in the DLL when this will be "processed" (maybe later the CTOR of the classes? maybe more times, wasting resources?). I don't think is a good way.
I could add extern as well on .h and define it as above, but for security reason is even worse (someone could access from the "extern" to it).
How would you manage this situation? Or its not possible share a common struct across objects?
"Bt I will never sure in the DLL when this will be "processed" (maybe later the CTOR of the classes?"
This is indeed the correct concern. DLL globals will be constructed from DllMain, and DllMain is heavily restricted in what it can do. You can't load other DLL's from there (loader lock), or do anything that would force Windows to load other DLL's. So using functions from other DLL's is generally also banned (exception: if your A.DLL causes your B.DLL to be loaded, then B.DLL may call functions from A.DLL. ).
Correct solution:
IText& getTextCustomControl();
...
IText& getTextCustomControl() {
static IText retval (12, &COLOR_WHITE, "Arial", IText::kStyleBold,
IText::kAlignCenter, 0, IText::kQualityDefault);
return retval;
}
This initializes the object on the first call to getTextCustomControl, which in general will happen way after all DllMain() functions have finished.
If you create the global variable inside the .cpp, there is no guarantee that it will be initialized prior to other global variables (see ยง3.6.3/2 [basic.start.dynamic]), in particular:
// dll.cpp
IText gTextCustomControl = ...;
ICustomKnob2::ICustomKnob2 () {
gTextCustomControl.doSomething();
}
// main.cpp
ICustomKnob2 knob2; // Oops! gTextCustomControl may be initialized after this.
If you want to be sure that gTextCustomControl initialized when you need it, you could put its declaration inside a function or some kind of singleton class, e.g.:
IText& get_gTextCustomControl () {
static IText ins(12, &COLOR_WHITE, "Arial", IText::kStyleBold,
IText::kAlignCenter, 0, IText::kQualityDefault);
return ins;
}
Then you use it instead of the global variable, this will ensure that the instance is constructed before any use of it.
ICustomKnob2::ICustomKnob2 () {
get_gTextCustomControl().doSomething();
}
Obviously, this will delay the construction of gTextCustomControl to its first use, which may not be what you want.
I am building an Xcode static library project, where I have a class defined as:
class New
{
public:
New() {
// Do something
};
static New fNew;
};
__attribute__((used)) New New::fNew;
The static global fNew should not be dead-code stripped by the linker as it has been force declared as ((used)). But I cannot see the constructor getting called for New. Any idea why this is happening?
EDIT:
Expectedly, when I add a dummy function to return a pointer to fNew
void* getDummyReference()
{
return (void*)&New::fNew;
}
and call the function from another file in the same static library project, I do not observe the issue any longer and the constructor is called as expected.
Why would the static global be stripped even with atribute((used))?
I've run into a confusing problem with CMake-generated DLL files on Windows. In my library, I use Curiously Recurring Template Pattern to give a certain classes a unique ID number:
// da/Attribute.h:
#ifndef DA_ATTRIBUTE_H
#define DA_ATTRIBUTE_H
namespace da {
typedef unsigned int AttributeId;
class AttributeBase {
public:
virtual AttributeId getTypeId() const=0;
protected:
/** Static ID counter. Every class that derives da::AttributeBase is
assigned an increment of this counter as its type ID number */
static AttributeId sNextId;
};
template <class Derived>
class Attribute : public AttributeBase {
private:
static AttributeId msTypeId;
public:
Attribute() {
if (msTypeId == 0) {
msTypeId = ++sNextId;
}
}
virtual ~Attribute() {
}
/** For static contexts */
static AttributeId typeId() {
if (msTypeId == 0) {
msTypeId = ++sNextId;
}
return msTypeId;
}
AttributeId getTypeId() const {
return typeId();
}
};
template <class Derived> AttributeId Attribute<Derived>::msTypeId = 0;
}
#endif
Problem is, when I link the DLL to an executable project, there appears to be some inconsistencies with the different ID methods. For example:
// Foo.h
struct Foo : public da::Attribute<Foo> {
Foo() { }
};
...
// main.cpp
Foo *foo = new Foo;
Foo->getTypeId() == 1 // True
Foo::typeId() == 1 // Should be true, but isn't. Foo::typeId() == 2
Running through with GDB, with a break in Foo::getTypeID(), I found that "msTypeId" and "Foo::msTypeId" had different memory addresses. What the hell.
This only happens when Foo is defined in the DLL, though. (And only in Windows 7, apparently--I don't have this problem in my Debian build) If I create the derived class inside main.cpp, or if I just compile all the code from the library into the executable, skipping the DLL step entirely, it works with no problems.
Everything was compiled using MSYS and MinGW, with GCC 4.7 on Windows 7 Home Premium.
Here's the CMakeLists.txt for the library, in case I messed something up there:
cmake_minimum_required(VERSION 2.6)
project(foo)
add_definitions(-std=c++0x)
set(CMAKE_BUILD_TYPE Debug)
set(sources
Foo.cpp
)
add_library(foo SHARED ${sources})
You have to export the types from the shared library. This is done using the __declspec(dllexport) and __declspec(dllimport) decorators. Read through the MSDN documentation; it's rather involved.
Since the header needs to have __declspec(dllexport) when building the library and __declspec(dllimport) when compiling the code that uses it, one usually defines a symbol, customarily called LIBRARYNAME_EXPORT and #ifdefs it depending on whether LIBRARYNAME_EXPORTS is defined.
CMake automatically defines target_EXPORTS when building (shared) library. It can be overridden by setting DEFINE_SYMBOL target property.
Unix chooses a different path and by default exports and also imports all symbols from shared libraries (except for static and explicitly hidden ones). This incurs a little bit of performance penalty as more symbols need to be resolved, but it much easier to use (no changes are needed to switch from static to shared library) and much more flexible (i.e. you can override symbols from shared libraries, which you can't do in Windows).