[net.lang.c] limitations of casts, pointer and fu

jim@ISM780B.UUCP (11/03/84)

>{
>        int x;
>        char *y;
>/*### [cc] illegal lhs of assignment operator = %%%*/
>        (char *)x = y;
>}
>
>I don't think that this error message is sensible, since statments like
>
>*((long *) 100) = 100;
>
>work.

(char *)x is not an lvalue.  Use

	*(char **)&x = y;

if you must at all (better yet, learn to write type-strict code).

>I would like to declare a pointer to a thing of its own kind, i.e.
>something of the form:
>
>typedef ref *ref;
>
>The point is that if a pointer of this type is dereferenced, it should
>have the same type again.  In addition, the type should be cast'able
>to/from integer, and the size associated with it should be that of a
>single pointer of its kind.  I.e. expressions of the following type
>should be possible:
>
>{
>        ref a,b;
>        long c;
>        *a = b;
>        a = *b;
>        c = (int)a;
>        a = (ref)c;
>        a++;
>}
>
>My first attempt was:
>
>typedef long base;      /* change this to int type with size of pointer */
>typedef base *ref;
>#define deref(thing) ((ref)(*thing))
>
>This works fine if one does all dereferencing through 'deref',
>except that assignments still don't work quite right, since
>'deref(thing)=thing;' still does not work, and the rhs has to
>be cast instead (i.e. '*thing = (base)thing').

Forget it.  The best you can do is

typedef long ref;
#define mkref(x) (*(ref *)&(x))

{
	ref a,b;
	long c;
	mkref(*a) = b;
	a = mkref(*b);
	c = (int)a;
	a = mkref(c);
	a++;
}

which doesn't work if c is a register variable, except on some compilers
which allow *&x even if x isn't addressable.

>Along the same lines, I'd like to be able to define a function
>returning a pointer to its own kind, i.e.
>
>typedef fun (*fun)();
>
>which is useful to implement continuations without having to resort
>to machine language hacks.

No need to use machine language.  Try

#include <stdio.h>

typedef char *  funval;
typedef funval  (*funp)();

int     bluemoon = 7;

funval
fun(x)
{
	return x == bluemoon? NULL : (funval)fun;
}

funp    funv = fun;

main()
{
	register funp   funx;
	register        i;

	for( i = 0, funx = funv;; i++ )
	{   funx = (funp)(*funx)(i);
	    if( !funx ) break;
	}
	printf("%d\n", i);
}

-- Jim Balter, INTERACTIVE Systems (ima!jim)

jim@ISM780B.UUCP (11/03/84)

>K&R 'defines' an lvalue as an 'expression referring to an object'.
>and an object as a 'manipulable region of storage'. '(char *)x' is a
>perfectly good lvalue: it refers to the object 'x' considered as
>a pointer to a character.

Quite wrong.  Do not be misled by the fact that some casts on some machines
generate no code.  A cast is a *conversion*, not a type pun.
"(char *)x" refers to the value of x converted to the type (char *).
It does not refer to the object x.

>>> typedef ref *ref;
>>
>>All types must reduce to a basic type (void, char, short, int, long,
>>float, double, struct/union) plus some number of operators (*, (), []).
>
>I think the semantics of this typedef are clear (and can be defined
>easily). Also, the above typedef is in no way different from the
>structure declaration 'struct foo {foo *ref;};'. FTSO consistency and
>considering its usefulness, I think the above typedef should be
>permitted. (BTW, a struct/union is not a basic type either).

If you want to fix C's notion of types, you wouldn't want to stop there.
The fact of the matter is that typedefs do not introduce new types,
only synonyms for basic or derived types (struct/unions are derived types).
There is no basic or derived type for which ref could possibly be a synonym.
Please read the manual carefully before discussing this further.

>>> The point is that if a pointer of this type is dereferenced, it should
>>> have the same type again.  In addition, the type should be cast'able
>>> to/from integer, and the size associated with it should be that of a
>>> single pointer of its kind.
>>
>>At present, the generic pointer type is (char *) (ANSI will probably
>>change this to (void *)).  A (char *) is castable to a (long) and back
>>without loss of information (note: NOT always to an (int)).  By using
>>appropriate type casts you can use the contents of a (char *) or (long)
>>for other purposes.
>
>Not quite: let p be defined as 'char *p;'. '((long *)p)++' does not
>work (see above). As I said, I don't want a 'generic' or 'untyped'
>pointer, but a pointer to something which has the same length and type
>as the pointer itself, so that I can use dereferencing and additive
>assignment operators on it freely. (BTW, I don't think that an untyped
>pointer is very useful: you might as well use a pointer to a character
>if you don't care about the size associated with some pointer).

Sorry, the language does not provide what you want.  As Doug pointed out,
though, you can get the effects you need with enough casting.

>No, recursive types are legal an possible in 'C'. Take the above
>example 'struct foo {struct foo *ref;};'. Unfortunately, this does not
>help to implement recursive pointers easily, since this structure
>cannot be cast directly to/from an integer type, and since it is even
>more inconvenient to include the suffix '.ref' everywhere than it is to
>cast explicitely everywhere.

struct foo is not a recursive type.  At best you might call it an
iterative type.  What would you think of struct foo {struct foo bar;}; ?

>Also, Pascal, which is doubtlessly a strongly typed language, does
>permit a type definition like 'type ref= ^ref;' and handles it
>correctly.  Strong typing is not contradicted by allowing a recursive
>pointer definition.  (BTW, I don't think that one can be dogmatic about
>a typing system as messy as that of 'C').

Pascal was designed by a formalist, not a hacker.
On the other hand, despite its formalistic shortcomings,
C is an undeniably pragmatic language.
Modula 2 is a Pascal-derived language which incorporates important C-like
systems programming features.  How about a C-derived language incorporating
modern formal notions?  Or is that C++?

>Again, my question is: given the 4.2 cpp and ccom, what is the most
>convenient way (i.e. requiring the least number of typecasts) of
>dealing with the case the 99% of all pointers give another pointer of
>their own kind upon dereferencing?

I provided some techniques in my previous posting.

-- Jim Balter, INTERACTIVE Systems (ima!jim)