problems with static libraries

Started by
8 comments, last by Galshin 18 years, 9 months ago
In my code, I use a series of macros to register class and property information for my classes. This info is then used by the scripting engine to instantiate object, and allows the scripting engine to access their properties, etc... Here's the problem. I recently moved a bunch of my core code into a static library, including the class registration stuff. But, when I do that, the macros that register a classes type and properties don't work. The macro works by creating a static variable in the c++, that registers the appropriate information. These static variables are instantiated at run-time, and their constructors do the registration... this way its all automatic, when the program runs all the classes registered with macros register themself. BUT... when you include those classes in a static library, the compiler doesn't compile in the classes that aren't explicitly used in c++, this includes the registration macro. So when a script goes to use a class that has been "optimized out" it fails. This all works if i put all the code into a single project... but I would love to be able to break my code up into different projects. Anyone have any ideas?
Advertisement
Here's an example of what I am talking about

assume some c++ file named "test.cpp"

#include <stdio.h>

class Test
{
public:
Test( void )
{
cout << "Imagine I registered something here!!";
}
};
static Test _Test;

---------------------------------------------------------------------

ok, if you put that file in a regular project it works... The constructor will get called on program executiong, and that string will be spit out. But, if you put that file into a seperate static library, then create a new project which links to that static library, the constructor will not get called, and the message WON'T be printed out. The only way to get it to print out is to explicity access something in that file from your primary project. But, in the case of my scripting engine, classes are used in script all the time that are never referred to in C++ code. And they are registered using a technique that is similar to the above example (except some complex registration is done in the constructor, instead of just printing out a string).

Is there some way to force the compiler to not optimize this code out from a seperate library, even though it is never explicitly referenced???


It is a problem of globals (statics) and libraries in general, I don't think it matters if it is a static or dynamic library.
Pretty much the only workaround is to explicitly call that static somewhere, as the compiler regards it as not being created until it is used.

Perhaps somebody else could supply a technical explanation why this happens?

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Quote:Original post by Galshin
Is there some way to force the compiler to not optimize this code out from a seperate library, even though it is never explicitly referenced???



I really do not recommend doing this. In C++ the order of initialization for objects declared with a static storage duration is unspecified. This means that you are not guaranteed the compiler will call the constructors in an order based on their dependencies. This means that if object A accesses object B during initialization it's possible that A will be initialized BEFORE B.

This is a problem because even if the program doesn't crash everything that was added to the list in B are no longer in the list - since B 'initialized' the list after A was added to it.
It's part of an established codebase that has been in use for a couple years now, so any such problems have long since been resolved. It's only recent that we wanted to try and break things up into seperate libraries.

If there is no way to fix this problem, does anyone have a way of accomplishing the same basic concept... that doesn't involve some master function somewhere, that knows about every class and initializes it? I like the ability to just drop a file in for a class, with no dependancies, and have it accessable to the scripting engine. It works great, and is extremely flexible - and has little to no dependancies... but it's just not working when we break stuff up into libraries..

suggestions?
Is there some other way to perform the registration? Anyone else trying to do anything like this?
You probably do not want to declare it as 'static'. By declaring it as static the compiler is most likely optimizing it out because there are no references within the file it's declared in.


If removing the static declaration does not work try the following:

class Test{     static Test staticImpl;public:     Test( void )     {          cout << "Imagine I registered something here!!";     }};Test Test::staticImpl;




Depending on how you are handling the declaration and registration of the objects you may need to use exemplars like so:


class Test{public:     Test( void )     {          cout << "Imagine I registered something here!!";     }};template<typename _TYPE> class Exemplar{     _TYPE *ptr;public:     Exemplar() {          ptr = new _TYPE;          RegisterObject(ptr);     }     ~Exemplar() {          UnregisterObject(ptr);          delete ptr;          ptr = NULL;     }};namespace exemplars_Test {     Exemplar<Test> test1;     Exemplar<Test> test2;}
Yeah, my mistake... the Test variable shouldn't be declared static, just regularly.

The exemplar technique has the same problem, if you put that code in a static library, the exemplars won't get instantiated unless you use the code in that file explicitly. For our purposes, where entire classes may not get used, except by script, entire files are not explicitly referenced in c++... and therefore, the global variables that are supposed to register the classes at execution time (prior to main getting called) don't get created, and therefore the classes don't get registered (some sort of optimization inherant in libraries I suppose).

So far, I haven't been able to find a workaround for this.
Quote:Original post by Galshin
Yeah, my mistake... the Test variable shouldn't be declared static, just regularly.


Some clarifications...(just to be sure)

1. Just to make sure you're aware, macros won't be included in the static library. This may not be what you were getting at but that's the impression your original message gave.

2. This is more of a problem with the linker rather than the compiler.


Again - just making sure.

I assume that what you are trying to avoid is having the end user call something like InitScriptObjects() which would then register all the items. Not sure why this isn't acceptable as I don't think it would be that big of a deal.


If you are using the MS C++ compiler chances are the linker is seeing that no items in the .obj file are being referenced so it's discarding it all together. Technically the objects SHOULD be included since the linker really has no way of telling whether they are supposed to be referenced or not. Not sure how much this is affected by using the 'function level linking' option but chances are it doesn't make much difference.


Here are solutions that I've tested that do work:


Start my placing the following code in the file where the static members are:

Test Test::local; // From the previous messageextern "C" void LocalEngineInitFunction() { }


Leave LocalEngineInitFunction empty for now.


Solutions:


#1 - Have a component in the static lib that is ALWAYS used call LocalEngineInitFunction().

#2 - If #1 doesn't work try this: in the main header file for the static library add the following:

// Force a reference to the init function#pragma comment(linker, "/include:_LocalEngineInitFunction");


In order for this to work you have to make sure that the /include option includes the underscore and that the init function is declared as extern "C" (otherwise you'll get a link error saying it can't find it - name mangling sucks sometimes).


Option #1 is the most portable while #2 is probably going to be the cleanest approach since it instructs the linker to force a reference to LocalEngineInitFunction without actually calling it.
Thanks! That does seem to work. Yeah, it's not unreasonable to have an init function that does all the registration, but like I mentioned, it's an existing code base... but I suppose I could make one :p


It is kind of nice to be able to just drop in a c++ file and have that class accessible to the scripting language, without having to update some global init function. So I'll try using the pragma for now, and see how it goes (which is fine, because there are no plans on porting the code)

This topic is closed to new replies.

Advertisement