In the file header a put a detailed description of what the file (class) does and why the file (object) was created if there is a technical reason. If the object's inner workings aren't painfully obvious (most of the time not -- there is no such thing as software that is too simple), I also put a high level design (not long -- the longest I've ever written was maybe 4 paragraphs) that someone who has never seen the code before can read and get a basic understanding of how the object works and how all the object's data members work.
For each function, I document precisely what the function does and what the function returns for all given inputs, valid or invalid. Example:
1 2 3 4 5 6
|
// Uses rand() to return a random number distributed uniformly in the range [ low, high ]. If
// high > low, throws std::range_error.
int get_rand_range( int low, int high )
{
// ...
}
|
If the implementation of the function ignores return codes, this must be documented either at the particular line of code or in the function header as a warning to future developers. If making certain changes to the implementation of the function would require changes to other functions, this must be documented. For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
// Sets the size, in pixels, of all of the controls to be placed into the dialog box.
// Warning: this function works in tandem with draw_controls() in that if you change
// the size of a control, you might need to adjust the x,y coordinates at which the
// controls get drawn.
void size_controls()
{
myDropBox.set_size( 200, 50 );
// Buttons are sized the same because non-uniform button sizes look weird
okButton.set_size( 100, 50 );
cancelButton.set_size( 100, 50 );
}
// Draws the controls in the dialog box if they have not already been drawn or are out-of-date.
// If the controls have already been drawn but are out of date, they are first removed, then
// redrawn. If the controls have already been drawn and are up-to-date, this function does
// nothing.
//
// Warning: this function works in tandem with size_controls() in that if you change
// the size of a control, you might need to adjust the x,y coordinates at which the
// controls get drawn.
void draw_controls()
{
// ...
// Warning: positions of these controls are dependent upon their size. See size_controls()
myDropBox.show_at( 10, 10 );
okButton.show_at( 10, 80 );
cancelButton.show_at( 120, 80 );
}
|
Individual lines or blocks of code should be documented if there is something non-obvious about them. For example, if it is not obvious that line 3 _must_ be executed before line 4, it should be documented. If it isn't obvious why the line of code is there in the first place, it should be documented. Do not document what the line does unless it is not obvious. Examples:
1 2 3 4 5 6 7 8 9 10 11 12 13
|
// Copies the file "src" (which may contain a path) to "dst" (which may contain a path).
// If "dst" already exists, it is overwritten.
//
// Returns true if the file was successfully copied, or false in all other cases. If false is
// returned, the destination file is not modified.
bool copy_file( const std::string& src, const std::string& dst )
{
std::ifstream input_file( src.c_str() );
// Per API, we don't want to touch dst if src can't be opened.
if( !input_file )
return false;
}
|
Example of bad comments:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
bool copy_file( const std::string& src, const std::string& dst )
{
std::ifstream input_file( src.c_str() );
if( !input_file )
return false;
// Create the output file (BAD: DUH, isn't it obvious that's what this line does?)
std::ofstream output_file( dst.c_str() );
// Loop while not at EOF of the source file (BAD: DUH, isn't it obvious we'd do that?)
while( input_file )
{
char ch;
input_file >> ch; // Read one character (BAD: DUH, obvious. Maybe say why one
// character at a time since this as bad as a file copy implementation
// could get.)
if( input_file )
{
output_file << ch; // Write to output file (BAD: DUH... obvious again.)
if( !output_file )
return false;
}
}
return true;
}
|
I avoid putting lengthy comment blocks in the middle of a function just because it detracts from the readability of the function by making the code lines far apart. If I can't explain succinctly yet thoroughly what I want to explain at the line of code, I'll put a block of comments in the function header and perhaps document the line of code with a one-liner and a "see function header for details".
I generally use doxygen-friendly comments, however I don't document things exhaustively just to make the doxygen output complete. For example, I don't use param comments for every parameter on every function. Sometimes its just too obvious and there isn't really anything more to say beyond the name of the parameter.
I also don't use comments just to adorn the code and make it pretty. Comments are to facilitate understanding, not to make the code pleasing to look at. That means skipping
the * on the right side of the comment block -- adding a box of asterisks around the comment does not aid in understanding the code and is, quite frankly, annoying to have to maintain.