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))?
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.
}
}
Very simply put:
I have a class that consists mostly of static public members, so I can group similar functions together that still have to be called from other classes/functions.
Anyway, I have defined two static unsigned char variables in my class public scope, when I try to modify these values in the same class' constructor, I am getting an "unresolved external symbol" error at compilation.
class test
{
public:
static unsigned char X;
static unsigned char Y;
...
test();
};
test::test()
{
X = 1;
Y = 2;
}
I'm new to C++ so go easy on me. Why can't I do this?
If you are using C++ 17 you can just use the inline specifier (see https://stackoverflow.com/a/11711082/55721)
If using older versions of the C++ standard, you must add the definitions to match your declarations of X and Y
unsigned char test::X;
unsigned char test::Y;
somewhere. You might want to also initialize a static member
unsigned char test::X = 4;
and again, you do that in the definition (usually in a CXX file) not in the declaration (which is often in a .H file)
Static data members declarations in the class declaration are not definition of them.
To define them you should do this in the .CPP file to avoid duplicated symbols.
The only data you can declare and define is integral static constants.
(Values of enums can be used as constant values as well)
You might want to rewrite your code as:
class test {
public:
const static unsigned char X = 1;
const static unsigned char Y = 2;
...
test();
};
test::test() {
}
If you want to have ability to modify you static variables (in other words when it is inappropriate to declare them as const), you can separate you code between .H and .CPP in the following way:
.H :
class test {
public:
static unsigned char X;
static unsigned char Y;
...
test();
};
.CPP :
unsigned char test::X = 1;
unsigned char test::Y = 2;
test::test()
{
// constructor is empty.
// We don't initialize static data member here,
// because static data initialization will happen on every constructor call.
}
in my case, I declared one static variable in .h file, like
//myClass.h
class myClass
{
static int m_nMyVar;
static void myFunc();
}
and in myClass.cpp, I tried to use this m_nMyVar. It got LINK error like:
error LNK2001: unresolved external symbol "public: static class...
The link error related cpp file looks like:
//myClass.cpp
void myClass::myFunc()
{
myClass::m_nMyVar = 123; //I tried to use this m_nMyVar here and got link error
}
So I add below code on the top of myClass.cpp
//myClass.cpp
int myClass::m_nMyVar; //it seems redefine m_nMyVar, but it works well
void myClass::myFunc()
{
myClass::m_nMyVar = 123; //I tried to use this m_nMyVar here and got link error
}
then LNK2001 is gone.
Since this is the first SO thread that seemed to come up for me when searching for "unresolved externals with static const members" in general, I'll leave another hint to solve one problem with unresolved externals here:
For me, the thing that I forgot was to mark my class definition __declspec(dllexport), and when called from another class (outside that class's dll's boundaries), I of course got the my unresolved external error.
Still, easy to forget when you're changing an internal helper class to a one accessible from elsewhere, so if you're working in a dynamically linked project, you might as well check that, too.
When we declare a static variable in a class, it is shared by all the objects of that class. As static variables are initialized only once they are never initialized by a constructor. Instead, the static variable should be explicitly initialized outside the class only once using the scope resolution operator (::).
In the below example, static variable counter is a member of the class Demo. Note how it is initialized explicitly outside the class with the initial value = 0.
#include <iostream>
#include <string>
using namespace std;
class Demo{
int var;
static int counter;
public:
Demo(int var):var(var){
cout<<"Counter = "<<counter<<endl;
counter++;
}
};
int Demo::counter = 0; //static variable initialisation
int main()
{
Demo d(2), d1(10),d3(1);
}
Output:
Count = 0
Count = 1
Count = 2
In my case, I was using wrong linking.
It was managed c++ (cli) but with native exporting. I have added to linker -> input -> assembly link resource the dll of the library from which the function is exported. But native c++ linking requires .lib file to "see" implementations in cpp correctly, so for me helped to add the .lib file to linker -> input -> additional dependencies.
[Usually managed code does not use dll export and import, it uses references, but that was unique situation.]
I tried to look this up, but did not find it anywhere. So here's the question:
Static functions in C/C++ can be used to "make them invisible to the outer world". Great, when having two same-named static functions in two different compiled units (.c files), it makes me sure that I call the right one. But can I also be sure that I call my local static function when there exists a same-named non-static function somewhere in the project or libraries? That is, does the static function locally hide the non-static one?
Sure I can test it (and I did) but I want to know whether this behaviour has fixed definition in C/C++. Thanks.
Edit: Simplified example code which caused unexpected behaviour to me. The question is about the fix of the problem (suppose I cannot change the library).
In mylib.c:
#include "mylib.h"
int send(void * data, int size);
...
int send(void * data, int size) {
return send_message(queueA, data, size);
}
void libfunc(void) {
send(str, strlen(str));
}
In mylib.h:
// only libfunc is declared here
void libfunc(void);
In myprog.c:
#include "mylib.h"
int send(void * data, int size);
...
int send(void * data, int size) {
return send_message(queueB, data, size);
}
void progfunc(void) {
// expected to send a message to queueB
// !!! but it was sent to queueA instead !!!
send(str, strlen(str));
}
Compiled mylib.c + further files -> mylib.a
Compiled myprog.c -> myprog.o
Linked myprog.o + mylib.a -> myprog
You'd get a compilation error because functions have default external linkage, thus the new static function would result in a conflict of linkage specifiers.
If the declaration of the non-static function isn't visible, the static one will be called:
void foo(); //external linkage
static void foo() {}; //internal linkage and error
It does not hide functions with same name declared in the same scope. However you may not have a function with the same signature declared as having internal and external linkage.
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.
Very simply put:
I have a class that consists mostly of static public members, so I can group similar functions together that still have to be called from other classes/functions.
Anyway, I have defined two static unsigned char variables in my class public scope, when I try to modify these values in the same class' constructor, I am getting an "unresolved external symbol" error at compilation.
class test
{
public:
static unsigned char X;
static unsigned char Y;
...
test();
};
test::test()
{
X = 1;
Y = 2;
}
I'm new to C++ so go easy on me. Why can't I do this?
If you are using C++ 17 you can just use the inline specifier (see https://stackoverflow.com/a/11711082/55721)
If using older versions of the C++ standard, you must add the definitions to match your declarations of X and Y
unsigned char test::X;
unsigned char test::Y;
somewhere. You might want to also initialize a static member
unsigned char test::X = 4;
and again, you do that in the definition (usually in a CXX file) not in the declaration (which is often in a .H file)
Static data members declarations in the class declaration are not definition of them.
To define them you should do this in the .CPP file to avoid duplicated symbols.
The only data you can declare and define is integral static constants.
(Values of enums can be used as constant values as well)
You might want to rewrite your code as:
class test {
public:
const static unsigned char X = 1;
const static unsigned char Y = 2;
...
test();
};
test::test() {
}
If you want to have ability to modify you static variables (in other words when it is inappropriate to declare them as const), you can separate you code between .H and .CPP in the following way:
.H :
class test {
public:
static unsigned char X;
static unsigned char Y;
...
test();
};
.CPP :
unsigned char test::X = 1;
unsigned char test::Y = 2;
test::test()
{
// constructor is empty.
// We don't initialize static data member here,
// because static data initialization will happen on every constructor call.
}
in my case, I declared one static variable in .h file, like
//myClass.h
class myClass
{
static int m_nMyVar;
static void myFunc();
}
and in myClass.cpp, I tried to use this m_nMyVar. It got LINK error like:
error LNK2001: unresolved external symbol "public: static class...
The link error related cpp file looks like:
//myClass.cpp
void myClass::myFunc()
{
myClass::m_nMyVar = 123; //I tried to use this m_nMyVar here and got link error
}
So I add below code on the top of myClass.cpp
//myClass.cpp
int myClass::m_nMyVar; //it seems redefine m_nMyVar, but it works well
void myClass::myFunc()
{
myClass::m_nMyVar = 123; //I tried to use this m_nMyVar here and got link error
}
then LNK2001 is gone.
Since this is the first SO thread that seemed to come up for me when searching for "unresolved externals with static const members" in general, I'll leave another hint to solve one problem with unresolved externals here:
For me, the thing that I forgot was to mark my class definition __declspec(dllexport), and when called from another class (outside that class's dll's boundaries), I of course got the my unresolved external error.
Still, easy to forget when you're changing an internal helper class to a one accessible from elsewhere, so if you're working in a dynamically linked project, you might as well check that, too.
When we declare a static variable in a class, it is shared by all the objects of that class. As static variables are initialized only once they are never initialized by a constructor. Instead, the static variable should be explicitly initialized outside the class only once using the scope resolution operator (::).
In the below example, static variable counter is a member of the class Demo. Note how it is initialized explicitly outside the class with the initial value = 0.
#include <iostream>
#include <string>
using namespace std;
class Demo{
int var;
static int counter;
public:
Demo(int var):var(var){
cout<<"Counter = "<<counter<<endl;
counter++;
}
};
int Demo::counter = 0; //static variable initialisation
int main()
{
Demo d(2), d1(10),d3(1);
}
Output:
Count = 0
Count = 1
Count = 2
In my case, I was using wrong linking.
It was managed c++ (cli) but with native exporting. I have added to linker -> input -> assembly link resource the dll of the library from which the function is exported. But native c++ linking requires .lib file to "see" implementations in cpp correctly, so for me helped to add the .lib file to linker -> input -> additional dependencies.
[Usually managed code does not use dll export and import, it uses references, but that was unique situation.]