When code is changing steadily, it can be a nuisance keeping header files in sync with function definitions unless you generate the headers automatically from the definitions.
Header files are important constructs that keep C declarations consistent in different compilations. However, creating and updating declarations and documentation in the header files takes time and is distracting. In general, it becomes an obstacle to efficient programming. I've worked on some projects with many hundreds of short functions, where this was not a trivial issue. I've automated the process so that the header files are automatically generated. This automated process was used successfully by a group of ten software engineers.
When I first considered this problem, I intended to use a program that extracted all function definitions from a file without having to modify the file. On further thought, I decided it was better to modify the files with special notation (in the form of C comments) that marked the beginning of each declaration to be extracted. This has three advantages, especially if you are writing the code yourself.
First, you can extract different functions to different output files (e.g., functions used only within a module directory and those for use by other modules). Second, the special comments can include documentation describing the functions, which can be automatically extracted at the same time to the same output file and even to formal documentation. Third, requiring some modifications in the source files greatly simplifies the extractor program.
I impose functions fairly arbitrary conditions on the input file. The special comments must begin at column one and begin with a special sequence of characters, such as
/*-E-or
//-E-The extractor ignores conditional compilation directives (enclosing #ifs) and enclosing comments; the rare problem cases can be manually declared.
The extractor design assumes that the compiler will ignore unused declarations and that the compiler (or linker) will catch any errors. I strongly recommend using a compiler that makes references to undefined functions an error.
The extractor simply reads through the file until it finds a magic character sequence starting in column 1, then emits the keyword extern and the magic character sequence. It then emits additional characters, and tracks comments, until it finds an uncommented terminating character. The terminating characters are the equal sign (=), left brace ({), and semicolon (;). Using these characters as terminators allows extraction of both declarations and definitions of both functions and global variables. The extractor replaces a terminating character in the output by a semicolon and then silently reads more characters while looking for the next specially formatted comment at the beginning of a line.
The generated files do not completely replace manually created header files, which are needed for struct, enum definitions, and #defines. I have had problems using a make utility since any minor change to the body (not the prototype) of a function would trigger a new extraction, which in turn would trigger recompilation of files including it. My current simple solution is to use a batch or script file that extracts to temporary output files, overwrites the permanent output file only if the new file differs in content, and then runs make. This solution is reasonably efficient because the extractor program is quite fast compared to the compiler.
Listing 1 shows the extractor program. Listing 2 shows a sample source file and output. I also use a similar but more complicated technique with C++ classes so that class functions can be written as if inline, but compiled out of line, and with an appropriate header file generated.
Ted Merrill (merrill@cruzio.com) lives in Ben Lomond, California, and writes device control software over the hill in San Jose. He came to software by way of degrees in engineering mathematics and mechanical engineering.