F
Floobar
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...
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...