views:

198

answers:

4

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?

A: 

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.

anon
+1  A: 

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.

Rexxar
Thanks! That's not exactly the solution I am looking for, but that already helps a lot :)
Drealmer
+1  A: 

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).

A: 

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.

Jasmeet