[comp.lang.c] Incomplete structure declarations as opaque data types

newman@athena.mit.edu (Ron Newman) (09/05/87)

The following program is legal, lint-free C, and demonstrates how one can
implement an abstract ("opaque") data type:

typedef struct _ThingRec ThingRec, *Thing;
extern Thing CreateThing();

main() {
   Thing thing = CreateThing (4, 7);
   OperateOnThing(thing);
   DestroyThing (thing);
   }

The idea here is that only the module(s) that implement Thing
need to have a complete declaration of struct _ThingRec.  Anyone
who just uses a Thing can use the incomplete declaration above,
and the details of a Thing are hidden from him.

Of course, in real life, the first two lines would appear in a header file
"Thing.h" shared by all users of Thing.  The complete declaration
would appear in "ThingPrivate.h", shared by all files that implement
the Thing data type.

This is a standard technique in "advanced" languages like Mesa and
Modula-2; I was surprised at first to find that C (perhaps
inadvertently) allows it as well.  I have found it to be a very
useful technique for hiding the "guts" of a data structure from
clients who have no need to look inside the structure.

However, this program raises some interesting questions about pointer
representation.   For this to work, all structure pointers must be the
same size, regardless of the structure contents.  Otherwise, the
compiler would not know how much storage to allocate for a Thing.

In addition, it appears that all structure pointers must have the same
REPRESENTATION too.  Otherwise, the caller and callee of CreateThing,
OperateOnThing, and DestroyThing would fail to agree on the
representation.  In addition, it's legal to write

   char *p = (char *) thing;

so the compiler must know how to cast a (struct foo *) to (char *)
without knowledge of what (struct foo) really is.

My question: since this program is legal, doesn't C implicitly require all
structure pointers to have identical size and representation?

/Ron Newman
 MIT Project Athena

note 1:  amazingly, even this is legal:

  extern ThingRec thingRec;
  Thing thing = &thingRec;

note 2:  the program is accepted, without warnings, even by the 
IBM RT "High C" compiler, which claims to be ANSI-conforming.

levy@ttrdc.UUCP (Daniel R. Levy) (09/10/87)

In article <1396@bloom-beacon.MIT.EDU>, newman@athena.mit.edu (Ron Newman) writes:
> The following program is legal, lint-free C, and demonstrates how one can
> implement an abstract ("opaque") data type:
> 
> typedef struct _ThingRec ThingRec, *Thing;
> extern Thing CreateThing();
> 
> main() {
>    Thing thing = CreateThing (4, 7);
>    OperateOnThing(thing);
>    DestroyThing (thing);
>    }

$ lint Thing.c

Thing.c
==============
(8)  warning: main() returns random value to invocation environment
(9)  warning: struct/union _ThingRec never defined
(9)  warning: struct/union ThingRec never defined


==============
name used but not defined
    CreateThing   	Thing.c(5)
    OperateOnThing   	Thing.c(6)
    DestroyThing   	Thing.c(7)
$ uname -a
ttrdc ttrdc 2.0v3 0915 3B-20S
-- 
|------------Dan Levy------------|  Path: ..!{akgua,homxb,ihnp4,ltuxa,mvuxa,
|         an Engihacker @        |		vax135}!ttrdc!ttrda!levy
| AT&T Computer Systems Division |  Disclaimer:  i am not a Yvel Nad
|--------Skokie, Illinois--------|