14/08/2011 @19:43:30 ^21:00:25

A tale of two GCC warning options, one successfully enabled in rboom, one not.

-Wshadow

This warns about variables that "shadow" others in outer scopes. In principle this could be a good idea, but in practice it isn't so useful, because it's too sensitive and complains about things you don't care about. It complains about everything possibly shadowed, from variables in outer blocks, to global functions defined in header files that you didn't even know existed.

My favourite example was that including math.h defines functions with hilariously generic names like "y0" and "y1". Imagine trying to write any code that deals with coordinates without eventually using such names...

-Wstrict-prototypes

void blah();

This is not a strict prototype as it does not define the parameters of the function. It requires quite a bit of work to make a doom engine strict-prototypes-clean. There are two major cases:

thinkers

Each thinker on the thinker list has its function pointer called every gametic. This is how the game world updates itself. There are lots of different kinds of thinkers - monsters, flashing lights, moving floors etc. - processed by different functions. An example - say, a moving floor - is a struct with a thinker embedded at the start.

All the explicit thinker functions - T_Floor, T_LightFlash, T_VerticalDoor and so on - have their own signatures. They all return void but take different structs (floor_t, lightflash_t, vldoor_t respectively) However, all these have a thinker struct embedded at the start, and the thinker function determines the type of the block in which it is embedded, in a form of run-time type information.

To make these strict we must make all the thinker functions have the same signature, void blah(thinker_t *), and at the start of each function, cast the thinker back to the type with which the function is guaranteed to be called.

code pointers

DeHackEd calls them code pointers, the game source calls them several things, action functions, actor functions, whatever. They're all named A_Something anyway. There's a big table of all the states a mobj can be in and when a mobj enters a state with a code pointer the function is called.

Unfortunately the state table is overloaded to contain both functions for mobjs (which take a pointer to the mobj) and for player weapons (which take a pointer to the player and a pointer to something called a pspdef)

In order to make these strict you have to convert all the code pointers to have the same signature; this is most easily done as taking a pointer to a mobj, since most of them are that way. The player weapon code pointers are passed a pointer to the mobj associalted with the player, which contains a pointer back to the player, in which you store extra information to recover the corresponding pspdef. Thus from a mobj pointer we can recover both parameters needed for the function, or bail out if the mobj is not a player.

The Eternity Engine, from which I stole the idea and some code, calls this "code pointer unification". It's also one less way for a DeHackEd patch to crash the game severely.