|
S60 3rd Edition SDK for Symbian OS Example Applications |
This section lists the rules for the coding conventions used. The rules are organised 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
general 'good practice' C++ coding rules
Symbian OS-specific rules
S60 recommended rules
Some entries are tagged as notes rather than rules.
|
|
|
|
A |
||||||
|
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>"
| |||
|
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".
| |||
|
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.
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:
| |||
|
|
||||||
C |
||||||
|
|
(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 also assertions |
||||||
|
|
|
|
See assertions. | |||
|
|
|
|
| |||
|
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:
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 |
(Nokia) |
|
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. | |||
|
|
|
|
| |||
|
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 |
(Nokia) |
|
There should be
| |||
|
convention |
(Nokia) |
|
The must be no more than 120 characters per line. Indents should be by exactly four spaces. | |||
|
convention |
(Nokia) |
|
The braces should be indented according to Symbian's standards. | |||
|
|
|
|
| |||
|
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.
Alternatively a shorter form can be used:
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.
| |||
|
convention |
(Nokia) |
|
Use // for permanent comments in .cpp file code. | |||
|
|
|
|
| |||
|
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. | |||
|
|
|
|
| |||
|
convention |
|
|
The name for a constant should be prefixed by a capital K. | |||
|
convention |
|
|
Constants should be implemented as either:
| |||
|
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. | |||
|
|
|
|
| |||
|
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!
| |||
|
|
||||||
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.
| |||
|
|
|
|
| |||
|
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 |
||||||
|
viability |
(Symbian) |
note |
You cannot use C++ style exceptions. | |||
|
|
||||||
F |
||||||
|
files - see also header files |
||||||
|
viability |
(Symbian) |
|
Place Control IDs, and defines in .hrh files. | |||
|
readability |
(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. | |||
|
|
|
|
| |||
|
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:
| |||
|
maintainability |
(C++) |
|
Never use goto. | |||
|
maintainability |
(C++) |
|
Single line statements after a for or an if statement should be parenthesised, that is:
| |||
|
|
|
|
| |||
|
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
Example:
Becomes:
Which can be improved further to become:
| |||
|
readability |
(C++) |
|
Separate functions into either
| |||
|
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 |
(Nokia) |
|
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. | |||
|
reliability |
(Symbian) |
note |
Function names that end in a "C" indicate that the function returns a pointer that has been pushed onto the cleanup stack. | |||
|
reliability |
(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__ 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:
| |||
|
convention |
(Nokia) |
|
Use #ifndef when including header files within header files. | |||
|
|
||||||
I |
||||||
|
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. | |||
|
|
|
|
| |||
|
|
(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. | |||
|
|
|
|
| |||
|
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 |
||||||
|
viability |
(C++) |
|
Assignment operators must handle self-assignment , though this becomes mostly irrelevant in Symbian OS. | |||
|
|
||||||
P |
||||||
|
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(...). | |||
|
|
|
|
| |||
|
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 |
(Nokia) |
|
Initialise all pointers to NULL (not 0). | |||
|
reusability |
(Symbian) |
|
Use descriptors and other specialised classes instead of pointers when storing data or handling strings. | |||
|
convention |
(Nokia) |
|
Check for non-null pointers via if (ptr) instead of if(ptr!=NULL). | |||
|
|
|
|
| |||
|
postconditions - see also assertions |
||||||
|
|
|
|
See assertions. | |||
|
|
|
|
| |||
|
preconditions - see also assertions |
||||||
|
|
|
|
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 initialise 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:
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:
Then we can simplify the previous example to the following (assuming we've derived from SRClassCleanup):
| |||
|
|
|
|
| |||
|
convention |
(Symbian) |
|
Place resource structures in .rh file. | |||
|
convention |
(Symbian) |
|
In a resource file, write resource names in lower case. | |||
|
|
||||||
S |
||||||
|
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! | |||
|
|
|
|
| |||
|
reusability |
(Symbian) |
|
Always use resource files instead of string constants. | |||
|
|
|
|
| |||
|
readability |
(C++) |
|
Always use class instead of struct. | |||
|
|
||||||
T |
||||||
|
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. | |||
|
|
|
|
| |||
|
|
(Symbian) |
note |
The compiler will not allow you to compare TBool types with ETrue and EFalse. Instead consider, for example, if ( !Visible() ) ). | |||
|
|
|
|
| |||
|
|
|
| ||||
|
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. | |||
|
|
|
|
| |||
|
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 |
||||||
|
reliability |
(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 |
|
|
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:
| |||
|
reliability |
(Nokia) |
|
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:
or
and not:
| |||
|
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. | |||
|
© Nokia 2006 |