Coding conventions

The coding conventions are organized under various headings, and each rule is tagged according to how it affects the program code.

viability - the code will not work unless you follow this rule.

reliability - the code may not work unless you follow this rule.

maintainability - the code may be difficult to modify unless you follow this rule.

readability - the code may be difficult to understand unless you follow this rule.

reusability - the code may be less usable in conjunction with other code unless you follow this rule.

convention - the code will be unconventional unless you follow this rule.

The rules are also tagged according to whether they are:

C++ - general 'good practice' C++ coding rules.

Symbian - Symbian OS-specific rules.

S60 - S60 recommended rules.

Some entries are tagged as notes rather than rules.

Contents

A

assertions

reliability

Symbian

A function body can start with a list of __ASSERT_ALWAYS() macros that verify that the parameters are valid. These are to check that the function is being called correctly. In particular they check that run-time input is valid, and that the object to which the function is being applied is in a valid state. A precondition can be read as "This function is not guaranteed to work correctly unless <precondition expression>

If the function isn't part of a public API consider using the macro ASSERT_DEBUG() instead.

Points to remember:

  • If you have a precondition that a pointer is not null, consider passing a reference instead of a pointer.

  • If you have a precondition that a value passed in is in a valid range, consider passing an enumeration or a T-class object.

  • Constructors may run precondition checks on their parameters. However, these parameters are used in the member initialisation list., so it is usually better to put the tests in the body of the constructor.

  • Precondition assertions should be documented as comments against their corresponding function prototype in the header file.

  • Any member function should cope with all situations not excluded by the preconditions expressed in the header.

  • Preconditions should not be placed in NewL() or NewLC() but instead within the constructor and ConstructL() functions. However, they are in fact still preconditions of NewL() and NewLC() and as such should exist as comments in the header file.

reliability

Symbian

At the end of any function it can be useful to check that the value you are returning is valid, or that the function has performed the task it promised. This is behavioural self-testing. Use the macro ASSERT() to perform these tests. These are to catch programmer errors in the function containing the postcondition. A postcondition can be read as "When this function returns, <postcondition expression> is guaranteed to be true, provided that all the preconditions held when it was called".

Points to remember

  • If a function has multiple returns, remember to put post-conditions before every return!

  • If you have a post-condition that the pointer you return is not null, consider returning a reference.

  • Post-conditions should be documented as comments against their corresponding function prototype in the header file.

reliability

Symbian

All non-trivial classes should implement invariant checking. Invariants can be thought of as preconditions and post-conditions that apply to all member functions of a class. The macro __DECLARE_TEST is a function declaration which should be invoked in the header file within a Protected clause. You need to implement the member function void __DbgTestInvariant() const. The first line should be a call to __DbgTestInvariant() on your base class (if the function is defined), followed by ASSERT_DEBUG() calls that verify that your member data is in a valid state.

Example:

void CMyClass::__DbgTestInvariant() const
{
ASSERT_DEBUG(iPointer);
ASSERT_DEBUG((iValue>=KMinValue) && (iValue>=kmaxvalue)); }
</pre>

Every public non-static function calls __TEST_INVARIANT at the start and at the end (or only once if the function is a single line query). Put Invariants before preconditions and after post-conditions. The last line in ConstructL() is __TEST_INVARIANT.

Points to remember:

  • You cannot call functions in __DbgTestInvariant() that call __TEST_INVARIANT (it is best not to call any functions).

  • If a function has multiple returns, remember to call __TEST_INVARIANT before every return!

  • You may call static functions at any time, as static member functions can't check the Invariant, not having any state to verify.

C

C classes

   

See also classes

 
 

Symbian (note)

C class objects cannot be passed or returned by value, but only through pointers or by reference.

 

Symbian (note)

Passing C class objects by pointer implies either the transfer of ownership, or that the parameter is optional. To remove this ambiguity, consider overloading functions that have optional parameters.

 

Symbian (note)

Returning C class objects by pointer implies either the transfer of ownership, or that the return value may be NULL. To remove this ambiguity, consider pushing the object on to the cleanup stack before you return it, and adding a trailing C to the function name.

class invariants

   
   

See assertions

classes

   

viability

Symbian

Every class in Symbian OS must be one of 5 kinds. To indicate which kind a class is, the name must be prefixed by a capital C, T, M, S, or R.

reliability

Symbian

C classes must always have a destructor (which is virtual by virtue of CBase). If the destructor is empty, then use the helpful comment //empty destructor.

maintainability

C++

Try to avoid using friend classes, as they break encapsulation. If you feel you need to use a friend class do the following:

Example:

Class B is a friend of Class A, in order that it can call the non-public member functions f(), g(), h(). We simply define an interface:

Class MyInterface
{
friend class B;
protected:
virtual void f() = 0;
virtual void g() = 0;
virtual void h() = 0;
};

and then derive Class A from MyInterface.

reusability

C++

Declare all classes in header files. If you want to hide a class, make it nested.

convention

S60

There should be one (or none) .cpp and one .h file per class (except for nested classes).

convention

Symbian (note)

Any class that is derived from CBase has a "C" prefix.

 

Symbian (note)

An M class only has virtual methods, and no data. The methods can at most have only a trivial implementation.

 

Symbian (note)

C class objects can be pushed onto the cleanup stack.

 

Symbian (note)

C class objects must either be newed, or (in rare cases) be a member variable of another C class.

 

Symbian (note)

C classes inherit a private assignment operator from CBase, so it is not necessary for you to define one.

coding style

   

readability

 

Keep all lines fairly short, and always break the line if it goes off the edge of the screen. When you break a line, break it in a sensible place (for example, after rather than before an operator) and indent the following lines.

readability

C++

Use white space to group related lines of code together.

readability

C++

Use parentheses to clarify ambiguous code.

convention

S60

There should be

  • no spaces around primary operators ( '.', '*', '->' ).

  • no spaces between unary operators and their operands

  • one space on each side of all other operators

  • no space between function names or macros and their arguments

  • one space between a keyword and its parenthesised arguments

  • matching spaces with matching parentheses.

convention

S60

There must be no more than 120 characters per line. Indents should be by exactly four spaces.

convention

S60

Braces should be indented according to Symbian's standards.

comments

   

readability

 

Don't write comments that simply mirror the code. Try to make your code less obscure instead of papering over it with comments.

convention

 

Stylised comment blocks in the format described below should be inserted into header files to help with automatic document generation. The convention is generally that used by Together, JavaDoc and CcDoc. There should be a separate comment block for each class, operation, and member variable declared in the header file.

A comment block starts with a '/**' and ends with a '*/'. Each line starts with an asterisk '*'. The basic format is:

/**
* <BriefDescription>
* <MarkUpTag>
* <MarkUpTag>
...
* <MarkUpTag>
*/

Alternatively a shorter form can be used:

/** <BriefDescription> */

The <BriefDescription> should be an informal, one-sentence description of the class, operation or member variable.

The <MarkUpTag> entries start with an @ sign and are the first things (after the optional leading asterisk) on the line.

Example:

/*
* A Rectangle object, which can be moved and rotated.
*
* @invariants iLength > 0;
*  iHeight > 0;
*/
class TRectangle
{
public:
/**
* Constructor
*
* @semantics Construct a new rectangle
*
at position ( aX , aY ),
* length aLength, height aHeight.
* @stereotype  constructor
* @preconditions aLength >= 0; aHeight >= 0;
*/
TRectangle( TReal x, TReal y, TReal aLength, TReal aHeight);
/** 
* Rotate operation
*
* @semantics Rotate this rectangle by aAngle degrees
* about its centre
* @stereotype modifier
* @preconditions aAngle >= 0; aAngle <= 360;
*/ void rotate(TReal aAngle);
/**
* Absolute move operation
*
* @stereotype  modifier
* @semantics Move this rectangle to position ( aX , aY ).
*/
void move_to(float x, float y);
//data
private:
/** The x position of this rectangle*/
TReal iPosX;
/** The y position of this rectangle*/
TReal iPosY;
/** The length of this rectangle*/
TReal iLength;
/** The height of this rectangle*/
TReal iHeight; 
}

convention

S60

Use // for permanent comments in .cpp file code.

compilation errors

   

reliability

C++

You should have zero compiler errors, except for unavoidable warnings that are due to the libraries you are using. Ideally you should also have a minimum of Lint warnings. Fix any Lint warnings that are fixable unless blatantly foolish and counter-productive.

constants

   

See also consts

 

convention

 

The name for a constant should be prefixed by a capital K.

convention

 

Constants should be implemented as either:

  • enums scoped within a class (for example, enum { KMagicNumber = 3 }; , that is to say, no type name, and all on one line), or

  • class scoped static members (for example, static const TReal KMagicNumber;) which must then be defined once only in a .cpp file. Note that this only works for built-in types, and that TInt64 is not a built-in type.

construction

   

See also constructors, destructors

 
 

Symbian (note)

C-classes are the only classes that you can new, and this is almost always the only way they are constructed.

 

Symbian (note)

All C-class member variables are nulled on creation.

constructors

   

See also construction, destruction, destructors

 

viability

Symbian

ConstructL() should never be a virtual function, since a partially constructed object isn't a polymorphic entity.

reliability

Symbian

Constructors and ConstructL() in C classes must be protected (or private), and should be empty apart from a member initialisation list and ASSERT macros. This enforces correct two-phase construction of objects.

reliability

Symbian

You must always have a NewLC() in a C class, unless it's a nested class where you have a bit more leeway. The NewL() is optional, and should always be implemented in terms of the NewLC(). Both should always follow the standard pattern, and call ConstructL(). Non standard construction makes code unclear, and prone to cleanup stack errors.

reliability

Symbian

In the implementation of ConstructL(), you should always call ConstructL() on the base class first. In certain circumstances you cannot help but break this rule, for example when you need to read in parameters from a resource file. Always scope this call to the base class to make things clearer.

 

Symbian (note)

For small C classes it is acceptable to have a public constructor to allow them to be member variables of other C classes. This is normally only done for classes without a ConstructL(), such as the Symbian OS array classes.

 

Symbian (note)

NewL() and NewLC() in C classes should be static, usually public, functions.

 

Symbian (note)

Copy constructors lose their "special status" in Symbian OS. Normally only classes with a destructor need copy constructors, and this means C-classes, but C-classes are only instantiated via NewL() and NewLC(). C-classes derive a private copy constructor from CBase, and since any you define are never public, they cannot be called implicitly.

consts

   

See also constants

 

reliability

C++

Const correctness should be applied throughout. Most functions that return a value should not change the object they are applied to. These are called queries, and can mostly be made const.

One word of caution, however: the compiler enforces that const functions preserve the object's bitwise state, but leaves it up to you to ensure that the object's abstract state is unchanged.

Example: If you have member variable that is a pointer to another object, then the compiler allows you to call non-const functions on that object from within a const function!

Class WorstCaseScenario
{

public:
WorstCaseScenario() : iThis(this)
{
}
void ChangeTheWorld();
TBool InnocentLookingFunction() const
{
iThis->ChangeTheWorld();
}
WorstCaseScenario* iThis;
}

D

destruction

   

See also construction, constructors, destructors

 
 

Symbian (note)

If an object is deleted twice, this is a fatal error. This will occur if a Leave occurs whilst an object is both on the cleanup stack and is to be deleted in some object's destructor.

destructors

   

See also construction, constructors, destruction

 

reliability

C++

In a destructor or function called from a destructor you cannot assume that the object is fully constructed, hence you must check the state of the object and cope with all situations. You must not throw any exceptions.

Example:

MyClass::~MyClass()
{
if(iObject) //might be NULL if constructor failed
{
iObject->Close();
delete iObject;
iObject = NULL;
}
delete iFred;   //delete NULL is allowed
iFred = NULL;
}

DLLs

   

viability

Symbian

You must not have any global non-constant data in a DLL. This includes static member variables. The only global data you may have is constant static built-in types, that is, ones with no constructor.

E

exceptions

   

viability

Symbian

You cannot use C++ style exceptions.

F

files

   

See also header files

 

viability

Symbian

Place Control IDs, and defines in .hrh files.

readabillity

Symbian

In a resource file, always use comments! Resource file comments are written to describe the usage of the file so that localisation can be based on them.

convention

 

Naming of .mmp and .rss files: If there is one, name it after the project (for example animation.mmp). If there are more than one, name them after the subproject (for example engine.mmp).

convention

 

There should be one directory per .mmp file, called group, or <subproject>. This should contain .mmp, .def and readme files.

convention

 

Public header files should go in a subdirectory inc, or <subproject> inc.

convention

 

Documents should all go in a subdirectory docs, or <subproject> docs.

convention

 

All files associated with the .aif file should be placed in a subdirectory aif. (To avoid name clashes with the .rss file)

convention

 

Test files should go in a subdirectory tsrc or <subproject> tsrc.

convention

 

All other files should go in a subdirectory src, or <subproject> src.

convention

Symbian

In a resource file, use the same bracketing conventions you're using in your source files.

flow control

   

reliability

C++

Switch statements should always have a default clause. If the default should never occur, the default clause should be an assertion to that effect. The only clauses that should follow through (that is, no break) are empty ones, for example:

switch(value)
{
case 0:
case 1:
case 2:
DoSomething();
break;
case 4:
case 5:
DoSomethingElse();
break;
default:
ASSERT(EFalse);//cannot occur
}

maintainability

C++

Never use goto.

maintainability

C++

Single line statements after a for or an if statement should be parenthesized, that is:

if(wantToDoSomething)
{
DoSomething();
}

functions

   

See also virtual functions

 

maintainability

C++

Do not use default parameters (though exceptions can be made for having one default parameter only). If you have a function with 'n' default parameters, replace it with 'n+' overloaded versions of that function. It is usual to discover that

  • not all the overloads are needed

  • some better choices of overload can be found

  • some of the overloads can be renamed more appropriately

Example:

AddControl(Control& ctrl, ControlObserver* observer = 0);

Becomes:

AddControl(Control& ctrl); 
AddControl(Control& ctrl, ControlObserver* observer);

Which can be improved further to become:

AddControl(Control& ctrl); 
AddControl(Control& ctrl, ControlObserver& observer);

readability

C++

Separate functions into either

  • modifiers - non-const functions that don't return a value, or

  • queries - functions that return a value, are usually const, and due to preconditions probably don't Leave.

readability

C++

Keep function bodies small.

readability

Symbian

Never return an error code. Use the Leave mechanism instead.

convention

C++

Function names should begin with a capital letter.

Normally Modifiers are expected to do something, and hence the name is in the form of an order.

convention

S60

Functions should be defined in the same order as in the header file.

convention

Symbian

All inline functions should be in an .inl file. During development don't use inline functions. When you need to optimise before releasing the code, cut and paste the functions to be inlined from the .cpp file to an inline file (".inl") which is then included by the header. Remember to add the keyword inline to the functions.

readability

Symbian (note)

Function names that end in a "C" indicate that the function returns a pointer that has been pushed onto the cleanup stack.

readability

Symbian (note)

Function names that end in a "D" indicate that the function call will result in the deletion of the object.

 

Symbian (note)

When a function "Leaves", control is passed up the function call stack to the first call which was protected with a TRAP clause. The cleanup stack is then unwound, and all objects which have been pushed onto the cleanup stack since this TRAP clause are deleted. This means that in code that can leave, the only destructors that you can ensure are called, are for objects that can be pushed onto the cleanup stack, or which are members of other objects that have been pushed onto the cleanup stack, and so on, transitively.

H

header files

   

See also files

 

convention

 

In header files, use standard anti-nesting, for example:

#ifdef __CMYCLASS__ 
#define __CMYCLASS__

body of header

#endif

Always use explicit access control specifiers. List the public members first, then the protected ones, then the private ones. List the data members in a group after the methods. Put any "special" methods first within each section (for example, NewL / NewLC / constructors / destructors / operators). Group methods by sets of functionality. Name variables in the function declaration. Group overridden methods together within each section, labelling which immediate base class they are from (via //from CMyBase Class), and do not specify the keyword virtual, for example:

//from CCoeControl
void Draw() const; 
//from MObserver
void Notify();
void StopObserving();

convention

S60

Use #ifndef when including header files within header files.

I

#include

   

convention

 

Put system includes first (using angle brackets), followed by local includes (using quotes).

convention

C++

Prefer forward declarations to #include statements, otherwise you end up with horrendously long build times on larger projects.

inheritance

   

See also virtual functions

 

maintainability

C++

Never use multiple inheritance except for interfaces (that is, mixin classes). Interfaces must have no member data. However they need not be pure virtual; they may have trivial implementation of functions.

maintainability

C++

Never use private or protected inheritance. Consider whether composition would be more appropriate.

invariants

   

See also assertions

 
 

See assertions.

L

local variables

   

See also variables

 

maintainability

C++

Local variables should be defined as late as possible, that is near to their first use.

maintainability

C++

Never use static local variables.

M

M classes

   
 

Symbian (note)

M class objects cannot be passed or returned by value. The choice between pointer and reference should be made according to whether a NULL value is acceptable.

macros

   

See also functions

 

reliability

C++

Avoid macros. Any macros you do use should be all capitals, using underscores to separate words. Use enums and const static instead of #defining constants. Inline functions can replace many other uses of macros. #ifdef DEBUG is acceptable, as is the ASSERT family of macros.

member variables

   

See also variables

 

reliability

C++

It is better by far to encapsulate member data than return references to it . If you must return references to member data, it is better to return a const reference. If you must return a non-const reference, you need to provide both a const and a non-const (overloaded) accessor function, or you won't be able to call it from a const member function!

reliability

C++

Whenever you delete member variables, you must set them to NULL. This is a safeguard against many nasty difficult to trace bugs.

maintainability

C++

Never have public member data.

O

operators

   

viability

C++

Assignment operators must handle self-assignment, though this becomes mostly irrelevant in Symbian OS.

P

parameters

   

readability

 

In source files, use the same parameter names as you had in the header file.

readability

C++

Never use ellipsis notation, that is, func(...).

pointers

   

reliability

C++

Never use pointer arithmetic.

reliability

C++

Never use pointer casts. Unfortunately, sometimes libraries you are using force you to cast return values. In this case encapsulate the cast in a function call, and use standard C++ casts rather than C style casts (for example, dynamic_cast).

reliability

S60

Initialize all pointers to NULL (not 0).

reusability

Symbian

Use descriptors and other specialized classes instead of pointers when storing data or handling strings.

convention

S60

Check for non-null pointers via if (ptr) instead of if(ptr!=NULL).

postconditions

   
 

See assertions.

preconditions

   
 

See assertions.

R

R classes

   

See also destructors

 

viability

Symbian

You cannot push R-class objects onto the cleanup stack, hence they must not have a destructor.

viability

Symbian

You shouldn't pass R class objects by value. Since R class variables are system resources you shouldn't make copies of them.

reliability

Symbian

R classes must initialize all member data in the constructor. You cannot rely on members being zeroed.

reliability

Symbian

R class objects must not be created via new.

 

Symbian (note)

It is generally necessary to "open" and "close" R-type objects, and hence you see code like this:

RFile file;
file.OpenL();
TRAPD(err,file.ReadL());
if(err)
{
file.CloseL();
User::Leave(err);
}
//etc. etc.

This is inefficient, and makes code quite unreadable, being (in effect) a return to the era of checking return values for error codes. There is a simple method for bypassing this, shown in the following example:

class SRClassCleanup
{
public:
static void OpenLC(RFile& aFile)
{
User::LeaveIfError(aFile.OpenL);
CleanupStack::PushL(TCleanupItem
(ReleaseRFileOnCleanup,&aFile));
}
private:
static void ReleaseRFileOnCleanup(TAny* aObject)
{
REINTERPRET_CAST(RFile*,aObject)->Close();
}
//repeat for all R-Classes in your project.
};

Then we can simplify the previous example to the following (assuming we've derived from SRClassCleanup):

RFile file;
OpenLC(file);
file->ReadL();
//etc. etc.
CleanupStack::PopAndDestroy();//close file

resource files

   

convention

Symbian

Place resource structures in .rh file.

convention

Symbian

In a resource file, write resource names in lower case.

S

statements

   

reliability

C++

Never modify two different objects in the same statement.

readability

C++

Break statements, multiple returns, and even continue may make your code clearer. Use them when they make your code clearer. Don't use them if they make it less clear!

strings

   

reusability

Symbian

Always use resource files instead of string constants.

structs

   

readability

C++

Always use class instead of struct.

T

T classes

   

viability

Symbian

Since T class objects cannot be pushed onto the cleanup stack, they must not have a destructor as you cannot ensure that it is called in all circumstances.

viability

Symbian

T class objects must not be created via new.

 

Symbian (note)

T class objects are returned by value, and passed by reference or value.

TBool types

   
 

Symbian (note)

The compiler will not allow you to compare TBool types with ETrue and EFalse. Instead consider, for example, if ( !Visible() ) ).

templates

   

reliability

C++

Templates should be used only for defining universal containers. Any template should be tested carefully on the target platform.

reliability

C++

Templates should never be used in conjunction with virtual functions.

types

   

viability

Symbian

Always use Symbian OS types, for example, TInt instead of int.

viability

Symbian

Don't use the explicit type forms (for example TInt16) except in rare circumstances (for example reading from resource files).

readability

C++

For conversion between base types, always use explicit casts - it clarifies your code and removes warnings.

convention

 

The name for the enumeration type should have a capital T prefix, the enumeration members should have a capital E prefix.

Use relevant, meaningful, and unambiguous names for enums.

U

unions

   

readability

C++

Never use a union, unless you are forced to when writing low-level machine-specific code.

V

variables

   

See also local variables, member variables

 

reliability

C++

Only a single variable declaration should be made per statement.

convention

Symbian

Put the pointer or reference indicator next to the type name, with no separating space.

convention

Symbian

Variable names start with (some or none) Symbian OS prefixes (i, a, C, M, T, R, E, K, S), followed by concatenated words (that is, no underscore separator), the first letter of each word being capitalised, followed by (possibly) Symbian OS postfixes (L, C, D).

The only exception to this rule is that local variables have a lower case first letter to distinguish them from functions.

Don't use numbers or abbreviations in names (except for test code).

convention

Symbian

Variable names should be abstract nouns like windSpeed, windowPosition. Make them long enough to be descriptive (although it is fine to use variables like i or j within a for loop).

convention

Symbian

Local variable names should start with a lower case letter.

convention

Symbian

Parameter names should be prefixed with a lower case "a", followed by an upper case letter.

convention

Symbian

Member data names should be prefixed with a lower case "i", followed by an upper case letter.

virtual functions

   

See also functions, inheritance

 

viability

C++

You should never call a virtual function from within a constructor or destructor.

reliability

C++

Never overload virtual functions.

reliability

C++

When implementing a virtual function you often need to call the base class implementation. When you scope the call you should use your immediate base class:

SportsCar::DoSomething()
{
FastCar::DoSomething();
//code here
}

reliability

S60

Virtual functions should not be inline, except for destructors.

maintainability

C++

When implementing a virtual function you often need to call the base class implementation. This should normally be done first or last:

SportsCar::DoSomething()
{
FastCar::DoSomething();
//code here
}

or

SportsCar::DoSomething()
{
//code here
FastCar::DoSomething();
}

and not:

SportsCar::DoSomething()
{
//code here
Car::DoSomething();
//some more code here
}

readability

C++

Never change the visibility (public/protected/private) when overriding a virtual function.

reliability

C++ (note)

In an overridden function you cannot make any more assumptions than the preconditions of the base class implementation and your own class Invariants.