Chapter 2. Debug, Test, Document

Crawling 
Over your window 
You think I’m confused, 
I’m waiting ... 
To complete my current ruse.

Wire, “I Am the Fly”

This chapter will cover tools for debugging, testing, and documenting your writing—the essentials to take your writing from a potentially useful set of scripts to something you and others can rely on.

Because C gives you the freedom to do idiotic things with memory, debugging means both the quotidian problem of checking logic (with gdb) and the more technical problem of checking for memory misallocations and leaks (with Valgrind). On the documentation side, this chapter covers one tool at the interface level (Doxygen) and another that helps you document and develop every step of the program (CWEB).

The chapter also gives a quick introduction to the test harness, which will allow you to quickly write lots of tests for your code, and concludes with some considerations about error reporting and handling input or user errors.

Using a Debugger

The first tip about the debugger is simple and brief:

Use a debugger, always.

Some of you will find this to be not much of a tip, because who possibly wouldn’t use a debugger? But many users come from languages that throw up a backtrace at the first sign of trouble, and didn’t get to the part where their C textbook introduces the debugger (it is often in the other topics segment, somewhere around Chapter 15). So now we’re all on the same page: the debugger exists.

I’ve found other people who worry that bugs typically come from broad errors of understanding, while the debugger only gives information at the low level of variable states and backtraces. Indeed, after you pinpoint a bug using the debugger, it is worth taking the time to consider what underlying problem and failure of understanding you have just discovered, and whether it replicates itself elsewhere in your code. Some death certificates include an aggressive inquiry into the cause of death: Subject died as a result of ______, as a result of ______, as a result of ______, as a result of ______, as a result of ______. After the debugger has helped you understand your code better, you can encapsulate your understanding in more unit tests.

About that always: there is virtually no cost to running a program under the debugger. Nor is the debugger just something to pull out when something breaks. Linus Torvalds explains: “I use gdb all the time … as a disassembler on steroids that you can program.”[5] It’s great being able to pause anywhere, increase the verbosity level with a quick print verbose++, force out of a for (int i=0; i<10; i++) loop via print i=100 and continue, or test a function by throwing a series of test inputs at it. The fans of interactive languages are right that interacting with your code improves the development process all the way along; they just never got to the debugging chapter in the C textbook, and so never realized that all of those interactive habits apply to C as well.

Whatever your intent, you will need to have human-readable debugging information (i.e., names for variables and functions) compiled into the program for any debugger to be at all useful. To include debugging symbols, use the -g flag in the compiler switches (i.e., your CFLAGS variable). Reasons to not use the -g flag are rare indeed—it doesn’t slow down your program, and adding a kilobyte to your executable is irrelevant for most situations.

I’m only covering GDB, because on most POSIX systems, it’s the only game in town.[6] You might be working from an IDE or other visual frontend that runs your program under GDB every time you click run. I’m going to show you commands from GDB’s command line, and you should have no trouble translating the basics here into mouse clicks on your screen. Depending on the frontend, you might be able to use the macros defined in .gdbinit.

When working with GDB directly, you will probably need to have a text editor in another window or terminal displaying your code. The simple GDB/editor combination provides many of the conveniences of an IDE, and may be all you need.

You can experiment in GDB with any program that has at least one function beyond main (so you have a nontrivial stack), but if you don’t have anything immediately on hand, try the New York Times headline downloader from the section libxml and cURL. On the shell command line, given an executable named nyt_feed, start GDB via gdb nyt_feed. You will then be at the GDB command line, where you can try any of a number of things:

  • Pause your program at a certain point.

    • break get_rss or break nyt_feeds.c:105, or if you are already in nyt_feeds.c, break 105. This will stop the program just before line 105 is executed.

    • List breakpoints with info break.

    • Turn one off with, e.g., disable 3 (where 3 is the breakpoint number you got from info break), and reenable it later with enable 3. If you have a lot of breakpoints set, disable by itself turns them all off, and then you can enable the one or two that you need at the moment.

    • Delete a breakpoint entirely with del 3.

    • Of course, you can’t stop until you start; run your program with run, and if it needs command-line arguments, put them here: run arg1 arg2.

  • Get the current value of any variables that exist in that function.

    • For one variable like url, use print url, or more simply, p url.

    • For all the function inputs: info args; for all the local variables: info local.

    • Use display url to print the value of url every time GDB stops, so you can watch it transform as you step line-by-line through a routine. Displayed elements will be numbered; use undisplay 1 to turn off the display of item 1.

  • Jump to a parent function and check variable values there. Get the list of frames on the stack via backtrace or bt, then use frame 2 or f 2 to jump to frame 2.

  • Step past the point where you paused, one line of code at a time. Typically one of snuc, but several options exist:

    • s: step one line, even if that means entering another function.

    • n: next line, but do not enter subfunctions, and possibly back up to the head of a loop.

    • u: until the next line forward from this (so let an already-visited loop run through until forward progress).

    • c: continue until the next breakpoint or the end of the program.

    • To return from the current function immediately, use ret (perhaps with a return value, like ret 0).

    • To make a more global move, j will jump to whatever line you please (within reason).

    • If you need to get your bearings from the GDB command prompt, you might need list (or just l) to get a printout of the 10 lines around the line you are currently on.

    • Just hitting Enter will repeat the last command, which makes stepping easier, or after l will list the next 10 lines after those you just saw.

GDB Variables

This segment covers some useful elements of GDB that will help you look at your data with as little cognitive effort as possible. All of the commands to follow go on the GDB command line; IDE debuggers based on GDB often provide a means of hooking in to these facilities as well.

Here’s a sample program that does nothing, but that you can type in for the sake of having a variable to interrogate. Because it is such a do-nothing program, be sure to set the compiler’s optimization flag to -O0, or else x will disappear entirely.

int main(){
    int x[20] = {};
    x[0] = 3;
}

Here’s tip zero: the @ shows you a sequence of elements in an array. For example, if you break on line 3 of this do-nothing program, you can display the first dozen elements of the array with:

p *x@12

Note the star at the head of the expression; without it, we’d get a sequence of a dozen hexadecimal addresses.

The next tip will only be new to those of you who didn’t read the GDB manual [Stallman 2002], which is probably all of you. You can generate convenience variables, to save yourself some typing. For example, if you want to inspect an element deep within a hierarchy of structures, you can do something like:

set $vd = my_model->dataset->vector->data
p *$vd@10

That first line generated the convenience variable to substitute for the lengthy path. Following the lead of the shell, a dollar sign indicates a variable. Unlike the shell, you need set and a dollar sign on the variable’s first use. The second line demonstrates a simple use. We don’t save much typing here, but if you suspect a variable of guilty behavior, giving it a short name makes it easier to give it a thorough interrogation.

These aren’t just names; they’re real variables that you can modify. After breaking at line three or four of the do-nothing program, try:

set $ptr=&x[3]
p *$ptr = 8
p *($ptr++)  #print the pointee, and step forward one

The second line actually changes the value in the given location. On the third line, adding one to a pointer steps forward to the next item in the list (as per All the Pointer Arithmetic You Need to Know). Thus, after the third line, $ptr is now pointing to x[4].

That last form is especially useful because hitting the Enter key without any input repeats the last command. Because the pointer stepped forward, you’ll get a new next value every time you hit Enter, until you get the gist of the array. This is also useful should you find yourself dealing with a linked list. Pretend we have a function that displays an element of the linked list and sets $list equal to the given element, and we have the head of the list at list_head. Then:

p $list=list_head
show_structure $list->next

and leaning on the Enter key will step through the list. Later, we’ll make that imaginary function to display a data structure a reality.

But first, here’s one more trick about these $ variables. Let me cut and paste a few lines of interaction with a debugger in the other screen:

(gdb) p x+3
$17 = (int *) 0xbffff9a4

You probably don’t even look at it anymore, but notice how the output to the print statement starts with $17. Indeed, every output is assigned a variable name, which we can use like any other:

(gdb) p *$17
$18 = 8
(gdb) p *$17+3
$19 = 11

To be even more brief, a lone $ is a shorthand variable assigned to the last output. So if you get a hex address when you thought you would get the value at that address, just put p *$ on the next line to get the value. With this, the above steps could have been:

(gdb) p x+3
$20 = (int *) 0xbffff9a4
(gdb) p *$
$21 = 8
(gdb) p $+3
$22 = 11

Print Your Structures

GDB lets you define simple macros, which are especially useful for displaying nontrivial data structures—which is most of the work one does in a debugger. Even a simple 2D array hurts your eyes when it’s displayed as a long line of numbers. In a perfect world, every major structure you deal with will have a debugger command associated to quickly view that structure in the manner(s) most useful to you.

The facility is rather primitive, but you probably already wrote a C-side function that prints any complex structures you might have to deal with, so the macro can simply call that function with a few keystrokes.

You can’t use any of your C preprocessor macros at the GDB prompt, because they were substituted out long before the debugger saw any of your code. So if you have a valuable macro in your code, you may have to reimplement it in GDB as well.

Here is a function you can try by putting a breakpoint about halfway through the parse function in libxml and cURL, at which point you’ll have a doc structure representing an XML tree. Put these macros in your .gdbinit.

define pxml
    p xmlElemDump(stdout, $arg0, xmlDocGetRootElement($arg0))
end
document pxml
Print the tree of an already opened XML document (i.e., an xmlDocPtr) to the
screen. This will probably be several pages long.
E.g., given: xmlDocPtr doc = xmlParseFile(infile);
use: pxml doc
end

Notice how the documentation follows right after the function itself; view it via help pxml or help user-defined. The macro itself just saves some typing, but because the primary activity in the debugger is looking at data, those little things add up.

GLib has a linked list structure, so we should have a linked list viewer. Example 2-1 implements it via two user-visible macros (phead to view the head of the list, then pnext to step forward) and one macro the user should never have to call (plistdata, to remove redundancy between phead and pnext).

Example 2-1. A set of macros to easily display a linked list in GDB—about the most elaborate debugging macro you’ll ever need (gdb_showlist)
define phead
    set $ptr = $arg1
    plistdata $arg0
end
document phead
Print the first element of a list. E.g., given the declaration
    Glist *datalist;
    g_list_add(datalist, "Hello");
view the list with something like
gdb> phead char datalist
gdb> pnext char
gdb> pnext char
This macro defines $ptr as the current pointed-to list struct,
and $pdata as the data in that list element.
end

define pnext
    set $ptr = $ptr->next
    plistdata $arg0
end
document pnext
You need to call phead first; that will set $ptr.
This macro will step forward in the list, then show the value at
that next element. Give the type of the list data as the only argument.

This macro defines $ptr as the current pointed-to list struct, and
$pdata as the data in that list element.
end

define plistdata
    if $ptr
        set $pdata = $ptr->data
    else
        set $pdata= 0
    end
    if $pdata
        p ($arg0*)$pdata
    else
        p "NULL"
    end
end
document plistdata
This is intended to be used by phead and pnext, q.v. It sets $pdata and prints its value.
end

Example 2-2 offers some simple code that uses the GList to store char*s. You can break around line 8 or 9 and call the previous macros.

Example 2-2. Some sample code for trying debugging, or a lightning-quick intro to GLib linked lists (glist.c)
#include <stdio.h>
#include <glib.h>

GList *list;

int main(){
    list = g_list_append(list, "a");
    list = g_list_append(list, "b");
    list = g_list_append(list, "c");

    for ( ; list!= NULL; list=list->next)
        printf("%s\n", (char*)list->data);
}

Note

You can define functions to run before or after every use of a given command. For example:

define hook-print
echo <----\n
end

define hookpost-print
echo ---->\n
end

will print cute brackets before and after anything you print. The most exciting hook is hook-stop. The display command will print the value of any expression every time the program stops, but if you want to make use of a macro or other GDB command at every stop, redefine hook-stop:

define hook-stop
pxml suspect_tree
end

When you are done with your suspect, redefine hook-stop to be nothing:

define hook-stop
end

Note

Your Turn: GDB macros can also include a while that looks much like the ifs in Example 2-2 (start with a line like while $ptr and conclude with end). Use this to write a macro to print an entire list at once.

Using Valgrind to Check for Errors

Most of our time spent debugging is spent finding the first point in the program where something looks wrong. Good code and a good system will find that point for you. That is, a good system fails fast.

C gets mixed scores on this. In some languages, a typo like conut=15 would generate a new variable that has nothing to do with the count you meant to set; with C, it fails at the compilation step. On the other hand, C will let you assign to the 10th element of a 9-element array and then trundle along for a long time before you find out that there’s garbage in what you thought was element 10.

Those memory mismanagement issues are a hassle, and so there are tools to confront them. Within these, Valgrind is a big winner. Get a copy via your package manager. Valgrind runs a virtual machine that keeps better tabs of memory than the real machine does, so it knows when you hit the 10th element in an array of 9 items.

Once you have a program compiled (with debugging symbols included via gcc’s or Clang’s -g flag, of course), run:

valgrind your_program

If you have an error, Valgrind will give you two backtraces that look a lot like the backtraces your debugger gives you. The first is where the misuse was first detected, and the second is Valgrind’s best guess as to what line the misuse clashed with, such as where a double-freed block was first freed, or where the closest malloced block was allocated. The errors are often subtle, but having the exact line to focus on goes a long way toward finding the bug. Valgrind is under active development—programmers like nothing better than writing programming tools—so I’m amused to watch how much more informative the reports have gotten over time and only expect better in the future.

To give you an example of a Valgrind backtrace, I inserted an error in the code of Example 9-1 by doubling line 14, free(cmd), thus causing the cmd pointer to be freed once on line 14 and again on line 15. Here’s the backtrace I got:

Invalid free() / delete / delete[] / realloc()
   at 0x4A079AE: free (vg_replace_malloc.c:427)
   by 0x40084B: get_strings (sadstrings.c:15)
   by 0x40086B: main (sadstrings.c:19)
 Address 0x4c3b090 is 0 bytes inside a block of size 19 free'd
   at 0x4A079AE: free (vg_replace_malloc.c:427)
   by 0x40083F: get_strings (sadstrings.c:14)
   by 0x40086B: main (sadstrings.c:19)

The top frame in both backtraces is in the standard library code for freeing pointers, but we can be confident that the standard library is well debugged. Focusing on the part of the stack referring to code that I wrote, the backtrace points me to lines 14 and 15 of sadstrings.c, which are indeed the two calls to free(cmd) in my modified code.

You can also start the debugger at the first error, by running:

valgrind --db-attach=yes your_program

With this sort of startup, you’ll get a line asking if you want to run the debugger on every detected error, and then you can check the value of the implicated variables as usual. At this point, we’re back to having a program that fails on the first line where a problem is detected.

Valgrind also does memory leaks:

valgrind --leak-check=full your_program

This is typically slower, so you might not want to run it every time. When it finishes, you’ll have a backtrace for where every leaked pointer was allocated.

A leak in a library function that could conceivably run a million times in the center of a user program’s loop, or in a program that should have 100% runtime for months, will eventually cause potentially major problems for users. But it is easy to find programs broadly deemed to be reliable (on my machine, doxygen, git, TeX, vi, others) that Valgrind reports as definitely losing kilobytes. For such cases, we can adapt a certain cliché about trees falling in the woods: if a bug does not cause incorrect results or user-perceivable slowdowns, is it really a bug?

Unit Testing

Of course you’re writing tests for your code. You’re writing unit tests for the smaller components and integration tests to make sure that the components get along amicably. You may even be the sort of person who writes the unit tests first and then builds the program to pass the tests.

Now you’ve got the problem of keeping all those tests organized, which is where a test harness comes in. A test harness is a system that sets up a small environment for every test, runs the test, and reports whether the result is as expected. Like the debugger, I expect that some of you are wondering who it is that doesn’t use a test harness, and to others, it’s something you never really considered.

There are abundant choices. It’s easy to write a macro or two to call each test function and compare its return value to the expected result, and more than enough authors have let that simple basis turn into yet another implementation of a full test harness. From [Page 2008]: “Microsoft’s internal repository for shared tools includes more than 40 entries under test harness.” For consistency with the rest of the book, I’ll show you GLib’s test harness, and because they are all so similar, and because I’m not going to go into so much detail that I’m effectively reading the GLib manual to you, what I cover here should carry over to other test harnesses as well.

A test harness has a few features that beat the typical homemade test macro:

  • You need to test the failures. If a function is supposed to abort or exit with an error message, you need a facility to test that the program actually exited when you expected it to.

  • Each test is kept separate, so you don’t have to worry that test 3 affected the outcome to test 4. If you want to make sure the two procedures don’t interact badly, run them in sequence as an integration test after running them separately.

  • You probably need to build some data structures before you can run your tests. Setting up the scene for a test sometimes takes a good amount of work, so it would be nice to run several tests given the same setup.

Example 2-3 shows a few basic unit tests of the dictionary object from Implementing a Dictionary, implementing these three test harness features. It demonstrates how that last item largely dictates the flow of test harness use: a new struct type is defined at the beginning of the program, then there are functions for setting up and tearing down an instance of that struct type, and once we have all that in place it is easy to write several tests using the built environment.

The dictionary is a simple set of key-value pairs, so most of the testing consists of retrieving a value for a given key and making sure that it worked OK. Notice that a key of NULL is not acceptable, so we check that the program will halt if such a key gets sent in.

Example 2-3. A test of the dictionary from Implementing a Dictionary (dict_test.c)
#include <glib.h>
#include "dict.h"

typedef struct {                                         1
    dictionary *dd;
} dfixture;

void dict_setup(dfixture *df, gconstpointer test_data){  2
    df->dd = dictionary_new();
    dictionary_add(df->dd, "key1", "val1");
    dictionary_add(df->dd, "key2", NULL);
}

void dict_teardown(dfixture *df, gconstpointer test_data){
    dictionary_free(df->dd);
}

void check_keys(dictionary const *d){                    3
    char *got_it = dictionary_find(d, "xx");
    g_assert(got_it == dictionary_not_found);
    got_it = dictionary_find(d, "key1");
    g_assert_cmpstr(got_it, ==, "val1");
    got_it = dictionary_find(d, "key2");
    g_assert_cmpstr(got_it, ==, NULL);
}

void test_new(dfixture *df, gconstpointer ignored){
    check_keys(df->dd);
}

void test_copy(dfixture *df, gconstpointer ignored){
    dictionary *cp = dictionary_copy(df->dd);
    check_keys(cp);
    dictionary_free(cp);
}

void test_failure(){                                     4
    if (g_test_trap_fork(0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR)){
        dictionary *dd = dictionary_new();
        dictionary_add(dd, NULL, "blank");
    }
    g_test_trap_assert_failed();
    g_test_trap_assert_stderr("NULL is not a valid key.\n");
}

int main(int argc, char **argv){
    g_test_init(&argc, &argv, NULL);
    g_test_add ("/set1/new test", dfixture, NULL,        5
                                  dict_setup, test_new, dict_teardown);
    g_test_add ("/set1/copy test", dfixture, NULL,
                                  dict_setup, test_copy, dict_teardown);
    g_test_add_func ("/set2/fail test", test_failure);   6
    return g_test_run();
}
1

The elements used in a set of tests is called a fixture. GLib requires that each fixture be a struct, so we create a throwaway struct to be passed from the setup to the test to the teardown.

2

Here are the setup and teardown scripts that create the data structure to be used for a number of tests.

3

Now that the setup and teardown functions are defined, the tests themselves are just a sequence of simple operations on the structures in the fixture and assertions that the operations went according to plan. The GLib test harness provides some extra assertion macros, like the string comparison macro used here.

4

GLib tests for failure via the POSIX fork system call (which means that this won’t run on Windows without a POSIX subsystem). The fork call generates a new program that runs the contents of the if statement, which should fail and call abort. This program watches for the forked version and checks that it failed and that the right message was written to stderr.

5

Tests are organized into sets via path-like strings. The NULL argument could be a pointer to a data set to be used by the test, but not built/torn down by the system. Notice how both the new and copy tests use the same setup and teardown.

6

If you don’t have setup/teardown to do before/after the call, use this simpler form to run the test.

Using a Program as a Library

The only difference between a function library and a program is that a program includes a main function that indicates where execution should start.

Now and then I have a file that does one thing that’s not quite big enough to merit being set up as a standalone shared library. It still needs tests, and I can put them in the same file as everything else, via a preprocessor condition. In the following snippet, if Test_operations is defined (via the various methods discussed later), then the snippet is a program that runs the tests; if Test_operations is not defined (the usual case), then the snippet is compiled without main and so is a library to be used by other programs.

int operation_one(){
    ...
}

int operation_two(){
    ...
}

#ifdef Test_operations

    void optest(){
        ...
    }

    int main(int argc, char **argv){
        g_test_init(&argc, &argv, NULL);
        g_test_add_func ("/set/a test", test_failure);
    }

#endif

There are a few ways to define the Test_operations variable. In with the usual flags, probably in your makefile, add:

CFLAGS=-DTest_operations

The -D flag is the POSIX-standard compiler flag that is equivalent to putting #define Test_operations at the top of every .c file.

When you see Automake in Chapter 3, you’ll see that it provides a += operator, so given the usual flags in AM_CFLAGS, you could add the -D flag to the checks via:

check_CFLAGS  = $(AM_CFLAGS)
check_CFLAGS += -DTest_operations

The conditional inclusion of main can also come in handy in the other direction. For example, I often have an analysis to do based on some quirky data set. Before writing the final analysis, I first have to write a function to read in and clean the data, and then a few functions producing summary statistics sanity-checking the data and my progress. This will all be in modelone.c. Next week, I may have an idea for a new descriptive model, which will naturally make heavy use of the existing functions to clean data and display basic statistics. By conditionally including main in modelone.c, I can quickly turn the original program into a library. Here is a skeleton for modelone.c:

void read_data(){
    [database work here]
}

#ifndef MODELONE_LIB
int main(){
    read_data();
    ...
}
#endif

I use #ifndef rather than #ifdef, because the norm is to use modelone.c as a program, but this otherwise functions the same way as the conditional inclusion of main for testing purposes did.

Coverage

What’s your test coverage? Are there lines of code that you wrote that aren’t touched by your tests? gcc has the companion gcov, which will count how many times each line of code was touched by a program. The procedure:

  • Add -fprofile-arcs -ftest-coverage to your CFLAGS for gcc. You might want to set the -O0 flag, so that no lines of code are optimized out.

  • When the program runs, each source file yourcode.c will produce one or two data files, yourcode.gcda and yourcode.gcno.

  • Running gcov yourcode.gcda will write to stdout the percentage of runnable lines of code that your program hit (declarations, #include lines, and so on don’t count) and will produce yourcode.c.cov.

  • The first column of yourcode.c.cov will show how often each runnable line was hit by your tests, and will mark the lines not hit with a big fat #####. Those are the parts for which you should consider writing another test.

Example 2-4 shows a shell script that adds up all the steps. I use a here document to generate the makefile, so I could put all the steps in one script, and after compiling, running, and gcov-ing the program, I grep for the ##### markers. The -C3 flag to GNU grep requests three lines of context around matches. It isn’t POSIX-standard, but then, neither are pkg-config or the test coverage flags.

Example 2-4. A script to compile for coverage testing, run the tests, and check for lines of code not yet tested (gcov.sh)
cat > makefile << '------'
P=dict_test
objects= keyval.o dict.o
CFLAGS = `pkg-config --cflags glib-2.0` -g -Wall -std=gnu99 \
           -O0 -fprofile-arcs -ftest-coverage
LDLIBS = `pkg-config --libs glib-2.0`
CC=gcc
$(P):$(objects)
------
make
./dict_test
for i in *gcda; do gcov $i; done;
grep -C3 '#####' *.c.gcov

Interweaving Documentation

You need documentation. You know this, and you know that you need to keep it current when the code changes. Yet, somehow, documentation is often the first thing to fall by the wayside. It is so very easy to say it runs; I’ll document it later.

So you need to make writing the documentation as easy as physically possible. The immediate implication is that you have the documentation for the code in the same file as the code, as close as possible to the code being documented, and that implies that you’re going to need a means of extracting the documentation from the code file.

Having the documentation right by the code also means you’re more likely to read the documentation. It’s a good habit to reread the documentation for a function before modifying it, both so that you have a better idea of what’s going on, and so that you will be more likely to notice when your changes to the code will also require a change in the documentation.

I’ll present two means of weaving documentation into the code: Doxygen and CWEB. Your package manager should be happy to install either of them.

Doxygen

Doxygen is a simple system with simple goals. It works best for attaching a description to each function, struct, or other such block. This is the case of documenting an interface for users who will never care to look at the code itself. The description will be in a comment block right on top of the function, struct, or whatever, so it is easy to write the documentation comment first, then write the function to live up to the promises you just made.

The syntax for Doxygen is simple enough, and a few bullet points will have you well on your way to using it:

  • If a comment block starts with two stars, /** like so */, then Doxygen will parse the comment. One-star comments, /* like so */, are ignored.

  • If you want Doxygen to parse a file, you will need a /** \file */ comment at the head of the file; see the example. If you forget this, Doxygen won’t produce output for the file and won’t give you much of a hint as to what went wrong.

  • Put the comment right before the function, struct, et cetera.

  • Your function descriptions can (and should) include \param segments describing the input parameters and a \return line listing the expected return value. Again, see the example.

  • Use \ref for cross-references to other documented elements (including functions or pages).

  • You can use an @ anywhere I used a backslash above: @file, @mainpage, et cetera. This is in emulation of JavaDoc, which seems to be emulating WEB. As a LaTeX user, I am more used to the backslash.

To run Doxygen, you will need a configuration file, and there are a lot of options to configure. Doxygen has a clever trick for handling this; run:

doxygen -g

and it will write a configuration file for you. You can then open it and edit as needed; it is of course very well documented. After that, run doxygen by itself to generate the outputs, including HTML, PDF, XML, or manual pages, as per your specification.

If you have Graphviz installed (ask your package manager for it), then Doxygen can generate call graphs: box-and-arrow diagrams showing which functions call and are called by which other functions. If somebody hands you an elaborate program and expects you to get to know it quickly, this can be a nice way to get a quick feel for the flow.

I documented libxml and cURL using Doxygen; have a look and see how it reads to you as code, or run it through Doxygen and check out the HTML documentation it produces.

Every snippet throughout the book beginning with /** is also in Doxygen format.

The narrative

Your documentation should contain at least two parts: the technical documentation describing the function-by-function details, and a narrative explaining to users what the package is about and how to get their bearings.

Start the narrative in a comment block with the header \mainpage. If you are producing HTML output, this will be the index.html of your website—the first page readers should see. From there, add as many pages as you’d like. Subsequent pages have a header of the form:

/** \page onewordtag The title of your page
*/

Back on the main page (or any other, including function documentation), add \ref onewordtag to produce a link to the page you wrote. You can tag and name the main page as well, if need be.

The narrative pages can be anywhere in your code: you could put them close to the code itself, or the narrative might make sense as a separate file consisting entirely of Doxygen comment blocks, maybe named documentation.h.

Literate Code with CWEB

TeX, a document formatting system, is often held up as a paragon of a complicated system done very right. It is about 35 years old as of this writing, and (in this author’s opinion) still produces the most attractive math of any typesetting system available. Many more recent systems don’t even try to compete, and use TeX as a back-end for typesetting. Its author, Donald Knuth, used to offer a bounty for bugs, but eventually dropped the bounty after it went unclaimed for many years.

Dr. Knuth explains the high quality of TeX by discussing how it was written: literate programming, in which every procedural chunk is preceded by a plain English explanation of that chunk’s purpose and functioning. The final product looks like a free-form description of code with some actual code interspersed here and there to formalize the description for the computer (in contrast to typical documented code, which is much more code than exposition). Knuth wrote TeX using WEB, a system that intersperses English expository text with PASCAL code. Here in the present day, the code will be in C, and now that TeX works to produce beautiful documentation, we might as well use it as the markup language for the expository side. Thus, CWEB.

As for the output, it’s easy to find textbooks that use CWEB to organize and even present the content (e.g., [Hanson 1996]). If somebody else is going to study your code (for some of you this might be a coworker or a review team), then CWEB might make a lot of sense.

I wrote An Agent-Based Model of Group Formation using CWEB; here’s a rundown of what you need to know to compile it and follow its CWEB-specific features:

  • Custom is to save CWEB files with a .w extension.

  • Run cweave groups.w to produce a .tex file; then run pdftex groups.tex to produce a PDF.

  • Run ctangle groups.w to produce a .c file. GNU make knows about this in its catalog of built-in rules, so make groups will run ctangle for you.

The tangle step removes comments, which means that CWEB and Doxygen are incompatible. Perhaps you could produce a header file with a header for each public function and struct for doxygenization, and use CWEB for your main code set.

Here is the CWEB manual reduced to seven bullet points:

  • Every special code for CWEB has an @ followed by a single character. Be careful to write @<titles@> and not @<incorrect titles>@.

  • Every segment has a comment, then code. It’s OK to have a blank comment, but that comment-code rhythm has to be there or else all sorts of errors turn up.

  • Start a text section with an @ following by a space. Then expound, using TeX formatting.

  • Start an unnamed chunk of code with @c.

  • Start a named block of code with a title followed by an equals sign (because this is a definition): @<an operation@>=.

  • That block will get inserted verbatim wherever you use the title. That is, each chunk name is effectively a macro that expands to the chunk of code you specified, but without all the extra rules of C preprocessor macros.

  • Sections (like the sections in the example about group membership, setting up, plotting with Gnuplot, and so on) start with @* and have a title ending in a period.

That should be enough for you to get started writing your own stuff in CWEB. Have a look at An Agent-Based Model of Group Formation and see how it reads to you.

Error Checking

A complete programming textbook must include at least one lecture to the reader about how important it is to handle errors sent by functions you have called.

OK, consider yourself lectured. Now let’s consider the side of how and when you will return errors from the functions you write. There are a lot of different types of errors in a lot of different contexts, so we have to break down the inquiry into several subcases:

  • What is the user going to do with the error message?

  • Is the receiver a human or another function?

  • How can the error be communicated to the user?

I will leave the third question for later (Return Multiple Items from a Function), but the first two questions already give us a lot of cases to consider.

What Is the User’s Involvement in the Error?

Thoughtless error-handling, wherein authors pepper their code with error-checks because you can’t have too many, is not necessarily the right approach. You need to maintain lines of error-handling code like any other, and every user of your function has internalized endless lectures about how every possible error code needs to be handled, so if you throw error codes that have no reasonable resolution, the function user will be left feeling guilty and unsure. There is such a thing as too much information (TMI).

To approach the question of how an error will be used, consider the complementary question of how the user was involved in the error to begin with.

  • Sometimes the user can’t know if an input is valid before calling the function, the classic example being looking up a key in a key/value list and finding out that the key is not in the list. In this case, you could think of the function as a lookup function that throws errors if the key is missing from the list, or you could think of it as a dual-purpose function that either looks up keys or informs the caller whether the key is present or not.

    Or to give an example from high-school algebra, the quadratic formula requires calculating sqrt(b*b - 4*a*c), and if the term in parens is negative, the square root is not a real number. It’s awkward to expect the function user to calculate b*b - 4*a*c to establish feasibility, so it is reasonable to think of the quadratic formula function as either returning the roots of the quadratic equation or reporting whether the roots will be real or not.

    In these examples of nontrivial input-checking, bad inputs aren’t even an error, but are a routine and natural use of the function. If an error-handling function aborts or otherwise destructively halts on errors (as does the error-handler that follows), then it shouldn’t be called in situations like these.

  • Users passed in blatantly wrong input, such as a NULL pointer or other sort of malformed data. Your function has to check for these things, to prevent it from segfaulting or otherwise failing, but it is hard to imagine what the caller will do with the information. The documentation for yourfn told users that the pointer can’t be NULL, so when they ignore it and call int* indata=NULL; yourfn(indata), and you return an error like Error: NULL pointer input, it’s hard to imagine what the caller will do differently.

    A function usually has several lines like if (input1==NULL) return -1; ... if (input20==NULL) return -1; at the head, and I find in the contexts where I work that reporting exactly which of the basic requirements enumerated in the documentation the caller missed is TMI.

  • The error is entirely an error of internal processing, including “shouldn’t happen” errors, wherein an internal calculation somehow got an impossible answer—what Hair (The American Tribal Love Rock Musical) called a failure of the flesh, such as unresponsive hardware or a dropped network or database connection.

    The flesh failures can typically be handled by the recipient (e.g., by wiggling the network cable). Or, if the user requests that a gigabyte of data be stored in memory and that gigabyte is not available, it makes sense to report an out-of-memory error. However, when allocation for a 20-character string fails, the machine is either overburdened and about to become unstable or it is on fire, and it’s typically hard for a calling system to use that information to recover gracefully. Depending on the context in which you are working, your computer is on fire-type errors might be counterproductive and TMI.

    Errors of internal processing (i.e., errors unrelated to external conditions and not directly tied to a somehow-invalid input value) cannot be handled by the caller. In this case, detailing to the user what went wrong is probably TMI. The caller needs to know that the output is unreliable, but enumerating lots of different error conditions just leaves the caller (duty-bound to handle all errors) with more work.

The Context in Which the User Is Working

As above, we often use a function to check on the validity of a set of inputs, and such usage is not an error per se, and the function is most useful if it returns a meaningful value for these cases rather than calling an error handler. The rest of this section considers the bona fide errors.

  • If the user of the program has access to a debugger and is in a context where using one is feasible, then the fastest way to fail is to call abort and cause the program to stop. Then the user has the local variables and backtrace right at the scene of the crime. The abort function has been C-standard since forever (you’ll need to #include <stdlib.h>).

  • If the user of the program is actually a Java program, or has no idea what a debugger is, then abort is an abomination, and the correct response is to return some sort of error code indicating a failure.

Both of these cases are very plausible, so it is sensible to have an if/else branch that lets the user select the correct mode of operation for the context.

It’s been a long time since I’ve seen a nontrivial library that didn’t implement its own error-handling macro. It’s at just that level where the C standard doesn’t provide one, but it’s easy to implement with what C does offer, and so everybody writes a new one.

The standard assert macro (hint: #include <assert.h>) will check a claim you make and then stop if and only if your claim turns out to be false. Every implementation will be a little bit different, but the gist is:

#define assert(test) (test) ? 0 : abort();

By itself, assert is useful to test whether intermediate steps in your function are doing what they should be doing. I also like to use assert as documentation: it’s a test for the computer to run, but when I see assert(matrix_a->size1 == matrix_b->size2), then I as a human reader am reminded that the dimensions of the two matrices will match in this manner. However, assert provides only the first kind of response (aborting), so assertions have to be wrapped.

Example 2-5 presents a macro that satisfies both conditions; I’ll discuss it further in Variadic Macros (and, for those of you unfamiliar with the custom, will give the rationale for the do-while wrapper in Cultivate Robust and Flourishing Macros). Note also that some users deal well with stderr, and some have no means to work with it.

Example 2-5. A macro for dealing with errors: report or record them, and let the user decide whether to stop on errors or move on (stopif.h)
#include <stdio.h>
#include <stdlib.h> //abort

/** Set this to \c 's' to stop the program on an error.
    Otherwise, functions return a value on failure.*/
char error_mode;

/** To where should I write errors? If this is \c NULL, write to \c stderr. */
FILE *error_log;

#define Stopif(assertion, error_action, ...) do {                 \
        if (assertion){                                           \
            fprintf(error_log ? error_log : stderr, __VA_ARGS__); \
            fprintf(error_log ? error_log : stderr, "\n");        \
            if (error_mode=='s') abort();                         \
            else                 {error_action;}                  \
        } } while(0)

Here are some imaginary sample uses:

Stopif(!inval, return -1, "inval must not be NULL");
Stopif(isnan(calced_val), goto nanval, "Calced_val was NaN. Cleaning
up, leaving.");
...
nanval:
    free(scratch_space);
    return NAN;

The most common means of dealing with an error is to simply return a value, so if you use the macro as is, expect to be typing return often. This can be a good thing, however. Authors often complain that sophisticated try-catch setups are effectively an updated version of the morass of gotos that we all consider to be harmful. For example, Google’s internal coding style guide advises against using try-catch constructs, using exactly the morass-of-gotos rationale. This advises that it is worth reminding readers that the flow of the program will be redirected on error (and to where), and that we should keep our error-handling simple.

How Should the Error Indication Be Returned?

I’ll get to this question in greater detail in the chapter on struct-handling (notably, Return Multiple Items from a Function), because if your function is above a certain level of complexity, returning a struct makes a lot of sense, and then adding an error-reporting variable to that struct is an easy and sensible solution. For example, given a function that returns a struct named out that includes a char* element named error:

Stopif(!inval, out.error="inval must not be NULL"; return out
             , "inval must not be NULL");

GLib has an error-handling system with its own type, the GError, that must be passed in (via pointer) as an argument to any given function. It provides several additional features above the macro listed in Example 2-5, including error domains and easier passing of errors from subfunctions to parent functions, at the cost of added complexity.



[6] By the way, a C++ compiler engages in what is known as mangling of the code. In GDB, it shows, and I’ve always found debugging C++ code from the GDB prompt to be painful. Because C code compiles without mangling, I find GDB to be much more usable for C, and having a GUI that unmangles the names is not necessary.