The Design of Software (CLOSED)

A public forum for discussing the design of software, from the user interface to the code architecture. Now closed.

The "Design of Software" discussion group has been merged with the main Joel on Software discussion group.

The archives will remain online indefinitely.

Optimising linker?

I'm not sure I understand what's happening here.

I have a program, which is using a static library I also wrote. And some of the classes in the lib aren't turning up in the resulting application. I think this is because the classes arne't used, but in fact they are (you just can't tell because their use is dependant on the data received by the program). Has anyone else seen this? Know how to stop it? (MSVC++ 6 if it makes a diff.)

Thursday, November 25, 2004
 
 
Upgrade to MSVC.Net? (really it does help)

Otherwise you'll have to dummy reference each class that it trims.
Mr Jack
Thursday, November 25, 2004
 
 
Just curious... How can you tell they aren't "turning up" in the executable if there are (I assume) no references to them in the code?

Don't tell me you're looking at the binary?
Alex Send private email
Thursday, November 25, 2004
 
 
I assume you're using the (optional) .MAP file of the .EXE to see what the EXE actually contains. Please let me know if not.

You mention a static library, try the obvious first: Be 100% certain that the static library .LIB is re-built and up-to-date. Ensure there aren't spurious old copies lying about that the system might be picking up instead. I know what you're thinking, but it does happen.

I don't understand what you mean by "classes", as such, because by the time we get to the link stage, classes don't have any *explicit* representation.

All you will see in the final EXE are the functions *within* those classes: ordinary members, statics, virtuals, constructors, etc...

Maybe you're expecting to see something that won't ever be there? Need more info!

If you mean that some *functions* within some of your classes appear to be missing, then that means the linker decided they weren't called. The linker is very rarely wrong ;)

If you're adamant that they ARE called, I strongly suggest checking through / stepping your code again to verify this. You can step from a program into a static lib too. Try that if you haven't already.

I recall that the Microsoft linker underwent an update that allowed it to do some late-inlining of functions (I believe) as an optimisation. I can't remember the exact rules or the precise version, I'll try to find out. This might be what's happening.
Jonathan Send private email
Thursday, November 25, 2004
 
 
I can tell they aren't there because I can't set breakpoints on them, though I can on code which is explicitly called. Nor do they appear in the Broswer Info.

Thursday, November 25, 2004
 
 
> the linker decided they weren't called

As I said, what could possibly be called depends entirely on what data the program gets. Have a Google search on "Industrial Strength Pluggable Factories" and you'll get the idea of what I am trying to do - the factories and what they produce are in the lib and are called from the program through a very narrow interface. Only abstract base classes are ever directly referenced.

Thursday, November 25, 2004
 
 
I use static libs of my own devising with VC++6 all the time.

The most common reasons why you can't set a breakpoint are function not called, or the source files you've got for the library don't quite match the LIB file because (as I mentioned before) it's out of date.

Have you tried ensuring the LIB is up to date? Force it with Rebuild All. Check hard disc for duplicates.

If it then still compiles and links OK, I can only suggest it's something with the logic of your program. A Homer Simpson "DOH" type situation maybe?
Jonathan Send private email
Thursday, November 25, 2004
 
 
Okay, you say only the abstract bases are referenced, but you've got to have some code somewhere that does something like:

new ConcreteDerived()

Do you have anything like this in your EXE or in your LIB? You must create the derived classes somewhere!

If they were created in the LIB by a global function, say Create(), then your missing classes would get linked by virtue of your EXE calling Create().

If you don't want the LIB to create the derived classes, then it's the responsibility of the EXE to create them?

ie: Where's the 'new'?
Jonathan Send private email
Thursday, November 25, 2004
 
 
Please look at the article I (indirectly) pointed you to. A derived factory is the only thing that references a derived product directly (i.e. does your "new ConcreteDerived"). The derived factories are static objects* which are inserted into a map so you can find the relevant one when you need it. No such factory-object is created (I see that when I debug into the code). Weird shit, no? I have a suspicion that it has something to do with them being static, but I can't actually find anything that says so.

I've just added an explicit (stack) variable of ConcreteFactory and guess what? The damned static factory-object is created as well, the class appears in the executable, etc.


* This is pretty nice, actually, although I normally try and avoid static objects. It means that a factory class really is unknown to code outside itself.

Thursday, November 25, 2004
 
 
Jonathan Send private email
Thursday, November 25, 2004
 
 
I am now ;-)

I think my article (I have it on paper) is from a different source, but the idea is basically the same. I haven't gone the template route described in the article you linked to, and I'm using MFC rather than the stl-like classes they refer to, but yeah it is basically the same.

Thursday, November 25, 2004
 
 
Okay, in C++ if you declare a static variable inside a class, it's basically a global variable.

Important point #1:

You must also "give it a home" in a .CPP file somewhere. This involves effectively re-stating the variable declaration. ie:

class MyClass
{
  static int MyVariable;
};

// in PRECISELY ONE .cpp file somewhere, you MUST have:

int MyClass::MyVariable;



Important point #2

You will not get an error if you fail to do this in your case because I strongly suspect the global variable is NOT MENTIONED anywhere else in your code! It merely exists so that it's default constructor can exert a side-effect of inserting the maker into the map.

From the article (please mentally substitute your classes):

class CircleMaker : public ShapeMaker {
private:
    CircleMaker() : ShapeMaker("Circle") {}
    static const CircleMaker registerThis;
};

But don't forget that in a CPP file somewhere, you MUST have:

const CircleMaker CircleMaker::registerThis;

The article I found did NOT state this.



Point #3:

This might explain why your explicit use of a stack-based 'CircleMaker' variable caused the problem to go away: Introducing the stack variable caused the default constructor CircleMaker() to be called, thus causing the missing registration to occur.

Which is why your code works when the stack-variable is introduced.

You think?
Jonathan Send private email
Thursday, November 25, 2004
 
 
I already have the static variable in the .cpp file. When I introduced the stack variable the constructor was called twice, once for the static var and later for the stack based variable. You're right though, the "global" variable isn't reference anywhere else in the program. I'm basically back to my original WAG that the linker has decided it can optimise it away (hey, in Release mode I could understand, but in Debug too...?).

Now I remember why I prefer perl and Java...

Thursday, November 25, 2004
 
 
I've set up two sample VC++ projects, a static lib and an EXE.

I'm afraid I just can't reproduce the problem in that case.

I'm giving the static variable a home within the LIB, and it just seems to be fine.
Jonathan Send private email
Thursday, November 25, 2004
 
 
CL.EXE    6.00.8804.0
C1.DLL    6.00.9782.0  (same for c1xx, c2)
LINK.EXE  6.00.8447.0
Jonathan Send private email
Thursday, November 25, 2004
 
 
That is weird. Can I borrow your computer? :o)

Thursday, November 25, 2004
 
 
Visual Studio is supposed to be a bit dodgy with this kind of thing sometimes. (I've heard about it, but have never got bitten myself.) You may have to bite the bullet and register all your types with the factory by hand.

My own crazy guess would be that the compiler has examined the tree of dependencies, determined your static library does not itself use the factory, and so removed every object on which the factory depends. But I don't know why this happens in a debug build, as I think this only comes from using the /OPT:REF linker switch.

(One thing the article doesn't appear to mention is static initialization order. I suppose you'd get a crash if you attempted to use the map before it has been initialized, but it's still worth catering for this as if it works it will be by luck.)
Tom_
Thursday, November 25, 2004
 
 
You think this is some sort of bug then? I can get around it, I guess, by just putting dummy refs in or putting all the code into the exe project instead of a separate lib. The latter isn't a requirement, I just wanted to make sure I really separated the UI & behaviour. I wondered if the behaviour I got was "by design" (which is why I put it in the Design section, I thought it was an odd design decision), though I hadn't ruled out a bug in my code.

(Yes, I'm aware of the static creation order problem. ISTR the last time I used this "pattern" I eventually made the map a singleton so that it would be created if it did not exist.)

Friday, November 26, 2004
 
 

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics
 
Powered by FogBugz