Statically-Linked Lib Headaches

Started by
6 comments, last by GameDev.net 18 years ago
I'm currently experimenting with building some re-usable C++ lib's that will give me some simple things like Math and Container classes. Thus far the effort has been going well, but I've hit a pretty dang big snag... I've got a RTTI (Run Time Type Identification) system in place that I'm implementing into all of my classes to allow me to, well, identify them at run time. The RTTI system itself is heavily inspired by the system in "3D Game Engine Architecture" by David Eberly, with a few minor implementation specific changes, but nothing that should be system breaking. In any case, the main chunk of code looks like this:


class CRTTI{

      ...

private:
	char*	       typeName;
	const CRTTI*   typeBase;
};

CRTTI::CRTTI( const char* name, const CRTTI* baseType ) {
	typeName = new char[strlen(name)+1];
	typeName[strlen(name)] = '\0';
	memcpy(typeName, name, (strlen(name)) * sizeof(char));
	typeBase = baseType;
}


The class itself is commonly initialized as a static member variable, and initalized as such. So here's the issue - If I am compiling this code into an EXE and then running through and debugging everything looks great. the string copies correctly, hte parent type is correctly assigned, and all is right with the world. IF, however, I compile that same code into a LIB and then link to it in a seperate EXE (even if its the same code as the non-linked compile) strange things start happening... For one, an object type that shows up as "CTest" in the non-linked version is shown to be "CTestýýýý««««««««îþîþîþ" when linking to the lib. Also, the base types do not seem to be assigned correctly in the linked version, often coming up as NULL. (This last symptom seems more common on the templated container classes, though, so that may have something to do with it.) Now to try and pre-emptively answer a few questions: -All builds, linked and otherwise, are being preformed in debug mode, no optimizations, under VC++ 2005 Express. (And yes, I'm compiling the LIB in debug mode too.) -The problem with the names seems to occur wether the RTTI object is created statically or Dynamically. The NULL parent issue seems to occur mostly in templated classes, but both happen only when linking to the RTTI class through a static lib. -I have tried assigning strlen(name) to a variable (int len) to see what we get. In the non-linked version this comes out to the length of the string, as expected. In the linked version this comes out to -858993460, which effectively means it's not being set at all. As stated, this is somewhat of my first forray into compiling my own libs, so I could be doing something entirely wrong here, but aside from this facet of things I consider myself to be a pretty competent programmer (enough to score myself a job as one.) So I'm reasonably sure I'm not doing anything dumb in the code logic itself. If anyone has any suggestions here, I'd love to hear them! Thank you! [Edited by - Toji on April 16, 2006 9:10:32 AM]
// The user formerly known as Tojiro67445, formerly known as Toji [smile]
Advertisement
It might help us if you told us how you created the actual RTTI objects (what you passed as the parameters is most likely the most important).

I actually see two dumb things in the code, one (the second) is only dumb is some cases. First you use char* instead of std::string, why? Second why do you implement your own RTTI system? If you only plan on using RTTI on some types then this might be a smart thing (for example you don't want any memory overhead on point2, text, color etc.).
Hm.. I guess a bit more code is required...

class CObject {public:	virtual const CRTTI*	GetType() const { return objectType; }	static const CRTTI	*objectType;};const CRTTI* CObject::objectType = new aQRTTI("CObject", CObject2::objectType); 


That's a good, simple, general case implementation.

As for your two dumb things:
1) Call me crazy, yes, but I'm trying to avoid std::string. I know this sets off all sort of alarms in the mind of the experienced program, but I do have my reasons. The first of which is that this entire project is first and foremost a learning experience, and part of that experience is creating my own string class. The catch is that the string class actually uses this RTTI system, so trying to use it here would create all sotrs of nasty circular dependancies ^_^ In that vein of thought, I also wanted to work on teaching myself the skill required to become independant of other people's libraries. No MFC, no Boost, no std, etc. Insane? Sure. Educational? Extremely!

As a side note to that, though, as I mentioned in my above post this same strange behavior exhibits itself with many types of variables. The int I was using to test the length, for example. I doubt that this is a result of char* abuse, and more of an issue of lib strangeness.

2) You hit the nail on the head here, actually. There are certain classes on which, for various reasons, I DON'T want to use this. Vector classes, for example (geometric, not std::vector). In order to easily use them in, say, a 3D engine the data must be contiguous, so absolutely NO memory overhead is allowed. Yes, declaring the RTTI as a static avoids most of this, but there are some other design considerations that have gone into it as well (See point 1)

Good questions, both, and I hope I've provided good answers. I suppose that I should point out, though, that the original question here is more aimed at lib use than the RTTI system.
// The user formerly known as Tojiro67445, formerly known as Toji [smile]
Okay, so I seem to have hit a bit of a dead end here. Perhapse I'm throwing people off with the whole RTTI thing, which was only meant as an example. Let me rephrase the question:

Is there any reason why operations performed within a statically linked lib should hae a different result than the same operations performed as part of the executable code? Additionally, are there any adverse affects to declaring static objects/member variables within a lib?

I would love any insight that can be given here! Thanks!
// The user formerly known as Tojiro67445, formerly known as Toji [smile]
Probably not related to your problem, but thought I'd mention it just in case...

I sometimes see behaviour like this if I build the library, change some common header file, then try linking against the library. So the library and the executable have different ideas of how a structure is laid out memory-wise, weird things start happening.

It doesn't even have to be caused by a changed header file. You might simply have a different set of preprocessor definitions setup (ie. one has _DEBUG defined and the other doesn't) which causes some difference between the library and the executable.

So, make sure all your compiler settings are the same (or, make sure you understand what the differences are and that they won't affect the way the library is built compared to the way the executable is built.)

-HQ.
A good suggestion. I have already gone through the settings 1 or 2 times, but it could never hurt to do so again! Assuming that is not the case, though, any other suggestions?
// The user formerly known as Tojiro67445, formerly known as Toji [smile]
Well, the next thing I'd try would be to step through the code in the debugger and keep an eye on the memory of the CRTTI object you're creating. My guess is that one of those lines in the constructor is clobbering some data that it shouldn't. Then, you can check out the disassembly to see what exactly is different between the two.

My guess is that the offsets the compiler is calculating to determine where the member variables are is different when compiling as a lib or as an executable. But, that's basically the same as I thought the first time. If you've checked the compiler settings though, it might be something else. In either case, it should give you slightly better idea of what's going on.

-HQ.
To use statically linked libs is not the same as to compile the source as part of the executable code. The linker decides what code to include from the library, and all code is not nesserarily included. The linker usually does a graph analysis on what object files to include from the library, based on identifier usage. This causes some staticaly run code not to be executed if it exist in an objectfile which does not have any direct or inderect references to it from executable code (Does that make any sense?).

I do not think this is your problem, but this can cause other problems with static objects/member variables within a lib.

This topic is closed to new replies.

Advertisement