SAIDA Coding Standard

Revision A - 1 APR 1996

 NamePositionE-mail address
Author(s):Mats GrahmTechnical Coordinatorpt93mg@pt.hk-r.se
Responsible:Mats GrahmTechnical Coordinatorpt93mg@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.

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