[comp.lang.c] How to get Ada private types in C

mnc@m10ux.UUCP (MHx7002 ) (03/09/87)

One of the important features of Ada that allows the safe implementation of
abstract data types is the "private" type feature.  You can declare a new
type in the specification of a module, but indicate that the details of the
type definition may not be seen by users of the module.  These users may
only declare variables of that type.  To perform any operations on the
variables, they must be passed as arguments to functions of the module
that "owns" the private type.  Obviously this is necessary to prevent users
of an abstract data type from writing code that depends on the particular
current implementation of the type, say as an array.  Such a dependency
would prevent changing the implementation later.

Wouldn't it be nice if the same feature could be obtained in a C header file
(which is the analog of an Ada specification)?  Well the following program
compiles without errors on Berkeley 4.3 and Sys V.2 (VAX versions):

	typedef struct PRIVATE_S *PRIVATE_P;
	main()
	{ PRIVATE_P x; if (x) exit();
	}

If this is legal C everywhere, it implies that one can obtain a private type
by declaring it to be a struct pointer in the header file (PRIVATE_P), but
only declaring the struct type (PRIVATE_S) in the module that owns the type.
Comments?
-- 
Michael Condict		{ihnp4|vax135|cuae2}!m10ux!mnc
AT&T Bell Labs		(201)582-5911    MH 3B-416
Murray Hill, NJ

bzs@bu-cs.UUCP (03/11/87)

Yes, your program certainly works, amazing what people will find,
very interesting.

One problem is, try:

	printf("%d\n",sizeof(x));

in the test program. I get an Unknown size error on the compilation.

This leads me to believe that although you could do something with
this it's probably just exploiting a missing error check in the
compiler caused by the need to "trust" forward references to struct
pointers or something like that. A bug I guess.

Ooops, that was on a SUN3. My Encore just barfs all over your original
example (I'm pretty sure they're using a green hills compiler, not a
pcc derivitive.) I think the ice is getting very thin.

	-Barry Shein, Boston University

congdon@ci-dandelion.UUCP (03/11/87)

In article <172@m10ux.UUCP> mnc@m10ux.UUCP (MHx7002 ) writes:
>Wouldn't it be nice if the same feature could be obtained in a C header file
>(which is the analog of an Ada specification)?  Well the following program
>compiles without errors on Berkeley 4.3 and Sys V.2 (VAX versions):
>
>	typedef struct PRIVATE_S *PRIVATE_P;
>	main()
>	{ PRIVATE_P x; if (x) exit();
>	}
>
>If this is legal C everywhere, it implies that one can obtain a private type
>by declaring it to be a struct pointer in the header file (PRIVATE_P), but
>only declaring the struct type (PRIVATE_S) in the module that owns the type.
>Comments?

I'm not sure if this is 'legal' C or not (any comments from the
language lawyers?) but lint (with -h switch) will warn about the
missing definition for 'struct PRIVATE_S'. The structure tag construct
is intended to allow definition of self-referential or
forward-referencing struct types (K&R pg. 197). For example:

	typedef struct Foo { 
	    struct Foo *next;
	    /* other stuff ... */
	} FOO;

But in such cases the struct type is eventually defined so the compiler
shouldn't complain. In your example, since the compiler never sees a
definition for the struct type it's reasonable for the compiler (or
lint) to warn about the missing struct type.

Pascal allows usage of a pointer types in type definitions before the
base type of the pointer type has been defined for self-referential
types as well.  In fact, I think that this is the only example of
'usage before definition' in Standard Pascal (where even labels have to
be pre-defined!).

-- 
Robert M. Congdon    UUCP: {talcott,vaxine,mit-eddie}!ci-dandelion!congdon
Cognition, Inc.      PHONE: (617) 667-4800
900 Tech Park Drive		
Billerica, MA 01821		

chris@mimsy.UUCP (03/12/87)

While I do not know the precise details of Ada private types, I
can say that in general, private types are in fact implemented
through the use of a `generic pointer' type.  That is, outside of
a specific module, the only handle anything has on an object of a
private type is a pointer to that object.  In 4BSD, the generic
pointer type is `caddr_t' (Core ADDRess Type); in ANSI draft C it
is `void *' (a name that I find utterly lacking in taste, though
it does avoid a new keyword).

For example, a private data structure that implments longer integers
might look like this:

	struct longer {
		long	low;
		long	high;
	};

but the routines that deal with one of these take and return only
`caddr_t' types:

	caddr_t
	new_longer()
	{
		struct longer *l;

		l = (struct longer *) malloc(sizeof (struct longer));
		if (l != NULL)
			l->low = l->high = 0;
		return ((caddr_t) l);
	}

	void
	incr(obj)
		caddr_t obj;
	{
		struct longer *l = (struct longer *) obj;

		if (++l->low == 0)	/* overflow */
			l->high+++;
	}

This is often employed in an `object oriented' manner by providing
a structure containing pointers to the object-specific routines,
as well as an instance of the object itself:

	struct representation {
		caddr_t	r_obj;		/* the object */
		void	(*r_incr)();	/* a function that counts it up */
		void	(*r_print)();	/* a function that prints it */
	};

In this case, only the routine that creates a specific instance
of an object need know the names of the functions that implement
that object.  Everywhere else in the code, only the operations
provided by the `representation' are available:

	add_1_and_print(r)
		register struct representation *r;
	{

		(*r->incr)(r->obj);
		(*r->print)(r->obj);
	}

Of course, doing this is considerably simpler if the compiler helps
out, which is why we have languages like C++ in the first place.
Otherwise we could all write the C code that the C++ compiler
produces.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690)
UUCP:	seismo!mimsy!chris	ARPA/CSNet:	chris@mimsy.umd.edu

guy@gorodish.UUCP (03/14/87)

>In 4BSD, the generic pointer type is `caddr_t' (Core ADDRess Type);

Well, "caddr_t" isn't specific to 4BSD; other versions of UNIX have it.
However, it's not really a "new" type; it's just a "typedef" name for
"char *".

>in ANSI draft C it is `void *' (a name that I find utterly lacking in taste,
>though it does avoid a new keyword).

An argument can be made that the name "void *" emphasises that these
pointers cannot be dereferenced, since dereferencing a "void *" would yield
a "void".  If this was, in fact, a reason why "void *" was chosed to be the
name of the "generic pointer" type, this should perhaps appear in the
Rationale.

faustus@ucbcad.berkeley.edu (Wayne A. Christopher) (03/17/87)

In article <15039@sun.uucp>, guy%gorodish@Sun.COM (Guy Harris) writes:
> Well, "caddr_t" isn't specific to 4BSD; other versions of UNIX have it.
> However, it's not really a "new" type; it's just a "typedef" name for
> "char *".

The problem with "caddr_t" is that it doesn't look like a pointer...  Although
that may be a good thing since it will make you not want to dereference it
without casting it...

	Wayne

bobd@dshovax.UUCP (Bob Dietrich) (03/18/87)

In article <513@ci-dandelion.UUCP> congdon@ci-dandelion.UUCP (Robert M. Congdon)
writes:
>
>Pascal allows usage of a pointer types in type definitions before the
>base type of the pointer type has been defined for self-referential
>types as well.  In fact, I think that this is the only example of
>'usage before definition' in Standard Pascal (where even labels have to
>be pre-defined!).
>
>-- 
>Robert M. Congdon    UUCP: {talcott,vaxine,mit-eddie}!ci-dandelion!congdon
>Cognition, Inc.      PHONE: (617) 667-4800
>900 Tech Park Drive		
>Billerica, MA 01821		

Pascal also allows another case (effectively): program parameters appear in
the program heading before their definition as variables. In addition,
Extended Pascal adds the identifiers that are exported as constituents of
a module interface.

				Bob Dietrich
				Intel Corporation, Hillsboro, Oregon
				(503) 681-2092
		usenet:		tektronix!reed!omssw2!dshovax!bobd