Evil preprocessor hack of the day
It's been a while since I had occasion to do anything blisteringly unpleasant with the C preprocessor. But I did today, so here it is.
If you have a set of conditions such that one of them is always supposed to be true, you might plausibly find yourself wanting to write code along the lines of
if (condition1)
statement1;
else if (condition2)
statement2;
else if (condition3)
statement3;
The trouble is, if you know one of the conditions is always supposed to be true but the compiler doesn't, this sort of code can get you sworn at for sins of the order of failing to reliably initialise a variable (if each of the then-
else /* if (condition3) */
statement3;
If you just want to put in an assert as protection against muppets passing invalid data to your code, you could add it in an additional else clause:
else if (condition3)
statement3;
else
assert(!"We shouldn't be here");
If you want both the above advantages, the best idiom I know of looks like this:
else {
assert(condition3);
statement3;
}
But that last one is particularly nasty in terms of readability, because that final else-
if (condition1)
statement1;
else if (condition2)
statement2;
else since (condition3)
statement3;
using the non-since to mean ‘enforce by assertion that condition3 is true, and then execute statement3’.
So of course I wondered if since could be implemented using preprocessor hackery. It doesn't look easy at first glance, because what I'm essentially trying to do is construct a statement block containing my assert statement followed by statement3, and all the obvious ways to do that require a closing brace after statement3, which I can't arrange using the invocation syntax shown. But with a bit more thought, it turns out it can be done.
To begin with, we need a piece of standard boilerplate which should be boringly familiar to anyone who's ever tried to do anything complicated with the C preprocessor:
#define CAT2(x,y) x ## y
#define CAT(x,y) CAT2(x,y)
This enables me to write CAT(x,y) and get a single token formed by gluing together the results of macro-x and y.
Now I can write since:
#define since(condition) \
if (1) { \
assert(condition); \
goto CAT(sincelabel,__LINE__); \
} else \
CAT(sincelabel,__LINE__):
That works regardless of whether statement3 is itself a braced block or not, and it's portable ISO C as far as I'm aware. The only restriction is that you mustn't put two since statements on the same source line (unless they're in different label scopes!).
Now I just need to decide whether I'm brave enough to actually check it into my code :-)
no subject
What's wrong with:
(assert is required to expand to a void expression.)
no subject
no subject
no subject
#ifdef NDEBUG
#define since(cond) if (0) ; else
#else
#define since(cond) if (!(cond)) { assert(0); } else
#endif
no subject
As a side note, since() plays merry hell with emacs's formatting…
no subject
no subject
no subject
no subject
no subject
sinceis anif.no subject
no subject
no subject
#define CAT(x,y) x ## y?
no subject
CAT(foo,__LINE__)gives you the fixed tokenfoo__LINE__, instead of giving youfoo23orfoo5097or other such things.no subject
no subject
ROFL. I love the way you say that.
In general. Yes, I agree, a spectacular epitome of preprocessor hacking.
What I'm thinking at the moment is: There should be a standard, sane way of adding keyword constructs to the language in such a way without using stupid text processing provided by the preprocessor, but also without encouraging a mad proliferation of every word anyone thinks might be useful, making ported code unreadable.
So, "since" and "with" might catch on, or certain other changes that seem useful (eg. new operators), but no more. Does that make sense?