2

When developing a shared library in C, it is common to separate the "public" headers from the "private" headers. The public headers contain all the functions and types that are meant to be used by the end-user of the library (i.e., another developer), while the private headers contain internal implementation details.

What is the best way to deal with situations where the exact makeup of a struct is meant to be a private implementation detail, but your public-facing functions deal with pointers of that struct type?

I've arrived at a somewhat awkward approach with a preprocessor define. Is there a more standard pattern, or perhaps an approach that sidesteps the problem altogether?

linked_list.c

#define __LINKED_LIST_PRIVATE__
#include "linked_list.h"
linked_list_node *llist_node_create(void *userdata)
{
 linked_list_node *node = malloc(sizeof(linked_list_node));
 node->next = NULL;
 node->userdata = userdata;
}
void llist_node_destroy(linked_list_node *node)
{
 free(node);
}
void llist_node_insert_after(linked_list_node *parent, linked_list_node *new)
{
 parent->next = new;
}
void *llist_node_get_userdata(linked_list_node *node)
{
 return node->userdata;
}
linked_list_node *llist_node_get_next(linked_list_node *node)
{
 return node->next;
}

inc/linked_list_private.h

typedef struct linked_list_node_s {
 struct linked_list_s *next;
 void *userdata;
} linked_list_node;

inc/linked_list.h

#ifndef __LINKED_LIST_PRIVATE__
// so that public users of the library can still have
// the "linked_list_node" as a type, but don't know
// the internal makeup of the structure.
typedef void linked_list_node;
#else
#include "linked_list_private.h"
#endif
linked_list_node *llist_node_create(void *userdata);
void llist_node_destroy(linked_list_node *node);
void llist_node_insert_after(linked_list_node *parent, linked_list_node *new);
void *llist_node_get_userdata(linked_list_node *node);
linked_list_node *llist_node_get_next(linked_list_node *node);
Christophe
81.9k11 gold badges135 silver badges201 bronze badges
asked Jan 22, 2021 at 17:18

1 Answer 1

2

The easiest way is to use the PIMPL idiom, also colloquially called "compilation firewall".

This technique does not need conditional compilation to define the structure differently: in your public header, you just define opaque pointers to a struct which you do not define. In this case C will handle the struct as an incomplete type, meaning that you can only use pointers to it, but never dereference it nor get its size.

answered Jan 22, 2021 at 21:02

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.