Reputation: 10144
In some vendor code I see structs used as objects with pointers to them as handles in a header file as follows
hdr.h
typedef struct _FOO_Obj_ {
uint16_t x;
} FOO_Obj;
typedef struct FOO_Obj * FOO_Handle;
src.c
#include <hdr.h>
void main (void) {
FOO_Handle bar = FOO_init(); // No error
bar->x = 5; // Error: pointer to incomplete class type
}
This allows the file that includes such header files to use the type FOO_Handle
but somehow prevents access to the members of the FOO_Obj
, e.g. at the marked line in src.c... what is happening here??
For comparison, I would previously have used the following:
struct FOO_Obj {
uint16_t x;
};
typedef struct FOO_Obj * FOO_Handle;
With this method, I could also do:
FOO_Obj y;
FOO_Handle bar (void) { // unintentional bicycle pun ftw
return &y;
}
But with the first method, this same function causes an error that the return type doesn't match???
Upvotes: 1
Views: 83
Reputation:
Generally, any time you mention a struct type that doesn't exist yet, you create it. If you specify the contents of the struct at the same time, it's created as a complete type, otherwise it's incomplete. Complete struct types allow the use of the .
and ->
operators. Incomplete struct types don't.
A struct type can be created with a declaration that does nothing else:
struct foo; /* Creates an incomplete type called "struct foo") */
struct bar { int x,y; }; /* Creates a complete type called "struct bar" */
Or it can be created with a declaration that also declares some variables.
struct foo *p; /* Creates an incomplete type called "struct foo"
and a variable of type "struct foo *" called "p" */
struct bar { int x,y; } v, *p; /* Creates a complete type called "struct foo"
and a variable of type "struct foo" called "v"
and a variable of type "struct foo *" called "p" */
Note that you can't just say struct foo v;
unless struct foo
already exists as a complete type. Incomplete types have unknown sizes, so the compiler wouldn't know how much space to allocate for p
. But pointers to structs have a known size even if the struct itself doesn't, so struct foo *p;
works fine with an incomplete type.
Or you can create a struct at the same time you create a typedef:
typedef struct foo f, *fptr; /* Creates an incomplete type with 2 names:
"struct foo" and "f"; also makes "fptr" an alias
for the type "struct foo *" */
typedef struct bar { int x,y; } b, *bptr; /* Creates a complete type with 2 names:
"struct bar" and "b"; also makes "bptr"
an alias for the type "struct bar *" */
The version where you declare a struct type and a variable of that type at the same time even works inside the parameter list of a function declaration:
int dosomething(struct foo *p) { ... }
int dosomethingelse(struct foo *p) { ... }
If struct foo
doesn't already exist, the above code creates a type struct foo
for the first function, and another type struct foo
for the second function. Each one has a separate scope. They're not the same type. This is not something you'll ever do on purpose.
Upvotes: 1
Reputation: 78943
I still don't get it how they intend to use this, but here is a general rule that applies to such stuff if you want to go beyond the usual "opaque pointer" method which just uses a pointer to struct
without defining the struct
.
You may have different struct
definitions in different compilation units (here the user code and the library) that are passed around as parameters to functions between the units, but
struct
has a tagname in one unit it also must have one in the otherstruct
tag, this tag must be exactly the sameOtherwise (one struct
with tag and one without, or different tag names) the behavior is undefined. Think e.g of link time optimization to see why this might be important.
There is another rule that applies in connection with C++ that I recently learnt the hard way. There, the C++ name for name mangling is either the struct
tag if it exists, or the typename
if it doesn't. So if you have struct
without tag, you'd better have the typedef
names agree, too.
Another, minor, point in the code that you found is that names starting with underscore are reserved in file scope, they should not be used.
Upvotes: 1
Reputation: 122453
The type of the structure is struct _FOO_Obj_
, after the typedef
, it has an alias FOO_Obj
. However, there's no such type as struct FOO_Obj
.
So this other typedef
line
typedef struct FOO_Obj * FOO_Handle;
should be either
typedef FOO_Obj * FOO_Handle;
or
typedef struct _FOO_Obj_ * FOO_Handle;
Upvotes: 2