Oooops…. who broke it?
“Fragile” code is code that breaks in one place because of changes you make in some other place. It’s most aggravating when you’re due to ship a new version tomorrow and you need to make one last tweak at 11:30 PM, or your client is looking over your shoulder and this little “harmless” change shows up as a smoldering heap during the demo.
In this case, “break” doesn’t ONLY mean “broken arrow” , or uncompilable code (at least you can chase those down easily enough). Here, “break” also means “operates incorrectly” or “completely wrecks itself like it never did before” or somewhere in between.
These sorts of breaks come from unrecognized dependencies, and they’re all too easy to make: the header size has been 3 for months and months now, so when you add a new function that needs it, it’s easy to stick in a constant 3 and be done with it.
DON’T DO IT.
If you’re an old-hand bit-banging cycle counter like me, it’s easy to think of saving a few cycles and adding up the bytes in this cluster, and using a constant of 53 when you need the size of it.
DON’T DO IT.
The problem is, or course, that when (not if, but when) these things change, then you will have to track down ALL the instances where you use this number and change them. Not a good plan. The compiler won’t complain – the code is still valid. But reading three bytes when you should be reading four will not get you where you want to go.
One tip to solving this is to reduce the number of places that you use such numbers. Focus such procedures into a single VI if you can. But the real key to solving this is to recognize these things when you originate them.
Example #1
Here we need to receive a packet header, consisting of a cluster of a U8 enum command and a U16 integer. It’s easy enough to count up to three bytes, and it would be easy to plop down a 3 constant. However, it is safer to use a constant of the header’s typedef and calculate its size in code. This might go against your instincts (it does mine), but in fact the extra time taken (to flatten into string and get string length) is trivial (measure it yourself if you have doubts). On top of a TCP READ operation, this burden is truly insignificant.
And the benefit is that when the integer needs to become an U32 or the command must become a U16, here’s one less thing YOU have to worry about. Since the constant here is a TYPEDEF, and since you’re calculating the size every time, then it will keep on working.
Example #2
This example is similar – sending a packet header plus a payload thru a connection. Even if your payload is always the same size, it’s better to calculate it than to use a constant. With any luck at all, the STRING LENGTH operation will get the same answer every time. and if you do change it at some point, then this code won’t break.
Nice. I’d just noticed the “constraining” that occurs when type-casting a number to an enum and that it doesn’t “go off the end”.
Thanks
Yeah, there’s really no other way it could work. If it’s a variable of your ENUM type, then it has to be a LEGAL value. Use that fact to your advantage.
A much better solution is to use the fact that incrementing an Enum will cause it to “wrap around”. So wire the Enum into a loop’s Shift register, and wire a copy into a tunnel (which will then hold the initial value). Inside the loop, use the value in the Shift register (current value), increment it (next value), and wire the Next Value and Initial Value (from the tunnel) to an “Is Equal” comparison, wired to the Stop control of the While loop.
The loop will execute once for every value of the Enum, even if there are more than 9999 values! No “magic” required.
Excellent, Bob!. That’s the same idea, but even MORE bulletproof!