radford@calgary.UUCP (Radford Neal) (09/08/84)
A number of articles have been posted recently regarding the C pre-processor
and the new C standard. None of the none of the authors seem to take
the correct -:) view that the C pre-processor is a complete botch and
should be discarded as soon as possible.
How many macro definitions like the following have you seen?
#define min(a,b) a>b ? b : a
The correct definition is
#define min(a,b) ((a)>(b) ? (b) : (a))
but few programmers are that careful. If you don't see the difference, try
the expressions x+min(y,z) and min(1,q?x:z).
If you think you'd never be that careless, go on to trying to write a
macro to write N spaces to standard output. This is IMPOSSIBLE if you
want the macro call to look like a procedure call. The best you can do
is the following:
#define spaces(n) { int i; for (i = (n); i>0; i--) putchar(' '); }
This naturally doesn't work in a call such as spaces(i*2) (no concept of
scope!). Furthermore, the following code segment doesn't work:
if (blat) spaces(10);
else spaces(20);
(You have to remove the first semi-colon).
The present pre-processor is also a fertile ground for programmers who
like to write obscure and misleading code. A relatively mild form of
this is the following sort of macro:
#define clear(v) ((v) = 0)
A call such as clear(i) looks just like a procedure call but alters
its argument, which would be impossible if it really was one.
Finally, the pre-processor doesn't fit with the rest of C syntax from
an aesthetic point of view. The commands can't be indented and they treat
end-of-line as significant, both contrary to the rest of the language.
I suggest that the new standard include a minimal form of the present
pre-processor for compatibility plus the following two new constructs
which would be preferred for most of the legitimate uses.
1) Allow a storage-class of 'const'. Examples would be
const int a = 1234;
const struct point origin = { 0.0, 0.0 };
The initialization would be optional for global const's, but would
be expected in some module. Integer constants initialized in the
current module could be used in array bounds, etc. You could
take the address of a constant. The compiler could figure out
whether it really needed to allocate space for it (in a write-protected
area of course).
2) Allow a "storage-class" of 'inline' for procedures. For example:
inline int min(a,b)
int a, b;
{ return a>b ? b : a;
}
Semantics identical to ordinary procedures except you can't get
pointers to them. Any decent compiler would generate in-line
code for these, which shouldn't be too difficult since C already
has declarations in inner blocks.
The only legitimate use of a real macro-like facility I can see is
for conditional compilation, and even that might be better done in most
cases by a compiler that's smart enough to recognize if statements with
constant conditions.
Radford Neal, University of Calgary, Calgary, Alberta, CANADA
chris@umcp-cs.UUCP (Chris Torek) (09/10/84)
I always parenthesize my min/max #define's. The big problem with those is that they still generate side effects twice. (But hey!, that's what macros are all about!) As for a macro to put out N spaces, how about: #define spaces(n) \ if (1) { \ register int _youwouldntusethisid = (n); \ while (--_youwouldntusethisid >= 0) \ putchar(' '); \ } \ else This gets around the if (expr) spaces(expr); else ... (dangling else) problem. Now your only trap is writing foo() { spaces(6) bar(); } (a missing semicolon makes unreachable code). Unfortunately, this gives lint indigestion (``constant in conditional context''). -- In-Real-Life: Chris Torek, Univ of MD Comp Sci (301) 454-7690 UUCP: {seismo,allegra,brl-bmd}!umcp-cs!chris CSNet: chris@umcp-cs ARPA: chris@maryland
laman@sdcsvax.UUCP (09/10/84)
If you want a macro to write "n" spaces try: #define spaces(n) printf("%*s", n, "") This of course will only work on systems that understand a '*' flag in printf(). The '*' flag goes back to V7, if memory serves me right. Mike Laman, NCR @ Torrey Pines UUCP: {ucbvax,philabs,sdcsla}!sdcsvax!laman P.S. What about a negative "n" you say. Try: #define spaces(n) printf("%*s", (n <= 0) ? 0 : n, "") If you want to worry about if-then-elses then go ahead and pull the check out of the arguments so you can side step the printf.
WMM@acc.ARPA (09/11/84)
From: Waiming Mok <WMM@acc.ARPA> Here is another alternative to spaces(n): #define spaces(n) do {int i; while (i--) putchar(' ');} while(0) Waiming Mok (wmm@ACC) ------
td@alice.UUCP (Tom Duff) (09/12/84)
The following macro prints n spaces with none of the problems described in previous suggestions #define spaces(n) printf("%*s", (n), "") Of course, this finesses the issues at hand (inadequate scope rules and syntactic glitches). It is hard for me to understand the mind of the code bummer that thinks that saving one function call (while still calling putchar n times) is worth breaking the language for, especially since this routine can't possibly be called at a high enough rate (it's execution speed is surely limited by I/O bandwidth) to justify any savings. You CAN make a case for inline procedures. This example is not the weapon with which to win the battle.
WMM%ACC@sri-unix.UUCP (09/17/84)
From: Waiming Mok <WMM@ACC> Correction to spaces(n) macro: #define spaces(n) do {int i=(n);while (i--) putchar(' ');}while (0) Sorry for the typo. - Waiming Mok (wmm@ACC) ------
warren@tikal.UUCP (warren) (09/18/84)
[There are no bugs...] I add my applause to the "const" declaration. Most useful for ROM based applications. If the compiler and linker can know to put const data in rom then much space can be saved. Also duplicate instances of the same constant can be co-resident, another saving. I also applaud the idea of "inline" functions, but feel that a programmer should be able to PREVENT taking the address of a function without having to make it an inline function (e.g. macro). Scope is a better protection than funny names. e.g #define macro(arg) {short _j___k__ ; ... } Defined constants are necessary for large system. Do not delete the existing features. teltone!warren