Header files in C/C++ are defining the interface between different modules. In this article I share some tips and tricks how create such interface files.

Creating Header File
Most IDEs have a helper to create header files, including Eclipse.

What I recommend is to add a custom text with license information. How to dos this is described in Custom C/C++ Headers with Eclipse.

Guards against recursive includes
Every header file shall have a #ifndef … #define … #endif as shown in the example, to avoid recursive includes. Recursion can happen if a header file gets either directly or indirectly included again.
Because there is only one global namespace for a #define, the symbol (MAIN_STEPPER_H_) shall be unique in the whole application. Eclipse helps with this using a define with the format <folder name>_<file name>_H_ to reduce chances of conflicts with other header files. This is just a convention, and can be changed if needed.
C++ Support
If the implementation is implemented in C, but if I want to use it from C++ too, then I should add the conditional extern “C” as below:

With the ‘extern “C'” the C++ compiler knows that the implementation is done in C, because the name encoding (functions, parameters) are different between C and C++. Adding C++ support is of course optional, but I recommend it so you can use the module with C++ later if needed.
Function Declarations
Below is an example of a function added to the interface:

I prefer to use a prefix (in this case STEPPER_): that way I see always from the code where the implementation is located.
Note that a function interface could be written with the ‘extern’ in front of it:
extern void STEPPER_Init(void);
but usually I’m too lazy to have it added, although some might prefer to have it added for consistency and clarity.
Module Initialization and De-Initialization
For me it is a good practice to provide a initialization and de-initialization interface to each module, even if they are empty. Think about a module constructor and destructor. The role is to initialize the internal state or variables (if any).

Variables
In my view, variables should not be exposed in interfaces, whenever possible. But if you decide to have it in the interface, do not forget to mark it extern:
Unlike for functions, the extern is mandatory for variable declarations:

In the supplementation (.c file) it would habe be defined like this:
/* implementation in stepper.c */
int STEPPER_Flags = STEPPER_DEFAULT_FLAGS;
As another rule: never define objects in the header file. It means never use ‘static’ for anything in the interface! ‘Static’ marks things with internal linkage, and internal things have no meaning in an (external) interface.

💡 Of course, there *can* be reasons for using static, but these are rare edge cases, e.g. if a header file is well-controlled (e.g. generated some kind of code or tables) and included only once.
One special case and imho the only exception is with using inline:

As for variables and functions (which includes inline definitions), it is about linkage: if that object can be linked outside or if it is invisible to other translation units. Having the inline function marked as static, only makes it visible inside the current unit, which makes sense.
One thing to keep in mind is: inline is just a hint (like register), and there is no guarantee that the compiler will actually inline that code. If it does not get inlined, it will be handled like if this would be a local static function. Or in other words: in such a case, if you include that header file multiple times and use that inline function in that compilation unit, you will end up with multiple (static) instances of that (not) inlined function! The same applies to ‘static const’ too!
In essence, you need to keep in mind that including a header file means for the pre-processor to ‘copy-paste’ the content for the current compilation unit. If you have static items in the header file, you end up with multiple static definitions, resulting in copies of that object for each compilation unit.
Now what about just ‘inline’ or even ‘extern inline’? Have a read at this StackOverflow article.
Setter/Getter
Instead exposing data in an interface, I recommend to write setter and getter functions instead.

This ensures encapsulation of the data and makes future modifications much easier.
Self-Contained Interfaces
A header or interface file shall be self-contained: it means that a module using it can simply include the interface and automatically includes the necessary other interfaces. Or in other words: whatever dependencies exist in the interface, it shall be resolved in the interface itself.
In the example below the interface uses the ‘bool’ type which is not part of the ‘standard C’. Therefore I have to include that header file for it (stdbool.h) to make sure the type is known:

Documentation
Of course an interface or header file needs some kind of documentation or description. What I prefer to use is a doxygen style of documentation.

Doxygen is a great tool which follows a ‘single-source’ approach: the source files are the documentation, and the documentation (HTML, PDF, …) can be derived from the source files: this removes the need to keep a separate documentation in place (at least in many cases).
Summary
Writing good header files and interfaces is important. I hope I gave you some food for thoughts and a few ideas how to create and maintain interface files.
Happy Interfacing:-)
Hello Erich! Excellent post talking about the fundamentals! I really appreciate. It is good to be refresh the topics! Thanks very much!
LikeLiked by 1 person
Hi Mauricio,
thanks a lot!
LikeLike
What do you think of ‘#pragma once’ instead of the header guard? This needs only one line and there could not be Name conflicts. Most compilers Support this pragma.
Also i like to structure my header Files with comments ( includes, defines, types, etc…) so that everyone reading/editing the file knows where specific parts of the code are located.
In the corresponding .c file i include the header file First to ensure that every dependency is included in the header file. This ensures that the header is self contained.
Besides the Init and Deinit functions often a Run function is needed to handle cyclic behavior of a modul.
LikeLiked by 2 people
I avoid pragmas, because they are largely not portable. And in this case, the language tools with the #ifndef … #endif does the job. Yes, there is a potential issue with the naming of the define. But one should be anyway careful with naming the header files, not be in conflict with an existing name, otherwise the include order might be an issue, depending what the compiler includes first, especially for recursive includes (which should not be used, anyway). So I do not recommend using such a #pragma once.
I agree to that ‘include your own interface in the implementation first’, this is just another mantra.
As for a ‘run’ function: most of my code uses an RTOS or scheduler of some kind. In that case the cyclic behavior will be created with a task during the init function: another nice way to use an RTOS, as that way I can hide more of the internal workings.
Thanks for all the good suggestions, I like the one with structuring the comments.
LikeLike
Interested in knowing what are your thoughts of implementation within header files? Especially in the case of a simple function such as a return of the inner variable in your getter function?
LikeLiked by 1 person
Hi John,
this leads to the ‘inline’ discussion which already has started, and which I wanted to avoid ;-). See the extended article where an inline function returns the value of the flags. This can speed up things, but only with the compiler actually inlining that function. Personally I don’t like it if it requires me to expose internal variables for efficiency. To me, there is always a trade-off between readability, encapsulation, data hiding and efficiency. You can pick one or two, but it will affect the other aspects in a negative way.
LikeLike
Hi Erich,
I don’t know why you write “never use ‘static’ for anything in the interface! “.
A lot of small functions are defined (not declared) in the .h files in NXP libraries (for example) and in this case you MUST use static (usually with inline to avoid call overhead).
Then first, you avoid to type twice the function header (declaration in .h and definition in .c) and second you let the user see the code in the .h file (in case of closed library).
What to you think?
LikeLiked by 1 person
Well, I wanted to avoid a discussion about inline, because this ads a lot more complexity to the discussion. Anyway, because you have asked and touched that subject, I have extended the article about using ‘static inline’: This is special because it is about function definitions. In short, using static inline makes a static copy of that object in case the compiler does not physically inline it. Here you might end up with multiple duplicates of non-inlined function definition, making the code much larger. If using extern inline (or just inline), that object gets external linkage and is only allowed to be present once in the application: so you will be limited to include that header file only once and nowhere else, which in most cases is not practical.
>>Then first, you avoid to type twice the function header (declaration in .h and definition in .c)
Well, this is just a consequence of interfaces in C/C++, that you have some duplication, as in other languages. I feel there is nothing wrong about it.
>>and second you let the user see the code in the .h file (in case of closed library).
Exposing code or having the user to see the code behind a function violates data hiding, so that would not be a good reason for me. And in case of a ‘closed’ library, doing that would be anyway not good. There is nothing bad if the user can check the implementation in the .c file. My mantra is: just put the needed stuff into the header file, nothing more and nothing less.
The reason for having inline is simply to give the compiler a chance to optimize things, and that’s it. But as outlined in my extension of the article: using inline even might explode your code size if not turning on the highest optimizations or if things are not inlineable by the compiler.
I hope this helps?
LikeLike
Pingback: Spilling the Beans: storage class and linkage in C, including static locals | MCU on Eclipse