Well, this had me baffled for a while. I’ve been beavering away on allowing custom, user-supplied memory allocators in Ogre, hopefully for inclusion in the upcoming 1.6, and I came across a very weird problem in OS X. The wrapper for customising regular allocations (ie new/delete as opposed to STL allocations, those are in a different std::allocator compatible class), looks like this:
template <class Alloc>
class AllocatedObject
{
protected:
static Alloc smAllocPolicy;
Pretty obvious stuff, we clearly redirect new/delete operators via that allocator policy. We then go on to define some default allocators (which can be replaced), something like this:
typedef AllocatedObject<StdAllocPolicy> DefaultAllocatedObject;
typedef DefaultAllocatedObject SomeObjectAlloc;
.. and then in our first-party class definition:
class SomeObject : public SomeObjectAlloc
{
All fairly straight forward - all that typedefing is just a compact way of allowing per-class user configurable allocators while still keeping the type duplication to a minimum. I can also place those typedefs in such a way that they can be easily replaced just by user headers or preprocessor blocks. Now, obviously I need to instantiate that smAllocPolicy member, otherwise I’m in trouble. So, here’s what I initially tried to do, in a cpp:
template <> DefaultAllocationPolicy DefaultAllocatedObject::smAllocPolicy;
Seems ok, right? Again I’ve used the typedefs here so that the instantiation will work even if I changed the basis for the types. This worked fine on Visual C++ 2005, Ubuntu 7.1 and in release mode on OS X. But, in debug mode on OS X, I got this:
ld: Undefined symbols:
__ZN4Ogre15AllocatedObjectINS_14StdAllocPolicyEE13smAllocPolicyE
/Users/steve/projects/Shoggoth/ogre/Mac/Ogre/../build/Ogre.build/Debug/Ogre.build/Objects-normal/ppc/OgreEntity.o reference to undefined __ZN4Ogre15AllocatedObjectINS_14StdAllocPolicyEE13smAllocPolicyE
/usr/bin/libtool: internal link edit command failed
WTF? I double-checked that the .cpp was included in the target, that the .LinkFileList file included the right object file, that ZeroLink was disabled (not that that would have any effect but to delay any link errors until runtime anyway). I tried getting rid of the typedefs and making it explicit, no dice. It had me scratching my head for some time, I was convinced I was instantiating this static and the object file was getting linked, so why the problem? In the end, the solution was to to change the instantiation to this:
template <> DefaultAllocationPolicy DefaultAllocatedObject::smAllocPolicy = DefaultAllocationPolicy();
I’m really not sure why in debug mode I was forced to include an explicit construction & assignment of this policy class rather than it being constructed by value in place, as seems to have happened with the other compilers and indeed in release mode under OS X. I usually don’t hold complex types statically by value, so usually I’d have an explicit initialisation in code like this to init pointers to 0 or numerics to something reasonable, so I wouldn’t have tried this particular thing on OS X before, but I did not expect to have to construct a complex type manually (and copy-construct effectively, although that’s likely to be optimised out). The fact that it worked in release mode and on other compilers makes me wonder how idiosyncratic that behaviour is. Maybe I’m just too tired and need to read Stroustrup again. If anyone has a rational explanation, I’d be glad to hear it.









May 17th, 2008 at 1:10 am
What link policy are you using for your debug builds? Are you using something called “zero link” (or similar sounding), because I have had some issues with that in the past. I develop on Mac/Linux/Windows as well and I don’t have to add assignment statement. I even compile with “g++ -g” on OS X by default.
Apple must be throwing an interesting setting at the compiler in “Debug mode”. You can view the actual commands Xcode sends to gcc. I would do that and figure out what its actually doing.
Of course I might not be using a new enough GCC but we do have people build our software on Leopard.
May 17th, 2008 at 4:59 pm
@Joseph: thanks for the suggestion but no, as mentioned in the original post I checked I wasn’t using ZeroLink in debug mode. I avoid this because it hides link errors until runtime which isn’t that helpful in practice. I did look through the commands it was sending to gcc, and checked the .LinkedFileList which it uses to specify all the object files to make sure the .o that contained the def was in there, and it was. Most puzzling.