[net.lang.c] SWAP macro

Schauble@MIT-MULTICS.ARPA (Paul Schauble) (06/23/86)

I've gotten lots of respones to my request for a form of the macro that
would make
  swap(*a++, *b++);
 work.  Almost all of them missed the point.  The swap requires a
temporary.  Generating that temporary requires knowing the type.
Several of the solutions would work fine if *a and *b were int's, but I
never said that.

Recall the my original message was posted as a justification for a
typeof macro, akin to sizeof.  I find that the need to generate
temporaries in a macro is not uncommon.  Unless one is willing to do a
different version of the macro for each type, you need a way to generate
a new variable of the same type as one of the macro args.  The language
provides no way to do this.

bzs@bu-cs.UUCP (06/25/86)

The attached bit of total brain-damage seems to do the job in YAW (yet
another way.) I don't actually proffer it as a solution that you might
use, no way, don't bother...BUT

Interesting question:

What is the semantics of:

		sizeof(*ip++)

?? Try it before you guess, I was shocked (late entry for the
obfuscated C contest??) The semantics I found are essential for
my solution.

P.S. It compiled and ran correctly on (code follows):

	Hardware	OS		Software	Notes
	--------	---		--------	---------------
	SUN3/180	UNIX4.2 R3.0	PCC
	DEC2060		TOPS-20 5.4	MIT/PCC		provided bcopy()
	ATT/3B5		UNIX SYSVR2	C		used memcpy()
	IBM3090/200	VPS/VM		PCC		provided bcopy()
	Encore/MultiMax	UNIX 4.2	PCC
	VAX/750		UNIX 4.2	PCC
	VAX/780		VMS4.3		VMS/C		provided bcopy()
	Celerity/1200	UNIX 4.2	PCC
	DG MV/10000	AOS/VS 6.03	MVUX 2.01/C	used memcpy()

I also tried it on the SUN, DEC2060, IBM3090, MV10000 and VAX750 with
double's instead of ints and it compiled and worked fine, I don't have
the energy right now to go through all the others, requests accepted.

It also passes lint.

YOW, am I portable yet?

	-Barry Shein, Boston University
____________
#include <stdio.h>

#define swap(x,y) \
     { \
       char t1[sizeof(x)], t2[sizeof(t1)]; \
       char *p, *q; \
 \
       p = (char *) &(x); \
       q = (char *) &(y); \
       bcopy(p,t1,sizeof(t1)); \
       bcopy(q,t2,sizeof(t1)); \
       bcopy(t2,p,sizeof(t1)); \
       bcopy(t1,q,sizeof(t1)); \
     }
	 
main(argc,argv) int argc; char **argv;
{
  int ai[2], *ip = ai;
  int bi[2], *ip2 = bi;

  ai[0] = 1;
  ai[1] = 2;
  bi[0] = 3;
  bi[1] = 4;
  swap(*ip++,*ip2++);
  printf("ai[0] = %d *ip = %d bi[0] = %d *ip2 = %d\n",
	 ai[0], *ip, bi[0], *ip2);
  exit(0);
}

Should print out something like:

ai[0] = 3 *ip = 2 bi[0] = 1 *ip2 = 4

If I said I used memcpy() I added:

#define bcopy(x,y,z) memcpy(y,x,z)

If I said I provided bcopy() I added:

bcopy(from,to,n) char *from, *to; int n;
{
	while(n--) *to++ = *from++;
}

sbanner1@uvicctr.UUCP (sbanner1) (06/25/86)

In article <1577@brl-smoke.ARPA> Schauble@MIT-MULTICS.ARPA (Paul Schauble) writes:
>I've gotten lots of respones to my request for a form of the macro that
>would make
>  swap(*a++, *b++);
> work.  Almost all of them missed the point.  The swap requires a
>temporary.  Generating that temporary requires knowing the type.
>Several of the solutions would work fine if *a and *b were int's, but I
>never said that.
>
>Recall the my original message was posted as a justification for a
>typeof macro, akin to sizeof.  I find that the need to generate
>temporaries in a macro is not uncommon.  Unless one is willing to do a
>different version of the macro for each type, you need a way to generate
>a new variable of the same type as one of the macro args.  The language
>provides no way to do this.

I don't think you were reading all of the replys too closely.  While
I can see the use of the 'typeof' operator [not macro], there is a 
relatively easy way to do this, as atleast one person mentioned :

    #define swap(type, a, b)  {type temp ... }

Granted this can get a bit ugly if you will need three or four
types, or if they are complex, but the facility *IS* there.

Disclamer : The veiws expressed here may not be shared my anyone else
on the face of the earth.

                               S. John Banner

!uw-beaver!uvicctr!sbanner1   :UUCP
ccsjb@uvvm   :Bitnet

shap@bunker.UUCP (Joseph D. Shapiro) (06/26/86)

In article <1577@brl-smoke.ARPA> Schauble@MIT-MULTICS.ARPA (Paul Schauble) writes:
>I've gotten lots of respones to my request for a form of the macro that
>would make
>  swap(*a++, *b++);
> work.  Almost all of them missed the point.  The swap requires a
>temporary.  Generating that temporary requires knowing the type.
>Several of the solutions would work fine if *a and *b were int's, but I
>never said that.
>

ok, then, how about

#define swap(x,y) \
	{ \
		char * px = (char *)&(x); \
		char * py = (char *)&(y); \
		char t; \
		int i; \
		for (i=sizeof(x); i; i--) \
		{ \
			t = *px; \
			*px++ = *py; \
			*py++ = t; \
		} \
	}

Note that the sizeof(x) is a compiler constant and will not generate any
code which might induce a side effect.

Also, no flames about using char pointers, please, such use is
guarenteed both in K&R and ANSI.

Let me also say that I am neutral on the typeof() debate, I did
this just for the challenge.
			
For the doubtful, here is a working program.  I have chosen x and y
to be structs to remove any (most anyway) doubt.  Program passes lint.

------ cut here ----

#define swap(x,y) \
	{ \
		char * px = (char *)&(x); \
		char * py = (char *)&(y); \
		char t; \
		int i; \
		for (i=sizeof(x); i; i--) \
		{ \
			t = *px; \
			*px++ = *py; \
			*py++ = t; \
		} \
	}

#define peek(exp) printf("%s(%d): %s == %d\n",__FILE__,__LINE__,"exp",exp)

struct sss
{
	int i;
	long l;
	char c;
} a,b, *pa, *pb;

main()
{
	a.i=1;
	a.l=2;
	a.c=3;

	b.i=11;
	b.l=22;
	b.c=33;

	peek(a.i);
	peek(a.l);
	peek(a.c);
	peek(b.i);
	peek(b.l);
	peek(b.c);

	pa= &a;
	pb= &b;

	peek(pa);
	peek(pb);

	swap( *pa++, *pb++ );

	peek(pa);
	peek(pb);

	peek(a.i);
	peek(a.l);
	peek(a.c);
	peek(b.i);
	peek(b.l);
	peek(b.c);
}
-- 
-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_

Joseph D. Shapiro			"He who hesitates
Bunker Ramo Information Systems		 is lunch"

...ittatc!bunker!shap
...decvax!bunker!shap

throopw@dg_rtp.UUCP (Wayne Throop) (06/27/86)

> Schauble@MIT-MULTICS.ARPA (Paul Schauble)
> I've gotten lots of respones to my request for a form of the macro that
> would make
>   swap(*a++, *b++);
>  work.  Almost all of them missed the point.  The swap requires a
> temporary.  Generating that temporary requires knowing the type.
> Several of the solutions would work fine if *a and *b were int's, but I
> never said that.

Yes, you did say that.  Your original posting gave the code fragment as:

>     int *a, *b;
>     swap (*a++,*b++);

This leads to the reasonable assumption that you were talking about the
case where a and b are integers.  Especially when the arguments you
mention have side effects, a notoriously difficult problem.

I think it completely justified that readers would assume you were
asking if there was any way to write a swap macro that works in the
presense of side effects.  In fact, I don't really see any other
plausible interpretation of the original posting.

> Recall the my original message was posted as a justification for a
> typeof macro, akin to sizeof.  I find that the need to generate
> temporaries in a macro is not uncommon.  Unless one is willing to do a
> different version of the macro for each type, you need a way to generate
> a new variable of the same type as one of the macro args.  The language
> provides no way to do this.

True, it does not.  But your original article did not mention "sizeof",
nor "typeof", nor propose any extensions to C.  Nor did it reference any
articles that did these things.  In any event, C *would* allow a macro
such as

        swap(a,b,int)
        swap(c,d,struct s *)

like so:

        #define swap(a,b,type) \
        {   type t;\
            t=a; a=b; b=t;\
        }

The trick to supress side effects can also be done, if needed.

        #define swap(a,b,type) \
        {   typedef type tdtype;\
            tdtype t, *pa = &(a), *pb = &(b);\
            t = *pa; *pa = *pb; *pb = t;\
        }
or
        #define swap(a,b,type) \
        {   type t; type *pa = &(a); type *pb = &(b);\
            t = *pa; *pa = *pb; *pb = t;\
        }

Note that these swap macros can only handle types that have no postfix
operators (that is, no [] or ()).  A typedef name must be supplied for
such cases.  Note also that the usual shortcomings of such macros apply.

Lastly, the stated problem can be solved fairly portably *without*
typeof.  That is, a fairly portable swap macro which works even for
arguments of unknown types, and works even in the presense of
side-effects is possible.  In vanilla C yet.

        #define swap(a,b) \
        {   char t[sizeof(a)], *pa = (char *)&(a), *pb = (char *)&(b);\
            memcpy(t,pa,sizeof(a)); memcpy(pa,pb,sizeof(a));\
            memcpy(pb,t,sizeof(a));\
        }

Limitations:
  - The usual problems with this macro being a bracketed construct at
        top level.
  - The usual name-hiding problems.
  - a and b must be the same type, and can't be register or bitfield.
  - It had better be legal on your machine to cast a pointer to any type
        you intend to swap to a pointer to character, and this cast must
        yield a manipulable character pointer.
  - Moving data of any type with character operations must yield valid
        data.
  - memcpy had better be builtin (or a macro) or this will run slower
        than ... well, pretty slow anyhow.

You wouldn't catch *me* using this macro, but it *does* solve the
stated problem, fairly portably, without resort to typeof.

--
"Typeof?  We don't need no steenkeen typeof!"
-- 
Wayne Throop      <the-known-world>!mcnc!rti-sel!dg_rtp!throopw

markb@sdcrdcf.UUCP (Mark Biggar) (06/27/86)

IT still doesn't work for REGISTER variables!

Mark Biggar
{allegra,burdvax,cbosgd,hplabs,ihnp4,akgua,sdcsvax}!sdcrdcf!markb

bzs@bu-cs.UUCP (Barry Shein) (06/28/86)

And then again, there's always this sort of thing (look in 4.2's
/usr/include/sys/mbuf.h for a strong precedent):

#define swap(x,y,t) { \
			t *p1 = &(x), *p2 = &(y), tmp; \
 \
			tmp = *p1; \
			*p1 = *p2; \
			*p2 = tmp; \
		}

which is called with something like:

	swap(x,y,double);

really not a bad thing (or is it too much bother for the programmer to
know what type s/he is working with?)  Still doesn't work with
registers of course (but I don't think typeof() would solve that
problem either.) Also, pointer types would have to be typedef'd to be
useful as swap(x,y,char *) would not quite work (but w/ a typedef
it works fine.)

	-Barry Shein, Boston University

pete@valid.UUCP (Pete Zakel) (07/03/86)

> Unless one is willing to do a
> different version of the macro for each type, you need a way to generate
> a new variable of the same type as one of the macro args.  The language
> provides no way to do this.

What's wrong with the "swap(type, a, b)" invocation? You only need one
macro declaration.  Of course, you must declare the type each time you use
it.
-- 
-Pete Zakel (..!{hplabs,amd,pyramid,ihnp4}!pesnta!valid!pete)

pete@valid.UUCP (Pete Zakel) (07/04/86)

> IT still doesn't work for REGISTER variables!
> 
> Mark Biggar

Doesn't that depend on the C compiler?  I've heard of compilers that ignore
the "register" declaration if the address of the variable is taken.  Of
course I would HOPE that you would at least get a warning.
-- 
-Pete Zakel (..!{hplabs,amd,pyramid,ihnp4}!pesnta!valid!pete)

mikeb@copper.UUCP (Mike Beckerman) (07/07/86)

I'm a little behind on my news for net.lang.c, but of the articles about the
"SWAP" macro which I've read several have some form of copy involved.  By copy
I mean the use of "memcopy", or a cast to character and copy, etc.  These may
work in many cases, but they could be quite harmful when used on objects
which have the "volatile" storage class.

For example, if the objects were ints, and the swap is implemented as swapping
4 characters for each int, then each of the two ints would be accessed in
some part 8 times (4 to read old 4 bytes, 4 to write new 4 bytes).

Any comments?


	Mike

LINNDR%VUENGVAX.BITNET@WISCVM.ARPA (07/11/86)

In article <2498@mit-eddie.MIT.EDU> ambar@mit-eddie.ARPA (Jean Marie Diaz)
writes:
>In article <1321@psivax.UUCP> friesen@psivax.UUCP (Stanley Friesen) writes:
>>In article <1946@brl-smoke.ARPA> gwyn@BRL.ARPA (VLD/VMB) writes:
>>>
>>>  I haven't seen call-by-name included in any language since Algol.
>>
>>    What about LISP? Or is LISP older than Algol?
>
>LISP and Pascal are both descendants of Algol 60.
>--

This is a very curious assertion to make considering that LISP was also
described in 1960. I would be interested in hearing why you think LISP is
an ALGOL derivative. (we may be straying a bit from the topic of this newslist)

David Linn
----------------------------------------------------------
ARPANET(?):     LINNDR%VUENGVAX.BITNET@WISCVM.WISC.EDU
BITNET:         LINNDR@VUENGVAX.BITNET
CSNET:          drl@vanderbilt.csnet
Ma Bell:        (615)322-7924
USPS:           P.O. 3241-B Vanderbilt
                Nashville, TN   37235
UUCP:           ...psuvax1!vuengvax.bitnet!linndr

mouse@mcgill-vision.UUCP (07/12/86)

In article <857@bu-cs.UUCP>, bzs@bu-cs.UUCP (Barry Shein) writes:
> And then again, there's always [...]
> 
> #define swap(x,y,t) { \
> 			t *p1 = &(x), *p2 = &(y), tmp; \
>  \
[...swap code...]
> Also, pointer types would have to be typedef'd to be useful as
> swap(x,y,char *) would not quite work (but w/ a typedef it works
> fine.)

     Because you wrote the declaration carelessly.  Should  work fine if
you write it as
	{ \
	 t *p1 = &(x);
	 t *p2 = &(y);
	 t tmp;
(assuming there are no side-effects in the type argument :-).   See, now
*I* always write declarations one declaration per variable, even writing
	int i;
	int j;
instead of
	int i,j;
This sort of thing is where the habit pays off (preen preen :-).

Now  you *still*  need  a  typedef  when  the  type involves  arrays  or
functions (such as a pointer to function, a  reasonable thing to want to
exchange).  I  suspect  there  is no  way  around  this, anyone  care to
produce a counterexample?
-- 
					der Mouse

USA: {ihnp4,decvax,akgua,utzoo,etc}!utcsri!mcgill-vision!mouse
     philabs!micomvax!musocs!mcgill-vision!mouse
Europe: mcvax!decvax!utcsri!mcgill-vision!mouse
        mcvax!seismo!cmcl2!philabs!micomvax!musocs!mcgill-vision!mouse
ARPAnet: utcsri!mcgill-vision!mouse@uw-beaver.arpa

"Come with me a few minutes, mortal, and we shall talk."