General Coding Guideline
The main purpose of this
guideline is to make sure the programs written
by different people follow a
consistent style, so that it is easy for everybody to understand and modify
the code.
Naming:
1. Use meaningful names for classes and methods. If
a name consists of multiple words, separate words by using underscores or
capitalize the first letter of each word. It may not be a good idea to use
names such as "a, b" or "arg1, arg2".
2. For global names (top level
classes, functions, variables), do not use very common words to avoid name
collision.
File names:
3. All header files use suffix .h, all C files
use suffix .c, all C++ files use suffix .cc.
4. Typically, a module
has a header file .h and a implementation part .cc or .c file. The stem of
the two files must be the same.
Indentation style:
4. The first line of the .cc files must be:
// -*- mode:C++; tab-width:4; c-basic-offset:4;
indent-tabs-mode:t -*-
5. The first line of the .c file must be:
//
-*- mode:C; tab-width:4; c-basic-offset:4; indent-tabs-mode:t -*-
6. For .h files, depends on whether it is written in C++ or C, you may choose
between 4 or 5 respectively.
Header file:
7. Header files should
follow the following format:
# ifndef __XYZ_H__
# define
__XYZ_H__
// put body here.
...
# endif
8. If a module
xyz is written in C, with its interface defined in xyz.h, hen we should
enclose the whole body using the following construct:
# ifdef
__cplusplus
extern "C" {
# endif
// put body
here
...
# ifdef __cplusplus
}
# endif
Copyright
notice:
9. The copyright notice should follow immediately after the
indentation line.
10. A brief description of the file and the authors
should follow the copyright notice.
Interface design guidelines:
11. Try to make sure that the interface is defined by necessity.
11.1 If a declaration can be put in the .c or .cc files, then do so.
11.2 If a method should not be called by external programs, declare
it as private.
11.3 If we only need to keep a pointer of an object,
then we do not really need to include the class interface file. Instead, we
can simply write a line "class ReferedClass".
11.4 If there is no dependence or hierarchy among the functions inside a .cc file, sort them by their names to
make reading the code easier.
12. Method inlining.
Only do so when the method is very short.
Miscellaneous:
13. Constructors. The simple initialization of member variables in
the constructor should follow the member variable initialization style,
aka:
Constructor(...) :
var1(...),
var2(...)
{
...
}
14. If you need to refer to a constant multiple times, try to do "#
define" or "enum" (only apply to integers).
My rules of thumb are:
(a) if the constant is not part of the interface, put them in .c .cc or
in the class definition's "private" section (for enum only), and vice
versa.
(b) if the constants are tightly coupled with the class, put them as
enum inside the class.
15. If most of the fields you are going to manipulate directly, try to
use "struct" instead of "class".
16. It is a bad habbit to put "printf" or "cout <<" in the program.
Instead, try to use the "tmpi_debug" module (this allows you to do
dynamically change from console output to log file output without
changing the programs).
Error Handling:
It is a complicated situation when it comes to error handling.
There are several mechanisms when may use:
(1) return an error status from a function.
(2) throw an exception.
(3) assertion failure.
(4) print out error messages.
The following guidelines range from relatively conventional and typical
usage to empirical rules in my common practices.
(a) assertion should be (and only be) used to test invariance which
should always hold (such as the object's internal status). If the
assertion fails, the program will be core-dumped. It is meant to be used
in situations when something should have never happened occurred. For
example, if you implement a double-link list, then for each item, you
can assert(item->next->previous == item). If an assertion fails, it
typically should reflect some logical bug in the program. Bad input
parameters typically should not be considered (there are exceptions).
(b) Failure in constructor should throw an exception. Since there is no
return value of constructors, I think it is better to throw an exception
instead of keep the program running. (Some of my old code does not
follow this rule, instead, I set a status variable inside the object,
and provide a function good() to check whether the initialization
succeeds or not).
(c) Error message or logging is an effective way to monitor a server
program in the long run (the server may have passed various short tests,
and is put into production use, and logging allow us to detect problems
that only show up in the long run.) Currently the "tmpi_debug" module
should suffice the purpose, I am planning to add more features to that
module. On the other hand, DO NOT directly print out error messages,
because your program may not run in console mode (may not even have a
terminal associated with it). Also, the main program may have its own
control of terminal (using ncurse library). And console output would
mingle with the terminal output.
(d) I personally do not see a clear cut between returning error status
and throwing exceptions. This is probably due to the fact that the error
status approach is compatible with a large amount of legend code
pre-dated C++. In general, exception should be a better way to report
errors, particularly UNEXPECTED ERRORS. This would simplify the flow of
the caller functions and make them easy to understand. On the other
hand, for errors that can be reasonably expected (for example, file not
found, end of the file), it is probably better to use error status
instead of exceptions.
(e) The ideal situation of error reporting is to concatenate error
messages while the stack unwinds (through either return or throw). The
error may eventually either be handled at a level that has sufficient
knowledge to recover from the error; or may just be dumped to the log or
screen and the program quits.
Last modified: Mon Jul 21 21:58:37 PDT 2003