Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

The Power of CPP, part II.

7 views
Skip to first unread message

Henry Baker

unread,
May 3, 1997, 3:00:00 AM5/3/97
to

In my previous posting, I bemoaned the fact that the C preprocessor (CPP)
has been neutered by restricting the rewrite of a macro 'foo' to no longer
expand an occurrences of 'foo'. I suspect that this rule gelds C macros
so that they can no longer expand arbitrarily.

After studying the code in the 'Obfuscated C' contest, and 'zsmall.c' in
particular, it became clear that '#include' has no such restriction. Thus,
if we define every macro-like thingy in a separate file, there appears to
be no restriction on recursive #include's -- at least to the depth of the
#include stack, which ANSI C requires to be at least 8 deep. Metrowerks
C for the Mac appears to handle nested includes deeper than 8 deep, but I didn't
go to the trouble to find out exactly how deep it would go.

We can now program the (iterative) factorial function as follows.

-----fact.h------
#if x==0
y
#else
#define newy (y*x)
#include "assignnewy.h"
#define newx (x-1)
#include "assignnewx.h"
#include "fact.h"
#endif
-----------------
------fact-driver.h----
/* set up arguments x and y, then 'call' factorial 'macro'. */
#define x 5
#define y 1
#include "fact.h"
-----------------------

Think BASIC. The idea here is to utilize the 'variables' x, y to hold the
'arguments' to the factorial 'macro' which is the file "fact.h". To 'assign' x
a new value, we first define its new value by means of a macro 'newx', and then
'call' the 'macro' (file) "assignnewx.h" which does the assignment x := newx.

The hard part is "assignnewx.h", which has to evaluate the new expression and
'assign' it to x. It turns out that this has to be done bit-by-bit for
the binary encoding of x. Thus, we compute newx0, newx1, newx2, newx3, etc.,
where newx<i> is the i'th bit of newx:

#if (newx%16)-(newx%8)
#define newx3 8
#else
#define newx3 0
#endif

etc.

We must then do the assignment x<i> := newx<i>, as follows:

/* Perform the assignment x3 := newx3 */
#undef x3
#if newx3
#define x3 8
#else
#define x3 0
#end

etc.

Finally, we put the updated x back together again:

#undef x
#define x (x15+x14+x13+x12+x11+x10+x9+x8+x7+x6+x5+x4+x3+x2+x1+x0)

We also have to kill off the newx's:

#undef newx
#undef newx0
#undef newx1
etc.

"assignnewy.h" is similar.

I programmed the above, and it does indeed work.

With a bit more work it should be possible to program recursive fibonacci
completely in the C preprocessor, and cause one of the longest CPP runs in
history! (Handling a stack/vector seems to require a bit more thought.)

Question: Can fact be done _without_ converting x to binary?

Question: Can fact be done _without_ creating a separate file for each
variable name?

0 new messages