printf this!
As I sit here waiting for another build to complete, I thought I'd share some tales from the front-line of Win32 game porting. Today's brutal example: printf and friends.
As every C programmer knows, printf (and its relatives, sprintf, fprint, wprintf, etc) are used to spew formatted text to various places. It's really common and you see it all the time as it's a core part of the standard library.
What may not be apparent is that it's a portability nightmare, at least when coming from apps built with Visual Studio. This point has been hammered home over and over while working on Civ4. How can such a basic staple of the language be unportable, I hear you ask? Let me count the ways.
1. The most common problem we hit has to do with wchar string formatting. Visual Studio takes a sharp left turn from the standard here, but in a subtle way. For the non-unicode printf family, you'd typically use the %s token to insert a regular char string, and the %ls or %S formatting tokens to insert a wchar string into the output.
Things get weird when you use the wchar variants, like wprintf. The standard states that, essentially, wprintf takes the same formatting tokens as the printf case: %s for char strings, %ls or %S for wchar strings. Visual Studio, however, does the exact opposite. in wprintf, it uses %s to represent wchar strings, and %S to represent regular char strings. Worse, they've added the notion of %hs to also represent regular char strings in a wprintf statement.
2. The second oddity has to do with 64-bit values. The standard states that something like %lld is the proper token to use for a long-long integer type (i.e. 64 bits). Visual Studio is not down with that though - it uses %I64.
3. The third oddity has to do with NULL string pointers. If you try to print the value of a string, and the pointer itself is NULL, standard-compliant libraries output nothing. On Visual Studio, it outputs "(null)". You wouldn't think this is important, but believe it or not, some games have relied on this behavior. Jedi Knight 2 and Jedi Academy have relied on this to detect the "dual-handed" lightsaber case. The games print a string containing the name of the lightsaber you're using for both hands of your player, send it across the net, and on the receiving end, use the detection of the "(null)" string to determine if the player has two lightsabers or not. This led to interesting bugs where you'd start a game and have a spare lightsaber sticking out of you or other odd behavior.
Comments
Thanks for the tips, I was trying to figure out how to do 64-bit values in printf. Just to let you know though, I got all of the following to work:
%lld
%llx
%I64d
%I64x
Posted by: Anonymous | September 1, 2007 02:06 PM
Thanks for the tips, I was trying to figure out how to do 64-bit values in printf. Just to let you know though, I got all of the following to work in Visual Studio 2005:
%lld
%llx
%I64d
%I64x
Posted by: Anonymous | September 1, 2007 02:08 PM