[comp.lang.c] Is this swap

brnstnd@stealth.acf.nyu.edu (01/17/90)

Say swap() is defined as

#define block do {
#define endblock } while(0)
#define swap(x,y,typ) block typ *_MV_x = x; typ *_MV_y = y; typ tmp;\
                    tmp = *_MV_x; *_MV_x = *_MV_y; *_MV_y = tmp; endblock

Now constructions like swap(f++,g++,real) work and are faster than a
function call. Most optimizers are even smart enough to realize that the
do { } while(0) is purely syntactic.

swap() does not work within an expression, and the macro will fail
miserably if _MV_whatever is another macro. Other than that, does swap()
have any side effects or other problems? Is anyone interested in a program
that converts functions to these inline functions automatically?

(Don't bother saying ``if typ is a struct or union and your compiler
isn't ANSI then swap() will fail.'' I'm thinking about the problems
of the conversion from function to macro, not of swap() in particular.)

---Dan

sullivan@aqdata.uucp (Michael T. Sullivan) (01/18/90)

From article <21068@stealth.acf.nyu.edu>, by brnstnd@stealth.acf.nyu.edu:
> Say swap() is defined as
> 
> #define block do {
> #define endblock } while(0)
> #define swap(x,y,typ) block typ *_MV_x = x; typ *_MV_y = y; typ tmp;\
>                     tmp = *_MV_x; *_MV_x = *_MV_y; *_MV_y = tmp; endblock

Why are "block" and "endblock" even bothered with here.  Why not just
put the "do {" and "} while (0)" in the definition of swap?  Could
somebody please enlighten me.
-- 
Michael Sullivan          uunet!jarthur!aqdata!sullivan
aQdata, Inc.              sullivan@aqdata.uucp
San Dimas, CA

eric@tqc.FIDONET.ORG (Eric Rouse) (01/18/90)

 > Message-ID: <21068@stealth.acf.nyu.edu>
 >
 > Say swap() is defined as
 >
 > #define swap(x,y,typ) block typ *_MV_x = x; typ *_MV_y = y; typ tmp;\
 >                     tmp = *_MV_x; *_MV_x = *_MV_y; *_MV_y = tmp;

      As long as your working with base types, or types you can cast to a base 
types like integers, longs etc, you might look into this one:

#define Swap(X1,X2) { X1 ^= X2; X2 ^= X1; X1 ^= X2; }

      It's quite nice for integers, unsigned, longs, pointers etc.  Plus it 
doesn't use any temp storage.

Eric Rouse -- via The Q Continuum (FidoNet Node 1:382/31)
UUCP: ...!rpp386!tqc!eric
ARPA: eric@tqc.FIDONET.ORG

mikes@rtech.UUCP (Mike Schilling) (01/19/90)

From article <1990Jan18.002842.441@aqdata.uucp>, by sullivan@aqdata.uucp (Michael T. Sullivan):
> From article <21068@stealth.acf.nyu.edu>, by brnstnd@stealth.acf.nyu.edu:
>> Say swap() is defined as
>> 
>> #define block do {
>> #define endblock } while(0)
>> #define swap(x,y,typ) block typ *_MV_x = x; typ *_MV_y = y; typ tmp;\
>>                     tmp = *_MV_x; *_MV_x = *_MV_y; *_MV_y = tmp; endblock
> 
> Why are "block" and "endblock" even bothered with here.  Why not just
> put the "do {" and "} while (0)" in the definition of swap?  Could
> somebody please enlighten me.
> -- 
In fact why use the do-while at all, and why use pointers?  The classic swap
macro is

# define swap(x, y, typ) 	\
	{			\
		typ tmp; 	\
				\
		tmp = y; y = x; x = tmp;	\
	}

Is this just as good, or am I being *exceptionally* dense today?

john@stat.tamu.edu (John S. Price) (01/19/90)

In article <4514@rtech.rtech.com> mikes@rtech.UUCP (Mike Schilling) writes:
>In fact why use the do-while at all, and why use pointers?  The classic swap
>macro is
>
># define swap(x, y, typ) 	\
>	{			\
>		typ tmp; 	\
>				\
>		tmp = y; y = x; x = tmp;	\
>	}
>
>Is this just as good, or am I being *exceptionally* dense today?

The only problem I see with this, and it's not much of a problem, is
that if you type

swap(x,y,int);

that will be expanded to

{
	int tmp;
	tmp = y; y = x; x = tmp;
};

with a semicolon at the end of the block, which isn't always
wanted.  The reason for the do { ... } while (0) is so that
the semicolon can be put there without any problem.
Most compilers just see the ; as a null statement and ignore it,
anyway, but style wise it's not wanted.

The do {...} while (0) loop is usually recognized by the compiler
as a useless block, and optimized out, so that it doesn't do
the comparison before it exits the block.  It will see the 0 as
always false, and optimize this out.


--------------------------------------------------------------------------
John Price                   |   Morals define our path through life,
john@stat.tamu.edu           |   And everyone's path is different... - Me
--------------------------------------------------------------------------

meissner@curley.osf.org (Michael Meissner) (01/19/90)

In article <4514@rtech.rtech.com> mikes@rtech.UUCP (Mike Schilling) writes:

>In fact why use the do-while at all, and why use pointers?  The classic swap
>macro is
>
># define swap(x, y, typ) 	\
>	{			\
>		typ tmp; 	\
>				\
>		tmp = y; y = x; x = tmp;	\
>	}
>
>Is this just as good, or am I being *exceptionally* dense today?

In terms of while the do/while(0), it is to support using the swap
macro in an if statement.  Consider the following fragment:

	if (condition)
		swap (a, b, int);
	else
		flag = TRUE;


If the macro is defined as above, the ';' after the macro invocation
will be treated as an empty statement, and the else will generate a
compiler error.  The do { ... } while (0) trick allows the ';' to
properly end the statement.  I originally wondered why the source to
GNU C used this construct, until I realized about else statements.
Michael Meissner	email: meissner@osf.org		phone: 617-621-8861
Open Software Foundation, 11 Cambridge Center, Cambridge, MA

Catproof is an oxymoron, Childproof is nearly so

rhg@cpsolv.CPS.COM (Richard H. Gumpertz) (01/19/90)

In article <21068@stealth.acf.nyu.edu> brnstnd@stealth.acf.nyu.edu (Dan Bernstein) writes:
>Say swap() is defined as
>
>#define block do {
>#define endblock } while(0)
>#define swap(x,y,typ) block typ *_MV_x = x; typ *_MV_y = y; typ tmp;\
>                    tmp = *_MV_x; *_MV_x = *_MV_y; *_MV_y = tmp; endblock
>
>Now constructions like swap(f++,g++,real) work and are faster than a

Stylistic nit: I would make it &(x) and &(y) in the first line of swap and
make typ the first parameter and then make the call you show be
	swap(real, *f++, *g++)
Maybe I am strange, but I find that easier to read.
-- 
  ==========================================================================
  | Richard H. Gumpertz    rhg@CPS.COM    (913) 642-1777 or (816) 891-3561 |
  | Computer Problem Solving, 8905 Mohawk Lane, Leawood, Kansas 66206-1749 |
  ==========================================================================

woody@rpp386.cactus.org (Woodrow Baker) (01/21/90)

In article <113.25B72444@tqc.FIDONET.ORG>, eric@tqc.FIDONET.ORG (Eric Rouse) writes:
> 
> 
> #define Swap(X1,X2) { X1 ^= X2; X2 ^= X1; X1 ^= X2; }
> 
>       It's quite nice for integers, unsigned, longs, pointers etc.  Plus it 
> doesn't use any temp storage.
I use xor for maintaing pointers for linked lists.  As the above line of
code clearly shows, you can given  a ^ b  extract a or b if you know the
other one.  If you are traversing a linked list, you know either a or b
(that is you know where you came from), so you can xor where you came from
with the current node link and find out where you are going.  It saves a
pointer.  Adding a node entry, you know where you are going to insert it,
thus you know where you came from (the node just prior) and where  the next
node will reside, so you can use XOR to construct the pointer.

Just a sidelight...
Cheers
Woody
f
> Eric Rouse -- via The Q Continuum (FidoNet Node 1:382/31)
> UUCP: ...!rpp386!tqc!eric
> ARPA: eric@tqc.FIDONET.ORG

bdm659@csc.anu.oz (01/22/90)

In article <17709@rpp386.cactus.org>, woody@rpp386.cactus.org (Woodrow Baker) writes:
> In article <113.25B72444@tqc.FIDONET.ORG>, eric@tqc.FIDONET.ORG (Eric Rouse) writes:
>>
>> #define Swap(X1,X2) { X1 ^= X2; X2 ^= X1; X1 ^= X2; }
>>
>>       It's quite nice for integers, unsigned, longs, pointers etc.  Plus it
>> doesn't use any temp storage.
> I use xor for maintaing pointers for linked lists.  As the above line of
> code clearly shows, you can given  a ^ b  extract a or b if you know the
> other one.  If you are traversing a linked list, you know either a or b
> ...

The portability of this practice is not guaranteed by the ANSI C Standard.
The arguments of ^ must have integral type, which doesn't include pointer
types.  You could try explicit casts, but there's no guarantee that any
integral type is long enough to hold all the values of a pointer type and,
even if there was, there is no guarantee that converting a pointer to an
integral type and back to a pointer recovers the original value.  The same
goes for the Swap() macro above if X1 and X2 have pointer type.

Brendan McKay.  bdm@anucsd.oz.au  or  bdm659@csc1.anu.oz.au