Reputation: 241
Suppose you are writing a library that uses internally certain data structures, and wants to export to the user only a subset of them (or hide the exact type using something like void *
). The definitions for all the structs and functions used in the library are in a header library.h
, which will be used when building the library.
Is it considered good practice to also produce another copy of library.h
that would not be used during the build process but only by users linking to the library?
For example suppose the library internally uses the following library.h
:
#ifndef LIBRARY_H
#define LIBRARY_H
struct myStruct {
int some_x;
void (*some_callback)(void);
};
typedef struct myStruct *myStruct_t;
#endif
While we would like to hide the definition of myStruct
to the user, so we export a header library.h
that is:
#ifndef LIBRARY_H
#define LIBRARY_H
typedef void *myStruct_t;
#endif
Upvotes: 1
Views: 357
Reputation: 133919
Notice that the C standard doesn't guarantee that a pointer to void has a representation that is compatible with a pointer to a struct! Thus:
typedef struct myStruct *myStruct_t;
typedef void *myStruct_t;
these two are not compatible and cannot be used in a strictly conforming program.
Another thing is that you usually shouldn't hide pointers, unless needed. Consider for example the FILE
in the standard library. Its contents are not defined anywhere, but all the functions specifically return a pointer to it and accept a pointer to it.
You can even use a simple struct
declaration, instead of definition:
struct myStruct;
Then external users can define a variable as a pointer to it
struct myStruct *handle;
Or if you wish to hide the fact that it indeed is a struct, use a typedef
:
typedef struct myStruct myStruct;
Then the users of the external interface can define their variables simply as
myStruct *handle;
Upvotes: 2
Reputation:
Is it considered good practice to also produce another copy of library.h that would not be used during the build process but only by users linking to the library?
No. While the details of a best practice for what you want to do are probably a matter of taste, delivering headers not used during building is objectively not a good practice: You risk to introduce typing errors that are never catched when you build your project.
So, without going into details on how you should organize that, what you should definitely do is have each "private" header #include
the respective "public" header and not repeat public declarations in the private header. For your example, this would look e.g. like:
library.h:
#ifndef LIBRARY_H
#define LIBRARY_H
typedef struct myStruct *myStruct_t;
// there's absolutely no need to use void * here. An incomplete struct
// type is perfectly fine as long as only pointers to it are used.
#endif
library_internal.h:
#ifndef LIBRARY_INTERNAL_H
#define LIBRARY_INTERNAL_H
#include "library.h"
struct myStruct {
int some_x;
void (*some_callback)(void);
};
#endif
Additional "best practice" notes:
Don't hide pointers behind typedef
s. Most C programmers are well aware that a pointer is part of the declarator and expect to explicitly see a pointer when there is one. Dereferencing something that doesn't look like a pointer will just cause confusion for others reading the code. You also might confuse consumers of your library into expecting a myStruct_t
to exhibit call-by-value semantics.
Don't define your own types with the _t
suffix. At least in POSIX, this is reserved for the implementation (of the compiler/runtime). There's nothing wrong with defining a type of the same name as a struct
tag.
Example with these additional suggestions:
library.h:
#ifndef LIBRARY_H
#define LIBRARY_H
typedef struct myStruct myStruct;
#endif
library_internal.h:
#ifndef LIBRARY_INTERNAL_H
#define LIBRARY_INTERNAL_H
#include "library.h"
struct myStruct {
int some_x;
void (*some_callback)(void);
};
#endif
Upvotes: 2