#ifndef HANDLE_H
#define HANDLE_H
struct List;
typedef struct List* HANDLE;
struct Person;
typedef struct Person Person_t;
HANDLE list_create(void);
void list_destroy(HANDLE);
void list_push_front(HANDLE, const Person_t*);
void list_pop_front(HANDLE);
#endif // HANDLE_H
Question: Are the forward declarations (List
and Person
) needed, and is it a good practice to forward declare them?
Forward declarations are only needed when the struct definition isn't already visible earlier inside the same translation unit or otherwise you wouldn't be able to use the struct. Forward declarations can be done with or without typedef
. Meaning that the struct Person;
etc parts are redundant here.
Regarding best practices:
It is often good practice to hide implementation details of a struct to a caller which should not access it directly. That is known as "opaque type" and is one way to achieve private encapsulation. See How to do private encapsulation in C?
It is widely considered as very bad practice to hide pointers behind typedef
. Even in the case of opaque types.
An example of one of the most infamous code bases out there is the Windows API, which uses something called HANDLE
very similar to what you have here. One of many problems with this is that the programmer doesn't realize that they are dealing with a pointer and therefore add one more level of needless indirection, leading to slower code which is harder to read. Another problem is that they might think they are performing a hard copy of the whole object when assigning one HANDLE to another. Just don't do this.