[Top] | [Contents] | [Index] | [ ? ] |
AnswerFinder programming guidelines
AnswerFinder project http://www.ics.mq.edu.au/~diego/answerfinder
Copyright 2006, Menno van Zaanen, Macquarie University.
Most of a programmer's efforts are aimed at the development of correct and efficient programs. But the readability of programs is also important. There are many "standards" in use. Since following them will not guarantee good code, these "standards" are actually guidelines of style. Each one has its proponents and detractors. This document collects a summary of guidelines for writing and commenting the AnswerFinder code. The essential point is that a program is a medium of communication between humans; a clear, consistent style will make it that much easier to communicate.
1. Important | The Most Important Points. | |
2. Program Documentation | Information on the documentation in the program code. | |
3. Names | How to name things in your code. | |
4. Variables and Constants | Information on variable and constant use. | |
5. Program Formatting | How to format your code. | |
6. Class Files | Information on the files containing classes. | |
Concept Index | An index of the concepts. |
1.1 General | General guidelines | |
1.2 Common-sense Rules | Explanation of the common-sense rules | |
1.3 General Documentation | How to write general documentation |
When you write a program, ask yourself these questions:
//
for commenting, and /* … */
to
comment-out code for debugging purposes.
2.1 Program Heading | Program header comments | |
2.2 Preprocessor Section | Comments in the preprocessor section | |
2.3 Function Headings | Comments in function headings | |
2.4 Group Ends | Commenting ends of groups | |
2.5 Comment Use | How to use comments in code | |
2.6 English | What English variant to use | |
2.7 Abbreviations | How to use abbreviations |
The main program file should begin with a comment section in the following form and with the following information filled in:
//////////////////////////////////////////////////////////////////////////////// // Filename: < filename > //////////////////////////////////////////////////////////////////////////////// // This file is part of the AnswerFinder package. //////////////////////////////////////////////////////////////////////////////// // < Description of what is in the file > //////////////////////////////////////////////////////////////////////////////// // $Log:$ //////////////////////////////////////////////////////////////////////////////// |
The end of the file should contain a line indicating that the file ends there:
// end file: < filename > |
In the preprocessor section, include statements for header files should have comments indicating the types, constants, variables, or functions used from the header. For example,
#include <iostream> // cin, cout, <<, >>, endl #include <cmath> // sin, cos #include "list.h" // List |
Include documentation, as needed, of the following form for each function:
//////////////////////////////////////////////////////////////////////////////// // Function: < Name of function > // < Short description of what function does in terms of the received // parameters > // < Assumptions about the state or values of the received parameters > //////////////////////////////////////////////////////////////////////////////// void sampleFunction(…) { … } // end function: sampleFunction |
Comment the ending curly brace of each compound statement if needed for clarity. Such comments should be quite brief, mnemonic, and UNIQUE within the function or main program. For example,
//////////////////////////////////////////////////////////////////////////////// // Function: try // Attempts to do some things… //////////////////////////////////////////////////////////////////////////////// void try(…) { … while (!done) { … } // end while: done is not yet true … } // end function: try |
Always comment the end of functions, struct, and class declarations.
Comment code to improve clarity. Comments should tell WHAT is being done or why it is being done, not how it is being done. For example,
i = i - 1; // Adjust i to point to the end of the previous word: |
is better than
i = i - 1; // Subtract 1 from i: |
Often these comments will be phrases found in the analysis and design.
Comments should be in good English. Grammar and spelling should be correct.
Abbreviations in comments should rarely be used, and then only those that would be found in a standard dictionary.
3.1 Language | What language to use | |
3.2 Name and Scope | Issues around names and scopes | |
3.3 Types | Names of types | |
3.4 Variables | Variable names | |
3.5 Constant Names | Names of constants | |
3.6 Methods and Functions | Names of methods and functions | |
3.7 Namespaces | Namespace names | |
3.8 Template Types | Names of types in templates | |
3.9 Common Prefixes and Suffixes | Info on how to use pre- and suffixes |
All names should be written in English. English is the preferred language for international development. Only use widely accepted abbreviations (e.g. those that you would find in a dictionary).
Variables with a large scope should have long names, variables with a
small scope can have short names. A programmer reading short named
variables should be able to assume that its value is not used outside
a few lines of code. Common scratch variables for integers are
i
, j
, k
, m
, n
and for characters
c
and d
.
Names representing types must be in mixed case starting with upper
case: Line
, SavingsAccount
. This is common practice
within the C++ development community.
Variable names must be in mixed case starting with lowercase:
line
, savingsAccount
. This is common practice within
the C++ development community. It makes variables easy to distinguish
from types, and efficiently resolves potential naming collision as in
the declaration,
Line line; |
Named constants (including enumeration values) must be all uppercase
using underscore to separate words: MAX_ITERATIONS
, PI
.
This is common practice within the C++ development community. In
general, the use of such constants should be minimised. Never use
#define
to define constants. Always use the type safe
const <type>
notation. In some cases implementing the value as
a method may be an even better choice. Instead of,
const int MAX_ITERATIONS = 25; |
You could write,
//////////////////////////////////////////////////////////////////////////////// // Function: getMaxIteration // Returns the number of maximum iterations. //////////////////////////////////////////////////////////////////////////////// int getMaxIteration() { return 25; } // end function: getMaxIteration |
This is especially important if you think the "constant" value may change in the future.
Names representing methods or functions must be verbs and written in
mixed case starting with lowercase: getName()
,
computeTotalWidth()
. This is common practice within the C++
development community. This is identical to variable names, but
functions in C++ are already distinguishable from variables by their
specific form.
Typical verbs used are:
get
set
When an attribute is accessed directly.
compute
When there is some (possibly time-consuming) operation.
find
When there is a simple look up method with a relatively small number of operations involved.
initialize
The American spelling should be preferred. Abbreviations (e.g. init) should be avoided.
Names representing namespaces should be all lowercase: analyzer
,
iomanager
, mainwindow
. This is common practice in the
C++ development community.
Names representing template types should be a single uppercase letter:
template<class T> … template<class C, class D> … |
This is common practice in the C++ development community. This makes template names stand out relative to all other names used.
The suffix List or Array can be used on names representing lists or
arrays: vertexList
, itemArray
. Simply using a plural
form should be avoided since it is difficult to distinguish from the
corresponding singular form.
The prefix n
should be used for names representing a number of
objects: nPoints
, nLines
. The notation is taken from
mathematics where it is an established convention for indicating a
number of objects.
The suffix No
should be used for variables representing an entity
number: tableNo
, employeeNo
. The notation is taken from
mathematics where it is an established convention for indicating a
number of objects. An elegant alternative is to prefix such variables
with an i
: iTable
, iEmployee
. This effectively
makes them named iterators.
4.1 Global Declarations | When to use global declarations | |
4.2 Function Variable Identifiers | Usage of function variables | |
4.3 Constants | Usage of constants | |
4.4 Pointers and References | Usage of pointers and references |
Constants should be declared globally if there is a possibility of using them in more than one function, otherwise they should be declared locally to the function that uses it. While there are valid reasons for having global variables, they generally should be avoided.
Each (non-class attribute) variable identifier that occurs in a function should be local to that function - that is, declared in the function's header or declaration part.
const
.
Constants in your algorithm should be replaced by constant identifiers
in your program. Exceptions should be made only when the constant
conveys its own meaning, such as 0
as an initial value for a
sum or 1
to start a count, or is part of a constant
mathematical formula, such as 2
in 2 * PI * r
.
Pointers and references should have their reference symbol next to the variable name rather than to the type name:
float *x; // NOT: float* x; int &y; // NOT: int& y; |
It is debatable whether a pointer is a variable of a pointer type
(float* x
) or a pointer to a given type (float *x
).
Important in the recommendation given though is the fact that it is
impossible to declare more than one pointer in a given statement using
the first approach. I.e. float* x, y, z;
is equivalent to
float *x; float y; float z;
. The same goes for references.
Note, however, that variables should always be declared on a new line
in a new statement.
5.1 Statements Outside Function Body | Global statements | |
5.2 Local Declarations | Info on local declarations | |
5.3 Blank Lines | How to use blank lines | |
5.4 Indenting | Code indenting | |
5.5 Groups | Grouping code | |
5.6 Declarations | Info on declarations | |
5.7 Function Heading | Function headers | |
5.8 Width | Code width | |
5.9 Control Statements | Info on control statements | |
5.10 Switch Statements | Info on switch statements | |
5.11 Block Alignment | Aligning code blocks | |
5.12 Conditionals | Info on conditionals | |
5.13 Comments | Using comment in code | |
5.14 Main Curly Braces | Placing curly braces | |
5.15 Blank Spaces | Where to place blanks | |
5.16 Function Size | Size of functions |
Any preprocessor statements (#include
, #define
, etc.)
should be at the beginning of a file (after the program file heading
comment). Any typedef type declarations should follow, then any
global constants and static variables. Finally, function prototypes
should appear just before the main function. No other statements
should appear outside a function body.
All local constants and local variables should be declared just before they are needed. By doing this, a variable will only be available in the minimum scope it can be. For example, variables that are only used inside a loop are only available within that loop.
A blank line should be used to separate declarations from the executable statements. In general, blank lines should be used wherever their use will improve readability. Use two blank lines to separate function, struct and class definitions and declarations.
Indenting should be used to convey structure. A level of indentation
should be exactly 2 spaces. Indenting should be consistent.
Normally, {
increases the level of indentation with one and
}
decreases it.
Always scope brackets for the blocks in a control structure, even if the block contains one sentence only:
if (num > 0) { cout << "The number is positive" << endl; } |
This way there is less re-editing to do if the block size grows larger than one statement. Less re-editing means less chance for bugs, and less file version differences when using source code control.
For declarations, each identifier should be declared on a separate line. The type identifier should be indented.
For example, in the main function, we might declare
int main(void) { const int MAX_SIZE = 100; const float PI = 3.14159; int i; // Outer loop index int j; // Inner loop index int testArray[MAX_SIZE]; // Array of test scores Point p1; … } // end function: main |
Note that variables should only be declared just before they are needed.
Also, comments should explain the function of each variable where appropriate.
In a function heading, there should be one formal parameter per line, lined up under each other as shown below. Comments about the parameters should go in the comment section of the function.
void findItem(int &item, int position) |
A statement should not be longer than a screen's width (80 characters). If a non-I/O, non-function call statement must be continued on more than one line, the second line should be indented and successive continuation lines should be aligned with the second line of the statement. For example, we might write
while ((('a' <= line[i]) && (line[i] <= 'z')) || (('A' <= line[i]) && (line[i] <= 'Z'))) { i++; } |
The indentation of the loop expression should be more than a regular indentation level. Also, the operator that links the sub-expressions should be on a new line, to show that it's a continuing expression.
An I/O statement should be broken up so that the <<
or
>>
operators line up. For example, we might write
cout << setw(15) << name << setw(30) << address << setw(15) << phone << endl; |
A function call statement should be broken up so that the function arguments are lined up. For example, we might write
void computeSample(argument1, argument2, argument3, argument4, argument5, argument6); |
For the statement that follows if
, else
, while
,
for
, do
, and switch
: The statement should start
on the next line and be indented. For example,
if (first <= last) { found = true; } |
Use the comment Fallthrough if a case
keyword does not have a
break
statement. Leaving the break
out is a common
error, and it must be made clear that it is intentional when it is not
there:
switch (condition) { case ABC: statements; // Fallthrough case DEF: statements; break; default: statements; } |
Don't indent the case labels, but do indent the statements within each case.
Closing curly braces should match the indentation level of the statement that contained the open curly brace. Remember that all compound statements (that contain other statements) should use curly braces.
Each line of the body of a compound statement should be indented. For example,
if (a[middle] == item) { item = a[middle]; found = true; position = middle; } // end if: match found |
Column alignment should be observed for each set of reserved words if and else. This include multi-branch constructs. However, this should be clear, because the open curly brace already results in a higher indentation level. For example:
if (x > 90) { grade = 'A'; } else if (x > 80) { grade = 'B'; } else if (x > 70) { grade = 'C'; } else if (x > 60) { grade = 'C'; } else { grade = 'F'; } |
Comments that describe one or more statements should be immediately above and aligned with the statement or collection of statements which they describe. For example,
j = i; while ((j > 1) && (a[j - 1] > a[j])) { // a[1..j - 1] is unsorted and a[j..i] sorted: swap(a[j], a[j - 1]); --j; } // end while |
The curly braces for functions should be similar to their use in conditional statements. For example,
void findMinIndex(…) { … } // end function: findMinIndex |
At least one space should be used in the following locations within C++ text (this does not apply within comments and character strings):
//
and two spaces before //
if the comment follows
code (excluding }
). However, keep track of the proper indentation
level;
{
in
function declarations.
This should lead to code that looks like:
if ((foo == 0) && (!bar)) { getFooBar(&foo, &bar); } |
A function should fit on one screen (about 25 lines) if possible and must fit on a listing page (about 50 lines). Ideally, the analysis and design will not produce code that is more than a page long, but if the code does not fit initially, introduce one or more new functions to split up the work in logical places. This, of course, is not a strict rule, more a rule of thumb.
6.1 Reusability | Reusability of code | |
6.2 Inclusion of Header File | Including headers | |
6.3 Header File Documentation | Comments in headers | |
6.4 Function Prototypes | Prototypes | |
6.5 Function Documentation | Comments in Functions | |
6.6 Constant Functions | ||
6.7 Class Documentation | Comments in classes | |
6.8 Order | Order of code | |
6.9 Function Comment Headers | Headers of functions |
Class definitions and implementations should be divided into multiple
files so that we can reuse them easily. By convention, the class
definition is stored in a header file which has the extension
.h
. The implementation of the class member functions are
stored in a source file with the extension .cpp
. The
main program is stored in a separate source file that includes the
header files for each class used.
Every class header file should use compiler directives #ifndef
,
#define
, and #endif
to ensure a header file is only
included once. The symbol should be the name of the header file in
all lower-case letters with an underscore (_
) replacing the dot
and two initial and trailing underscores. For example, for the header
file counter.h
, the directives would be:
#ifndef __counter_h__ #define __counter_h__ //////////////////////////////////////////////////////////////////////////////// // Class: Counter // Stores some things… //////////////////////////////////////////////////////////////////////////////// class Counter { … }; // end class: Counter #endif // __counter_h__ |
Every header file should start with a comment block with the following information filled in:
//////////////////////////////////////////////////////////////////////////////// // Filename: < filename > //////////////////////////////////////////////////////////////////////////////// // This file is part of the AnswerFinder package. //////////////////////////////////////////////////////////////////////////////// // < Description of what is in the file > //////////////////////////////////////////////////////////////////////////////// // $Log:$ //////////////////////////////////////////////////////////////////////////////// |
The end of the file should contain a line indicating that the file ends there:
// end file: < filename > |
In class definitions, the qualifiers public, protected and private should not be indented and the member function prototypes and data member declarations should be indented. Data members should start with an underscore. Free function prototypes should appear after the class definition. For example,
//////////////////////////////////////////////////////////////////////////////// // Class: Counter // Stores some things… //////////////////////////////////////////////////////////////////////////////// class Counter { friend … public: //////////////////////////////////////////////////////////////////////////////// // Function: Counter // Constructor //////////////////////////////////////////////////////////////////////////////// Counter(); //////////////////////////////////////////////////////////////////////////////// // Function: Increment // Increments the counter by 1 //////////////////////////////////////////////////////////////////////////////// void Increment(); … protected: private: //////////////////////////////////////////////////////////////////////////////// // Variable: _value; // Current value of the counter //////////////////////////////////////////////////////////////////////////////// int _value; }; // end class: Counter //////////////////////////////////////////////////////////////////////////////// // Function: operator>> // bla //////////////////////////////////////////////////////////////////////////////// istream & operator>> (istream &in, Counter &theCounter); //////////////////////////////////////////////////////////////////////////////// // Function: operator<< // bla //////////////////////////////////////////////////////////////////////////////// ostream & operator<< (ostream &out, const Counter &theCounter); |
In class definitions, short comments describing what member functions do and what member data represent should be included as shown above.
Member functions that do not change the state of the object should be
declared as const
. For example,
//////////////////////////////////////////////////////////////////////////////// // Class: Counter // Stores some things… //////////////////////////////////////////////////////////////////////////////// class Counter { public: //////////////////////////////////////////////////////////////////////////////// // Function: getValue // Returns the current value of the counter //////////////////////////////////////////////////////////////////////////////// int getValue() const; … } // end class: Counter |
If needed (in abstract classes), certain member functions should be
declared = 0
.
Every class implementation file should start with a comment block with the following information filled in:
//////////////////////////////////////////////////////////////////////////////// // Filename: < filename > //////////////////////////////////////////////////////////////////////////////// // This file is part of the AnswerFinder package. //////////////////////////////////////////////////////////////////////////////// // < Description of what is in the file > //////////////////////////////////////////////////////////////////////////////// // $Log:$ //////////////////////////////////////////////////////////////////////////////// |
The end of the file should contain a line indicating that the file ends there:
// end file: < filename > |
Class member, friend, and overloaded function implementations should appear in the same order as presented in the class specification and class definition.
Function comment headers as described above should be included with member function definitions. If there are many function definitions, comments indicating grouping are helpful.
Jump to: | A B C D E F G H I L M N O P R S T V W |
---|
Jump to: | A B C D E F G H I L M N O P R S T V W |
---|
[Top] | [Contents] | [Index] | [ ? ] |
[Top] | [Contents] | [Index] | [ ? ] |
This document was generated by Diego Molla on February, 20 2007 using texi2html 1.76.
The buttons in the navigation panels have the following meaning:
Button | Name | Go to | From 1.2.3 go to |
---|---|---|---|
[ < ] | Back | previous section in reading order | 1.2.2 |
[ > ] | Forward | next section in reading order | 1.2.4 |
[ << ] | FastBack | beginning of this chapter or previous chapter | 1 |
[ Up ] | Up | up section | 1.2 |
[ >> ] | FastForward | next chapter | 2 |
[Top] | Top | cover (top) of document | |
[Contents] | Contents | table of contents | |
[Index] | Index | index | |
[ ? ] | About | about (help) |
where the Example assumes that the current position is at Subsubsection One-Two-Three of a document of the following structure:
This document was generated by Diego Molla on February, 20 2007 using texi2html 1.76.