A Silk Purse from a Sow's Ear: Header Files

Modern header files are a clear improvement over C code of the past that had #ifdef scattered throughout (click here for an example of how unreadable it used to get, with compiler incompatibilities). Header files are no longer necessary in many languages, and many C programmers also put the bare minimum necessary. Here is an example of the style, which is clean, bare, and concise:

#ifndef _VMBUF_H_ #define _VMBUF_H_ #include "vmbuf.h" int vmbuf_deflate(struct vmbuf *buf); int vmbuf_deflate2(struct vmbuf *inbuf, struct vmbuf *outbuf); int vmbuf_deflate3(struct vmbuf *buf, int level); int vmbuf_inflate(struct vmbuf *buf); int vmbuf_inflate2(struct vmbuf *inbuf, struct vmbuf *outbuf); #endif

Header files seem like redundant, unnecessary appendages. Is there a way we can use these appendages for something better?

In this essay we look at ways to use header files.

SDL, the graphics library, and has some of the best documentation out there. The creators realized they needed to have header files, and decided to use them for documentation. In fact, a lot of projects use header files for documentation. Here is an example chunk from SDL_mouse.h:

/** * \brief Create a cursor, using the specified bitmap data and * mask (in MSB format). * * The cursor width must be a multiple of 8 bits. * * The cursor is created in black and white according * to the following: * * data mask resulting pixel on screen * 0 1 White * 1 1 Black * 0 0 Transparent * 1 0 Inverted color if possible, black * if not. * * * \sa SDL_FreeCursor() */ SDL_Cursor *SDLCALL SDL_CreateCursor(const Uint8 * data, const Uint8 * mask, int w, int h, int hot_x, int hot_y); /** * \brief Moves the mouse to the given position in * global screen space. * * \param x The x coordinate * \param y The y coordinate * \return 0 on success, -1 on error (usually: unsupported * by a platform). * *

ote This function generates a mouse motion event */ int SDLCALL SDL_WarpMouseGlobal(int x, int y); /** * \brief Set the active cursor. */ void SDLCALL SDL_SetCursor(SDL_Cursor * cursor);

Header files can be used for polymorphism, for unit tests. For a while it became popular in Java to make an interface iSun.java for every class Sun.java to make it easy to test. The plan was to replace the actual class with a different subclass using the same interface. That can be done in C, too, with header files being the interface, and the .c file being swapped out at build time with a special .c file made for testing.

/* in the .h file */ /* Will keep trying to send bytes on 'sock' until there is an error, or the bytes are sent.*/ bool send_bytes(int sock, char *buf, int len); /* Will keep trying to read bytes from 'sock' until 'len' byts are read or until there is an error.*/ bool read_bytes(int sock, char *buf, int len); --- /* in the .c file */ static char *test_file_in = "testfiles/183459.bin"; bool send_bytes(int sock, char *buf, int len) { //stub function for unit testing: writes bytes to file } static char *test_file_out = "testfiles/183489.bin"; bool read_bytes(int sock, char *buf, int len) { //stub function for unit testing: reads bytes from file }

Unfortunately, every project I've seen that uses this technique has been proprietary, so I can't give a good real-life example. Hopefully you understand it with that mock-example.

NextStep has some nice header file documentation; so nice that in NextStep it's often better to look in the header files than in the 'official' documentation, because the header files are easier to read. The comments are like a contract, defining the behavior of the function.

/* Invoke the block registered with the cancellationHandler property, if there is one, and set the cancelled property to YES. Do this for the receiver, any descendants of the receiver, the instance of NSProgress that was published in another process to make the receiver if that's the case, and any descendants of such a published instance of NSProgress. */ - (void)cancel; /* Invoke the block registered with the pausingHandler property, if there is one, and set the paused property to YES. Do this for the receiver, any descendants of the receiver, the instance of NSProgress that was published in another process to make the receiver if that's the case, and any descendants of such a published instance of NSProgress. */ - (void)pause; /* Invoke the block registered with the resumingHandler property, if there is one, and set the paused property to NO. Do this for the receiver, any descendants of the receiver, the instance of NSProgress that was published in another process to make the receiver if that's the case, and any descendants of such a published instance of NSProgress. */ - (void)resume; /* The fraction of the overall work completed by this progress object, including work done by any children it may have. */ @property (readonly) double fractionCompleted;

If you have a Mac, this file can currently be found in Xcode.app/ Contents/ Developer/ Platforms/ MacOSX.platform/ Developer/ SDKs/ MacOSX10.11.sdk/ System/ Library/ Frameworks/ Foundation.framework/ Versions/ C/ Headers/ NSProgress.h

Another technique: you can use header files to replace some function calls, for debugging purposes. In this example, we see the standard library functions like malloc() and free() have been overridden with debug functions, so every call can be tracked.

#ifndef MEM_DEBUG_H #define MEM_DEBUG_H #define malloc(a) malloc_debug (a, __FILE__, __LINE__) #define realloc(a) realloc_debug(a, __FILE__, __LINE__) #define calloc(a) calloc_debug (a, __FILE__, __LINE__) #define free(a) free_debug (a, __FILE__, __LINE__) size_t mem_used(); void print_all_mem(); #endif

The point here isn't that you should follow any of these techniques, I advocate using the right tool for the job; but rather: things that seem useless often aren't useless.

Useless things often aren't useless. That is something I learned from Eric Phelps as the Edison Effect.