C++, macros, and job security

Macros sure can be fun -- and profitable. This actually worked -- it might work for any of you guys too.

The trick is to make your code look sensible, but be actually very hard to modify without introducing unexpected side effects, so that anyone hired to replace you will resign, shoot himself, jump off a bridge, or some such within weeks of exposure to your code that they'd have to maintain.

At <company name deleted> a friend wrote code like this:

<in some header somewhere> #define DOUBLE(x) (x+x)

Naturally, elsewhere in the code he had

int j = DOUBLE(i++);

and of course depended on i getting increased by 2, j being twice the original value plus one, etc.; e.g. if i was 6 it was supposed to end up 8, and j equal to 13.

During the dot-com bust he predictably got laid off, but the guy who took over maintaining his code ... well, let's just say he now resides in a home with no sharp objects or corners and 24/7 on-call assistance whether he wants assistance or not. >;-)

It started with an obscure bug he had to track down. Eventually, he found the line

int j = DOUBLE(i++);

and verified that yes, DOUBLE was a macro. He changed the macro to ((x)+(x)) and the above to

int j = DOUBLE(i); i++;

Of course, now i was 7 instead of 8 and j was 12 instead of 13, under the same circumstances (i originally 6) as above, and the algorithm broke. Now there were 2 bugs. The regression had him revert the code back to the way it was and go on hunting for the real bug. It took three weeks.

Later that year, he came across the definition of the DOUBLE macro again, all memory of the previous encounter forgotten, and realized it was unsafe if the argument had side effects. He assumed it was best to leave it as a macro, as the intent was probably to provide the most efficient doubling implementation on each supported platform, eventually by using #ifdefs and alternative definitions, and the current implementation was for their main platform where adds were less expensive than multiplies. So, cleverly, he rewrote it as:

(fix::temp = (x), fix::temp+fix::temp)

and included elsewhere in the header

namespace fix { int temp; }

Of course, one source file now refused to compile at

string s = <stuff>; <more stuff>; string s_repeated = DOUBLE(s);

Changing to s_repeated = s + s, partly just to fix it and partly to futureproof it against an anticipated

#ifdef FOO_PLATFORM #define DOUBLE (x*2) #endif

didn't quite fix things. Complex mathematical calculations were now off, which naturally resulted from code like:

double d; <stuff>; foo = DOUBLE(d);

and the huge roundoff errors resulting from d taking a round trip via a temporary declared as "int".

He changed DOUBLE to an inline function after spending weeks tracking down the bug, only to be tripped up by

#define inline my_inline

buried seven includes further down and included before DOUBLE. Sure enough, the code was full of "inline" used as a variable.

It was only after several more events like this (and finding out that no, this wasn't originally C code ported to C++ later on, so variables named "inline" didn't have to be grandfathered in in such a clumsy way) that he began to suspect the awful truth.

I think what clinched it though was the source file with a load of SUM(x,y) in it and halfway down between one function definition and another (belonging to the same class at that) a "#include sum2.h". It read, in its entirety:

#undef SUM #define SUM(x,y) x-y

(no parentheses even)

I don't think he found this next one amusing either, but he finally snapped when he encountered "sum2.h" and was fired and charged with uttering a death threat. After three more firings, an untimely death (ruled an accident, but believed to have been suicide), and umpteen requests for transfers, my friend was eventually rehired to maintain this particular code -- because nobody else could do it.

Oh, the other real gem was:

#ifndef __FOO_H__ #define __FOO_H__

<pages of assorted stuff>

#endif // __FOO_H__

// and another 150 or so blank lines

#ifdef th/ row #undef th/ row #endif // th/ row #define th/ row scuzzlefuzzlefoobarbazquux )46d{]3[dsf3

He'd been tearing his hair out for weeks trying to figure out why adding some proper exception handling invariably caused the affected code to completely fail to compile. He'd even done two or three global searches of the project files for "throw", suspecting more fun with macros, but of course grep kept coming up empty...

http://groups.google.com/group/comp.lang.c++/browse_thread/thread/e1cdd3a9aa3f46e9/b0e49ea6c8626d7b?lnk=st#b0e49ea6c8626d7b

發佈了41 篇原創文章 · 獲贊 7 · 訪問量 21萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章