8.1 Writing C header files

Writing portable C header files can be difficult, since they may be read by different types of compilers:

C++ compilers

C++ compilers require that functions be declared with full prototypes, since C++ is more strongly typed than C. C functions and variables also need to be declared with the extern "C" directive, so that the names aren’t mangled. See Writing libraries for C++, for other issues relevant to using C++ with libtool.

ANSI C compilers

ANSI C compilers are not as strict as C++ compilers, but functions should be prototyped to avoid unnecessary warnings when the header file is #included.

non-ANSI C compilers

Non-ANSI compilers will report errors if functions are prototyped.

These complications mean that your library interface headers must use some C preprocessor magic to be usable by each of the above compilers.

foo.h, defined in the tests/demo.at Autotest of the libtool distribution, serves as an example for how to write a header file that can be safely installed in a system directory.

Here are the relevant portions of that file:

#ifndef FOO_H
#define FOO_H

#ifdef __cplusplus
extern "C" {
#endif

int foo (void);
int hello (void);

#ifdef __cplusplus
}
#endif

#endif /* !FOO_H */

This can also be achieved by utilizing macros:

/* BEGIN_C_DECLS should be used at the beginning of your declarations,
   so that C++ compilers don't mangle their names.  Use END_C_DECLS at
   the end of C declarations. */
#undef BEGIN_C_DECLS
#undef END_C_DECLS
#ifdef __cplusplus
# define BEGIN_C_DECLS extern "C" {
# define END_C_DECLS }
#else
# define BEGIN_C_DECLS /* empty */
# define END_C_DECLS /* empty */
#endif

/* PARAMS is a macro used to wrap function prototypes, so that
   compilers that don't understand ANSI C prototypes still work,
   and ANSI C compilers can issue warnings about type mismatches. */
#undef PARAMS
#if defined __STDC__ || defined _AIX \
        || (defined __mips && defined _SYSTYPE_SVR4) \
        || defined WIN32 || defined __cplusplus
# define PARAMS(protos) protos
#else
# define PARAMS(protos) ()
#endif

These macros can be used in foo.h as follows:

#ifndef FOO_H
#define FOO_H

/* The above macro definitions. */
#include "..."

BEGIN_C_DECLS

int foo PARAMS((void));
int hello PARAMS((void));

END_C_DECLS

#endif /* !FOO_H */

Note that the #ifndef FOO_H prevents the body of foo.h from being read more than once in a given compilation.

Also the only thing that must go outside the BEGIN_C_DECLS/END_C_DECLS pair are #include lines. Strictly speaking it is only C symbol names that need to be protected, but your header files will be more maintainable if you have a single pair of these macros around the majority of the header contents.

You should use these definitions of PARAMS, BEGIN_C_DECLS, and END_C_DECLS into your own headers. Then, you may use them to create header files that are valid for C++, ANSI, and non-ANSI compilers9.

Do not be naive about writing portable code. Following the tips given above will help you miss the most obvious problems, but there are definitely other subtle portability issues. You may need to cope with some of the following issues:


Footnotes

(9)

We used to recommend __P, __BEGIN_DECLS and __END_DECLS. This was bad advice since symbols (even preprocessor macro names) that begin with an underscore are reserved for the use of the compiler.