Well Commented Code
This page needs updated to talk about Doxygen and Javadoc.
In class when I am programming examples live I generally do not put very many comments in the code. This is really for three (intertwined) reasons: first, I am actively talking about the code, so it is really verbally commented in class; secondly, since it is verbally discussed, I don’t want to take the time to type out comments in class; and thirdly, since I am displaying in a large font, I want to have as much code as I can on the screen.
However, when writing real code, commenting is very important!
Examples on this page are taken from one source file of the Tcl scripting language implementation, tclUnixChan.c.
File Header Comments
Every file ought to have a header comment block. This is a comment block at the beginning of the file which generally contains:
- A short description of the file’s purpose or contents
- The (original) author’s name
- A copyright notice
- Possibly automatically generated version information
- Possibly automatically generated history content – the original creation date, a series of modification dates along with who modified it and why
- An optional longer description of the file’s purpose/contents or any special needs or concerns that someone needs to know about in order to use or modify the code in it.
Here’s an example:
/*
* tclUnixChan.c
*
* Common channel driver for Unix channels based on files, command
* pipes and TCP sockets.
*
* Copyright (c) 1995-1997 Sun Microsystems, Inc.
* Copyright (c) 1998-1999 by Scriptics Corporation.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
* RCS: @(#) $Id: tclUnixChan.c,v 1.17 2000/04/19 09:17:03 hobbs Exp $
*/
Declaration Comments
A file often has various declaration blocks after its header. These might be defined constants, macros, user-defined data types, structs, and function prototypes. These all need explained to a greater or lesser extent.
A type definition example:
/*
* This structure describes per-instance state of a file based channel.
*/
typedef struct FileState {
Tcl_Channel channel; /* Channel associated with this file. */
int fd; /* File handle. */
int validMask; /* OR'ed combination of TCL_READABLE,
* TCL_WRITABLE, or TCL_EXCEPTION: indicates
* which operations are valid on the file. */
struct FileState *nextPtr; /* Pointer to next file in list of all
* file channels. */
} FileState;
A constant declaration example:
/*
* The following defines the maximum length of the listen queue. This is
* the number of outstanding yet-to-be-serviced requests for a connection
* on a server socket, more than this number of outstanding requests and
* the connection request will fail.
*/
#ifndef SOMAXCONN
#define SOMAXCONN 100
#endif
#if (SOMAXCONN < 100)
#undef SOMAXCONN
#define SOMAXCONN 100
#endif
Function Header Definition Comments
Functions (procedures, methods, etc) always should have a header comment describing them. This should state the name, the purpose, the inputs, the outputs, and any side effects. Here’s one example:
/*
*----------------------------------------------------------------------
*
* FileInputProc --
*
* This procedure is invoked from the generic IO level to read
* input from a file based channel.
*
* Results:
* The number of bytes read is returned or -1 on error. An output
* argument contains a POSIX error code if an error occurs, or zero.
*
* Side effects:
* Reads input from the input device of the channel.
*
*----------------------------------------------------------------------
*/
static int
FileInputProc(instanceData, buf, toRead, errorCodePtr)
ClientData instanceData; /* File state. */
char *buf; /* Where to store data read. */
int toRead; /* How much space is available
* in the buffer? */
int *errorCodePtr; /* Where to store error code. */
{
...
Note that in the example above, the inputs to the function (the parameters) are described in separate comments from the header comment. That is the style this project chose to stick to. I would probably use a different style.
In-line Code Comments
Some people might claim that well-written code is “self-documenting” and doesn’t need comments, but this is not true! Even well-written code with the best-chosen variable and function names still often needs explained in a more natural manner.
It is true that silly comments like “increment x” are of no use and actually make code worse because of their clutter, but well-reasoned explanations for why a block of code is what it is are very useful. An example is:
/*
* Assume there is always enough input available. This will block
* appropriately, and read will unblock as soon as a short read is
* possible, if the channel is in blocking mode. If the channel is
* nonblocking, the read will never block.
*/
bytesRead = read(fsPtr->fd, buf, (size_t) toRead);
if (bytesRead > -1) {
return bytesRead;
}
*errorCodePtr = errno;
Another example is:
/*
* If the baud rate does not correspond to one of the known mask values,
* choose the mask value whose baud rate is closest to the specified
* baud rate.
*/
for (i = 0; speeds[i].baud >= 0; i++) {
diff = speeds[i].baud - baud;
if (diff < 0) {
diff = -diff;
}
if (diff < bestDiff) {
bestIdx = i;
bestDiff = diff;
}
}
Another example is:
if (translation != NULL) {
/*
* Gotcha. Most modems need a "\r" at the end of the command
* sequence. If you just send "at\n", the modem will not respond
* with "OK" because it never got a "\r" to actually invoke the
* command. So, by default, newlines are translated to "\r\n" on
* output to avoid "bug" reports that the serial port isn't working.
*/
if (Tcl_SetChannelOption(interp, fsPtr->channel, "-translation",
translation) != TCL_OK) {
Tcl_Close(NULL, fsPtr->channel);
return NULL;
}
}
In all of these examples, the comment explains some high-level requirement for why the code is there and what it is doing. It does not bother with the detail of each statement. These are examples of great comments.
Super-Powering Comments with Auto-doc Tools
A recent trend in software development is the introduction of document-generation tools that process your source code and automatically produce documents that describe it. While they look at your data types, procedure and class declarations and such, their real power is in processing structured comments.
When deciding to use these tools, you place special annotations in your comments and you structure your comments is specific ways so that the auto-doc tool extracts out what you wrote and generates meaningful documentation from it. So not only do your comments help explain the code in-place, they also help form external documentation which someone can use in addition to the code.
The first really successful tool that popularized this approach was Javadoc, which was for the Java language. It was shipped by Sun along with the Java compiler and runtime, so everyone who was programming Java had it, and started using it. You can find information on it at Javadoc.
A popular C/C++ auto-doc tool is Doxygen.
NaturalDocs looks interesting.