[comp.lang.c] macros and semicolons

markhall@pyrps5.pyramid.com (Mark Hall) (06/27/91)

In article <1991Jun24.213932.595@otago.ac.nz> andrew@otago.ac.nz writes:
>I often get pissed off with the C pre-processor.  Here is one thats been
>getting up my wick for months.
>
>#define SWAP(a, b) {int c;  c = a; a = b; b = c}

Forgive me for this unsufferable horn-tooting; I just couldn't resist
(I also couldn't believe no one else sent this reply :-). . . .

NO macro will work for a swap.  You suffer from the call-by-name rule
which undid ALGOL in this case.  Consider the expansion of SWAP(i,a[i]):

	int c; c = i; i = a[i]; a[i] = c;

-Mark Hall (smart mailer): markhall@pyrps5.pyramid.com
	   (uucp paths): {ames|decwrl|sun|seismo}!pyramid!markhall

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (06/27/91)

In article <160662@pyramid.pyramid.com>, markhall@pyrps5.pyramid.com (Mark Hall) writes:
> Forgive me for this unsufferable horn-tooting; I just couldn't resist
> (I also couldn't believe no one else sent this reply :-). . . .
> NO macro will work for a swap.  You suffer from the call-by-name rule
> which undid ALGOL in this case.  Consider the expansion of SWAP(i,a[i]):
> 
> 	int c; c = i; i = a[i]; a[i] = c;

 Er, this turns out not to be the case.  Consider
	#define swap(Type, This, That)				\
	    do {						\
		Type *ThisQZ = &(This), *ThatQZ = &(That), t;	\ 
		t = *ThisQZ, *ThisQZ = *ThatQZ, *ThatQZ = t;	\
	    } while (0)

What happens to swap(int, i, a[i])?
	do { int *ThisQZ = &i, *ThatQZ = &a[i], t;
	     t = *ThisQZ, *ThisQZ = *ThatQZ, *ThatQZ = t;
	} while (0);

This is essentially what you'd get from inlining a call to a swap
procedure with reference parameters.
-- 
I agree with Jim Giles about many of the deficiencies of present UNIX.

grue@cs.uq.oz.au (Frobozz) (06/27/91)

In <160662@pyramid.pyramid.com> markhall@pyrps5.pyramid.com (Mark Hall) writes:

>NO macro will work for a swap.  You suffer from the call-by-name rule
>which undid ALGOL in this case.  Consider the expansion of SWAP(i,a[i]):

>	int c; c = i; i = a[i]; a[i] = c;

Something like:

#define SWAP(a, b) {int *ap = &a, *bp = &b, t; t = *ap; *ap = *bp; *bp = t; }

removes the problems associated with the call by name (there are other
problems present, but the call by name is gone).  Each argument gets
evaluated exactly once.  There is no longer any call by name problems.

This code will not work for register variables, but they do not suffer
from the kind of problems above (if one parameter is a register variable,
you can use it instead of it's pointer in the above and it will still work).
Register variables are only an optimisation anyway, so they are not all that
important --- I have a naive trust in modern compiliers to get the register
allocation right.




        						Pauli
seeya

Paul Dale               | Internet/CSnet:            grue@cs.uq.oz.au
Dept of Computer Science| Bitnet:       grue%cs.uq.oz.au@uunet.uu.net
Uni of Qld              | JANET:           grue%cs.uq.oz.au@uk.ac.ukc
Australia, 4072         | EAN:                          grue@cs.uq.oz
                        | UUCP:           uunet!munnari!cs.uq.oz!grue
f4e6g4Qh4++             | JUNET:                     grue@cs.uq.oz.au
--

kers@hplb.hpl.hp.com (Chris Dollin) (06/27/91)

Mark Hall writes:

   NO macro will work for a swap.  You suffer from the call-by-name rule
   which undid ALGOL in this case.  Consider the expansion of SWAP(i,a[i]):

	   int c; c = i; i = a[i]; a[i] = c;

Disclaimer: I am *not* advocating SWAP macros. But:

    #define SWAP(x, y) \
	{ int *splodge_x = &(x); int *splodge_y = &(y); 
	int splodge_t = *splodge_x; \ 
	*splodge_x = *splodge_y; *splodge_y = splodge_t; }

does not suffer from the call-by-name problem (Algol 60 did not have
call-by-reference, but Algol 68 does (in a manner of speaking). Hence Mark's
criticism fails.

However, the amount of effort required to write even a specialised swap macro
should have suggested by now [anyone who hasn't, go read the FAQ] that swap
macros are dead ducks. 

Dead, you hear me? D - E - D, DEAD.

We now return you to your regular sniping about the NULL pointer.
--

Regards, Chris ``GC's should take less than 0.1 second'' Dollin.

volpe@camelback.crd.ge.com (Christopher R Volpe) (06/28/91)

In article <160662@pyramid.pyramid.com>, markhall@pyrps5.pyramid.com
(Mark Hall) writes:
|>>
|>>#define SWAP(a, b) {int c;  c = a; a = b; b = c}
|>
|>Forgive me for this unsufferable horn-tooting; I just couldn't resist
|>(I also couldn't believe no one else sent this reply :-). . . .
|>
|>NO macro will work for a swap.  You suffer from the call-by-name rule
|>which undid ALGOL in this case.  Consider the expansion of SWAP(i,a[i]):
|>
|>	int c; c = i; i = a[i]; a[i] = c;

#define SWAP(a,b) {int c, *pa=&(a), *pb=&(b); c=*pa; *pa=*pb; *pb=c;}

This doesn't solve all problems, but it gets the one you mentioned. I
think. :-)
                
==================
Chris Volpe
G.E. Corporate R&D
volpecr@crd.ge.com

brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (06/28/91)

In article <6531@goanna.cs.rmit.oz.au> ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) writes:
> 	#define swap(Type, This, That)				\
> 	    do {						\
> 		Type *ThisQZ = &(This), *ThatQZ = &(That), t;	\ 
> 		t = *ThisQZ, *ThisQZ = *ThatQZ, *ThatQZ = t;	\
> 	    } while (0)

You should call this SWAP. Without the uppercase, someone might be
fooled into thinking that it works like a function. Namespace issues
aside, there are three problems with this as a function: (1) You're
using This and That by reference. (2) You're passing a Type argument.
(3) This is a statement rather than an expression.

SWAP(x,y,int) would be fine, though I don't think you lose anything by
making the user indirect x and y: SWAP(&x,&y,int).

---Dan

mrm@nss1.com (Michael R. Miller) (06/28/91)

In article <160662@pyramid.pyramid.com>, markhall@pyrps5.pyramid.com (Mark Hall) writes:
> ...
> NO macro will work for a swap.  You suffer from the call-by-name rule
  ^^^^^^^^
> which undid ALGOL in this case.  Consider the expansion of SWAP(i,a[i]):
> 
> 	int c; c = i; i = a[i]; a[i] = c;

How about:

#define swap(x, y)  (x ^= y ^= x ^= y)

Why won't this do the swap as requested?  I've done this before.  The
only presumption, which was carried forward from the above text, is
the types are compatible (not only size but type).

torek@elf.ee.lbl.gov (Chris Torek) (06/29/91)

In article <1991Jun28.134517.5972@nss1.com> mrm@nss1.com
(Michael R. Miller) writes:
>#define swap(x, y)  (x ^= y ^= x ^= y)

See the Frequently Asked Questions.  This macro produces undefined
results because it modifies an object more than once without an
intervening sequence point.
-- 
In-Real-Life: Chris Torek, Lawrence Berkeley Lab CSE/EE (+1 415 486 5427)
Berkeley, CA		Domain:	torek@ee.lbl.gov

mouse@thunder.mcrcim.mcgill.edu (der Mouse) (06/29/91)

In article <6531@goanna.cs.rmit.oz.au>, ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) writes:
> In article <160662@pyramid.pyramid.com>, markhall@pyrps5.pyramid.com (Mark Hall) writes:
>> NO macro will work for a swap.

>  Er, this turns out not to be the case.  Consider
> 	#define swap(Type, This, That)				\
> 	    do {						\
> 		Type *ThisQZ = &(This), *ThatQZ = &(That), t;	\ 
> 		t = *ThisQZ, *ThisQZ = *ThatQZ, *ThatQZ = t;	\
> 	    } while (0)

That won't work because of the space following the third backslash.

More seriously, that won't work if an argument doesn't have an address
(eg, is a bitfield or a register variable) or one of the last two
arguments happens to be called ThisQZ or ThatQZ.

It also breaks if ThisQZ, ThatQZ, or t happens to be #defined to
something other than a simple identifier.

I'll stand with markhall on this one, at least until C gets gensyms and
typeof.  (Yes, I know gcc already has typeof.  C doesn't.)

					der Mouse

			old: mcgill-vision!mouse
			new: mouse@larry.mcrcim.mcgill.edu

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (06/30/91)

In article <1991Jun29.125314.22043@thunder.mcrcim.mcgill.edu>, mouse@thunder.mcrcim.mcgill.edu (der Mouse) writes:
> In article <6531@goanna.cs.rmit.oz.au>, ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) writes:
> > In article <160662@pyramid.pyramid.com>, markhall@pyrps5.pyramid.com (Mark Hall) writes:
> >> NO macro will work for a swap.
> 
> >  Er, this turns out not to be the case.  Consider
> > 	#define swap(Type, This, That)				\
> > 	    do {						\
> > 		Type *ThisQZ = &(This), *ThatQZ = &(That), t;	\ 
> > 		t = *ThisQZ, *ThisQZ = *ThatQZ, *ThatQZ = t;	\
> > 	    } while (0)
> 
> More seriously, that won't work if an argument doesn't have an address

That one I can't handle.  The rest of the objections I can meet.

static void swap(size_t size, char *a, char *b)
    {
	char t;
	while (size--) t = *a, *a = *b, *b = t;
    }

#define swap(This, That) (swap)(sizeof (This), (char*)&(This), (char*)&(That))

(I know the function and the macro have the same name.  I may have slipped
up in the _way_ I did it, but ANSI C lets you do it.)

-- 
I agree with Jim Giles about many of the deficiencies of present UNIX.