By-value static member variables & XCode debug builds

· by Steve · Read in about 3 min · (530 Words)

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><br /> class AllocatedObject<br /> {<br /> protected:<br /> &nbsp;&nbsp;&nbsp; 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;<br /> typedef DefaultAllocatedObject SomeObjectAlloc;

.. and then in our first-party class definition:

class SomeObject : public SomeObjectAlloc<br /> {

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:<br /> __ZN4Ogre15AllocatedObjectINS_14StdAllocPolicyEE13smAllocPolicyE<br /> /Users/steve/projects/Shoggoth/ogre/Mac/Ogre/../build/Ogre.build/Debug/Ogre.build/Objects-normal/ppc/OgreEntity.o reference to undefined __ZN4Ogre15AllocatedObjectINS_14StdAllocPolicyEE13smAllocPolicyE<br /> /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.