Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to force inclusion of "unused" object definitions in a library

Tags:

c++

linker

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, but 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.

like image 649
Edward Strange Avatar asked Dec 08 '10 02:12

Edward Strange


2 Answers

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.

like image 197
Edward Strange Avatar answered Nov 03 '22 07:11

Edward Strange


There's the linker option /OPT:REF and /OPT:NOREF, accessed through linker->optimization->references.

like image 24
Puppy Avatar answered Nov 03 '22 07:11

Puppy