3

Since I am mainly an Electrical Engineer, forgive me if I am somewhat off with some terminology.

I am currently programming a display driver/interface module. And like so many times before, I have reached a point where I am unsure on how to separate function prototypes and definitions that are meant for internal use of the module from the public interface.

Consider the following code as as an example:

C-file:

// display.c
#include "display.h"

// Belongs to public Interface
void SetFont(Font_t fnt) {
    // implementation
}

// Belongs to public Interface
void PrintCharAt(int x, int y, char ch) {
    // implementation
}

// For internal use only 
void SendDataToDisplay(DisplayTelegram_t telegram) {

}

Header:

// display.h
typedef struct {
    // implementation
} Font_t;

typedef struct {
    // implementation
} DisplayTelegram_t;

void SetFont(Font_t fnt);
void PrintCharAt(int x, int y, char ch);
void SendDataToDisplay(DisplayTelegram_t telegram);

The article What Belongs in a C .h Header File? covers several points that I mostly agree with.

DO include in the header file all of the function prototypes for the public interface of the module it describes.

DON’T include in the header file any other function or macro that may lie inside the module source code. It is desirable to hide these internal “helper” functions inside the implementation. If it’s not called from any other module, hide it!

So far so good, but what is the best practice for hiding the internal prototypes etc? There is no reason to have the DisplayTelegram_t type and the SendDataToDisplay() prototype in the header. It may even confuse someone who just wants to use the interface when things get more complex.

I can think of three options:

  1. Just put all internal prototypes, typedefs, defines etc. to the top of the .c-file. I have done this in the past, but I do not like it. It does not seem consistent to pollute the .c-file with header-material.

  2. Create a second header file like display_internal.h and put everything for internal use only there. I have read a lot about this approach, but barely seen it used.

  3. Don't care about hiding internal prototypes etc. and just put everything in the header-file. Maybe grouping public and internal stuff together respectively and leaving a comment. This is dirty from a responsibility-perspective but at least its consistent and clean for the author of the code.

Is there any approach I am missing? How is this handled on a professional level?

  • Approach 1 is often used, and it is acceptable. You then declare static the functions (or variables) that are internal to your compilation unit. – Basile Starynkevitch Jun 4 '15 at 12:40
4

1.Just put all internal prototypes, typedefs, defines etc. to the top of the .c-file. I have done this in the past, but I do not like it. It does not seem consistent to pollute the .c-file with header-material.

Function prototypes are not header-material. Public API function prototypes are header-material.

If you had a single compilation unit, you would not need the header file at all. Then, you could add all prototypes (that are needed in that compilation unit in order to compile) to the top of the C file.

You can still do so, but since you usually have more compilation units, and they reuse each other's public APIs, it makes sense that the public APIs are in separate files, which are then reused (thus avoiding duplicate code).

Your second alternative ( create a second header file like display_internal.h and put everything for internal use only there ) is not really an alternative: the moment you add function prototypes to a header file to use it in other compilation units, that is public interface by definition (and should be in the public API header).

The third alternative is bad for project maintenance (very bad!). In practice, people working on client code, find the prototypes and figure "hey, I'm doing code reuse ;)" without caring that there is a comment in the header saying "part of the private api". This way, they end up adding undocumented interdependencies on private APIs.

TLDR: The first option is the correct one to take.

  • Thank you, your points address anything I wanted to know. I think your first sentence hits the nail on the head. Its really about thinking about the header as the public interface - period. Its not meant to separate implementation from definitions and static information in general. – Rev1.0 Jun 5 '15 at 10:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.