Game Development Community

Defensive netMask coding tip for TG*

by Orion Elenzil · in Torque Developer Network · 03/25/2009 (3:31 pm) · 5 replies

this is a C++ coding tip which technically doesn't belong in a public forum, but since it applies to TGE, TGEA, T3D, TNL, and possibly several other T* products ima post here anyhow. if there's a better place for such posts i'd love to be pointed to it.

I was adding a new NetMask to ShapeBase today, and went to check to make sure that inserting a new NetMask in ShapeBase would not overflow the netmasks up in Player, so i just added a temporary print to print both the highest netmask i had defined as well as NextFreeMask. to my surprise, they were both Zero! this means that i'd already overflowed the 32 netMasks, and my last one was occupying the same bit as the first one, and i was just lucky that my code was working at all.

In thinking about this, it dawned on me how to design netmask declarations a little bit more defensively.

Here's the stock Player::MaskBits enum from TGE 1.4:
enum MaskBits {
      ActionMask   = Parent::NextFreeMask << 0,
      MoveMask     = Parent::NextFreeMask << 1,
      ImpactMask   = Parent::NextFreeMask << 2,
      NextFreeMask = Parent::NextFreeMask << 3
   };

my suggested alternative pattern is to add a "HighestMask" enum in each class which is the same as the highest used mask,
and then to enum NextFreeMask to be HighestMask shifted by one.

eg:
enum MaskBits {
      ActionMask   = Parent::NextFreeMask << 0,
      MoveMask     = Parent::NextFreeMask << 1,
      ImpactMask   = Parent::NextFreeMask << 2,
      HighestMask  = Parent::NextFreeMask << 2,  // note: shift value is same as the previous mask.   "0" is invalid
      NextFreeMask = HighestMask          << 1   // note: shift value is "1".                         "0" is   valid
   };

and then, at the top of Player::packUpdate, we can add an Assert:

AssertFatal(HighestNetMask != 0, "The highest netmask has wrapped-around! This is critical.");


So now if you insert a new netmask in some parent class such as ShapeBase without realizing that you're overflowing the masks down the class heirarchy, a debug build of your app will fail to run, thus alerting you to the problem.


#1
03/25/2009 (3:44 pm)
I'm going to one-up you :). Add the following to platformAssert.h (or wherever AssertFatal is defined in your codebase)

namespace Private
{
   template<int> struct CompileTimeError;
   template<> struct CompileTimeError<true> {};
}

#define AssertStatic(expr, msg) \
   do { Private::CompileTimeError<((expr) != 0)> \
   ASSERT_##msg; (void)ASSERT_##msg; } while(false)

And then instead of
AssertFatal(HighestNetMask != 0, "The highest netmask has wrapped-around! This is critical.");
use
AssertStatic(HighestNetMask != 0, highest_netmask_has_wrapped_around);

Now when your highest netmask has wrapped, Torque will fail to compile!
#2
03/25/2009 (4:30 pm)
that's sweet!!

but it doesn't compile ;)
i get
1>..\engine\game\player.cc(7256) : error C2079: 'ASSERT_highest_netmask_has_wrapped_around' uses undefined struct 'Private::CompileTimeError<__formal>'
1>        with
1>        [
1>            __formal=0
1>        ]

does this whole construct compile down to a no-op ?
#3
03/25/2009 (4:38 pm)
The whole thing compiles down to a no-op.
#4
03/25/2009 (4:44 pm)
Nice one Alex! Never really occurred to me before so thanks Orion.
#5
03/27/2009 (9:06 am)
Useful, thx!