[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

3. Modules

A module is a logically related collection of data and code grouped together for convenience. A module consists of a public header file and one or more C source files that implement the public interface and any needed private interfaces. If the module contains multiple C source files, then it may also have a private header file for use only by its own source files.

name prefixes
To avoid name clashes, each identifier that appears in a public header file must have a prefix of at least three letters followed by an underscore. This applies to all identifiers: variable names, macros, structure and union tags, structure members, enumerations, parameter names in function prototypes, and so on. The same prefix should be used throughout a module.

Names of non-static, private functions shared between source files in a multi-file module must also include the prefix.

one purpose
A module should have a single purpose. A module that defines an ADT should define only a single ADT. See section 7. ADTs, for more information.

logical division
Divide a module into multiple source files logically. Don't divide one source file into several just because it is becoming "too large", unless there is a way to logically and systematically divide it.

file names
The file name for a module's public header file should be the name of the module, or an abbreviation for it: `module.h'. In a module with a single source file, this file should be named similarly: `module.c'. Otherwise, add a dash and another part to the name for each source file: `module-part.c'. The private header file for a module should be named `moduleP.h' (note the capital `P').

Limit file names to 14 characters at most due to a limitation of old System V filesystems. Abbreviate aggressively to fit within this limit.

header files
Header files should #include a minimal set of needed header files. "A minimal set" does not mean one header file that in turn #includes every needed or unneeded header! In fact, don't depend on included header files to include your own dependencies, just their own.

In particular, if a header file uses size_t, be sure to #include <stddef.h>, or some other standard C library header that also defines it if it is needed for another reason. If a header file uses FILE, be sure to #include <stdio.h>.

Do not include a header file just for struct tag;, which creates unnecessary dependencies. Write the struct tag; line inline.

Header file inclusion should be idempotent; that is, including a header multiple times should have the same effect as including it once. One possible exception is headers for debugging, like `debug-print.h' (discussed below).

initialization
If a module, as opposed to any ADT it defines, requires initialization, name the routine to initialize it module_module_init(). The corresponding uninitialization routine is module_module_uninit(). Check with an assertion at every function entry point that the module has been initialized.

Alternatively, don't require explicit initialization: initialize at the first call to any function in the module. For an ADT usually the first call has to be to one particular function anyhow.

debug flags
Define NDEBUG to turn off assertions and similar code that only kicks in when something goes wrong. These should be nonintrusive tests that do not cause anything user-visible to happen when nothing untoward is detected, although they may legitimately slow the program down considerably and may add members to structures in public header files. Due to the last property, normally the entire program should be compiled with the same NDEBUG setting (either defined or undefined).

Define DEBUG to turn on intrusive debug code. This code may do things such as print extra messages to stderr or stdout, create log files, and so on. Whether DEBUG is defined should not affect definitions in public header files. This flag should be defined or undefined on a per-module basis. It is reasonable to include a #define or #undef for it at the top of a module's source file or private header file. DEBUG should not be defined when NDEBUG is defined.

Use the macros defined in `debug-print.h' for conditional output to stderr based on whether DEBUG is defined. Be sure to #include this file after the #define or #undef for DEBUG, if any.

unit tests
It is a good idea to include a unit test for each module. These can either be implemented as part of the module's own code, turned on by defining UNIT_TEST, or by another file named `module-test.c'.


[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by Ben Pfaff on September, 11 2001 using texi2html