[comp.lang.c] Calling multiple functions in a mac

mcdaniel@uicsrd.csrd.uiuc.edu (11/04/88)

Written  1:43 pm  Nov  1, 1988 by knudsen@ihlpl.ATT.COM in comp.lang.c:
> In article <353@marob.MASA.COM>, daveh@marob.MASA.COM (Dave Hammond) writes:
>> #define FOO()	do { foo1(); foo2(); foo3() foo4(); } while(0)
> Why use the do and while?  C has blocks, you can say just plain
>	{foo1(); ... foo4();}

Try compiling this code:
	#include <stdio.h>
    
	#define FOO() { printf("Hello, "); printf("world!\n"); }
    
	main() 
	{
	    if (1)
		FOO();          /* line 8 */
	    else		/* line 9 */
		exit(1);
	    exit(0);
	}

If your compiler is like mine (PCC, on a VAX, under BSD 4.3), it will
reply:
	"t.c", line 9: syntax error

According to K&R, first edition, the syntax of the "if" statement is
	if ( <expression> ) <statement> else <statement>
and <statement> is
	;
(the null statement) or
	{ <declaration-list-opt> <statement-list-opt> }
(or "for ...", "while ...", "<expression> ;", et cetera)  Thus
	if (1) { } ; else ;
is illegal. "{ }" is one statement, and ";" is another (a null
statement), and you can have only one statement between an "if" and
its "else".

One choice is to remove the ";" on line 8.  Then it looks like a
function call without a ";" after it, which looks weird.

Thus, the usual way to define such a FOO() is
	#define FOO()	do { foo1(); foo2(); foo3() foo4(); } while(0)
or
	#define FOO()	if (1) { foo1(); foo2(); foo3() foo4(); } else
NOTE the "else" in the second case.  If it were omitted,
	if (x) FOO(); else BAR();
would cause a syntax error, and if the semicolon were removed,
	if (x) FOO()  else BAR();
the "else" pairs with the "if" in FOO, NOT the "if (x)".

The problem with THESE solutions is that "lint -h" under BSD 4.3 starts
spitting out messages like
	t.c(7): warning: constant in conditional context
	t.c(8): warning: constant argument to NOT
You DO lint all your code, don't you? 8-(  To remove these messages, I
use a common ".h" file with
	#ifdef lint
	static int true = 1;
	static int false = 0;
	#else
	#define true 1
	#define false 0
	#endif
and use "do ... while (false)" and "if (true) ... else".  All for the
lack of an "inline" keyword in standard C.  *sigh*

A better definition is
>> #define FOO()        foo1(), foo2(), foo3(), foo4()
because it can be used in expressions.  However, you should use
parentheses around macro text!  Consider
	bar(FOO())
You expect bar() to be called with one argument, the value of foo4(),
but instead bar() is called with 4 arguments.  (Lint catches most
errors of this kind, which means most programmers will never notice
this error until it causes the program to core dump.  8-( )

In general, when you write macros that evaluate to expressions, you
should parenthesize the result, as above.

By the way, you should also parenthesize all uses of macro arguments.
Consider
	#define two_it(x) (2*x)
The whole thing is parenthesized, which is as suggested above.
However, if it is called as "two_it(x+y)", this call becomes
"(2*x+y)", which is probably NOT what you expected.
	#define two_it(x) (2*(x))
works better.

-- 
Tim, the Bizarre and Oddly-Dressed Enchanter
Center for Supercomputing Research and Development
at the University of Illinoid at Urbana-Champaign

Internet, BITNET:  mcdaniel@uicsrd.csrd.uiuc.edu
UUCP:    {uunet,convex,pur-ee}!uiucuxc!uicsrd!mcdaniel
ARPANET: mcdaniel%uicsrd@uxc.cso.uiuc.edu
CSNET:   mcdaniel%uicsrd@uiuc.csnet
DECnet?: GARCON::"mcdaniel@uicsrd.csrd.uiuc.edu"

davidsen@steinmetz.ge.com (William E. Davidsen Jr) (11/12/88)

In article <44200018@uicsrd.csrd.uiuc.edu> mcdaniel@uicsrd.csrd.uiuc.edu writes:

| A better definition is
| >> #define FOO()        foo1(), foo2(), foo3(), foo4()
| because it can be used in expressions.  However, you should use
| parentheses around macro text!  Consider
| 	bar(FOO())
| You expect bar() to be called with one argument, the value of foo4(),
| but instead bar() is called with 4 arguments.  (Lint catches most
| errors of this kind, which means most programmers will never notice
| this error until it causes the program to core dump.  8-( )
| 
| In general, when you write macros that evaluate to expressions, you
| should parenthesize the result, as above.

Somewhere the "as above" with correct parens seems to have gotten lost.
I'm sure what you meant was to state the example:
    #define FOO()        (foo1(), foo2(), foo3(), foo4())
as you explain in your text. I'm afraid that some readers may look at
the example and miss your points about macros in procedure calls, etc.

| 
| By the way, you should also parenthesize all uses of macro arguments.
| Consider
| 	#define two_it(x) (2*x)
| The whole thing is parenthesized, which is as suggested above.
| However, if it is called as "two_it(x+y)", this call becomes
| "(2*x+y)", which is probably NOT what you expected.
| 	#define two_it(x) (2*(x))
| works better.
| 
| -- 
| Tim, the Bizarre and Oddly-Dressed Enchanter
| Center for Supercomputing Research and Development
| at the University of Illinoid at Urbana-Champaign
| 
| Internet, BITNET:  mcdaniel@uicsrd.csrd.uiuc.edu
| UUCP:    {uunet,convex,pur-ee}!uiucuxc!uicsrd!mcdaniel
| ARPANET: mcdaniel%uicsrd@uxc.cso.uiuc.edu
| CSNET:   mcdaniel%uicsrd@uiuc.csnet
| DECnet?: GARCON::"mcdaniel@uicsrd.csrd.uiuc.edu"


-- 
	bill davidsen		(wedu@ge-crd.arpa)
  {uunet | philabs}!steinmetz!crdos1!davidsen
"Stupidity, like virtue, is its own reward" -me