Chris said:
[on opaque types, and "typedef" vs "struct"]
In any case that's not really the issue. The question is
not whether client code _can_ know something, but whether it
_must_ know something. An opaque type is one where
knowledge of the type's representation should be confined to
the module that supplies/implements it.
Indeed -- but the problem with "typedef" is that, in C, you must
know whether the typedef-name is a synonym for an array type,
because array types do not behave the same as any other type.
Hence, typedef is not at all useful for opaque types in C, unless
you also add a (programmer-imposed) constraint that no typedef ever
names an array type.
If you simply make every opaque type a structure -- no pointer is
required -- you get the desired effect:
struct user_type_0;
struct user_type_1;
struct user_type_2;
All three types are unique and well-behaved, and the client code
need not know anything at all. If we "peek behind the curtain",
we might find, e.g.:
struct user_type_0 { double val; };
struct user_type_1 { double val; };
struct user_type_2 { struct u2_implementation *ptr; };
but all the user knows about them is that each one behaves like
other primitive types in C (of course, one can never apply arithmetic
operators directly, for instance).
Your comments don't make sense to me. If I'm defining
an opaque type that needs an array representation, the
type can be defined as an array wrapped in a struct:
typedef struct { whatever_t v[SOME_N]; } opaque;
If I'm defining an opaque type that shouldn't be assigned
or have its value passed directly, but always by address,
the type can be defined wrapping the representation type
in an array:
typedef whatever_t opaque[1];
void assign_opaque( opaque *, opaque * );
...
opaque o1, o2;
assign_opaque( &o1, &o2 ); /* can't do o1 = o2; */
If I'm defining an opaque type that needs to have
some scalar operations exposed (eg, == 0), the type
can be defined in a usual way:
typedef struct some_struct_t *opaque;
...
opaque o1;
if(!o1) ...
If I'm defining an opaque type that's a scalar type
but should _not_ have scalar operations exposed, the
type can be wrapped in a struct:
typedef struct { scalar_t v; } opaque;
And finally, if I'm defining an opaque type that's
a scalar type that shouldn't be assigned and also
shouldn't be accidentally turned into a pointer to
its scalar representation, the type can be wrapped
in struct wrapped in an array:
typedef struct { scalar_t v; } opaque[1];
In all of these cases the client code uses just the
name that was typedef'ed. Using typedef is more
flexible than always using struct, not less flexible.