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