|
SAIDA Coding Standard
Revision A - 1 APR 1996
|
| Name | Position | E-mail address |
---|
Author(s): | Mats Grahm | Technical Coordinator | pt93mg@pt.hk-r.se |
Responsible: | Mats Grahm | Technical Coordinator | pt93mg@pt.hk-r.se |
To: | SAIDA | | saida@pt.hk-r.se |
Cc: | | | |
Abstract
This document describes the coding rules and recommendation that are to be followed in the SAIDA project. The standard is based on Ellemtel's coding standard [Ellemtel 92], modified to be compatible with the Microsoft environment, AppWizard generated code, and SAIDA needs.
References in this document has the following meaning:
[Enn] Ellemtel rule #nn, possibly slightly modified.
[Rnn] Ellemtel recommendation #nn.
[S] Rule stated by SAIDA, or Ellemtel rule heavily modified to better suit SAIDA.
[Pnn] Ellemtel recommendation #nn, promoted to rule in SAIDA.
1 SAIDA Coding Rules
1.1 General
1 [E0] Every time a rule is broken, this must be clearly documented.
1.2 Files
2 [S] Include files always have the file name extension ".h".
3 [S] Implementation files always have the file name extension ".cpp".
4 [S] All file names should be in lower case.
5 [S] Implementation and header files for a specific class should have the base name equal to the class name (without the leading SC_).
1.3 Preprocessor & directives
6 [S] Every include file must prevent multiple inclusions of the file by defining a header-file specific constant. If the file defines a class, this constant should be constructed by appending the prefix SAIDA_ with the class name in uppercase, not including the SC_-prefix of class names. E.g:
#ifndef SAIDA_CLASSNAME
#define SAIDA_CLASSNAME
class SC_ClassName
{
...
};
#endif // SAIDA_CLASSNAME
7 [S] When classes are used as base classes, as members, or as return values, the header file might have to #include other header files. Classes that are only accessed via pointers (*) or references (&) shall not be included as include files. Instead, use class prototypes. E.g:
class SC_AnotherClass;
class SC_ThisClass
{
public:
void Foo(SC_AnotherClass& acRef);
};
8 [E10] Never specify relative names in #include directives.
9 [E35] Do not use the preprocessor directive #define to obtain more efficient code; instead, use inline functions. SAIDA comment: Efficiency is not a primary goal during the development phase. All sort of optimizations, including inlining, should be performed only after full functionality is implemented.
10 [E36 ] Constants are to be defined using const or enum; never using #define. E.g:
#define SAIDA_OK 0 // WRONG!
const int SAIDA_OK = 0; // permitted
enum RC{ OK, FILE_EROR, OTHER_ERROR } // permitted
1.4 Naming & Declarations
11 [S] Variables begin with a lowercase letter.
12 [S] Functions (including member functions) begin with an uppercase letter.
13 [S] Class names begin with "SC_", followed by the name. Both the "SC" and the first letter of the name should be uppercase. Ex: SC_String.
14 [S] Long names should be made more readable by starting each significant syllable with a uppercase letter. E.g. myLittleCosyVariable. Underscores are normally not used within the name, but can be used to separate a prefix from the actual name.
15 [S] Constants and enumeration values are all uppercase. This makes it easy to identify constant values irrespectively of how they are implemented.
16 [E16] Never begin an identifier with underscore.
17 [E21] No member functions are to be defined within the class definition.
18 [E32] The names of formal arguments to functions are to be specified and are to be the same both in the function declaration and in the function definition.
19 [E33] Always specify the return type of a function explicitly.
20 [E31] Do not use unspecified function arguments (ellipsis notation, variable number of arguments like printf() ).
21 [E39] Normally each variable is to be declared in a separate declaration statement. Several variables declared in one statement is not recommended, and allowed only for simple types, never for pointers.
22 [P26] The dereference operator '*'and the address-of operator '&' should be directly connected with the type names in declarations and definitions. E.g:
char* name;
SC_Person* parent;
It is more natural to think about "name" as a "pointer to char", than "name dereferenced"
as a "char". The latter is of course also true, but hardly relevant;
23 [P21] When declaring functions, the leading parenthesis and the first argument (if any) are to be written on the same line as the function name. If space permits, other arguments and the closing parenthesis may also be written on the same line as the function name. Otherwise, each additional argument is to be written on a separate line (with the closing parenthesis
directly after the last argument). E.g:
void ShortFunction(int firstArg, double secondArg);
longReturnType* SC_LongClassname::LongFunction(int firstArg,
double secondArg);
24 [P42] Use pass by value only for scalar built-in types. Use pointers if a function stores the address for later use, or if it sometimes is necessary to use NULL-values. In all other cases, use references.
1.5 Scope & Access
25 [S] Normally, always make member variables private. If the values are to be accessed from outside the class, use Get/Set methods.
26 [S] For closely related classes (i.e. collections and iterators), friend declarations are allowed.
27 [S] Only use protected if the class is designed to be subclassed.
28 [S] If you resort to public variables, document why it is needed.
29 [E38] Variables are to be declared with the smallest possible scope.
30 [E23] A member function that does not affect the state of an object (its instance variables) is to be declared const.
31 [E24] If the behaviour of an object is dependent on data outside the object, this data is not to be modified by const member functions.
1.6 Comments
32 [E4] Every file that contains source code must be documented with an introductory comment that provides information on the file name and its contents. SAIDA's template should be used. (saida_comment.txt)
33 [E6] All comments are to be written in English.
34 [P9] Always use // for all comments. /* ... */ should be reserved for debugging purposes, and since such comments can not be nested, they should be avoided in normal comments.
35 [P9] Two styles of comments can be used; strategic and tactic:
Strategic comments are used to describe the overall function of a following block of code, i.e. a function. E.g:
///////////////////////////////////////////////////////////////
// A revolutionary new algorithm that solves the travelling
// salesman problem in linear time!
Tactic comments is written on the same line, and to the right of the code it explains. It is
recommended that complex sequences of code is described this way. E.g:
for(step = start; step; step = step->next) // iterate through list in
{ // order to multiply members
temp = (step->GetIntValue()) << count; // rotating count bits
if(temp > MAX) break; // done when found big enough
if(temp > currentMax)
{
step->SetResult(temp); // update if bigger than current
currentMax = temp; // ... and remember the biggest
}
}
Make sure that the comments EXPLAIN what is done, not just repeat what is obvious
from reading the code. Also, keep the lines short.
1.7 Statements & Expressions
36 [E37] Avoid the use of numeric values in code; use symbolic values instead. E.g:
const double PI = 3.141592;
37 [E40] Every variable that is declared is to be given a value before it is used.
38 [E41] If possible, always use initialization instead of assignment. E.g:
int myValue = 0;
39 [S] Use NULL, not 0 when assigning or testing pointers.
40 [S] Always use explicit cast when formal and actual arguments differ, or when performing calculations with mixed-type operands. Never rely on implicit conversions. E.g:
r = sqrt((double) x);
i = (int) (PI * (double) i);
41 [E47 ] The code following a case label must always be terminated by a break statement.
42 [E48] A switch statement must always contain a default branch which handles unexpected cases.
43 [E49 ] Never use goto.
44 [E50] Do not use malloc, realloc or free. (Use new and delete.)
45 [E51] Always provide empty brackets ("[]") for delete when deallocating arrays. E.g:
delete[] myArray;
46 [P24] Braces ("{}") which enclose a block are to be placed in the same column, on separate lines directly before and after the block. The braces should be placed on the OUTER block`s indentation level. See comment example above. (Rule 35)
47 [S] For bottom level blocks, with no further nested blocks, two variants are permitted. First, an if, for, or while statement can be followed by a simple statement on the same line. E.g:
if(a > b) cout << a << " is bigger" << endl;
Note that the following is NOT permitted:
if(a > b)
cout << a << "is bigger" << endl;
The reason is that it is easy to add new lines at the inner level without realizing that the
code now will need braces.
48 [S] For innermost blocks the following format is also allowed:
for(i = 0; i < 100; i++) {
// some code WITHOUT braces
}
Some people like this style, others find it hard to read. Restricting it's use to bottom level
blocks is hopefully a compromise most people can live with.
49 [P23] Always write the left parenthesis directly after a function name. Comment: This means without any whitespace between.
50 [P27] Do not use spaces around '.' or '->', nor between unary operators and operands.
51 [S] In a function definition, the return type of the function may be written on a separate line directly above the function name. E.g:
longReturnType*
SC_LongClassname::LongFunction(int firstArg, double secondArg);
52 [S] Placement of arguments in function definitions and function calls should follow the same rule as for function declarations. (Rule 23)
53 [S] Use break if this avoids the use of flags, or if the condition is so complex that the while(..) or for(...) statement does not fit on one line, but can be made simpler by leaving out some break condition. Only use continue if this simplifies the control structures.
54 [S] Only write logical expressions of the type if(done) or while(!error) if the variable name clearly indicates the meaning of TRUE resp. FALSE. Statements like while(--i) and if(a) should be avoided.
55 [P56] Use parentheses to clarify the order of evaluation for operators in complex expressions, even if the evaluation order is unambiguous by priority and/or associativity.
56 [S] In all comma-separated lists (i.e argument lists) a space should be inserted directly after the comma. E.g:
FuncCall(firstArg, secondArg, thirdArg);
57 [S] Operators and operands should be separated by one space in expressions. Parenthesis should not. E.g:
a = (a + b * (c - d));
58 [S] When breaking a long logical expression into several lines, break it after the && or ||. E.g:
if(a < (b + c) &&
c != (d * e) &&
d == e)
1.8 Defensive rules & Safeguards
59 [E25,27] A class which uses "new" to allocate instances managed by the class, must define a copy constructor and an assignment operator unless it obviously is used as a singleton object.
60 [E26] All classes which are used as base classes and which have virtual functions, must define a virtual destructor.
61 [S] A function must never return a reference or a pointer to a local variable.
62 [E45] Never convert pointers to objects of a derived class to pointers to objects of a virtual base class.
63 [E46] Never convert a const to a non-const.
64 [S] Always use unsigned for variables which cannot reasonably have negative values. Exception: When you have to use existing types that are signed, but in a context where negative values are impossible, it is better to use ASSERT (see Rule 65 below) than forcing a cast.
65 [S] Always use the ASSERT macro to validate argument to functions when the possible range is known. Always test pointer arguments for NULL through ASSERT. E.g:
void Func(int size, SC_MyClass* obj)
{
ASSERT(size >= 0)
ASSERT(size < BUFSIZE)
ASSERT(obj != NULL)
...
}
66 [P51] Always use inclusive lower limits and exclusive upper limits. E.g: In the following loop...
for(i = START; i < STOP; i++)
...STOP should be one step beyond the last element, and the loop condition "less then", not
"less or equal".
67 [S] When turning over an object created by new to other parts of the program, document by comments what object/function/subsystem is finally responsible for deallocating it.
68 [R59] Always assign a new value (normally NULL) to a pointer that points to deallocated memory. Exception: Not needed if the pointer is a local variable that clearly wont be used before it goes out of scope.
2 Recommendations
1 [R1] Optimize code only if you know that you have a performance problem. Think twice before you begin.
2 [R3] An include file should not contain more than one class definition, or possibly a few small, closely related classes.
3 [R6] Always give a file a name that is unique in as large a context as possible.
4 [R14] Do not use type names that differ only by the use of uppercase and lowercase letters.
5 [R16] A variable with a large scope should have a long name.
6 [R17] Choose variable names that suggest the usage.
7 [R32] Friends of a class should be used to provide additional functions that are best kept outside of the class.
8 [R34] An assignment operator ought to return a const reference to the assigning object.
9 [R36] When two operators are opposites (such as == and !=), it is appropriate to define both. Comment: Implement the second by negating the result of a call to the first.
10 [R41] Avoid functions with many arguments.
11 [R44] When overloading functions, all variations should have the same semantics (be used for the same purpose).
12 [R47] Avoid long and complex functions. Rule of thumb: Consider breaking apart a function when it gets bigger than one page, definitely break it apart if it gets bigger than 2 - 3 pages.
13 [R49] Use a typedef to simplify program syntax when declaring function pointers.
14 [R57] Avoid global data if at all possible.
15 [R58] Do not allocate memory and expect that someone else will deallocate it later.
16 [S] Only use operator overloading if the operator make sense, and really describe the intended semantic.
17 [S] Avoid multiple inheritance if possible.
18 [S] Be even more reluctant to multiple inheritance from two branches of the same class hierarchy.
19 [S] Use template classes for homogenous collections. Use existing collection classes where (preferable MFC) available.
3 Justifications
This section lists all Ellemtel rules and recommendation that has been left out, modified, or promoted from recommendation to rule. A short motivation for the change is given. Original Ellemtel text is in italic.
- [E1] Original says ".hh". SAIDA standard changed to fit Microsoft standard
- [E2] Original says ".cc". SAIDA standard changed to fit Microsoft standard
- [E3] Inline definition files always have the file name extension ".icc".
Not relevant for Microsoft environment
- [E5] All files must include copyright information.
?? Maybe this should be left in ??
- [E7] Every include file must contain a mechanism that prevents multiple inclusions of the file. SAIDA standard is stricter; it also specifies how this should be done.
- [E8,9] When the following kinds of definitions are used (in implementation files or in other include files), they must be included as separate include files:
classes that are used as base classes,
classes that are used as member variables,
classes that appear as return types or as argument types in function prototypes.
function prototypes used in inline member f
unctions that are defined in the file.
Definitions of classes that are only accessed via pointers (*) or references (&) shall not be included as include files.
Ellemtel 8 & 9 is somewhat incomprehensible. SAIDA standard says essentially the same
in fewer words and with an example.
- [E11] Every implementation file is to include the relevant files that contain:
- declarations of types and functions used in the functions that are implemented in the file.
- declarations of variables and member functions used in the functions that are implemented in the file.
Since this is necessary to even compile the program it has no place in a guide that tries to be a
s brief as possible.
- [E12] The identifier of every globally visible class, enumeration type, type definition, function, constant, and variable in a class library is to begin with a prefix that is unique for the library.
SAIDA will not implement libraries, and to keep the standard short this rule is left out
- [E13-19] The names of variables, constants, and functions are to begin with a lowercase letter.
The names of abstract data types, structures, typedefs, and enumerated types are to begin with an uppercase letter.
In names which consist of more than one word, the words are written together and each word that follows the first is begun with an uppercase letter.
Do not use identifie
rs which begin with one or two underscores ('_' or '__').
A name that begins with an uppercase letter is to appear directly after its prefix.
A name that begins with a lowercase letter is to be separated from its prefix using an underscore ('_').
A name is to be separated from its suffix using an underscore ('_').
Ellemtel`s naming convention is totally replaced with something c
loser to Microsoft`s in order to keep consistency with AppWizard generated code.
- [E20] The public, protected, and private sections of a class are to be declared in that order (the public section is declared before the protected section which is declared before the private section).
In AppWizard generated code this rule will be frequently broken. Therefore, it is removed.
- [E22] Never specify public or protected member data in a class.
SAIDA standard is less strict, but recommends the same as Ellemtel.
- [E28] An assignment operator which performs a destructive action must be protected from performing this action on the object upon which it is operating.
Unclear what this mean.
- [E29-30] A public member function must never return a non-const reference or pointer to member data. A public member function must never return a non-const reference or pointer to data outside an object, unless the object shares the data with other objects.
Too strict, and in the same time too sloppy. The "unless shared" part of the statement makes it a truism.
- [E34] A public function must never return a reference or a pointer to a local variable.
Not only public, but every function must adhere to this rule.
- [E42] Do not compare a pointer to NULL or assign NULL to a pointer; use 0 instead.
To make code consistent with AppWizard, NULL is recommended. The justifications for this rule given in Ellemtel papers is not especial relevant in Microsoft only environment.
- [E43] Never use explicit type conversions (casts).
This is rubbish! Using casts is MUCH better than relying on implicit conversion, and sometimes you just have to convert ints or floats to double, for instance to use <math.h> functions.
- [E44] Do not write code which depends on functions that use implicit type conversions.
Unclear what this means, therefore removed from SAIDA standard.
- [R2] If you use a C++ compiler that is based on Cfront, always compile with the +w flag set to eliminate as many warnings as possible.
Not relevant to SAIDA.
- [R3] An include file should not contain more than one class definition.
SAIDA standard is somewhat less strict.
- [R4] Divide up the definitions of member functions or functions into as many files as possible. Only reason given for this in Ellemtel papers is size of linked code, and linking speed. Strict following this rule would mean one implementation file per member function. This is clearly overkill, and size of implementation files is probably better left to the programmers judgement.<
P>
- [R5 ] Place machine-dependent code in a special file so that it may be easily located when porting code from one machine to another.
Not relevant for SAIDA.
- [R7] An include file for a class should have a file name of the form <class name> + extension. Use uppercase and lowercase letters in the same way as in the source code.
In SAIDA, all file names are lowercase.
- [R8] Write some descriptive comments before every function.
Overkill to always do so, for instance simple Get/Set methods.
- [R9 ] Use // for comments.
Promoted to rule in SAIDA, and strengthened to state ALL comments. /* ... */ comments are reserved for debugging purposes
- [R10, R11] Use the directive #include "filename.hh" for user-prepared include files.
Use the directive #include <filename.hh> for include files from libraries.
Rec 10 & 11 is obvious and needed to even get the program through compilation.
- [R12 &R13] Every implementation file should declare a local constant string that describes the file so the UNIX command what can be used to obtain information on the file revision.
Never include other files in an ".icc" file.
Rec 12 & 13 not relevant in Microsoft environment.
- [R15] Names should not include abbreviations that are not generally accepted.
Hard to follow in practice, therefore left out.
- [R18] Write code in a way that makes it easy to change the prefix for global identifiers.
Too unspecific to be left in without examples, and not important enough to justify such examples.
- [R19] Encapsulate global variables and constants, enumerated types, and typedefs in a class.
Only a way to follow the more general rec. about not using globals.
- [R20] Always provide the return type of a function explicitly.
Promoted to rule
- [R21] When declaring functions, the leading parenthesis and the first argument (if any) are to be written on the same line as the function name. If space permits, other arguments and the closing parenthesis may also be written on the same line as the function name. Otherwise, each additional argument is to be written on a separate line (with the closing parenthesis directly after the l
ast argument).
Promoted to rule, and extended to function calls, not only declarations.
- [R22] In a function definition, the return type of the function should be written on a separate line directly above the function name.
Unnecessarily strict. "Should" replaced by "may". In this less strict form promoted to rule.
- [R24] Braces ("{}") which enclose a block are to be placed in the same column, on separate lines directly before and after the block.
In SAIDA promoted to rule, in a slightly relaxed version.
- [R25 ] The flow control primitives if, else, while, for and do should be followed by a block, even if it is an empty block.
Considered too strict.
- [R23] Always write the left parenthesis directly after a function name.
Promoted to rule.
- [R26] The dereference operator '*' and the address-of operator '&' should be directly connected with the type names in declarations and definitions.
Promoted to rule.
- [R27] Do not use spaces around '.' or '->', nor between unary operators and operands.
Promoted to rule.
- [R28] Use the c++ mode in GNU Emacs to format code.
Not relevant to SAIDA.
- [R29-31] Access functions are to be inline.
Forwarding functions are to be inline.
Constructors and destructors must not be inline.
SAIDA standard specifies that no optimization, including inlining should be performed
until a program works.
- [R33] Avoid the use of global objects in constructors and destructors.
Unclear what this means, and therefore left out
- [R37] Avoid inheritance for parts-of relations.
This is a design error, not implementation.
- [R38] Give derived classes access to class type member data by declaring protected access functions.
Seems unnecessarily strict.
- [R39] Do not attempt to create an instance of a class template using a type that does not define the member functions which the class template, according to its documentation, requires. Obvious, and code not following this "recommendation" will simply not compile. Therefore left out.
- [R40] Take care to avoid multiple definition of overloaded functions in conjunction with the instantiation of a class template.
Hard to explain without big examples.
- [R42-43] If a function stores a pointer to an object which is accessed via an argument, let the argument have the type pointer. Use reference arguments in other cases.
Use constant references (const &) instead of call-by-value, unless using a pre-defined data type or a pointer.
Rec 42 & 43 replaced by new text with similar meaning.
- [R45] Use inline functions when they are really needed.
Not in SAIDA. Not until the program works.
- [R46] Minimize the number of temporary objects that are created as return values from functions or as arguments to functions.
This is optimization, and therefore left out
- [R48] Pointers to pointers should whenever possible be avoided.
I don't agree.
- [R50 ] The choice of loop construct (for, while or do-while) should depend on the specific use of the loop.
Obvious, left out to keep paper brief.
- [R51] Always use unsigned for variables which cannot reasonably have negative values.
Promoted to rule, in combination with using ASSERT.
- [R52] Always use inclusive lower limits and exclusive upper limits.
Promoted to rule
- [R53] Avoid the use of continue.
I don't agree.
- [R54] Use break to exit a loop if this avoids the use of flags.
Promoted to rule in somewhat modified form.
- [R55] Do not write logical expressions of the type if(test) or if(!test) when test is a pointer.
Promoted to rule in a modified form
- [R56 ] Use parentheses to clarify the order of evaluation for operators in expressions.
Promoted to rule
- [R59] Always assign a new value to a pointer that points to deallocated memory.
Promoted to rule with some changes and exceptions.
- [R60] Make sure that fault handling is done so that the transfer to exception handling (when this is available in C++) may be easily made.
Exception handling is now present.
- [R61] Check the fault codes which may be received from library functions even if these functions seem foolproof.
This is not always a good idea.
4 Appendix
4.1 Literature
[Ellemtel 92], E. Nyquist & M. Henricson, "Rules and Recommendations",
Ellemtel Telecommunication System Laboratories 1990 - 1992
- SAIDA Coding Standard
-
- Abstract
-
- 1 - SAIDA Coding Rules
-
- 1.1 - General
-
- 1.2 - Files
-
- 1.3 - Preprocessor & directives
-
- 1.4 - Naming & Declarations
-
- 1.5 - Scope & Access
-
- 1.6 - Comments
-
- 1.7 - Statements & Expressions
-
- 1.8 - Defensive rules & Safeguards
-
- 2 - Recommendations
-
- 3 - Justifications
-
- 4 - Appendix
-
- 4.1 - Literature
-
Info
Team
WebCam
Documents
Schedule
CustomerOnly
© 1996, The SAIDA Project