Skip to main content
GameDev.net gamedev.net
🔒 Locked

Static initialization within a library

Started by gameXcore Jun 8, 2010 at 11:57 AM 7 replies 4.6k views
Original Post
gameXcore
gameXcore
Hey all,

So this is post number 1 million regarding problems revolving around me moving my engine into a seperate library. This time my issue is with static variables not initializing.

I utilize the pluggable factory pattern in a few places around my engine, using a similar implementation to the one described in the article on this site "why pluggable factorys rock my multiplayer world".

Within my engine I have a standard subset of items that should be added to the factory, these are compiled within the library, I then have another set of items that are game-specific and added in the project that is using the engine.

The problem I'm having is that the static makers within the library never seem to initialize, and thus never get registered with the factory.

Is this expected behaviour within a library, or should I be looking for the problem elsewhere? If so, how do I go about ensuring these statics initialize?

Thanks, Scott
Game development blog and portfolio: http://gamexcore.co.uk
tivolo
tivolo
This is a known problem when dealing with libraries which are linked into the executable.
You need to make sure of two things:
- Make sure that those statics end up in your compiled library (can be checked with e.g. dumpbin on Windows)
- Make sure that those statics end up in the executable

The second can be tricky, because the linker is "free" to do whatever he wants when looking for symbols. The problem with statics is that they do not get referenced anywhere in other code, and hence the linker sees no reason to pull in your .obj files where those statics are located.

The linkers I've worked with (MSVC, GCC, Codewarrior) only pull in those .obj files they need in order to be able to fully resolve all symbols.

So what can you do about that?
Under Windows it's actually not so hard to do: use pragma(comment(linker, "/include:yourSymbolNameHere")) (http://msdn.microsoft.com/en-us/library/7f0aews7%28VS.80%29.aspx) to force the linker to include a certain symbol. The ugly-looking pragmas can be wrapped into macros, if needed (use __pragma under MSVC).

Two common pitfalls with that approach:
- the pragma MUST be seen when compiling the executable, otherwise your symbol is only going to be in the library, but not in your executable. This is easily achieved by e.g. putting those into a precompiled header file (or any other file which needs to be included by the user).

- the name of the symbol must be the C++ name (= mangled name), but this can easily be found by using dumpbin (again), or checking the compiler's ABI on how mangled names are constructed.


Of course, using pragma(comment(linker, ***)) is a MSVC-only solution. On other platforms, you can e.g. put all those symbols into separate data-segments and force the linker to link all symbols of certain segments by using a corresponding linker-file.


Hope that helps,
-tiv
majickthise
majickthise
The Pluggable Factory pattern (at least the standard implementation in C++ which depends on static initialization) is broken on most platforms in most scenarios. It doesn't work at all with static libraries and monolithic executables, and it doesn't work with Windows DLLs. So far, I have only seen it working with shared libraries on Linux.

One reason for this is that the linker will not include an object in the final executable if no symbols from that object are referenced. This is why the pattern breaks in static libraries and monolithic executables.

DLLs have similar behavior: If no symbol in the library is referenced, then the DLL will not be loaded if you link against it implicitly.

What you can try is explicit linking using LoadLibrary.
gameXcore
gameXcore
Well this is bit of a headache, luckily I'm not too worried right now about cross compiler portability, so long as it compiles on MSVC I'm happy, so the pragma option should work.

The only problem is, I've never come across dumpbin before, and cannot find it, the internet says I should already have it installed.

Any ideas?

Thanks, Scott
Game development blog and portfolio: http://gamexcore.co.uk
ApochPiQ
ApochPiQ
It should install with Visual Studio/Visual C++. The easiest way in general to access the command line tools is with the Visual Studio Command Line shortcut that should be placed in the Start Menu group along with the other VS stuff.
tivolo
tivolo
Quote:
Original post by gameXcore
Well this is bit of a headache, luckily I'm not too worried right now about cross compiler portability, so long as it compiles on MSVC I'm happy, so the pragma option should work.

The only problem is, I've never come across dumpbin before, and cannot find it, the internet says I should already have it installed.

Any ideas?

Thanks, Scott



If you're using some macro instead of the #pragma directly, multi-platform portability can be achieved at a latter point in time and you don't have to worry about it for now.

About dumpbin: Use the Visual Studio Command Prompt shortcut which is installed with Visual Studio. From there, it's located in the "bin"-folder.

-tiv
theOcelot
theOcelot
Globals...[headshake]. IMO, it would be better to just register manually in a single function called once at game startup. It imposes a minimal amount of fuss, and you may even be able to put the extra flexibility to some use. More fuss and more flexibility if you ditch globals altogether.

@majickthise: The pluggable factory pattern does not require the use of static initialization, at least not as described here, but works just fine as a regular object.
Hodgman
Hodgman
This is why I hate static "registration" objects...

You need to force them to get linked into your exe -- the pragma option from tivolo works, but it's messy :(

Another way is to convince the linker that the application uses your static objects by accessing them from the app somehow, e.g.
//MyLibrary.hclass StaticRegistration{public:  StaticRegistration( ... ) { DoRegistrationStuff( ... ); }  int dummy;};//MyLibrary.cppStaticRegistration g_RegisterMyClass( "MyClass", &MyClass::Create );//MyApp.cppvoid ForceLinkingPlease(){  extern StaticRegistration g_RegisterMyClass;  g_RegisterMyClass.dummy = 42;  extern StaticRegistration g_RegisterMyOtherClass;  g_RegisterMyOtherClass.dummy = 42;}int main(){  ForceLinkingPlease();//Now the compiler knows we're using those objects and has to link them  ...}
This should work across compilers without the need for pragmas.

Seeing you're forced to deal with these work-arounds though, I just use a standards-compliant method from the beginning these days, like:
//MyLibrary.hvoid RegisterClasses( Factory& );//MyLibrary.cppvoid RegisterClasses( Factory& f ){  f.Register( "MyClass",      &MyClass::Create );  f.Register( "MyOtherClass", &MyOtherClass::Create );}//MyApp.cppint main(){  Factory f;  RegisterClasses( f );}
tivolo
tivolo
Quote:
Original post by Hodgman
Seeing you're forced to deal with these work-arounds though, I just use a standards-compliant method from the beginning these days, like:*** Source Snippet Removed ***


We are using the #pragma (and data-segment) option at work because we do not want to force the user to

a) create factory instances for each factory
b) register each class at the respective factory

IMO, this adds an additional problem if you're dealing with classes in both library and user-code, and want to register them at a factory. Additionally, if the library adds a new class, the user has to change HIS code to actually get that class registered in the factory.
If you're only dealing with that code yourself, it is a perfectly valid option. However, I believe that if you built libraries which are to be used by other people, you have to think about other options, too.


-tiv

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.