As I noted in a terse comment, I think you will need two headers (or, alternatively, I'd approach this using two headers). One header defines the public interface. The other defines the protected interface. The second should include the first to ensure consistency. Your code uses the protected interface. Everyone else’s code uses the public interface.
What I mean is that the public interface is an opaque type and the “protected” interface defines the private details. The functions in the interface will use pointers to the opaque types, so your public header might be:
linkedlist.h
#ifndef LINKEDLIST_H_INCLUDED
#define LINKEDLIST_H_INCLUDED
typedef struct listNode ListNode;
typedef struct linkedlist LinkedList;
typedef int Data;
extern LinkedList *ll_create(void);
extern ListNode *ln_create(Data value);
extern void ll_destroy(LinkedList *ll);
extern void ln_destroy(ListNode *ln);
…
#endif /* LINKEDLIST_H_INCLUDED */
You'd also have a second header which gives the protected access, call it linkedlistp.h
:
#ifndef LINKEDLISTP_H_INCLUDED
#define LINKEDLISTP_H_INCLUDED
#include "linkedlist.h"
struct listNode
{
Data data;
ListNode *next;
};
struct linkedlist
{
ListNode *head;
};
#endif /* LINKEDLISTP_H_INCLUDED */
Your source file linkedlist.c
would include linkedlistp.h
; so would any other source file that needs to be a party to the internals of the data structure. Ordinary user code would only have access to linkedlist.h
and would use only the public interface, therefore.
It's mostly rebranding the private implementation details in a header that can be used by multiple privileged source files instead of being hidden in a single source file as is more usual (and as you currently have it). It doesn't enforce the separation — unless you don't distribute the private header, anybody can decide that their code is privy to the internals by including the protected header. In a project within an organization, you can institute rules to disallow the use of the protected header. If you release the code as an open source project, you can't control what people do with the protected header.
Note that the protection is 'all or nothing'; there is no half-way house. The code either knows all about the internals of the structures (because it included linkedlistp.h
) or nothing about the internals of the structure (because it only included linkedlist.h
).
The naming conventions should be reviewed. You have different capitalizations for your structure tags, which is not good; I stayed with your spellings. I guessed at ll_
and ln_
prefixes for 'linked list' and 'list node' functions — you can use whatever you like, more or less verbosely. The filename suffix p
(as in linkedlistp.h
) is a terse but moderately common way to indicate that the header is 'private' or 'protected'. You can choose an alternative, probably more verbose, name.