Reputation: 5676
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
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
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
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
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
Reputation: 1946
To solve this in :
I have not found a solution that I really like.
Upvotes: 4
Reputation:
Yes, I've had this problem too. The only sure ways round it that I found were:
or:
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