Sunteți pe pagina 1din 58

Version 1.

0 May 5, 2004

S Y M B I A N

Symbian OS: Coding Conventions in C++

O S

Symbian OS: Coding Conventions in C++ | 2

Legal Notice
Copyright 2004 Nokia Corporation. All rights reserved. Nokia and Nokia Connecting People are registered trademarks of Nokia Corporation. Java and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. Other product and company names mentioned herein may be trademarks or trade names of their respective owners. Disclaimer The information in this document is provided as is, with no warranties whatsoever, including any warranty of merchantability, fitness for any particular purpose, or any warranty otherwise arising out of any proposal, specification, or sample. Furthermore, information provided in this document is preliminary, and may be changed substantially prior to final release. This document is provided for informational purposes only. Nokia Corporation disclaims all liability, including liability for infringement of any proprietary rights, relating to implementation of information presented in this document. Nokia Corporation does not warrant or represent that such use will not infringe such rights. Nokia Corporation retains the right to make changes to this specification at any time, without notice. License A license is hereby granted to download and print a copy of this specification for personal use only. No other license to any other intellectual property rights is granted herein.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 3

Contents
1.
1.1 1.2 1.3

Introduction ......................................................................................................... 7
Purpose and Scope.........................................................................................................7 Motivation ........................................................................................................................7 Document Outline ...........................................................................................................7

2.
2.1

Coding Conventions ........................................................................................... 8


General Principles...........................................................................................................8 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.2 2.2.1 2.2.2 2.3 2.4 2.3.1 2.4.1 2.4.2 2.5 2.5.1 2.5.2 2.6 2.6.1 2.6.2 2.6.3 2.6.4 2.6.5 2.7 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.8 2.8.1 Reusability ..............................................................................................................8 Maintainability.........................................................................................................8 Build process ..........................................................................................................8 Internationalization .................................................................................................8 Specific Symbian OS issues...................................................................................9 General issues........................................................................................................9 File types ..............................................................................................................10 Naming and writing conventions ..........................................................................13 Spacing.................................................................................................................21 Placement of braces and parentheses.................................................................21 General commenting conventions........................................................................22 Comment layouts..................................................................................................22 Constants..............................................................................................................23 Variables...............................................................................................................24 Types ....................................................................................................................25 Typecasting ..........................................................................................................25 Pointers.................................................................................................................26 Class behavior......................................................................................................26 Class interfaces ....................................................................................................28 Virtual member functions......................................................................................29 Inline member functions .......................................................................................29 Multi-inheritance ...................................................................................................30 Cleanup stack.......................................................................................................30 Descriptors and character sets.............................................................................31
Version 1.0 | May 5, 2004

Source Files ....................................................................................................................9

Language ......................................................................................................................13 Indenting Conventions ..................................................................................................21

Commenting Conventions.............................................................................................22

Data Types and Usage .................................................................................................23

Objects and Classes .....................................................................................................26

Internationalization Coding Guidelines .........................................................................30

Symbian OS: Coding Conventions in C++ | 4

2.8.2 2.8.3 2.8.4 2.8.5

Text.......................................................................................................................31 Graphics and screen layout..................................................................................32 Locale-specific data..............................................................................................32 Fonts.....................................................................................................................34

3.
3.1

Coding Idioms ................................................................................................... 36


General Principle...........................................................................................................36 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.2 3.2.1 3.2.2 3.2.3 3.3 3.3.1 3.3.2 3.3.3 3.4 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.5 Basic types ...........................................................................................................36 String and buffer classes ......................................................................................36 Date classes .........................................................................................................36 Class types ...........................................................................................................37 Protect objects......................................................................................................37 Check for out-of-resource errors ..........................................................................38 Conventional error-checking methods..................................................................39 Problems with conventional methods...................................................................39 Rule 1: Functions that leave, and trap harnesses................................................40 Rule 2: Using the cleanup stack...........................................................................42 Rule 3: Two-phase construction...........................................................................44 Misuse of TRAP and TRAPD ...............................................................................46 Misuse of the new operator ..................................................................................47 Misuse of the L suffix............................................................................................47 Memory leaks .......................................................................................................47 Using tools in the WINS/WINSCW emulator ........................................................48

Exception Handling in Symbian OS C++ ......................................................................38

Solutions in Symbian OS ..............................................................................................40

Common Mistakes ........................................................................................................46

Asserts and Panics .......................................................................................................50

4.
4.1 4.2

System Resource Usage (ROM and RAM) ...................................................... 51


Importance ....................................................................................................................51 Reducing Code Size .....................................................................................................51 4.2.1 4.2.2 4.2.3 4.2.4 4.2.5 4.2.6 4.2.7 4.2.8 4.3 4.3.1 Unnecessary exported functions ..........................................................................51 Copy and paste ....................................................................................................51 Obviously decomposable functions......................................................................51 Excessive TRAP harnesses .................................................................................51 Debug code in release..........................................................................................52 Unnecessary virtual functions ..............................................................................52 Use common controls...........................................................................................52 Misuse of the _L macro ........................................................................................52 Use bit fields rather than many Tbools.................................................................52
Version 1.0 | May 5, 2004

Reducing RAM Usage...................................................................................................52

Symbian OS: Coding Conventions in C++ | 5

4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.3.7 4.3.8 4.4 4.4.1 4.4.2 4.4.3 4.5

Use caution with array granularities .....................................................................52 Avoid global data ..................................................................................................52 Beware of base class member data .....................................................................53 Use the cleanup stack correctly ...........................................................................53 Delete early ..........................................................................................................53 Test on hardware with the maximum data set......................................................53 Break up long, complex operations ......................................................................53 Use descriptors correctly ......................................................................................53 Beware of recursion, and build in limits................................................................54 Beware of logging code ........................................................................................54

Reducing Stack Usage..................................................................................................53

Low Disk Handling ........................................................................................................54

5.
5.1 5.2 5.3

Building for ARM Targets ................................................................................. 55


General Issues ..............................................................................................................55 Function Exports ...........................................................................................................55 The "MyDll.DLL has (un)initialized data" error from PETRAN ......................................55

6.

Terms and Abbreviations ................................................................................. 58

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 6

Change History
May 5, 2004 Version 1.0 Replaces documents Coding Idioms for Symbian OS C++ Programmers v1.0 and Coding Idioms for Series 60 Developers v1.1

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 7

1.
1.1

Introduction
Purpose and Scope

This document is aimed at all developers who use the C++ language to develop applications for Symbian OS C++ programming. The purpose of this document is to assist designers in the task of C++ programming. All software authors are expected to adopt the conventions detailed here as standards. The apocryphal 80-20 development rule states that 80 percent of the time will be spent fixing 20 percent of the problems that occur in development. This document addresses that problematic 20 percent.

1.2

Motivation

Programming style can be described as, "The way that a programmer brings clarity, maintainability, testability, reliability, and efficiency to the code of a module." This definition sets the objectives for good programming style but it does not help to determine whether a piece of software has good or bad style. One can easily conclude that coding itself is only one factor in reaching these goals: a badly designed module can hardly be clear, maintainable, testable, or reliable, even if it is coded in the best possible style. On the other hand, even the most excellent design cannot make the software clear and maintainable if its implementation, the code, is of poor quality. In a software platform, the programmers must have a similar style, otherwise software maintenance is too expensive and difficult, and reuse becomes almost impossible. A uniform way of coding contributes strongly to the programming productivity of the organization.

1.3

Document Outline

The first part of the document introduces the coding convention; this is followed by practical coding idioms that provide beginners with a good understanding of Symbian OS C++ programming. The last part of the document addresses system resource usage.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 8

2.

Coding Conventions

This section will describe how to write good-style Symbian OS C++ applications. Although not strictly required for writing good Symbian OS applications, it is good software practice for all programmers. A good programming style can greatly improve reusability, maintainability, and readability. Once a convention is defined, it should be used consistently among all programmers in the team and all applications.

2.1

General Principles

For clear and maintainable code, certain general rules can be applied to all languages. 2.1.1 Reusability

Avoid rewriting or copying code that someone else has written. Identify a need for a common module (or component) that you and others might require and communicate the need forward to other persons in your project. You or someone else should create a well-designed, well-documented, and well-tested component. For example, file system dialogs, formatters, and checking routines are good candidates to be grouped into common modules. By creating common modules you can reduce the code size; however, remember that reusing an insufficiently documented or tested module can sometimes be very expensive. Make sure the code can protect itself against misuse. If this is not possible, remember to document all restrictions, especially error codes returned by methods. Comments in code can make the code more understandable and easier to read. However, if the code needs at lot of explanation, it is probably not clear enough. 2.1.2 Maintainability

One of the properties of good code is that it is understandable and easy to read. If/when code needs to be maintained, it can be a very difficult and timeconsuming job if the maintenance person does not understand what the code does. Thus, when writing code you should always ask yourself how others will understand it. From a software development management point of view, the ideal scenario would be if the code developed by one person could be taken over by another person without difficulty. If the code of a module becomes too long or complex, check to see whether the module should be reorganized. 2.1.3 Build process

There must be a well-known, very good reason to ignore warnings. A common header file should suppress warnings that should genuinely be suppressed throughout the system. Use PC-Lint or a similar source code checker to make additional checks on code to catch some of the bugs, inconsistencies, and oversights. 2.1.4 Internationalization

Internationalization means designing and coding software to allow for localization and smooth production of language variants with a minimum of engineering changes. Internationalization aims at single code base.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 9

2.1.5

Specific Symbian OS issues

Symbian OS is designed to deliver high-function applications in a very resourceconstrained environment. Robustness is critical to end-user acceptance of portable devices. The Symbian OS resource management and cleanup framework provides a robustness and scalability unparalleled in this application sector. Note: Every programmer should be familiar with the cleanup and memory management issues of Symbian OS. The important issues in coding are reliability, size, and speed, in that order.

2.2

Source Files
2.2.1 General issues

Never use white space or non-ASCII characters in file names. Also, the following characters are not allowed: & ^ + - @ $ % * ( ) | \ / [ ] { } < > ? ; : , " ' Write file names in lowercase, but with the first letters of words capitalized. Write file names as if they were case-sensitive so that they are spelled identically in the file system and in source code files. Do not, however, rely on case-sensitivity in file names. In other words, do not name two different files so that their names differ only by upper-/lowercase letters. (Example: If file "CallEdit.h" already exists, do not name another file "CalledIT.h"). References to physical file system paths must not be used. When adding a new class into an existing library, always use a separate file for the class. Only tiny helper classes can be located together with their main class. When writing applications, the engine, views, and main application code should be written in separate files. If it is probable that a single engine will be accessed simultaneously by multiple applications, consider implementing a client/server interface. Machine- or compiler-dependent parts of code are isolated in separate files. Header files are kept in the directory ".\inc" or in directories mentioned in the bld.inf file. Private header files located in the process or library directory of the same project are enclosed in quotation marks ("") in #include directives. Public headers are enclosed in angle brackets (<>).

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 10

2.2.2

File types

2.2.2.1 File name extensions The following file name extensions are used for source and binary files: File name extension h .inl .hrh .cpp .mmp bld.inf .mak .rh .rss .loc .rsc .rXX .bmp .mbm .aif .app .dll .exe Description C++ header file C++ inline function implementation file Common header file for resources and C++ programs C++ source file Symbian OS project specification file (for makmake) Build information file Makefile Resource header file Resource file Localization string resource header file Compiled resource file (no language specified) Compiled resource file (XX is the language specifier) Windows bitmap file Symbian OS multibitmap file Symbian OS application information file Executable Symbian OS application, polymorphic DLL Shared library DLL Symbian OS server or executable program

Table 1: File name extensions used for source and binary files 2.2.2.2 C++ header file (.h) Header file comments are written for the user of the module. They are adequate if the test cases for the module can be drawn up on the basis of them; see Section 2.5, Commenting Conventions. Write one class in one header file. Only tiny helper classes may be grouped together with their "parent" class. Add #ifndef CLASSNAME_H and #define CLASSNAME_H statements in the beginning of a header to protect it against multiple includes from other files. This anti-nesting mechanism prevents multiple inclusions of the header files.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 11

Note: The #ifndef XXX form of the guard against multiple inclusion is significantly quicker than the apparently equivalent #if !defined(XXX) form. Inside a header file include only those other header files that are required for class definition or to use the class. Do not use #ifndef statements when including other header files. Remember to use your own prefix (e.g., CTetrisAppUi) in class names. Forward declarations are preferable to #include statements. If the class is accessed only via pointers or references, it should not be included with #include. Including unnecessary header files may very easily increase build time and object file size significantly. Most #include statements are actually unnecessary. It is often adequate to forward-declare the class. Wrong:
// MyClass.h // This pollutes include-hierarchy: #include OtherClass.h class CMyClass: public CBase { ... private: COtherClass* iPointer; };

Including "OtherClass.h" is unnecessary, but pollutes the include hierarchy for "MyClass.h". A better solution would be: Example:
// MyClass.h class COtherClass; class CMyClass: public CBase { ... private: COtherClass* iPointer; };

Note that now only "MyClass.cpp" has to include "OtherClass.h". Other modules using just "MyClass.h" never need "OtherClass.h". Place constructors and destructors at the beginning. Place data members at the end. Always use separate sections for derived methods and add comments to describe where they have been inherited from. Do not implement inline methods in a header file. Put them into a separate .inl file. Use standard headers, giving standard information; be consistent; for example:
// EXAMPLE.H // Copyright (c) 2003 Symbian Ltd. All rights reserved. // Example module header //

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 12

2.2.2.3 C++ source file (.cpp) Implementation file comments are written for the maintainer of the module. They are adequate if they meet the following requirements: o o They describe class level implementation and clarify all the nontrivial issues. Member function comments cover explanations for all the nontrivial implementation code. Other functions must also contain parameter and return value comments. Be consistent in the use of programming conventions.

Write the implementation of one class in one file. Methods should be defined in the same order as in the header file. Use the same names for the method parameter as you have used in the header file. 2.2.2.4 Resource file (.rss) Remember all the necessary #includes for your resource structures. Use a unique NAME identifier (maximum length is four characters). Place enums in .hrh and resource structures in .rh. Write resource names in lowercase; the resource compiler generates uppercase identifiers for C++ programs automatically. Do NOT put strings to be localized into resource files. Use logical names in resource files and put the actual strings in a separate resource header file (.loc), i.e., not to the same header file as resource structures. If you are using a TBUFnn variant, e.g., TBUF80 or TBUF64, and you are loading the string into a local TBuf<nn> variable, ensure that both "nn"s are equal. This prevents nasty buffer overflows at run time. The preferred way is to use pure TBUF and use StringLoader::Load or iEikonEnv>AllocReadResourceL methods in your code. 2.2.2.5 Common header file (.hrh) The common header file must meet both C++ and resource file syntax. Usually only control IDs, defines, and enums must be placed in the common header file. Resource IDs will be collected automatically into filename.rsg and must not be defined in the .hrh file. Resource structure definitions must be in the .rh file they do not meet C++ syntax and thus cannot be compiled by the C++ compiler. 2.2.2.6 Localization string resource header file (.loc) The localization file lists all of the logical names (actually C macros) for texts to be localized.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 13

Use comments in the .loc file to facilitate easy localization. Comments should be descriptive; describe the functionality of the text item in English. Note: For common and generic texts, check if it already exists in avkon.loc, and use the text string from AVKON. Avoid duplicate and overlapping text items. 2.2.2.7 Project specification file (.mmp) Note: Use the LANG SC statement to allow for conditional software builds. Using LANG SC allows for adding new language variants without having to update .mmp files.

2.3

Language
2.3.1 Naming and writing conventions

Note: These rules follow Symbian OS naming conventions. For further information, please see Symbian OS SDK documentation. Use descriptive names for identifiers. If you cannot name an identifier, consider whether it has the right to exist at all. In general, the first letter of words should be capitalized for functions (exceptions noted explicitly). Use full English descriptors that accurately describe the variable/field/class/. For example, use names like firstName, grandTotal, or CorporateCustomer. Although names like x1, y1, or fn are easy to type because theyre short, they do not provide any indication of what they represent and result in code that is difficult to understand, maintain, and enhance. Use terminology applicable to the domain. If your users refer to their clients as customers, then use the term Customer for the class, not Client. Many developers will make the mistake of creating generic terms for concepts when perfectly good terms already exist in the industry/domain. Use mixed case to make names readable. Use lowercase letters in general, but capitalize the first letter of class names and interface names, as well as the first letter of any noninitial word. Use abbreviations sparingly and intelligently. Maintain a list of standard short forms (abbreviations), choose them wisely, and use them consistently. For example, if you want to use a short form for the word number, then choose nbr, no, or num; document the one you choose (it doesnt really matter which one); and use only that one. Avoid long names (less than 15 characters is a good idea). Although the class name PhysicalOrVirtualProductOrService might seem to be a good class name at the time (an extreme example), this name is simply too long and you should consider renaming it to something shorter, perhaps Offering. Avoid names that are too similar or differ only in case. For example, the variable names persistentObject and persistentObjects should not be used together, nor should anSqlDatabase and anSQLDatabase.
Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 14

Capitalize the first letter of standard acronyms. Names will often contain standard abbreviations, such as SQL for Standard Query Language. Names such as sqlDatabase for an attribute or SqlDatabase for a class are easier to read than sQLDatabase and SQLDatabase. All names should be in English, because the reserved words of the programming language are in English, too. In addition, in an international organization, English is usually the only language everybody understands. Write all words adjoined, for example "LikeThis". Avoid numbers and abbreviations in names. Never use _ (underscore). The only exception is _ as a delimiter in preprocessor macro names in #define; see Section 2.3.1.6, Macros and other #defines.
void TObject::PrepareForCommit(); class CGlobalText; // see note on class names and types TInt elementOffset; // automatic variables begin in lowercase

Function, class, typedef, struct, and method names begin with an uppercase letter. Using automatic variables o o All automatic variable names begin with a lowercase letter. Do not declare automatics until required (avoid the Kernighan and Ritchie (K&R) C style of declaring all the automatics used in a routine at the top of that routine). Always try to initialize variable when they are declared rather than assigning values to them. Never initialize or instantiate multiple items on the same line.

o o

Example:
TInt CTetrisClass::Function() // Member function { TInt automaticVariable( KInitialValue ); // Integer variable TBool status( IsItTrue() ); // Boolean variable iMemberClass.Method(); // Calls a method ... }

Wrong:
TBool Variable = is_it_true(); // Wrong type specifier, name of an automatic variable // mistyped, assignment used instead of initialization and name // of a function miswritten.

Explanation: Having the first letter of the automatic variable lowercase is simply a convention to help reviewers and maintainers follow your code. The main reason for not declaring automatics until required is because they may not be required; declaring them at the start of a routine is therefore inefficient in terms of execution speed and use of stack space. For lengthy routines, declaring automatics as close as possible to the point of use can also help people understand code fragments without having to refer back to the top.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 15

Initializing variables when they are declared is more efficient (smaller and faster code) and avoids the risk of using the variable in an uninitialized state. Declaring multiple items on the same line can lead to errors such as:
TText* data1, data2; TInt int1, int2 = 0;

when the programmer meant:


TText* data1; TText* data2; TInt int1=0; TInt int2=0;

2.3.1.1 Class names Class names consist of <Symbian OS prefix><Project prefix>Name The Symbian OS prefix is one of the following: o o o o C (heap-allocated class derived from a base class CBase) R (resource class containing handles to a real resource, which is maintained elsewhere) T (simple type) M (mix-in class / interface class)

The class-naming distinction helps reinforce valuable programming idioms. For further information about the definition of the class types, please refer to the SDK help. Structs are T. Exceptionally, driver classes known by the kernel are D classes. See the Symbian Developer Library for more information. Use references in preference to pointers if none of the criteria for using pointers apply. Pay attention to const; use const for parameters and return values if the data is not to be modified. Static classes have no prefix letter. Example:
class MTetrisObserver; // Observer class (mix-in) /** Tetris game factory. */ class TetrisFactory // Static class { public: /** Returns new CTetrisItem object. */ static CTetrisItem* NewItem();

...

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 16

2.3.1.2 Functions Parameter names have an a prefix. Parameter names must be mentioned in both declaration and definition. If some parameter is not referenced inside the function, its name can be omitted from the definition. Example:
// tetrisc.h: /** Base class of Tetris game. */ class CTetrisClass : public CBase { public: /** * Initializes a game. * @param aNumber Difficulty level. * @return Status code (0 is success, -1 is error). */ TInt MemberFunction( TInt aNumber ); ... // tetrisc.cpp: ... // -------------------------------// CTetrisClass::MemberFunction // Implementation description. // -------------------------------TInt CTetrisClass::MemberFunction( TInt aNumber ) { ... }

Use a const attribute where appropriate to indicate that a method does not change the state of an object or a parameter, which is read-only. Ellipsis notation is forbidden. Example:
TInt Add( const TInt aFirstNumber, const TInt aSecondNumber );

Wrong:
TInt BadFunction( ... ); // Ellipsis notation must not be used

A trailing L indicates that function or method might leave. It is very important to know whether or not a function might leave. A trailing C indicates that a function or method places one item on the cleanup stack and leaves it there. The caller must pop the item later from the cleanup stack. A trailing D indicates that the method will destroy the object in question. Example:
static CTetrisClass* NewLC(); // May leave and place an it May leave and will destroy the object (in this case, it will destroy the dialog after use)

// the cleanup stack TInt ExecuteLD( TInt aResourceId );// // // //

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 17

Wrong:
TInt MyFunction() { FunctionThatMayLeaveL(); ... // If you call a function that may leave, you must either // state that MyFunction can leave too (using L-postfix) or // use TRAP-harness to catch the error that might occur.

Use the following naming convention when defining data exchange methods. Notice the const keywords: Example:
TInt Width() const; const CHugeMatrix& Matrix() void SetWidth( TInt aWidth ) void GetWidth( TInt& aWidth ) const; returns returns changes returns a value a reference the value a value in reference arg.

Wrong:
TInt GetCount(); // Get-prefix is not used if function just returns the value. // Count() would be the correct name for this function. // Also "const" keyword is missing.

Encourage generality; use the least derived class possible. Example:


void Foo(const CArrayFix<X>& aX);

rather than:
void Foo(const CArrayFixFlat<X>& aX);

2.3.1.3 When to use which form of parameter / return By const value const X o Don't do this; it only restricts the code in the implementation, and has no impact on clients.

By value X o o For return values or arguments that are concrete data types or are small (< 8 bytes). For return values where the object has to be built (i.e., cannot return a reference to one you have already); note that for large classes it is preferable to use a reference argument to get a value, as this reduces stack usage.

By const reference const X& o o o Do not do this for concrete (built-in) types, especially enumerations, as the target (gcc) compiler will fault. Use for larger arguments (> 8 bytes). If in doubt, pass all class arguments using this form.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 18

By reference X& o o o To allow a function to return values through these arguments, or modify the contents of them. For return values, where the object exists and can be modified by the caller; note the lifetime of the object being. Returned must extend beyond the scope of the function.

By const pointer const X* o o Const for items that can be used (but not modified) by the recipient; e.g., a buffer. If a NULL (nonexistent) object is meaningful (so const X& cannot be used), i.e., the argument/return value is optional; if this is the case, consider using a function overload instead one taking a reference and one taking no arguments if it makes the API clearer.

By pointer X* o o For a modifiable (non-const) object that is optional; again, consider an overload if it makes the interface more intuitive. To imply transfer of ownership, rather than just available for use (owns vs. uses); the recipient of the object is now responsible for deleting the object.

2.3.1.4 Member data Use preceding i for instance data. DLLs must not have any global non-const data. This includes static member variables. Example:
/** * Tetris game base class. */ class CTetrisClass: public CBase { ... private: TInt iIntegerMember; COtherClass* iPointerToAnotherClass; ... }

Wrong:
class BadClass { ... // This member function is OK: EXPORT_C static TInt MemberFunction(); ... // But this data member isnt: static TInt iStaticMember; // DLLs can not have any global non-const data.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 19

2.3.1.5 Pointers and references When declaring or defining pointers or references, place a specifier (* or &) next to type, not next to name. Example:
CTetrisClass* pointer; void Modify( TInt& aInteger ); // Pointer to CTetrisClass // Reference parameter used

Wrong:
CTetrisClass *badNotation; void Modify( TInt &aBadOne );

2.3.1.6 Macros and other #defines Avoid macros in release code. Macros are interpreted using text replacement, which is error-prone and never type-safe. Exception: Debug and test code often requires precompiler macros. Write all letters capitalized. Underscore ( _ ) separates words. Avoid #defines; use real constants instead. Example:
const TInt KMagicNumber = 42;

Wrong:
#define MAGIC_NUMBER 42 // const TInt should be used

2.3.1.7 Constants Use a K prefix in const names. Do not pollute global namespace; use relevant prefixes in names. Example:
const TInt KTetrisInitialBlockSize = 20;

2.3.1.8 Enumerated types Enums should be scoped within the relevant class. Do not pollute the global name space. Enums and their members must have relevant, meaningful, and unambiguous names. Enumerations are types, therefore they have a T prefix. Use an uppercase E prefix for enum members. Class-specific constants can be implemented as enums and in that case are K.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 20

Example:
class CTetrisBlock { ... enum TTetrisBlockType { ETetrisLBlock, ETetrisSquareBlock, ... }; ... };

2.3.1.9 Global variables Use of global variables is discouraged. Start with a capital letter. In cases where confusion might be caused (for instance, if the capital letter in question is an H, a T, or a C, and there's no obvious alternative name), you may use a small g prefix, e.g. gWhatever In Symbian OS, DLLs must not have any global non-const data; it is better to use thread local storage (TLS). 2.3.1.10 Control structures Every switch() statement must have a default: clause, at least for detecting unexpected switch expressions. Case branches without a break statement should be avoided. If one has to be used, the flow through must be explicitly indicated by a comment. Use always compound statement braces ({, }) in all of the control flow statements, if() - else, while(), do - while() and for(), even if there was only a single statement or an empty block. Example:
if ( error ) { Panic(); }

The control flow statements break, go to, and continue must not be used. An exception: break should be used in case statements. 2.3.1.11 Exceptions and templates C++ style exceptions must never be used. Symbian OS provides TRAP-harness, which should be used to handle error conditions. Avoid defining your own templates. If you have to, test them carefully on the target platform. Keep in mind that different C++ compilers have different problems with templates. Handle all errors.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 21

2.4

Indenting Conventions
2.4.1 Spacing

All C++ statements within a block of code should be indented exactly four spaces deeper than the enclosing block. Configure your editor so that it uses spaces instead of tab characters. Note that some editors are not able to show possibly existing tabs. Primary operators should be written with no spaces around them. Primary operators are: o o o o o lvalue.id var.*ptr table[index] pstruct->id pstruct->*id

Unary operators should be written with no spaces between them and their operands. Exceptions: new and delete. All other operators should be written with one space on each side of the operator. Example:
if ( iPointer->Method() > KOurMaxValue ) { iCounter++; variable = iTable[a] + iOffset; }

2.4.2

Placement of braces and parentheses

The placement of the { and } braces must be at the same level of indentation as the code they are enclosing. Note that some editor tools can be configured to indent the code according to this rule. There should be a maximum of 80 characters per line. Longer lines are hard to read and some editors cannot handle them properly. Longer lines are split into several lines, for example:
class CMessageListView: public CPknView, public MEikListBoxObserver, public MmsvSessionObserver

or:
void DoActivateL( const TVwsViewId& aPrevViewId, TUid aCustomMessageId, const TDesC8& aCustomMessage );

or:
if ( isThisIntegerNumber > 42 && isThisIntegerNumber < 142 && somethingElse >= 0 )

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 22

Matching parentheses should be written with matching spaces, e.g., if there is no space on the inside of a ( then there must be no space on the inside of its matching ). There must be no space between a function or macro name and its parenthesized argument(s). There must be a single space between a keyword (e.g., for, if) and its parenthesized argument(s). Example:
iMember.Method( first, second ); if ( i < KtetrisMaxIterator ) ...

Wrong:
Modify(parameter ); // Matching number of spaces should be used on both sides. if( error ) ... // There should be one space between if and (.

Parentheses should always be used where there is potential ambiguity.

2.5

Commenting Conventions
2.5.1 General commenting conventions

Comments are written in English. Other comments must be inserted into the code as it is being developed. They must appear with the relevant code. Avoid commenting a matter in many places and avoid repeating code in comments. Comments should explain why something has been done rather than what has been done. Choose C++ style comments ("// Comment") and avoid C style ("/* Comment */"). Exception: File header comments can be written in C style. Exception: Class and method header comments should be written in Javadoc style, as defined in Section 2.5.2, Class and method header file comments. 2.5.2 Comment layouts

A boxed comment has the following layout:


// ============================== // Boxed comment example. // ==============================

An exception is a file header comment where you can use C style comments. A block comment has the following layout:
// Block comment example. // The second line of comment. //

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 23

Block comments should be indented so that the opening / (back slash) is aligned with the first line of code to which it refers. The comment is placed before the code it describes. An appended comment consists of a few words on the same line as the item to which it relates.
If ( aColour == KRgbRed ) // Is it red?

A file header comment has the following layout. Note that the Interface part, which is used to categorize APIs, is used only in .h files. If the category of the API has not been set, it should be left empty.
/* * ============================================================= * Name : ?FileName.h * Part of : ?Subsystem_name / ?Module_name * Interface : ?Interface_category, ?Interface_name * Description : ?Description * Version : ?Version * * Copyright (c) 2002 Nokia Corporation. * This material, including documentation and any related * computer programs, is protected by copyright controlled by * Nokia Corporation. All rights are reserved. Copying, * including reproducing, storing, adapting or translating, any * or all of this material requires the prior written consent of * Nokia Corporation. This material also contains confidential * information which may not be disclosed to others without the * prior written consent of Nokia Corporation. * ============================================================= */

Class and method header file comments have the following layout:
/** * Short class description goes here ending with dot. * Second sentence is here. * Third sentence is here. */ class CMyClass : public CBase { public: /** * Method header documentation goes here. * @since 2.0 * @param I Parameter description. * @return Description of the returned value */ TInt MyMethod( Tint I ); ... }

2.6

Data Types and Usage


Note: Most of these rules have been adapted from Symbian OS SDK and Symbian OS coding conventions. 2.6.1 Constants

Never use hard-coded constants, such as "magic numbers," in your code. Use enums, resources, and real const definitions instead.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 24

Example:
switch ( aCommand ) { case ETetrisDropBlock: ... break; case EEikCmdExit: ... break; default: // Handle unexpected CommandID here. ... }

Avoid preprocessor macros (#defines). Collect your const and enum definitions to a single place so that they can be easily spotted for changes. If the interface of the class itself uses enum values (for example, as default arguments), the enum values must be defined before they are used in a function declaration. Always use a predefined library constant if one exists. Never define a string constant. Use a resource file instead. As an exception, string constants may be used where they do not appear on the UI and are not language-dependent, e.g., protocol strings, resource names, system file names. Never define a color constant of your own. Use a predefined library constant instead. Example:
const TRgb KTetrisBgColor( KRgbBlack ); ... TRgb backgroundColor( KTetrisBgColor );

2.6.2

Variables

Initialize variables when you create them. Use initialization instead of assignment when it is more effective. Never initialize or instantiate multiple items on the same line. Do not declare automatic variables until required. Never use static local variables. Example:
TRect rectangle( 0,0,0,0 ); CTetrisClass* pointer = NULL;

Wrong:
static int a, b, c; // Static local variables are not allowed, TInt type must be // used, variables should be initialized (to zero, for example) // and multiple items should not be declared on the same line.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 25

2.6.3

Types

Never use primitive types (such as int, char, float, double, or bool) directly. Instead, use concrete types provided by Symbian OS. Note that TBool is ETrue or EFalse but in an if() statement you should never compare it to any exact value. Example:
TBool booleanVariable( EFalse ); ... if ( booleanVariable ) // Is it true? ...

Use descriptors (TDes, TPtr) and buffers (HBuf, TBuf) instead of ordinary pointers when storing data and handling strings. 2.6.4 Typecasting

Casts, as in other operating systems, should be used with caution. If a cast seems to be needed, check that this does not reflect a design weakness. Prior to Symbian OS v6.0, gcc did not support the C++ casting operators, and hence Symbian OS defined macros as simple C style casts for that compiler: REINTERPRET_CAST, STATIC_CAST, CONST_CAST, MUTABLE_CAST. From Symbian OS v6.0 onwards gcc fully supports the C++ casting operators, and the macros have been changed to reflect this in Symbian OS v6.1. This means that from Symbian OS v6.0 onwards, developers should use the native C++ casting operators rather than the macros. The more sophisticated C++ dynamic_cast operator should not be used because EPOC does not enable or support Run Time Type Information (RTTI). Use explicit typecasts to clarify your code and to avoid problems caused by unexpected implicit casts, such as integral promotions. Example:
TUint16 wd( TUint16( TUint16( aPlace[aIndex] ) * TUint16(0x100) ) ); // This is the right way to do it without compiler warnings.

Avoid casting to any type that has a different size than the original. Avoid removing a const attribute. Use a separate cast for constness and representation. In case a static_cast or a reinterpret_cast to a non-const type is used, whose argument is a const object of an otherwise appropriate type, use an additional const_cast to the argument in question. Typecast from a derived class to a base class is, in most cases, implicit. If an explicit cast is needed or if you need to cast from a base class to a derived class, use static_cast<type>(exp).

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 26

Example:
class CDerived : public CBase ... CBase* basePointer = NULL; basePointer = new CBase; ... CDerived* derivPointer = NULL; derivPointer = static_cast<CDerived*>(basePointer); // Note that you must be sure that basePointer really contains a // pointer to CDerived object. Dynamic cast isnt supported !

Typecasting between any other incompatible type must be done with a reinterpret_cast(type,exp) macro. See the ANSI C++ section expr.reinterpret.cast for a description of permitted conversions. Never break 4-byte alignment with any typecast! 2.6.5 Pointers

Never return a pointer or a reference to any local data. Wrong:


TInt& AddFive( TInt aNumber ) { TInt result( aNumber + 5 ); return result; // Whoops, local reference returned }

Initialize the pointer to NULL, not zero. In an if() statement, you should never compare any pointer to any exact value. Example:
CTetrisClass* tetrisPointer = NULL; ... if ( tetrisPointer ) // Check if it points to an object ...

Assign or initialize a pointer to NULL every time, when it does not point to any existing object.

2.7

Objects and Classes


Note: For further information, please see Symbian OS SDK documentation.

2.7.1

Class behavior

Every class must have a default constructor and a destructor. Whenever possible, declare the default constructor, a copy constructor, and an assignment operator as private in order to prohibit accidental access to them. Note that a C++ default constructor must not contain any code that can leave.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 27

Example:
/** * A tiny class. */ class TinyClass { public: // Constructors and destructor virtual ~TinyClass(); private: TinyClass(); // Prohibit copy constructor and assignment operator // in our class not derived from CBase. TinyClass( const TinyClass& aSource ); // copy constructor TinyClass& TinyClass::operator=( const TinyClass& aParam ); }; /** * A tiny class based on CBase. */ class CTinyClass: public CBase { public: // Constructors and destructor virtual ~TinyClass(); private: TinyClass(); // CBase prohibits copy constructor or assignment operators. // No need to do it here. };

Every class that owns any pointers requires two-phase construction.


class CComplexClass : public CBase { public: // Constructors and destructor ... static CComplexClass* NewL(); static CComplexClass* NewLC(); private: CComplexClass(); void ConstructL(); ... };

An assignment operator must handle self-assignment. Example:


const CMyClass& CMyClass::operator=( const CMyClass& aParam ) { if ( this == &aParam ) { return *this; } ... }

Every time you allocate an object, you must ensure that it is destroyed correctly. The same program entity (e.g., class) that allocates memory releases it, too.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 28

After deleting an object, assign a NULL value to it to prevent later attempts to delete a nonexisting object. Exception: In the destructor it is not necessary to nullify a deleted member pointer. Example:
CMyClass::~CMyClass() { delete iAnotherClass; } CMyClass::SomeMethod() { // delete iAnotherClass; iAnotherClass = NULL; // }

Do not use any global or static data in constructors or destructors. Avoid using friend functions or classes. Always write access-control specifiers; this removes all ambiguity. List the public methods first, with special member functions at the top. List public member data before private member data (otherwise, changing the private data would break binary compatibility). Use explicit access specifiers at the start of each class section: o o o o o o o friend classes public methods protected methods private methods public data protected data private data

Heed binary compatibility issues with respect to promoting functions' access control specifiers; (if you change the access for something after an API freeze, you may have to leave it in exactly the same place as before). Use white space to group related functions and improve readability. Function arguments are named in the function declaration (as well as the definition); this improves readability. Obey the indentation conventions. 2.7.2 Class interfaces

Avoid declaring public data. Use data exchange methods and private data members instead. Interface member functions (public methods) should not usually return a nonconst reference or pointer to a member variable. Returning handles to member data breaks object encapsulation.
Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 29

If you return a pointer or a reference, remember that the lifetime of the object being returned must always extend beyond the scope of the function! Arguments of class type should be passed to functions by reference. Example:
TInt MatrixWidth( const CHugeMatrix& aParam );

In the public API, pointer arguments imply passing ownership, and reference arguments imply usage only. List the public methods first, with special member functions, such as constructors and destructors, at the beginning. Use explicit access specifiers at the start of each class section. Example:
class CMyClass : public CBase { public: // Constructors and destructor ... public: // New functions ... public: // from CBase ... private: // New functions ... };

Use line feeds to group related functions and improve readability. Do not implement member functions in the class declaration. Inline functions are implemented in a separate .inl file. Do not define unnecessary variables as member variables. If you need some variable, only temporarily allocate it either from stack or heap and delete it after you do not need it anymore. Never push member data on the cleanup stack since this may create a double deletion. 2.7.3 Virtual member functions

The destructor is always virtual. The constructor (one or two phased) is never virtual. Virtual functions that replace inherited behavior must be grouped together in the header, with a comment documenting which class the behavior is inherited from. Specify the virtual keyword only when you declare a new member function, not when you replace an inherited one. Never make virtual functions inline; it is difficult to know exactly how a compiler treats these, and can lead to code bloat. Exception: Destructors may be inline. 2.7.4 Inline member functions

Specify inline explicitly for inline functions. Do not leave it to the compiler.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 30

Do not provide the inline implementation in the class definition; this confuses and pollutes the header, making it less readable. Implement inline functions in a distinct file included by the header (.inl). Inline functions must be small; typically they just get or set a member variable, which has the amount of one or two machine word data. 2.7.5 Multi-inheritance

Only M classes may be used in multi-inheritance. Only the interface may be inherited. Therefore all M classes must be pure virtual. Inherit from the CBase-derived class first to achieve the correct layout of the v-table. Example:
class CDerivedClass : public CBase, public MSomeObserver

M classes may only be derived from other M classes. 2.7.6 Cleanup stack

Use the cleanup stack in preference to TRAP harnesses where appropriate. Deriving a class from CBase provides support for using the cleanup stack. For other classes you can provide explicit cleanup support via the TCleanupItem class. Never push member data on the cleanup stack.

2.8

Internationalization Coding Guidelines

This chapter gives guidelines on how to create code that can be localized to various languages and regions most easily and efficiently, minimizing engineering changes. Divide the project deliverables and any configurable resources into those that may require localization and those that will not. These will include, for example, texts that need to be translated and graphical images that may require localization. They also require layout rules. If in doubt, allow for localization. The rule of thumb for implementation: Do not hard code anything that is visible in the user interface. For example, the following items must be in a separate resource file: All the texts visible in the user interface (including application names). These go into .loc files. File names Sounds Time and date Etc.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 31

2.8.1

Descriptors and character sets

All Symbian OS builds are Unicode, and descriptors containing an internal representation of text will be 16-bit strings. Internally, Symbian OS uses UCS-2 (Little Endian). Class names such as TPtr should be used in code; objects of this class will be built as objects of the class TPtr16. Use explicit 8-bit versions of descriptors such as TPtr8 to hold binary data or text in external character sets. Use the CCnvCharacterSetConverter class to convert text between the internal Unicode representation and externally used character sets. A unique identifier is assigned to each of the commonly used character sets; the CCnvCharacterSetConverter object will convert text between the specified character set and Unicode. 2.8.2 Text

2.8.2.1 Logical name The logical name convention demands that all logical names are unique throughout the software. The same logical name should not be used for two different functions and layouts, even though the string might be the same in English. For example, On and Off are always the same in English. In Italian, "On" can be translated as Attivo, Attiva, Attive, Attivi, Attivato, Attivata, Attivate, Attivati, or Si. Respectively, the same number of translation options is applicable for the term "Off" the translation depends on the context. Where you have to make decisions about the length of resource strings, allow at least 30 percent headroom for translated text in general, but if the string is a single word then make allowance for at least 100 percent expansion in the translation whenever possible. This is all because the development language is English, which itself uses reasonably short words. 2.8.2.2 String Avoid dynamic or run-time concatenation of different strings to form new compound strings (e.g., composing messages by combining frequently used strings). Example: qtn_common_new "new" qtn_msg_message " message" qtn_msg_received " received" In Finnish, "new message received" can be translated uusi viesti saapunut, but if the string qtn_common_new was used for "3 new messages received," the translation would not work, as it should read "3 uutta viesti saapunut," that is, two of the words are inflated. Note that in this example, if the same string "new" is used in the beginning of a concatenated text, in languages where there is distinction made with the casing, lowercase would simply be wrong in the beginning of the text item displayed on the UI.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 32

2.8.2.3 Style Avoid using a style of text for a particular purpose to make a definite distinction. Most non-Latin-based writing systems do not make a distinction between upper- and lowercase, or the italics type face may be missing. Do not use folding to achieve a lowercase or uppercase effect; instead, use TChar::UpperCase or TChar::LowerCase for this purpose. Folding removes case distinctions, and also, by default, does many other things such as removing accents and changing Hiragana to Katakana, which may not be desired. Text localized to a non-English language (e.g., German) can easily expand up to 100 percent. The general recommendation is to allow at least 30-50 percent for horizontal expansion and 10-20 percent for vertical expansion. 2.8.3 Graphics and screen layout

Avoid using text in graphical UI elements because there may be instances where the graphics need to be redrawn for new markets to take cultural requirements into account. If text is really needed for graphics, a better solution would be to use a text string on top of graphics. Do not hard code screen sizes (calculate the layout at run time). 2.8.4 Locale-specific data

The API of the class TLocale is documented fully in the Series 60 SDK. All Symbian OS developers should be familiar with the class and it should be used where appropriate to produce locale-independent code. The User class library also possesses static functions, some of which are language-dependent. These include User::Language(), which returns the language of the current locale. The hard-coded language or country-dependent information such as date, time, currency, etc., should be removed from program code. 2.8.4.1 Date, time Code your date and time locale independently, using Symbian OS formatting functions TTime::FormatL() to format a date or time. This can be done to honor locale-specific settings within TLocale that include the order of day, month, and year within a short date. Use formatting strings in following format "%/0%1%/1%2%/2%3%/3" instead of "%M%D%Y". Standard date, time, and duration formatting strings can be found from AVKON resources. It is recommended that AVKON formatting strings be used. This example shows how to produce a short date of 10/20/99 (M/D/Y) locale independently by using formatting strings from AVKON resources.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 33

#include <avkon.rsg> TTime homeTime; homeTime.HomeTime(); // get home time HBufC* dateFormatString = iEikonEnv->AllocReadResourceLC (R_QTN_DATE_SHORT_WITH_ZERO); // Read format string from AVKON resource TBuf<32> buf; homeTime.FormatL(buf,*dateFormatString); // Format the date CleanupStack::PopAndDestroy(); // dateFormatString

Available formatting strings in AVKON resources. Resource name R_QTN_DATE_SHORT R_QTN_DATE_SHORT_WITH_ZERO R_QTN_DATE_USUAL R_QTN_DATE_USUAL_WITH_ZERO R_QTN_DATE_WITHOUT_YEAR R_QTN_DATE_WITHOUT_YEAR_WITH_ZERO R_QTN_TIME_USUAL R_QTN_TIME_USUAL_WITH_ZERO R_QTN_TIME_LONG R_QTN_TIME_LONG_WITH_ZERO R_QTN_TIME_DURAT_SHORT R_QTN_TIME_DURAT_SHORT_WITH_ZERO R_QTN_TIME_DURAT_LONG R_QTN_TIME_DURAT_LONG_WITH_ZERO R_QTN_TIME_DURAT_MIN_SEC R_QTN_TIME_DURAT_MIN_SEC_WITH_ZERO Examples of Formatted Output 1.9.00 01.09.00 1.9.2000 01.09.2000 1.9. 01.09. 13:15, 3:15 03:15 13:15:15, 3:15:15 03:15:15 13:15, 3:15 03:15 13:55:23, 3:15:12 03:15:12 13:15, 3:05 13:15, 03:05

Table 2: Available formatting strings in AVKON resources Make sure that the long date format is not defined by the short date format. The two should not be interdependent. Example: In Swedish, the standardized short date format is YMD (1999-09-22) but the long date format is DMY (den 22 september 1999). If the format for long dates is dependent on the short date format, the long date will appear as "1999 september den 22," which is incorrect.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 34

2.8.4.2 Currency value Never assume the location of a currency symbol with reference to the number, number of characters for the currency symbol, and how the negative currency is formatted. Example: In the United States, it is $5 In France, it is 5 Use TCurrencySymbol::Set() to get the currency symbol and the formatting functions in TLocale to format a currency value. 2.8.4.3 Collation Collation should only be applied to whole strings; in general, collation should use the CompareC() descriptor function and not pattern-matching functions based on single characters. Symbian OS supports multiple collation methods within a locale, each of which has a unique identifier. Many locales will have customized standard collation methods, and applications should not rely on collation methods being the same across all locales. The static function Mem::CollationMethods() can be used to get the number of collation methods supported by the current locale. In addition, within a collation method, Symbian OS supports collation levels, which can be used to specify the degree of matching required. 2.8.4.4 Decimal separators Never assume what character is used for decimal separator. Use TLocale::DecimalSeparator() to get the locale-dependent decimal separator. 2.8.5 Fonts

Never hard code a font name. Use a resource file and then reference the font name by its logical name in the code. There is a possibility that a font name may also require localization. Example of bad coding practice:
CAgnAppEnv::CAgnAppEnv(CAgnAppUi* aApp, CEikonEnv* aEikEnv ) : iApp(aApp), iTheEikonEnv(aEikEnv), iNormalTextFormat(_L("Arial"),180), iBoldTextFormat(_L("Arial"),150), iBoldUnderlineTextFormat(_L("Arial"),150), iEntryCodeFormat(_L("Arial"),150), iSymbolFormat(_L("Symbol"),150), iTextCursorFormat(_L("Arial"),150) { __DECLARE_NAME(_S("CAgnAppEnv")); }

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 35

Example of good coding practice:


void CCalcMemoryDialog::DoCalculateColumnWidths() { TBuf<KFontNameSize> listboxFontName; iCoeEnv->ReadResource(listboxFontName, QTN_CALC_LISTBOX_FONT_NAME); TFontSpec spec(listboxFontName, KFontNameSize); iFont=iEikonEnv->CreateScreenFontL(spec); ... }

Asian fonts often have to be taller than Latin ones as the minimum size in pixels that can be used is greater. This may affect assumptions about the number of lines allowed in a dialog or menu.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 36

3.

Coding Idioms

This section contains a review of the techniques that Symbian OS provides for preventing memory-related problems. All developers should have a clear understanding of them they are the crux of Symbian OS programming!

3.1

General Principle
Do NOT put strings to be localized into code files. Use logical names in code files and put the actual strings in a separate resource header file (.loc), i.e., not to the same header file as resource structures. Build code that gives zero compiler warnings and zero link errors; it is dangerous to ignore compiler warnings, and these are highly likely to generate errors on other compilers. Complacency regarding compiler warnings may lead to a situation where important new compiler warnings are not noticed among a pile of familiar warnings. Strive to write tight and efficient code. Minimize the stack use. Minimize the number of calls to the allocator. Always ask the question, what would happen if this code were to leave? Avoid hard-coding magic numbers; use constants or enumerations. 3.1.1 Basic types

e32def.h defines concrete types; use them


TInt32 integer;

and not
long int integer;

3.1.2

String and buffer classes

Use the Symbian OS descriptor classes, such as TPtr, TDesC, and TBuf<n>, instead of native text string classes or handcrafted buffer classes. These result in code that is much more easily converted between wide-character and narrowcharacter builds, and also has gains in efficiency and robustness over other string classes. For the details of the class usage, please refer to SDK help for more details. 3.1.3 Date classes

Use the Symbian OS date and time classes, such as TDateTime, instead of handcrafted alternatives.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 37

3.1.4

Class types

It is strongly recommended that you use direct initialization for class types. The use of copy initialization is deprecated. Explanation: If you declare a class type using the copy constructor = like this:
TParsePtrC parser=TPtrC(FileName);

then the initialization of TParsePtrC effectively requires a call to TParsePtrC::TParsePtrC(const TPtrC&) followed by a call to TParsePtrC's copy constructor TParsePtrC::TParsePtrC(const TParsePtrC&). This is significantly more expensive than directly initializing the class type, which is done like this:
TParsePtrC parser((TPtrC(FileName)));

and eliminates the redundant call of the copy constructor. Note that you need the extra set of brackets around (TPtrC(FileName)) in order to allow the compiler to recognize that you are instantiating an object of type TParsePtrC and not declaring a function that returns something of type TParsePtrC. Without the brackets the code is ambiguous. 3.1.5 Protect objects

Catch programming and run-time errors early by using pre- and post-conditions in functions i.e., assert that those conditions required for correct execution hold true. Two mechanisms support this programming style: __ASSERT_ALWAYS / __ASSERT_DEBUG class invariants. Both of these mechanisms must be used. They catch programming errors early and aid in communicating the design and purpose of the class. 3.1.5.1 Assertions __ASSERT_ALWAYS to catch run-time invalid input. __ASSERT_DEBUG to catch programming errors. 3.1.5.2 Class invariants Define class invariants for nontrivial classes using: __DECLARE_TEST to specify the allowed stable states for that class. Call the invariant at the start of all public methods (where feasible) using: __TEST_INVARIANT to ensure the object is in a stable state prior to executing the function. Calls are compiled out in release software builds.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 38

For non-const methods call the invariant at the end of the method. This ensures that the object has been left in a stable state after executing the method:
// Removes text content, commencing at position aPos, over aLength

// number of characters
void CComplexTextObject::Delete(TInt aPos,TInt aLength) { __TEST_INVARIANT; __ASSERT_ALWAYS(aPos>0,Panic(EPosOutsideTextObject)); iTextBuffer->Delete(aPos,aLength); __TEST_INVARIANT; }

3.1.5.3 Private inheritance Avoid private inheritance: use composition instead. Inheritance should be restricted to cases when the relationship really is "a kind of". 3.1.5.4 Multiple inheritance Multiple inheritance is fine; only M classes (mix-ins) are inherited. Mix-ins specify interface only, not implementation. Inherit from CBase-derived class first; achieve correct layout of the v-table. Inherit from only one CBase-derived class; other superclasses must be mixins. Refer to the Symbian Developer Library for further information. Refer to Section 2.7.5, Multi-inheritance.
class CGlobalText : public CPlainText, public MLayDoc, public MformatText { };

3.2

Exception Handling in Symbian OS C++


3.2.1 Check for out-of-resource errors

Memory is always a scarce resource in handheld devices. As more applications are launched and more resources are allocated in the device, eventually all of the resources will be used up. In any application, an error may occur at run time due to a lack of resources, for example, the machine may run out of memory, or a communication port may be unavailable. This type of error is known as an exception. An exception should be distinguished from a programming error: A programming error can be addressed by fixing the program, but it is impossible to make a program free from the possibility of an exception occurring. Therefore, programs should be able to recover from exceptions when they occur. This is particularly important in Symbian OS, for the following reasons: Symbian OS applications are designed to run for long periods (months or even years) without interruption or system reboot.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 39

Symbian OS applications are designed to run on machines that have limited resources, particularly memory. Therefore, an out-of-resource error is more likely to occur than in a desktop application. Not all Symbian OS-based devices will have the same resources i.e., an application designed and verified on one type of Symbian OS-based device might run into resource exceptions on another Symbian OS-based device from another manufacturer. 3.2.2 Conventional error-checking methods

In a conventional C or C++ program, an if statement might typically be used to check for an out-of-resource error. For example:
if ( (myObject = new CSomeObject()) == NULL ) { PerformSomeErrorCode(); }

3.2.3

Problems with conventional methods

There are two problems with this conventional approach: It requires many extra lines of code to put such error checking around every single function that might cause an out-of-resource error. This increases code size and reduces readability. If a constructor fails to allocate resources, there is no way to return an error code, because constructors have no return value. The result would be an incompletely allocated object, which could cause a program crash. C++ exception handling (try, catch, and throw) provides solutions to these problems, but is not used in Symbian OS because: Compiler support for C++ exception handling was unavailable or inadequate at the time Symbian OS was designed. Symbian OS exception handling is specialized for use with other Symbian OS conventions (C and T classes, 32-bit integer error codes), allowing lower overheads than for C++ exception support. Existing C++ programs for other platforms that use exception handling must be modified before they can be used. Symbian OS provides its own system of exception handling. The basic operating system support for exceptions comes from: The TRAP macro and its variant, TRAPD, which allow code to be run under a trap harness. The User::Leave() call, which terminates the current function, and returns to the trap harness, specifying an error code. These are analogous to C++s exception-handling support (try/catch and throw).

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 40

3.3

Solutions in Symbian OS

Symbian OS applications can achieve efficient exception handling by following these rules: Rule 1: All functions that can leave should have a letter L at the end of their names. Leaves propagate back up through the call stack, via "leave functions" until they are caught by a trap harness. This is normally implemented in the E32Main() main function for console-based applications, or is provided as part of the application framework for GUI programs. Rule 2: When allocating memory on the heap, if the pointer to the memory is an automatic variable (i.e., not a member variable), it should be pushed onto the cleanup stack so that it can be deleted when a leave occurs. All objects pushed onto the cleanup stack should be popped off it before they are destroyed. Rule 3: A C++ constructor or destructor must not leave or fail. Therefore, if construction of an object can fail with an out-of-resource error, all the instructions that might fail should be taken out of the C++ constructor and put into a ConstructL() function, which should be called after the C++ constructor has completed. This is called two-phase construction. 3.3.1 Rule 1: Functions that leave, and trap harnesses

3.3.1.1 Functions that leave Instead of returning an error code, functions in Symbian OS should leave whenever an out-of-resource error occurs. A leave is a call to User::Leave(), and it causes program execution to return immediately to the trap harness within which the function was executed. All functions that can leave should have the suffix L. This enables programmers to know that the function might leave. For example:
void MyFunctionL() { iMember = new (ELeave) CMember; iValue = AnotherFunctionL(); User::LeaveIfError(iSession.Connect()); }

Each line in MyFunctionL could cause a leave. Any of these lines makes MyFunctionL a leaving function. Note, however, that it is very rarely necessary for application code to use a TRAP, since the application framework provides traps in the right places, and code to handle them. Traps should not be used in normal coding. In general, the way to deal with leaves is to simply allow them to propagate through the function by adding a letter L suffix to the function name. 3.3.1.2 The new (ELeave) operator The possibility of the new operator failing arises so often in Symbian OS that the new operator has been overridden to take a parameter, ELeave. When called with this parameter, the overridden new operator will leave if it fails to allocate the required memory. ELeave is defined as follows in e32std.h: enum TLeave {ELeave};

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 41

This is implemented globally, so any class can use the new (ELeave) version of the operator. For example:
CSomeObject* myObject = new CSomeObject; if ( !myObject ) User::Leave(KErrNoMemory); // Can be replaced in by: CSomeObject* myObject = new (ELeave) CSomeObject;

3.3.1.3 The NewL() and NewLC() conventions By convention, Symbian OS classes often implement the methods NewL()and NewLC(). These are declared as static methods in the class definition, which allows them to be called before an instance of the class exists. They are invoked using the class scope. For example:
CSomeObject* myObject = CSomeObject::NewL();

NewL() creates a new instance of the class on the heap and leaves if an out-ofmemory error occurs. For simple objects this simply involves a call to new (ELeave). However, for compound objects it can incorporate two-phase construction (see Rule 3, below). NewLC() creates a new instance of the class on the heap and pushes it onto the cleanup stack (see Rule 2, below), leaving if an out-of-memory error occurs. (In general, the C suffix at the end of a method means that it pushes a created object onto the heap before returning.) When creating C-class objects, programs should use NewL() if a member variable will point to the object, and NewLC() if an automatic variable will point to it. However, it is not always advisable to implement NewL() and NewLC() for every class. If NewL() or NewLC() are called from only one place in the application, implementing them will actually use more lines of code than it saves. It is a good idea to assess the need for NewL() and NewLC() for each individual class. 3.3.1.4 Using a trap harness: TRAP and TRAPD In exceptional circumstances, a developer is permitted to handle a leave by using a trap harness. However, the use of TRAP and TRAPD is only for special situations; for all general coding they should not be used. Usually the best course of action is to allow the leave to propagate back to the Active Scheduler for default handling. If there is any doubt about whether a trap harness is really needed, there is probably a cheaper or cleaner way to achieve the same functionality. Symbian OS provides two trap harness macros, TRAP and TRAPD, which are both very similar. Whenever a leave occurs within code executed inside the harness, program control returns immediately to the trap harness macro. The macro then returns an error code, which can be used by the calling function. To execute a function within a trap harness, use TRAPD as follows:
TRAPD( error, doExampleL() ); if ( error != KerrNone ) { // Do some error code }

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 42

TRAP differs from TRAPD only in that the program code must declare the leave code variable. TRAPD is more convenient to use, as error is declared inside the macro. Using TRAP, the above example would become:
TInt error; TRAP( error, doExampleL() ); if ( error != KerrNone ) { // Do some error code }

Any functions called by doExampleL() are also executed within the trap harness, as are any functions called by them, and so on. A leave occurring in any function nested within doExampleL() will return to this trap harness. Other TRAP harnesses can also be nested within the first, so that errors are checked at different levels within the application. 3.3.2 Rule 2: Using the cleanup stack

3.3.2.1 Why the cleanup stack is needed If a function leaves, control returns immediately to the TRAP harness within which it was called, generally the default TRAP harness inside the threads Active Scheduler. This means that any automatic variables within functions called inside the TRAP harness are destroyed. However, a problem arises if any of these automatic variables are pointers to objects allocated on the heap. When the leave occurs and the pointer is destroyed, the object it pointed to is orphaned and a memory leak occurs. Example:
void doExampleL() { CSomeObject* myObject1 = new (ELeave) CSomeObject; CSomeObject* myObject2 = new (ELeave) CSomeObject;// WRONG }

In this example, if myObject1 was created successfully, but there was insufficient memory to allocate myObject2, myObject1 would be orphaned on the heap. Thus, you need some mechanism for retaining any such pointers, so that the memory they point to can be freed after a leave. Symbian OS provides a mechanism for this in the cleanup stack. 3.3.2.2 Using the cleanup stack The cleanup stack is a stack containing pointers to all the objects that need to be freed when a leave occurs. This means all C-class objects pointed to by automatic variables rather than instance data. When a leave occurs, the TRAP or TRAPD macro pops and destroys everything on the cleanup stack that was pushed onto it since the beginning of the TRAP. All applications have their own cleanup stack, which they must create. (The application framework automatically creates one in GUI applications.) Typically, all programs will have at least one object to push onto the cleanup stack. Objects are pushed onto the cleanup stack using CleanupStack::PushL() and popped from it using CleanupStack::Pop(). Objects on the cleanup stack should be popped when there is no longer a chance that they could be orphaned
Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 43

by a leave, which is usually just before they are deleted. PopAndDestroy() is normally used instead of Pop(), as this ensures the object is deleted as soon as it is popped, avoiding the possibility that a leave might occur before it has been deleted, causing a memory leak. A compound object that owns pointers to other C-class objects should delete those objects in its destructor. Therefore any object that is pointed to by member data of another object (rather than by an automatic variable) does not need to be pushed onto the cleanup stack. In fact, it must not be pushed onto the cleanup stack, or it will be destroyed twice if a leave occurs: once by the destructor, and once by the TRAP macro. 3.3.2.3 Guideline for using cleanup stack Where appropriate, use it in preference to TRAP harnesses as it is quicker and more efficient. Use the checking methods of CleanupStack::Pop to check that the PushL and Pop are balanced if possible. Use CleanupStack::PopAndDestroy if you are finished using the object. Standard usage: For CBase'd heap-created objects, push and pop the pointer to the object, for example:
CObject* object = CObject::NewL(); CleanupStack::PushL(object); object->LeavingFunctionL(); CleanupStack::PopAndDestroy(object);

RClasses For RClass objects that define a Close() method, use CleanupClosePushL where applicable:
RObject object; object.Open(); CleanupClosePushL(object); object.LeavingFunctionL(); CleanupStack::PopAndDestroy(&object);

This may not be suitable for all RClass objects. For instance it may not be suitable for RPointerArray if you have already added objects to the array. Calling Close() in that instance could orphan the added objects unless they are also on the cleanup stack. If you wanted to call nonleaving functions after LeavingFunctionL, then it is preferable to leave the object on the cleanup stack rather than calling CleanupStack::Pop() and then the nonleaving function, as it results in less code.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 44

TcleanupItems Some classes or methods will require more complicated cleanup. Use a TCleanupItem and provide a function to call that does the required cleanup. Declare these functions as either local functions or static class functions:
// Allows correct cleanup of specified objects by using the // cleanup stack instead of a trap harness. void RObject::ReleaseOnCleanup(TAny* aObject) { reinterpret_cast(aObject)->Release(); }

// may increase share on a reference counted object RObject*object = GetObject(); CleanupStack::PushL(TCleanupItem(ReleaseOnCleanup,object)); iContainer->AddL(object); CleanupStack::Pop(object); // Pop the TCleanupItem::iPtr

Heap allocated arrays Use CleanupArrayDeletePushL() when pushing an array of objects onto the cleanup stack. This ensures that PopAndDestroy() will call delete[] to delete the array correctly.
TObject objectArray = new(ELeave) TObject[numberOfElements]; CleanupArrayDeletePushL(objectArray); CleanupStack::Pop(objectArray);

3.3.3

Rule 3: Two-phase construction

Sometimes a constructor will need to allocate resources, such as memory. The most ubiquitous case is that of a compound C-class: If a compound class contains a pointer to another C-class, it will need to allocate memory for that class during its own construction. Note: C-classes in Symbian OS are always allocated on the heap, and always have CBase as their ultimate base class. In the following example, CMyCompoundClass has a data member that is a pointer to a CMySimpleClass. Heres the definition for CMySimpleClass:
class CMySimpleClass : public CBase { public: CMySimpleClass(); ~CMySimpleClass(); private: TIntiSomeData; };

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 45

Heres the definition for CMyCompoundClass:


class CMyCompoundClass : public CBase { public: CMyCompoundClass(); ~CMyCompoundClass(); private: CMySimpleClass* iSimpleClass; // owns another C-class };

Consider what a developer might be tempted to write as the constructor for CMyCompoundClass:
CMyCompoundClass::CMyCompoundClass() { iSimpleClass = new CMySimpleClass; }

// WRONG

Now consider what happens when a new CMyCompoundClass is created:


CMyCompoundClass* myCompoundClass = new (ELeave) CMyCompoundClass;

With the above constructor, the following sequence of events occurs: 1. Memory is allocated for the instance of CMyCompoundClass. 2. The constructor of CMyCompoundClass is called. 3. The constructor creates a new instance of CMySimpleClass and stores a pointer to it in iSimpleClass. 4. The constructor completes. But, if stage 3 fails due to insufficient memory, what happens? There is no way to return an error code from the constructor to indicate that the construction was not completed. The new operator will return a pointer to the memory that was allocated for the CMyCompoundClass, but it will point to a partially constructed object. If we make the constructor leave, we can detect when the object was not fully constructed, as follows:
CMyCompoundClass::CMyCompoundClass() // WRONG { iSimpleClass = new (ELeave) CMySimpleClass; }

However, this is not a viable method for indicating that an error has occurred, because we have already allocated memory for the instance of CMyCompoundClass. A leave would destroy the pointer to the allocated memory (this), and there would be no way to free it, resulting in a memory leak. The solution is to allocate all of the memory for the components of the object, after the C++ constructor has initialized the compound object. In Symbian OS this is performed in a ConstructL() method by convention. For example:
void CMyCompoundClass::ConstructL() // RIGHT { iSimpleClass = new (ELeave) CMySimpleClass; }

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 46

The C++ constructor should contain only initialization code that cannot leave (if any):
CMyCompoundClass::CMyCompoundClass() // RIGHT { // Initialization that cannot leave. }

The object is now constructed as follows:


CMyCompoundClass* myCompoundClass = new (ELeave) CMyCompoundClass; CleanupStack::PushL(myCompoundClass); myCompoundClass->ConstructL(); // RIGHT

This can be encapsulated in a NewL() or NewLC() method for convenience. 3.3.3.1 Implementing two-phase construction using NewL() / NewLC() If a compound object has a NewL() method (or NewLC()), then this should contain both stages of construction. After the allocation stage, the object should be pushed onto the cleanup stack before ConstructL() is called, in case ConstructL() leaves. For example:
CMyCompoundClass* CMyCompoundClass::NewLC() { CMyCompoundClass* self = new (ELeave) CMyCompoundClass; CleanupStack::PushL(self); self->ConstructL(); return self; } CMyCompoundClass* CMyCompoundClass::NewL() { CMyCompoundClass* self = new (ELeave) CMyCompoundClass; CleanupStack::PushL(self); self->ConstructL(); CleanupStack::Pop(); // self return self; }

3.4

Common Mistakes
3.4.1 Misuse of TRAP and TRAPD

Some classes repeatedly include code of the form:


void NonLeavingFunction() { TRAPD(error, LeavingFunctionL()); }

While this is legitimate code, it should not be widely used. A trap harness is expensive in terms of executable binary size and execution speed, and unless used very carefully can lead to errors being lost. Often a trap is used where it would simply be better to add a letter L to the end of the method name and allow the leave to propagate up. Note, however, that to maintain library compatibility, this may not always be possible. Library design should always take account of future leave needs.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 47

The following code is particularly bad, as the whole TRAP is meaningless!


void NonLeavingFunction() { TRAPD(error, LeavingFunctionL()); if ( error != KErrNone ) User::Leave( error ); }

3.4.2

Misuse of the new operator

This code is illegal and dangerous:


void NonLeavingFunction() { bar* foo = NULL; TRAPD(error, foo = new bar()); foo->DoSomething(); }

In this situation it is essential to use the new (ELeave) version of the operator, as new on its own will not leave. This could lead to both memory leaks and use of an uninitialized pointer. 3.4.3 Misuse of the L suffix

void NonLeavingFunction() { LeavingFunctionL(); bar* foo = new (ELeave) bar(); bar* foo1 = bar::NewL(); }

All three lines of this function break the L suffix rule. There are two options here: 1. The leaving lines must be caught in a TRAP (probably not the right solution). 2. The NonLeavingFunction should become an L function (probably better). Note that this code also breaks Rule 2 (Using the cleanup stack, above), because if NewL leaves, foo will become orphaned on the heap. 3.4.4 Memory leaks

It is very important to carry out memory testing regularly during the development of Symbian OS code. If a memory leak is discovered, it is much easier to locate it within the context of the work that is being currently undertaken, rather than having to search through the whole application. Symbian OS provides heap failure tools for debug builds, which can be used to assist in the stress testing of memory in Symbian OS code. In doing this, you are looking for two aspects of the applications behavior: Behavior of the application when memory runs out. Memory leakage, reported when the application is closed. It is particularly important to actually close the application using the Back soft key when memory testing. Closing the emulator directly using the top-right close button will not allow the memory checking code to run.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 48

3.4.5

Using tools in the WINS/WINSCW emulator

The WINS/WINSCW emulator provides a tool for checking memory behavior by pressing CTRL-SHIFT-ALT-P. Details are presented in the SDK documentation and also in the Professional Symbian Programming book, on page 158. The utility described in the book is available in most SDKs that are based on Symbian OS, such as the Series 60 SDK. The outlook of the testing utility screen is different from one SDK to another:

Figure 1: The Series 60 terminal emulator memory stress testing utility

Figure 2: The Nokia 9210 Communicator emulator memory stress testing utility Debugging a memory leak in Symbian OS can be daunting, but there are techniques that make the process fairly painless. However, memory leaks are never trivial to find, and prevention is the best cure! The following tips will prevent leaks appearing in the first place, and ease the pain of searching them out later: Understand the cleanup stack and the Leave/TRAP paradigm. Build and run code regularly if a leak appears, it will be more obvious where it was caused. Use Symbian OS v6.x/v7.0s heap-checking macros. When testing, exit the application. Dont just kill the emulator. Code reviews are very useful. There are two types of memory leak. A static leak is a repeatable leak that always occurs when the application is run; it is caused by mismatched new and
Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 49

delete operators. These are relatively easy to find because they always happen in the same place and thus can be debugged. A dynamic leak is one that is nontrivial to repeat, for example a leak caused by an error condition, or race condition. This is trickier to find, as not all paths through the code will cause it. 3.4.5.1 What leaked? When an application is closed, the emulator will panic if memory has leaked (this is actually a _UHEAP_MARKEND macro running). An application should always exit cleanly, even in development. If a "panic on close" appears while developing, it can be fixed straight away. If it is left, it will be 10 times more difficult to track down later. Under the Microsoft Visual C++ debugger, the panic appears as a "User Breakpoint called from code at location 0xxxxxx" dialog. The stack trace (use View>Debug Windows>Call Stack) shows that it is in the destructor of CCoeEnv. To continue, press OK and F5. Another User Breakpoint will be reached, this time at DebugThreadPanic. The output window now shows Panic ALLOC and an address. Select this address and copy it to the clipboard (Edit>Copy). This is the hexadecimal address of the memory cell that has not been deallocated. From this address, it is possible to find out the type of the leaked class by attempting to cast the address to a few possible types. Use the "Quick watch" window in Visual Studio, and try to cast the pointer badCell to the following types: CBase* (in case it is a CBase-derived object). TDesC16* (in case it is a string). If neither of these casts gives any useful information, it could be an R-Class (resource handle), although the server should normally panic if a client is not closed. Alternatively it could be a T-Class that is mistakenly on the heap. Note that this technique could give slightly skewed information if a large compound CClass has leaked, as it is likely that one of the members of the large class will be reported, rather than the parent class itself. 3.4.5.2 Where was it allocated? Once the address of the leaked memory is known, its allocation point can be found by setting a conditional breakpoint in the heap allocator function. All heap memory allocation goes through the function RHeap::Alloc(int), so start by putting a breakpoint there. Symbian does not currently supply the source code for this function, but a breakpoint can be set explicitly using the Edit>Breakpoints>Break At feature of Microsoft Visual C++. Use Debug>Go (F5) to continue until the system's first allocation. The source code is not viewable, but the disassembled code can be seen. Scroll down through the disassembled code, passing the label retryAllocation, until the line before the start of the next function, roundToPageSize. Put a breakpoint on the RET line before it, at which point, the register EAX will contain the return value from the RHeap::Alloc function. Use Edit>Breakpoints to set up a breakpoint when the value is equal to the offending cell. First, disable the breakpoint at RHeap::Alloc, select the new breakpoint, and use Condition to set the condition that the return value is the cell being tracked.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 50

Click OK to both dialogs, and then use Debug>Go to continue. Run the application as before. When execution stops at the breakpoint, examine the stack to see where the offending cell was allocated. Sometimes the same cell may be allocated and deallocated a few times. Only the final allocation is of interest. If the cell doesn't get allocated, it may be because this run was a bit different from the first one, and the leaking cell was at a different location. Continue until application exit, and find a new offending cell address. Then set that as an additional breakpoint at the same location as the other but with a condition to catch the new offending cell, and use Debug>Restart to restart. The error message "Cannot restore all the breakpoints" may occur. This is because the EUSER DLL isn't loaded when the emulator executable (EPOC.EXE) first starts. The solution is to re-enable the RHeap::Alloc(int) breakpoint, run until that is called, and then restore the other, conditional, breakpoints. Note that code slows down a lot with this breakpoint in place, so enable it at the last moment! Also, the same address may be allocated many times which one is the leak? This involves investigating the call stack each time the breakpoint is hit to find the context!

3.5

Asserts and Panics

Using __ASSERT_DEBUG test macros can prevent many problems. They should be used liberally to check for silly parameters coming into functions, null pointers, and other error conditions. Many error conditions will not trip up the application straight away, but will lead to side effects later. If errors are caught as soon as they occur, debugging becomes much easier later. For example:
CMyClass::Function(CThing* aThing) { __ASSERT_DEBUG(aThing, Panic(EMyAppNullPointerInFunction)); }

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 51

4.
4.1

System Resource Usage (ROM and RAM)


Importance

A phone is a resource-limited device. However, there is a great deal of functionality available, and this places great demands on the available system resources. Developers need to be aware of these constraints, and attempt to use as few as possible of these limited resources.

4.2

Reducing Code Size

It is important that final compiled code be as small as possible, to maximize the available space on the device. The following tips will provide guidance on how to ensure that no space is wasted. Take time to check code for these issues, and think about other ways in which the compiled size could be reduced. 4.2.1 Unnecessary exported functions

When functions are exported using IMPORT_C and EXPORT_C from a DLL, they use up space for the export table. Only functions that need to be used outside of the DLL should be exported. 4.2.2 Copy and paste

Copy and paste often leads to code bloat. When reusing code from other modules, ask the following questions: 1. Is this code actually needed? 2. Is too much code being copied for the task? 3. Would it be better to abstract the function into a base class or helper module so it can be used from more than one place, rather than copy it? 4. Could the code be rewritten more efficiently for the required task, rather than copying something that is "close" to what is required? 4.2.3 Obviously decomposable functions

There are many places where a number of functions that perform very similar tasks are present in a class. Often this common code can be abstracted out into a single function, which is parameterized to perform the different tasks required. A common example of this kind of thing is a class that implements both NewL and NewLC. Rather than duplicate the code in both functions, NewL can just call NewLC, performing a CleanupStack::Pop afterwards. 4.2.4 Excessive TRAP harnesses

Trap harnesses use up space when they are compiled. Code that contains many TRAP macros (e.g., more than five in a class) is using up too much space. It is also probably incorrectly designed, as the TRAP harness is not intended for use extensively in normal code. It is there to allow advanced development of special error handling and recovery routines.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 52

4.2.5

Debug code in release

If there is any code for logging, debugging, or testing, it needs to be excluded in release builds. The compiler directive #ifdef _DEBUG can be used for this purpose. 4.2.6 Unnecessary virtual functions

Unnecessary virtual functions are bad, for reasons similar to exports, as they create extra vtable functions. 4.2.7 Use common controls

If possible, use framework controls that are available in the system (or other shared DLLs) instead of developing new ones. 4.2.8 Misuse of the _L macro

The _L literal macro is now deprecated and replaced by the more efficient _LIT macro, which should be used instead.

4.3

Reducing RAM Usage

There are a number of ways to reduce RAM usage. Some methods for doing this (such as bit fields) can obfuscate code, so there is often a compromise to be struck between the reduction in RAM usage and the increase in complexity of the code. 4.3.1 Use bit fields rather than many Tbools

Consider using bit fields to store large amounts of Boolean data in classes. Every TBool requires 32 bits of RAM, but that same 32 bits could hold 32 Boolean values in a bit field. As mentioned above, compare the potential benefits of this with the added code complexity. 4.3.2 Use caution with array granularities

All CArray-derived classes can specify granularity. This is to make code more efficient by only allocating space for the array in certain size chunks. It is effective, but some thought needs to go into the choice of granularity. If an array is needed for typically 5 to 8 objects, then a granularity of 4 or 5 is sensible. If the array always contains 15 objects, then the granularity should be 15. However, if there are typically 2 or 3 objects, having a granularity of 100 would be silly. Similarly, if there are typically 101 to 105 objects, a granularity of 100 would be silly, as it would mean that 200 spaces are always allocated. However a granularity of 1 would also be silly, as it would involve many reallocations. The final choice depends on the usage pattern. 4.3.3 Avoid global data

Don't use global data. Use local variables instead of member variables where they are only used in one function.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 53

4.3.4

Beware of base class member data

If you are writing a base class that is intended for wide use, use caution with member data. Do not add member data that is only useful to some derived classes, as every derived class will have no choice but to own it. Take care to only include truly generic member data. 4.3.5 Use the cleanup stack correctly

If the cleanup stack is used correctly, there should be no memory leaks in the code which in itself guarantees that the application is not using more RAM than it needs to. 4.3.6 Delete early

If temporary objects are allocated on the heap, delete them as soon as they are no longer needed. If the life of temporary objects is longer than they are needed, then the typical RAM consumption of the application will be higher than it needs to be. Remember, however, that if a temporary object is deleted while a pointer to it is still in scope, the pointer should be set to NULL to prevent illegal access or double deletions. 4.3.7 Test on hardware with the maximum data set

If a data set has an upper limit, then test on hardware with the maximum data set. It is very easy not to notice that some operation is very slow, or causes problems, if it is never tested on hardware at its limits. Always be sure to load up test scenarios with large data sets. 4.3.8 Break up long, complex operations

Displaying long lists on screen can be a source of heavy RAM usage, and bad performance while the list controls are initialized (e.g., a list of all contacts or notes on the device). This can be avoided by writing specific controls, which only populate the list with the entries that are visible on the screen. Scrolling then deletes the entries that leave the screen and adds new ones.

4.4

Reducing Stack Usage

The stack available to an application on target hardware is smaller than the giant stack available to the emulator in the Windows NT environment. This can lead to code that appears to run perfectly in the WINS/WINSCW emulator not running on the hardware, with apparently random panics. Reducing stack usage is not easy, but there are a couple of things to watch out for: 4.4.1 Use descriptors correctly

There are two types of descriptors: heap descriptors (HBufC) and stack descriptors (TBuf). All descriptors use one of these types for storage. When the stack overflows, 90 percent of the time it is caused by large descriptors on the stack. Be aware of operations that cause implicit copying of descriptors, and avoid them where possible. It may be better in some situations to allocate HBufCs to work on rather than TBufs.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 54

Some related Symbian OS classes, such as TParse, can also use up a lot of stack space. Consider using alternative versions that use less stack space (e.g., TParseBase). Passing descriptors by reference is preferred to passing by value. 4.4.2 Beware of recursion, and build in limits

If recursive programming is necessary, be aware of the demands on the stack. Try to minimize the size of the parameters being passed down, and try to move local automatic variables out of the recursive part of the function. If at all possible, build in a recursion depth limit to prevent the stack overflowing. 4.4.3 Beware of logging code

Logging code typically involves the formatting of long descriptors and their writing out to files. For this reason they are often the cause of stack overflows.

4.5

Low Disk Handling

There are two levels defined by the system monitoring the free space available on the Flash File System (FFS, alias the C: drive): Warning Level (WL) and Critical Level (CL). When free disk space meets one of these levels, the system (EikSrvUI) will display a global note, which warns the user about the situation. Applications and servers can therefore ignore the Warning Level and concentrate on the Critical Level. All operations that create or write to a file on disk with a known file size must first check the Critical Level with that size as a parameter to the FFSSpaceBelowCriticalLevelL method. If the disk space is already below, or going to go below, Critical Level, this method will return ETrue. Applications must not write data and should indicate to the user that the disk is full. (Leaving with the KErrDiskFull error code can achieve this.) All operations that create or write to a file on disk with an unknown file size must check the Critical Level first, passing a good estimate of size or "0" (the default) as a parameter to the FFSSpaceBelowCriticalLevelL method. The zero can be used to check whether you are already below the critical level. Cases where single items are created in databases (e.g., contacts) are difficult. Here, an estimate can be used of the increase in database size the item add would cause. The Critical Level checking method is available in SysUtil.h/SysUtil.dll. Its API looks like this:
/** * Checks if the free FFS (internal Flash File System) storage * space is or will fall below Critical Level (CL). * The CL and FFS drive letter is defined by this module. * @param aFs File server session. * Must be given if available in the caller, * e.g. from EIKON environment. * If NULL this method will create a temporary session for * a check, but then the check is more expensive. * @param aBytesToWrite number of bytes the caller is about to add * FFS, if known by the caller beforehand. * The default value 0 checks if the current * space is already below the CL. * @return ETrue if storage space would go below CL after adding * aBytes more data, EFalse otherwise. * Leaves on error. */ IMPORT_C static TBool FFSSpaceBelowCriticalLevelL(RFs* aFs, TInt aBytesToWrite = 0);
Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 55

5.
5.1

B u i l d i n g f o r A R M Ta r g e t s
General Issues

Building for ARMI will in general be more difficult than building for WINS/WINSCW and it is normal to find additional compiler errors and warnings from gcc on the first attempt. This is firstly because gcc is in many situations stricter than the Microsoft compiler, and also has some subtle differences that will come out the first time an ARMI build is attempted. The following sections cover a few of the most common pitfalls.

5.2

Function Exports

The gcc tool chain is stricter than that of WINS/WINSCW when it comes to specifying exported functions. The correct way to export a function from a DLL is as follows: In the header file:
class CMyClass : public CBase { IMPORT_C void Function(); }

and then in the CPP file:


EXPORT_C void CMyClass::Function() { }

The WINS/WINSCW tool chain does not mind if the EXPORT_C is excluded from the CPP file; it exports the function anyway. However, the gcc tool-chain requires the IMPORT_C and EXPORT_C to be perfectly matched. If they are not, the function will not be exported from the DLL, which will eventually lead to errors such as "Cannot Find Function" when attempting to link to this DLL.

5.3

The "MyDll.DLL has (un)initialized data" error from PETRAN

The Symbian OS architecture does not allow DLLs to have a data segment (static data, either initialized or uninitialized). There are problems with deciding what such a data segment would mean: Do all users of the DLL share it? Should it be copied for each process that attaches to the DLL? In addition, there are significant run-time overheads in implementing any of the possible answers. However, because the WINS/WINSCW emulator uses the underlying Windows DLL mechanisms, it can provide per-process DLL data using "copy-on-write" semantics. This is why the problem goes undetected until the code is built for an actual Symbian OS device. Consider this section of C++ code, added to a file QSORT.CPP, which is part of ESTLIB.DLL:
// variables struct div_t uninitialised1; static struct div_t uninitialised2; // in .DATA // in .BSS
Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 56

struct div_t initialised1 = {1,1}; static struct div_t initialised2 = {2,2}; // constants const struct div_t const1 = {3,3}; const static struct div_t const2 = {4,4}; const TPoint none(-1,-1); static const TText* plpPduName[12] = { _S("Invalid"), _S("DataFlowOff"), _S("DataFlowOn"), _S("ConnectRequest"), _S("ConnectResponse"), _S("ChannelClose"), _S("Info"), _S("ChannelDisconnect"), _S("End"), _S("Delta"), _S("EndOfWrite"), _S("PartialWrite") };

// in .DATA // in .DATA

When this code is built, the messages from PETRAN look something like: PETRAN - PE file preprocessor V01.00 (Build 170) WARNING: Dll 'ESTLIB[10003B0B].DLL' has initialised data. WARNING: Dll 'ESTLIB[100002C3].DLL' has uninitialised data. The associated .map file contains information that helps to track down the source file involved. Look in Symbian OS\release\arm4\urel\dllname.map. Search for .data or .bss. In this example, we find: .data 0x10017000 0x200 0x10017000 __data_start__=. *(.data) .data 0x10017000 0x40 ..\..\Symbian OS\BUILD\STDLIB\BMMP\ESTLIB\ARM4\UREL\ESTLIB.in(QSORT.o) 0x10017000 initialised1 *(.data2) *(SORT(.data$*)) 0x10017040 __data_end__=. *(.data_cygwin_nocopy) .bss 0x10018000 0x18 0x10018000 __bss_start__=. *(.bss) .bss 0x10018000 0x18 ..\..\Symbian OS\BUILD\STDLIB\BMMP\ESTLIB\ARM4\UREL\ESTLIB.in(QSORT.o) 0x10018008 uninitialised1 *(COMMON) 0x10018018 __bss_end__=. So the DLL has 0 x 18 bytes of uninitialized data (.bss) and 0 x 40 bytes of initialized data (.data), all of which comes from qsort.o. The variables initialised1 and uninitialised1 both have global scope, so the .map file lists them by name (and puts them both in the initialized data). The static variables don't get named in the .map file.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 57

Removing the first four lines of code shown above leaves just variables that are declared as const, but only reduces .bss to 0x08 bytes, and .data to 0x30 bytes. There are two problems remaining: 1. Declaring C++ objects as const doesn't help if they have a constructor. The 8 bytes of uninitialized data are allocated to hold the TPoint object, but it won't become const until after the constructor has completed. 2. The declaration const TText* says that the TText values may not be altered, but it does not make the pointer a constant as well. The 48 bytes of initialized data are the 12 pointers in the plpPduName array. To make the pointers constant as well as the values they point to, the declaration needs an additional const after the TText*:
static const TText* const plpPduName[12] = { _S("Invalid"), _S("DataFlowOff"), _S("DataFlowOn"), _S("ConnectRequest"), _S("ConnectResponse"), _S("ChannelClose"), _S("Info"), _S("ChannelDisconnect"), _S("End"), _S("Delta"), _S("EndOfWrite"), _S("PartialWrite") };

Removing the TPoint global variable and adding the extra const to the plpPduName array finally removes the last of the offending .bss and .data.

Version 1.0 | May 5, 2004

Symbian OS: Coding Conventions in C++ | 58

6.

Te r m s a n d A b b r e v i a t i o n s
Meaning ARM processor ARM 4 instruction set with Thumb interworking ARM 4 instruction set without Thumb interworking Compiler tool chain for ARM processor Integrated Development Environment; a system for supporting the process of writing software, including a syntax-directed editor, graphical tools for program entry, and integrated support for compiling and running the program and relating compilation errors back to the source. For example, Visual C++. Software Development Kit; set of programming tools for creating applications and enhancing the use of certain software. ARM Thumb instruction set with ARM Refers to the target platform for the Microsoft Windows hosted emulator with Microsoft Visual C++ IDE Refers to the target platform for the Microsoft Windows hosted emulator with Metrowerks CodeWarrior IDE

Term or abbreviation ARM ARMI ARM4 GCC IDE

SDK THUMB WINS WINSCW

Version 1.0 | May 5, 2004

S-ar putea să vă placă și