[comp.lang.misc] Escape from strong typing

karl@haddock.ima.isc.com (Karl Heuer) (09/01/89)

In article <10897@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn) writes:
>In article <1545@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes:
>>... so that (use int)x means treat x as type int no matter what it was.
>>This is another example of keeping the tools from the programmer.
>
>No, this is another example of not knowing how to use the tools that are
>already provided.

I agree with Herman, for a change.  An lvalue take-as operator is one of the
few "universal escapes" that is not provided by C, and is not easy to fake for
the general case.  (I believe it's Modula-2 that has this feature as a builtin
operator named ESCAPE.)

>If it wasn't represented with the same number of bits as an int, what
>could this possibly mean?  If you're going to invent something, make sure
>that it is sufficiently useful first.

Those are two separate issues.  One could probably formalize the semantics via
an equivalent statement about unions.  If x has more bits than an int, it's
well-defined (though perhaps not very useful).  If it has fewer, then the
result is "undefined behavior", just as it is for unions or gross pointer
kludgery.

As for usefulness, I'll supply an anecdote.  I once wanted to write
	unsigned int copybit(unsigned int x) {
		if (x & 0x10) x |= 0x40; else x &= ~0x40;
		return (x);
	}
in such a way that the highly-optimizing compiler would generate a simple
move-bit instruction appropriate to the architecture.  An appropriate escape
operator would have allowed me to write something like
	typedef struct { unsigned int :4, src:1, :1, dst:1; } hack;
	unsigned int copybit(unsigned int x) {
		escape(x, hack).dst = escape(x, hack).src;
	}
which also exhibits the copy better than the original.  Btw, it wouldn't have
worked to write "*(hack *)&x" since the implementation passed arguments in
registers; taking the address forced a store to memory.  Nor could I use an
intentional misdeclaration of the argument ("hack copybit(hack x)"), since the
compiler didn't believe that struct types could be placed in a register.

(Yes, I'm aware of all the portability problems in the above.  For this
application it didn't matter, as long as it worked.)

But Doug actually said "sufficiently useful", and here I agree with him: it's
not worth changing the existing language.  "Fix it in `D'", as they say.

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint
Followups to comp.lang.misc.

pardo@cs.washington.edu (David Keppel) (09/02/89)

karl@haddock.ima.isc.com (Karl Heuer) writes:
>[`copybit' should generate a simple move-bit instruction]
>[I wanted to write]
>	unsigned int copybit(unsigned int x) {
>		if (x & 0x10) x |= 0x40; else x &= ~0x40;
>		return (x);
>	}
>[Suggesting:]
>	typedef struct { unsigned int :4, src:1, :1, dst:1; } hack;
>	unsigned int copybit(unsigned int x) {
>		escape(x, hack).dst = escape(x, hack).src;
>	}
>[Yes, I'm aware of the portability problems.]

In cases such as this, GNU CC provides another solution: typed
asm's that take variables as arguments.  If you care, you can
even put this function in to your .h file, qualify it with
`inline', and away you go!

#ifdef __GNUC__
	unsigned int
    copybit (unsigned int x)
    {
    #ifdef __vax__
	asm ("mumble %0,%1" : "=r" (x) : "0" (x));
    #elseif defined (__machine__)
	...
    #else
	{ NEED A MACHINE TYPE! }	/* Compile-time error. */
    #endif
	return (x);
    }
#else
	unsigned int
    copybit (..) {...}
#endif

		;-D on  ( A void* warranty )  Pardo
-- 
		    pardo@cs.washington.edu
    {rutgers,cornell,ucsd,ubc-cs,tektronix}!uw-beaver!june!pardo

billwolf%hazel.cs.clemson.edu@hubcap.clemson.edu (William Thomas Wolfe,2847,) (09/02/89)

From karl@haddock.ima.isc.com (Karl Heuer):
> An lvalue take-as operator is one of the few "universal escapes" that is 
> not provided by C, and is not easy to fake for the general case.  (I 
> believe it's Modula-2 that has this feature as a builtin operator 
> named ESCAPE.)

    Actually, according to my Modula-2 manual (which is the "second,
    corrected edition"; I think there's now a "third, corrected edition"),
    Modula-2 provides type transfer functions which do not involve the
    generation of any machine instructions, and which carry the name of
    the type being converted to.  Any representation differences between
    the two types would presumably have to be addressed via an encapsulating
    procedure which performed the necessary transformations.

    Ada provides Unchecked_Conversion, which essentially does the same
    thing except for the fact that any use of Unchecked_Conversion will
    immediately flag your program as potentially non-portable; standard
    type conversion functions exist carrying the name of the type which
    are guaranteed to be portable, but they only cover the predefined types.

    Modula-2 "second, corrected edition", doesn't seem to be overly concerned
    with identifying potential portability problems, although this may have
    been addressed in the "third, corrected edition".  


    Bill Wolfe, wtwolfe@hubcap.clemson.edu

gwyn@smoke.BRL.MIL (Doug Gwyn) (09/03/89)

In article <14500@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer) writes:
>Btw, it wouldn't have worked to write "*(hack *)&x" since the implementation
>passed arguments in registers; taking the address forced a store to memory.

That's in fact what I had in mind when I implied that C has existing tools
that can do the job.  The particular implementation didn't need to allocate
actual memory for the operation; presumably it did so because its optimizer
wasn't sufficiently clever.  (I assume that this would have been coded as a
macro, not as a function call.)