Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP
Posting-Version: version B 2.10.2 9/18/84; site brl-tgr.ARPA
Path: utzoo!watmath!clyde!burl!ulysses!allegra!mit-eddie!godot!harvard!seismo!brl-tgr!tgr!cottrell@nbs-vms.ARPA
From: cottrell@nbs-vms.ARPA
Newsgroups: net.lang.c
Subject: type punning
Message-ID: <8125@brl-tgr.ARPA>
Date: Thu, 7-Feb-85 22:54:00 EST
Article-I.D.: brl-tgr.8125
Posted: Thu Feb  7 22:54:00 1985
Date-Received: Sun, 10-Feb-85 04:10:23 EST
Sender: news@brl-tgr.ARPA
Organization: Ballistic Research Lab
Lines: 87

/*
As promised, a good reason for type punning. We start off with the
definition of basic doubly linked circular lists:
	
	typedef struct link {		/* list entry */
		struct link *fwd;	/* foreward pointer */
		struct link *bwd;	/* backward pointer */
	} LINK, *LINKP;

	typedef struct head {		/* list head */
		LINK	link;		/* obvious */
		int	cnt;		/* how many in list */
		char	id[3];		/* general use */
		char	lock;		/* for test & set */
	} HEAD, *HEADP;

Now we define other objex such as buffers & channels:

	typedef struct buf {
		LINK	link;		/* to buffers */
		....			/* more declarations */
		char	data[SIZE];	/* data goes here */
	} BUF, *BUFP;

	typedef struct chan {		/* com channel */
		LINK	link;		/* to other channels */
		....			/* other stuff */
		HEAD	rcvq;		/* to buffers */
		HEAD	xmtq;		/* to buffers */
		....			/* other stuff */
		LINK	misc;		/* to other channels */
		....			/* chained for some reason */
	} CHAN, *CHANP;

Notice that the first two items of any struxure are forward & backward links!
Now we create two funxions to manipulate links only. They are modeled after
the vax instruxions INSQUE & REMQUE:

LINKP remque(LINKP p);		/* remove p from list */
{	if (p)	(p->fwd->bwd = p->bwd)->fwd = p->fwd;
	return(p);
}

LINKP insque(LINKP p,LINKP q);	/* insert p after q */
{	if (p)	(((p->fwd = q->fwd)->bwd = p)->bwd = q)->fwd = p;
	return(p);
}

At the next level we have four funxions that insert & remove `objex'
to/from the head/tail of the list:

LINKP Get_Head(HEADP h);		/* get from head of list h */
{	if (!h || !h->cnt) return(0);	/* null protect */
	--h->cnt;			/* one less */
	return(remque(h->link.fwd);	/* remove the head */
}

LINKP Get_Tail(HEADP h);		/* get from tail of list h */
{	if (!h || !h->cnt) return(0);	/* null protect */
	--h->cnt;			/* one less */
	return(remque(h->link.bwd);	/* remove the tail */
}

LINKP Put_Head(HEADP h,LINKP p);	/* put p to head of list h */
{	if (!h) return(0);		/* null protect */
	++h->cnt;			/* one more */
	return(insque(h->link.fwd,p);	/* put to head */
}

LINKP Put_Tail(HEADP h,LINKP p);	/* put p to tail of list h */
{	if (!h) return(0);		/* null protect */
	++h->cnt;			/* one more */
	return(insque(h,p);		/* put to tail */
}

The Put funxions return a value so one can move an entire list by:
	while (Put_Tail(free,Get_head(list));
Note the lack of cast on h in Put_Tail. And for those of you who
like out of bounds array refs, note that head->id[3] is available
to use if the particular list is never locked via test and set.
Now in the modules where we deal primarily with buffers, Get_Head is
declared as BUFP Get_Head; where we deal with channels, CHANP Get_Head.
And where widgets are used Get_Head is of type WIDGETP. And so on.
Lint will go bonkers over this! I for one can do without all those
extra casts cluttering up my code. Pretty soon programmers, like actors
will be saying: "Break a leg!"
*/