[net.lang.c] context-independent macros

mwm@ucbtopaz.CC.Berkeley.ARPA (02/01/85)

Instead of:
	# define macro(args)\
		if (1) {\
			/* macro body */\
		}\
		else

How about:
	#define	macro(args)\
		(Statement1 ,\
		Statement2 ,\
		...
		Statement3)

This should work correctly anywhere a function call works, as it is a
single statement. The question is whether or not the evaluation order is
preserved. Anybody want to say whether order is/isn't preserved (or do K&R
remain silent), or poke any holes in this?

	<mike

For real pain, consider:
#define macro(args)	((state1) + (state2) + ... + (staten))

chris@umcp-cs.UUCP (Chris Torek) (02/03/85)

> How about:
> 	#define	macro(args)\
> 		(Statement1 ,\
> 		Statement2 ,\
> 		...
> 		Statement3)

I use this whenever possible, but it doesn't always suffice.  When it
doesn't, I've usually used

	if (1) { stuff to do } else

though

	do { stuff to do } while (0)

might be better.
-- 
(This line accidently left nonblank.)

In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 4251)
UUCP:	{seismo,allegra,brl-bmd}!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris@maryland

msb@lsuc.UUCP (Mark Brader) (02/04/85)

(main article ~35 lines ... quotes at end, with remarks interspersed)

There have been a number of items lately about the apparently simple macro

	#define	assert(x)	if (!(x)) complain ("x not true");

As has been said, if you write it this way, then you can't say something
like
	if (z) assert (z == 1); else z = a;

Various fixes have been discussed, with dummy ifs, dummy dos, braces,
and omitted semicolons.  Each method either has its own similar problems,
or is ugly, or is subject to dangerous semicolon typos.
The key is that what we really want is for

	assert(x);

to behave syntactically just as if it was really a function call.  Well,
suppose it was.  Then this would be a valid statement because an *expression*
followed by a semicolon is a statement.  But a compound statement with
braces, or an if-statement, or a do-statement, is *not* an expression.
This is the root of the difficulties.

So the solution is to write the macro to only use an expression:

	#define	assert(x)	((x)? 0: complain ("x not true"))
or
	#define	assert(x)	((x) || complain ("x not true"))

I think everybody will find the first form clearer except sh programmers. :-)

Of course, this solution fails when the macro really has to contain
a genuine loop or its own local variables, but I claim that most of
the time an if is all you want.  And in that case, writing the macro
to expand to an expression is possible and safe.

Mark Brader




arnold@gatech.UUCP (Arnold Robbins) reposts:

> #define assert(x)  do { if (!(x)) die_miserably("x bombed\n"); } while (0)

> which will get executed exactly once, and protects the macro syntactically.
> ...
> The other way is this:

> #define assert(x)	if(x) ; else die_miserably ("x bombed\n")

All I can say is that I find both of these uglier than mine.

> This topic has gone around (several times) before.

Sorry to add to it, but I haven't seen before what I'm saying this time.



mwm@ucbtopaz.UUCP (Praiser of Bob) writes:

> 	#define	macro(args)\
> 		(Statement1 ,\
> 		Statement2 ,\
> 		...
> 		Statement3)
> 
> This should work correctly anywhere a function call works, as it is a
> single statement. The question is whether or not the evaluation order is
> preserved.

This is correct if the word "statement" is replaced by "expression"
throughout.  In that case it becomes a companion to my suggestion above,
for use when you want sequential instead of conditional operations.
Order of evaluation is indeed guaranteed.

radford@calgary.UUCP (Radford Neal) (02/05/85)

> Instead of:
> 	# define macro(args)\
> 		if (1) {\
> 			/* macro body */\
> 		}\
> 		else
> 
> How about:
> 	#define	macro(args)\
> 		(Statement1 ,\
> 		Statement2 ,\
> 		...
> 		Statement3)
> 
> This should work correctly...

It doesn't work correctly. The statements might not be expressions (e.g. they
could be loops).

I posted a flame months ago on the deficiencies of the preprocessor. I
advocated an "inline" attribute for procedures to allow one to avoid its use.
It is quite impossible to write a macro to duplicate the following (for 
example):

   int f(a)
     char *a;
   { int c;
     c = 0;
     while (*a) { putchar(*a); c += 1; a += 1; }
     return c;
   }

The example above, however, can be done repressing only moderate amounts of
disgust via:

   #define macro(args) do { statement1; statement2; ... ; } while (0)

Note no semicolon at the end.

     Radford Neal
     The University of Calgary

jim@ISM780B.UUCP (02/08/85)

>So the solution is to write the macro to only use an expression:
>
>        #define assert(x)       ((x)? 0: complain ("x not true"))
>or
>        #define assert(x)       ((x) || complain ("x not true"))
>
>I think everybody will find the first form clearer except sh programmers. :-)

The first form gets "null effect" from lint, but the second form is just
right, and I myself will start using it immediately.  Thanks.

-- Jim Balter, INTERACTIVE Systems (ima!jim)

mwherman@watcgl.UUCP (Michael W. Herman) (02/09/85)

> I posted a flame months ago on the deficiencies of the preprocessor. I
> advocated an "inline" attribute for procedures to allow one to avoid its use.

C++ has such an attribute for functions.

bsa@ncoast.UUCP (Brandon Allbery) (02/12/85)

The problem with

#define mac(args) (st1, st2, ..., stn)

is that any true C statement (as opposed to expression -- I am talking
about if, while, for, break, continue, return, do, ...) will produce
a syntax error.

Brandon (bsa@ncoast.UUCP)
-- 
Brandon Allbery, decvax!cwruecmp!ncoast!bsa, "ncoast!bsa"@case.csnet (etc.)
6504 Chestnut Road, Independence, Ohio 44131 +1 216 524 1416 (or what have you)