[net.lang.c] C pre-processor and ANSI standard

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