If you really can't stand typing the struct keyword, typedef struct foo foo; (note: get rid of the useless and problematic underscore) is acceptable. But whatever you do, never use typedef to define names for pointer types. It hides the extremely important piece of information that variables of this type reference an object which could be modified whenever you pass them to functions, and it makes dealing with differently-qualified (for instance, const-qualified) versions of the pointer a major pain.

'Never' is rather strong here: the whole point of opaque types is to hide the implementation from users of your api, making changes to the former independant of the latter, and providing a measure of safety by restricting direct modifications by the user; I see nothing wrong with aliasing pointer types or hiding qualifiers in such cases (ie if they are implementation details)
–
ChristophOct 19 '10 at 8:08

2

@Christoph: One problem with encapsulating e.g. const into the typedef is if you have a situation where you want to express that some API functions modify the object, and others don't. You could have typedef struct foo *foo; typedef struct const foo *const_foo;, but that's grim!
–
Oliver CharlesworthOct 19 '10 at 10:42

4

Whether a type is a pointer or not is not an implementation detail. It's fundamental to the semantics of any operation in which you might use the type. This is one 'never' I stand by completely.
–
R..Oct 19 '10 at 17:11

2

A type with a builtin const qualifier is not valid for immutable strings (or any allocated object) because your implementation of the object cannot free a const-qualified pointer (free takes a non-const-qualified void *, for good reason). This is not a technicality but a matter of violating the semantics of const. Sure you can cast the const away in your immutable_string_free function, but now we're getting into the territory of dirty hacks. Any opaque object allocation function should always return footype *, and the function to free should take footype *.
–
R..Oct 20 '10 at 14:51

bar(const fooRef) declares an immutable address as argument. bar(const foo *) declares an address of an immutable foo as argument.

For this reason, I tend to prefer option 2. I.e., the presented interface type is one where cv-ness can be specified at each level of indirection. Of course one can sidestep the option 1 library writer and just use foo, opening yourself to all sorts of horror when the library writer changes the implementation. (I.e., the option 1 library writer only perceives that fooRef is part of the invariant interface and that foo can come, go, be altered, whatever. The option 2 library writer perceives that foo is part of the invariant interface.)

Regarding your last sentence, these constructions do not admit opaque types. If you use them, you're exposing the definition of the structure in your header for the calling application to abuse.
–
R..Oct 19 '10 at 4:36

In neither option is the layout of foo part of the interface. That's the whole point of doing things this way.
–
Ben VoigtDec 5 '10 at 1:09