[net.lang.c] compile-time function

jack@hp-dcde.UUCP (10/14/83)

#N:hp-dcde:20000001:000:517
hp-dcde!jack    Oct 12 19:00:00 1983

I want to define a compile-time function.  For example:

#define LOG2(n)	\
#if n==1	\
	0	\
#endif		\
#if n==2	\
	1	\
#endif		\
#if n==4	\
	2	\
#endif		\
... and so on ...

	i = LOG2(4);

Which I want to produce i = 2.

However, it seems that the whole macro definition gets put together
on a single line before the preprocessor checks for #if's, so the
#if's aren't recognized.  How can I (for the general case, not just LOG2)
do something like this?

					-Jack Applin
					Hewlett-Packard
					(hplabs!hp-dcd!jack)

leichter@yale-com.UUCP (Jerry Leichter) (10/15/83)

You can get your particular example to work by doing:

#define LOG2(n) ((...(n == 2) ? 1 ) : (n == 4) ? 2 : ...

(You can probably do without all the parens, but I can never remember for
sure how precedence and associativity works for ?: so I play it safe...)

The above will give you the code you want as a side-effect of constant folding,
which is present in every C compiler worth looking at.

It's an interesting fact that many compilers do constant folding over expres-
sions, but hardly any - in fact, I don't know of any at all - do it across
statements - although taking constants out of loops is, in a sense, doing
exactly that.  I don't think any C compiler will "fold"

	if (2=1)
		a = 0
	else if (2 == 2)
		a = 1
		etc.

This is unlikely to come up, except in code generated by some other program
(or a macro - but C macros aren't really very handy for constructing more
than single statements anyway) - but this isn't common, so the compilers aren't
designed to deal with it.

So:  In answer to your general question:  If you can express your compile-
time function as a single expression, constant-folding should get you the
effect you desire.  The presense of the ?: operator means you can put in
conditionals.  You can also put in calls to other macros.  Unfortunately,
you CANNOT use recursion:

#define fact(n) (n == 1) ? 1 : n * fact(n - 1)

will put every C compiler I've ever seen in a loop:  Unfortunately, the
compile-time evaluators do NOT respect the "non-monotonicity" (to use the
term from formal semantics) of :?, &&, and ||.  They always evaluate ALL
arguments, and then discard the one they don't need.  I.e.

	a = (X == 0) : 0 ? (1 / X)

(where X is a pre-processor constant) SHOULD work; it would work if X were a
variable; but it will cause most C compilers to die a horrible death when
X happens to be 0.

If :? was handled correctly, #define could be used recursively and the resulting
compile-time language would be universal.  (At least in principle.  Most C
compilers also have some hard limits on expression complexity, and will blow
up if you really try this for more than "toy" cases.)
							-- Jerry
					decvax!yale-comix!leichter leichter@yale

kolstad@parsec.UUCP (10/16/83)

#R:hp-dcde:20000001:parsec:37300002:000:347
parsec!kolstad    Oct 15 18:35:00 1983

Here's how I did LOG2:

#define LOG2(x)  ((x)<=2?1: (x)<=4?2: (x)<=8?3: (x)<=16?4: \
	 (x)<=32?5: (x)<=64?6: (x)<=128?7:  (x)<=256?8: (x)<=512?9: \
	 (x)<=1024?10:  (x)<=2048?11:12 )

NB:  The C compiler will evaluate this expression to a single constant
	if it is supplied with a constant argument.  The preprocessor
	does NOT do the reduction.

malcolm@pur-ee.UUCP (10/16/83)

#R:hp-dcde:20000001:ecn-ee:13100001:000:214
ecn-ee!malcolm    Oct 16 02:54:00 1983

Not to be outdone on this trivial programming assignment....

I use the following one-liner to find the base two logarithm at run-time.
	for (logN=0;(1<<logN)<N;logN++);

						Malcolm Slaney
						Purdue EE Dept.

chris@umcp-cs.UUCP (10/19/83)

(I sent this as a reply, but I thought I'd better post it also, to
prevent people from believing something that isn't right.)

The example

	#define	fact(n)	(n == 1 ? 1 : n * fact (n-1))

doesn't put the compiler into a loop.  It puts the *preprocessor* into
a loop.  You've overlooked the fact that the preprocessor is a separate
entity, and does not know C.  The preprocessor does macro expansions
textually, not semantically.  The reason the LOG2 macro works is because
it's not textually recursive, and the compiler does constant folding.
If you were to define it as

	#define LOG2(n)	((n) == 0 ? 1 : 1 + LOG2(n/2))

you would again put the preprocesor into a loop.  (Eventually, at least
in the 4BSD cpp, you'll get a message about a macro stack overflow, or
something like that.)

Also, one thing that is almost a misfeature:  cpp looks only at lines
beginning with #, and at the same time folds multi-line #define's into
one line.  This makes things like

	#define fact(n) \
	#if (n) == 1 \
		1 \
	#else \
		((n) * fact(n-1)) \
	#endif

useless.  (If that were not so, that macro would textually expand (e.g.)

	fact(6)

to

	((6) *
	((6-1) *			/* Note to yale-com!lichter: */
	((6-1-1) *			/* I wrote these wrong in my reply. */
	((6-1-1-1) *			/* Sorry about that... */
	((6-1-1-1-1) *
	1
	)))))

which the compiler would fold into 720.  However, it might cause real
trouble if you gave it a variable instead of a constant...)
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci
UUCP:	{seismo,allegra,brl-bmd}!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris.umcp-cs@CSNet-Relay