Drealmer
Drealmer

Reputation: 5676

Prevent linker from removing globals

I am using static global variables constructors as a trick to conveniently register functions, the idea goes something like this:

typedef int (*FuncPtr)(int);

struct RegHelper
{
    RegHelper(const char * Name, FuncPtr Func)
    {
        Register(Name, Func);
    }
}

#define REGISTER(func) RegHelper gRegHelper_ ## func (#func, func);

Now I can register functions this way (I use it to implement some kind of reflection):

int Foo(int a)
{
    return a * 123;
}

REGISTER(Foo)

int Bar(int a)
{
    return a * 456;
}

REGISTER(Bar)

The problem is that if I use this in a static library, sometimes the linker detects that the compilation unit is not used, and it drops the whole thing. So the global variables are not constructed, and the functions are not registered...

My question is: What can I do to work around this? Calling dummy functions in each compilation unit during initialization seems to trigger the construction of the global variables, but that doesn't feel very safe. Any other suggestion?

Upvotes: 17

Views: 6369

Answers (6)

elephant
elephant

Reputation: 149

Another possible solution to the same problem. Get the constant value via a function call. Like this:

constant.h

const char blah[10];

extern const char *get_blah();

constant.c

#include "header.h"

const char *get_blah()
{ return blah; }

This helped me do the trick!

Upvotes: 1

BigSandwich
BigSandwich

Reputation: 2806

See answer here: force visual studio to link all symbols in a lib file

This one actually worked for me. The other suggestions didn't.

Upvotes: 3

Jasmeet
Jasmeet

Reputation: 2352

If you are in a UNIX environment invoking ld with the whole-archive option would force all object files to be included in the static library regardless of use.

Upvotes: 1

user48956
user48956

Reputation: 15788

Check out the answer to "Best Way To Build a List of Per Type Data"

There are two key important concepts in there. First:

(void) register_object;  

uses the object to ensure that the linker doesn't strip it, and,

template<typename D> typename automatic_register<D>::exec_register 
    automatic_register<D>::register_object;

ensures that a static global is allocated for each instance. You should hold your data in this object. Its a little different in that its per object rather than per class, but if you adapt your macro to create

// Global list of objectsh
struct FpList
{
   FpList( FuncPtr func ) :
      func(func)
   {
      head = next;
      next = this
   }
   FpList* next;
   FuncPtr func;

   static FpList* head;
};
// In .cxx:
FpList* FpList::head = 0;  

Then modify you register macro so that REGISTER( Foo ), so that it creates:

struct register_Foo : FpList
{
   register_Foo( FuncPtr fn ): FpList(fn)
   {
      (void) register_object;  
   }
   static register_Foo register_object;
};

I think this is not quite enough. You still need to instaniate the template, pass if &Foo and ensure that a

register_Foo register_Foo::register_object

instance is created somewhere. The template code for automatic_register shows how to do this in a header. If you can put your macro in a .cxx, just declare:

register_Foo register_Foo::register_object( &Foo );

as part of your macro. I think that it might work. (all from memory, so who knows).

Upvotes: 1

Rexxar
Rexxar

Reputation: 1946

To solve this in :

  • Visual studio (in the same solution) : Linker > General > Use library Dependency Inputs = yes
  • Gcc : link directly with .o files

I have not found a solution that I really like.

Upvotes: 4

anon
anon

Reputation:

Yes, I've had this problem too. The only sure ways round it that I found were:

  • make the library into a DLL

or:

  • move the registration objects into the executable

Neither of these is perfect, though the DLL solution is OK if you don't mind using DLLS, and I too would be interested to hear of other solutions.

Upvotes: 1

Related Questions