How to force inclusion of "unused" object definitions in a library
My question is similar to these but doesn't seem to correlate exactly:
How to force inclusion of an object file in a static library when linking into executable?
Forcing symbol export with MSVC
What I've got is something like so:
struct thingy;
struct container
{
static container& instance(); // singleton
int register_thingy(thingy*);
};
struct thingy
{
virtual ~thingy() {}
virtual int id() const = 0;
};
//template trick to force registration.
template < typename Derived >
struct registered_thingy : thingy
{
registered_thingy() : my_id(my_static_id) {}
int id() const { return my_id; }
private:
int my_id;
static int my_static_id;
}
template < typename Derived >
int registered_thingy<Derived>::my_static_id =
container::instance().register_thingy(new Derived);
Now, in a concrete_thingy.cpp
file I have:
struct my_concrete_thingy : registered_thingy<my_concrete_thingy>
{
my_concrete_thingy() {} // registered_thingy's constructor not called otherwise
};
Of course, the above is totally useless, but there's real behavior being abstracted here.
This works wonderfully when used in an application that is compiled as a whole. The issue now is that I'm not able, so far, to use this technique while bottling up the behavior behind collection
in a library. In other words, I've got a thingys.lib
file that contains concrete_thingy.cpp
but the registration is not occurring when that is linked to an executable. The collection
ends up existing and working fine, bu开发者_运维问答t it's empty.
Now, this is a STATIC library, not a DLL. That may change the issue a little and the techniques spoken about in the links above don't seem to apply. The one of course is about functions and I don't see how I could apply it to these C++ structures.
I've tried to use the #pragma comment
method with the following three lines (individually of course) in concrete_thingy.cpp
, none of which worked:
#pragma comment (linker, "/export:concrete_thingy")
#pragma comment (linker, "/export:concrete_thingy::my_static_id")
#pragma comment (linker, "/export:registered_thingy<concrete_thingy>::my_static_id")
If concrete_thingy.cpp
is in the executable rather than the library everything works fine.
So then, here's my questions:
1) Is it possible to do what I'm trying to do? I'm guessing yes, but I just don't know how.
2) If it is possible, how would I get MSVC++ 2010 to do it?
3) If it is possible, how could I do it in a portable way?
In short, what I'm trying to do would be similar to creating an abstract factory that creates implementations of an abstraction. It knows nothing about these implementations, which are registered using global initialization trickery. This should all be in a static library that can be linked to by an application and these implementations should be available through that factory. Nobody knows anything about these implementations except themselves and thus normal linking is causing them, and their registration globals, to disappear.
It's not exactly what I'm up to, but it's close enough.
Edit: ====================================================
Looks like this behavior is "by design". MS recognizes that construction of object that cause side effects should occur whether or not their used, they use a loophole in the standard that allows them to not include translation units in which nothing is used :\
https://connect.microsoft.com/feedback/viewfeedback.aspx?FeedbackID=244410&wa=wsignin1.0&siteid=210
The /OPT:NOREF option is designed to not do anything in this case apparently.
Well, the other answers where good attempts but ultimately fruitless. I am going to use the refit trick but the rest appears to have been a red herring; it kind of makes sense since the template in question isn't actually used anywhere else so the fact that it's not explicitly instantiated shouldn't make a difference...the declaration of the global still happens in A translation unit, it has side effects...I don't think the standard allows it to be optimized away.
The unfortunate bit about the standard not saying whether or not it is required to include a translation unit at all is the ultimate issue. I think C++0x is doing something about this but maybe not... At any rate, MS feels free to not include the unit at all, and since it doesn't the global isn't ultimately included in the executable and thus none of the other crap happens.
What I've decided to do, and there are of course many other ways, is to create a file 'tag' variable. That tag is then assigned to in a function that is globally accessible (it HAS to assign or assign from or the reference is optimized away). Then that function has to be called from the executable.
I decided to do it this way because then the rest still works the same as it always has. I don't end up ultimately changing behavior like I could if I simply wrote a registration function that hand-registered the types. Plus I can do other things this way...I just have to make sure that anything that might fall into this classification of fucktardery has a tag and that tag is accessed.
I'll be writing a bunch of helper macros to make this mostly painless.
There's the linker option /OPT:REF and /OPT:NOREF, accessed through linker->optimization->references.
After a lot of head-scratching and trying ugly hacks, I ended up making my library dynamic (shared). That fixed the issue
A quick look through the documentation for LINK.exe points to the /WHOLEARCHIVE
option:
The /WHOLEARCHIVE option forces the linker to include every object file from either a specified static library [...] By default, the linker includes object files in the linked output only if they export symbols referenced by other object files in the executable.
精彩评论