Sie sind auf Seite 1von 46

This whitepaper explains how to provide a clean separation between Qt and Symbian C++

code using the PIMPL pattern.


In addition, it describes how to write code that safely mixes the two environment’s exception
handling mechanisms, coding styles and idioms, strings, geometry, containers, images, and
data. Each section gives a high level overview of how a particular task is done in Symbian
C++ and Qt, and how the idioms are mixed - along with links to key documents explaining the
approach in more detail.
The accompanying example code delivers a Qt-style API (and dialog) for discovering remote
Bluetooth devices. This uses the PIMPL pattern to obtain the information from the underlying
platform, and demonstrates how to safely mix the exception handling mechanisms, coding
styles and strings.
An up-to-date version of this document can be found at the following location:
http://developer.symbian.org/wiki/index.php/Using Qt and Symbian C++ Together
Contents
1. Introduction 3

2. Bluetooth Discovery Example 5


2.1. Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.2. API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.3. Building and Running the Example . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.4. File Listing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

3. Platform Specific Implementations 9


3.1. Pointer to Implementation Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.1.1. Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.1.2. Bluetooth Example Public API . . . . . . . . . . . . . . . . . . . . . . . . . . 12
3.1.3. Project File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.1.4. Public Class Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.1.5. Private Platform Implementation . . . . . . . . . . . . . . . . . . . . . . . . 16
3.2. Platform Specific Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.3. File and Class Naming Conventions . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

4. Multi-Tasking 20
4.1. Multi-Tasking on the Symbian Platform . . . . . . . . . . . . . . . . . . . . . . . . 20
4.2. Multi-tasking with Qt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
4.3. Converting Active Objects to Signals and Slots . . . . . . . . . . . . . . . . . . . . 22
4.3.1. Signalling from the Active Object . . . . . . . . . . . . . . . . . . . . . . . . 23
4.3.2. RunL() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
4.3.3. SetActive() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
4.3.4. DoCancel() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
4.4. Callback APIs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

5. Coding Standards and Conventions 27


5.1. Symbian Coding Standards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
5.2. Qt Coding Standards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
5.3. Coding Standards For Mixed Symbian C++ and Qt Code . . . . . . . . . . . . . . . . 27

6. Exceptions and Error Handling 28


6.1. Symbian Exceptions and Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
6.2. Qt Exceptions and Error Codes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
6.3. Mixing Symbian C++ and Qt Exception Handling . . . . . . . . . . . . . . . . . . . 30

7. Converting Between Common Qt and Symbian Types 32


7.1. Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
7.1.1. Symbian Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
7.1.2. Qt Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
7.1.3. Converting a Descriptor to a QString . . . . . . . . . . . . . . . . . . . . . . 33
7.1.4. Converting a QString to a Descriptor . . . . . . . . . . . . . . . . . . . . . . 34
7.2. Input/Output and Binary Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
7.2.1. Symbian Binary Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
7.2.2. Qt Binary Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
7.2.3. Converting Between Qt and Symbian Binary Data . . . . . . . . . . . . . . . 36
7.3. Geometry: Points, Sizes, Rectangles . . . . . . . . . . . . . . . . . . . . . . . . . . 37
7.4. Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
7.4.1. Symbian Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
7.4.2. Qt Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
7.4.3. Inter-Working Symbian and Qt Image Formats . . . . . . . . . . . . . . . . . 38
7.5. Containers/Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
7.5.1. Symbian Containers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
7.5.2. Qt Containers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
7.5.3. Converting Between Qt and Symbian Containers . . . . . . . . . . . . . . . . 41

8. Summary 43

2
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

1. Introduction

The Symbian platform is an open-source software platform for mobile devices. Earlier versions of
the Symbian platform (known as S60 and Symbian OS) can be found in hundreds of millions of
devices, and are available globally from over 100 network operators.

Qt is a cross-platform application and UI framework that allows developers to write applications


that can be deployed to desktop, mobile, and embedded operating systems without need to
rewrite the source code. From Qt 4.6, Qt includes the Symbian platform as a build target; Qt
applications to run on Symbian platform devices, and on earlier S60 devices from S60 3rd Edition
FP1. As shown in Figure 1, Qt on the Symbian platform is layered over both native Symbian C++
platform APIs and its standard C/C++ compatibility layer (Open C and Open C++).

While Qt has a rich set of APIs and development tools, inevitably some developers will need
access to platform-level functionality that neither generic Qt or standard C++ APIs provide. This
is particularly true when targeting mobile devices because the APIs to access important mobile
functionality (like the camera, Bluetooth, contacts etc.) are not yet available. New Qt APIs are
starting to address common mobile use cases, but some developers will always need or want to
access native operating system functionality.

Where Qt does not provide a required API, the recommended way to access platform specific func-
tionality is to write a generic cross-platform wrapper API and provide a private platform specific
implementation. This approach, which makes it easier to port applications to other platforms, is
discussed in the Platform Specific Implementations section on page 9.

Accessing device-specific functionality is not simply a matter of using familiar standard C++ APIs
and idioms to call phone-specific APIs. Symbian’s native programming language is Symbian C++,
a variant of C++ that has evolved specifically to address the needs of resource constrained devices.
Symbian C++ uses programming idioms and frameworks that promote robust and memory effi-
cient code, sometimes at the expense of usability. It has its own exception handling mechanism,
and omits standard C and C++ libraries that were not available (or considered too heavyweight)
when it was created.

Code from the different C++ variants can be mixed, but this requires care, and some understand-
ing of Symbian C++. The whitepaper explains how to inter-work the different exception han-
dling mechanisms, strings, geometry, containers, images, data, and approaches to multitasking
etc. Each section provides a concise overview of what you need to do on each platform (with
links to the key references), followed by examples/discussion on how they are mixed. Both Qt
and Symbian C++ developers should get a good overview of what they can use of their existing
knowledge, and where to go to get more.

Before we start discussing the mechanisms for cross platform compatibility, the following section
provides a brief overview of the example code that is used to illustrate some of the key points.

3
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

Figure 1: Qt on the Symbian Platform

4
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

2. Bluetooth Discovery Example

2.1. Overview

The example code delivers the BluetoothLibrary.dll, and a test application testbluetoothdis-
covery.exe which uses it.

BluetoothLibrary.dll exports a Qt API (BluetoothDiscovery) for finding nearby bluetooth de-


vices. BluetoothLibrary.dll has a private Symbian C++ implementation and a stub implementa-
tion for other platforms. BluetoothLibrary.dll also exports a convenient Qt dialog from which
users can search for and select one or more devices.

Bluetooth Discovery Dialog Bluetooth Discovery Dialog


(single selection) (multi-selection)

The test application is GUI Main Window application which displays an empty screen on startup.
It has softkey menu options for launching the single and multi selection dialogs.

The figures below show the dialogs for single and multiple device selection on the Nokia 5800,
and a diagram showing the relationships between the main components.

5
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

Figure 2: Component Interactions

2.2. API

The BluetoothDiscovery API provides slots to start and stop a search for nearby devices, and
signals to notify connected clients when discovery is started or stopped, every time a new device
is detected, and whenever and error occurs.

Once started the search will continue until the underlying platform determines that there are no
more devices; at which point it will signal that discovery has stopped. Discovery will also cease
in the event of an error, which will be signaled to clients through a locally defined error code
enumeration.

The dialog QBluetoothRemoteDeviceDialog class exposes two static methods for launching
the dialog and returning the user selection: the first method returns a single selection while the
second allows users to make multiple device selections:

static QList<QBluetoothAddress> QBluetoothAddress::getRemoteDevices(QWidget *parent = 0);

In addition to the dialog class a number of related Qt classes have been created QBluetoothAd-
dress for a device address, QBluetoothRemoteDevice to represent a remote device, and QBlue-
tooth for miscellaneous enumerations describing possible device service classes and behaviour.

A simplified diagram of the public API is given in Figure 3. Note that this does not show the
QBluetoothAddress or QBluetoothRemoteDevice classes.

The example dialog could be extended; it does not allow filtering of displayed devices (based
on device type, service class or SDP profile) or specification of a local bluetooth device to use
for searching, it ignores window flags passed to its constructor, and it doesn’t display icons to
indicate the type of device.

6
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

Figure 3: Main Classes

7
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

2.3. Building and Running the Example

Before building this example you should already have set up your development environment and
device, as described in the Qt Quick Start.

The QtBluetoothDiscoveryExample/qtbluetoothdiscoveryexample.pro project file provides the


easiest way to import the example into your IDE. This file specifies the files used for the example
and test code, and the required build order. The example builds on the both Symbian platform
(Emulator and mobile devices) and on Windows; note however that it can’t search for devices on
Windows because no Bluetooth implementation is provided.

The example DLL and test executable are deployed to the device using the test code installation
file (testbluetoothdiscovery.sisx) in the usual manner. The executable is run on the Symbian
platform by selecting the testbluetoothdiscovery icon from the Applications folder.

2.4. File Listing

The package has the following structure (under the qtbluetoothdiscoveryexample folder):

Folder and Files Description


BluetoothLibrary\ BluetoothLibrary\
bluetoothdiscovery.cpp BluetoothDiscovery public API header and Source
bluetoothdiscovery.h
bluetoothdiscovery stub.cpp Private implementation (stub) header and source
bluetoothdiscovery stub.h files for non-Symbian platform targets
(BluetoothDiscoveryPrivate)
bluetoothdiscovery symbian.cpp Private Symbian platform implementation header
bluetoothdiscovery symbian.h and source files (BluetoothDiscoveryPrivate)
Project file for the BluetoothLibrary.dll
BluetoothLibrary.pro Global #defines used to define exports to be
globalbluetooth.h imported/exported from DLL
QBluetooth header file (contains public
qbluetooth.h enumerations used by other classes)
qbluetoothaddress.cpp QBluetoothAddress public header and source
qbluetoothaddress.h files
qbluetoothaddressdata.cpp QBluetoothAddress data implementation (for
implicit sharing)
qbluetoothremotedevice.cpp QBluetoothRemoteDevice public header and
qbluetoothremotedevice.h source files
QBluetoothRemoteDeviceDialog.cpp QBluetoothRemoteDeviceDialog public header
and source files
QBluetoothRemoteDeviceDialog.ui QBluetoothRemoteDeviceDialog Qt designer file
eabi\
bluetoothlibraryu.def Frozen ordinals definition
testbluetoothdiscovery\
main.cpp main entry point for test application
testbluetoothdiscovery.cpp Test code public source and header files.
testbluetoothdiscovery.h
testbluetoothdiscovery.pro Test code project file
testbluetoothdiscovery.ui Test code Qt designer file

8
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

3. Platform Specific Implementations

While Qt provides a rich set of APIs, at the time of writing Symbian developers will not yet find
Qt APIs to communicate using Bluetooth or IrDA, access camera, location, accelerometer, bio-
metric or other sensors, send SMS or MMS messages, or read/write user data like the calendar or
contacts. Qt Development Frameworks is working on providing cross platform APIs for many of
these through the Qt Mobility project. If you want to use this functionality you you will need to
use both Qt and Symbian C++. Doubtless there will be other functionality in future for which this
is also true.

Where Qt does not provide a required API, the recommended way to provide access to plat-
form specific functionality is to create public Qt-style APIs that have separate private platform
specific implementation(s). This approach makes it easier to write the bulk of your code using
the developer-friendly Qt programming idioms, while still making it easy to port applications to
other platforms. In fact this is exactly the same approach that that Qt uses to abstract the under-
lying operating system so that developers don’t need to be aware of each platform’s underlying
programming idioms, APIs, installation mechanisms, build systems and other limitations.

There are a number of design patterns that can be used to structure your code. In the following
section we discuss the Pimpl (pointer to implementation) idiom, in which the (private) platform
specific implementations are hidden behind a pointer in the public API. The technique has a num-
ber of variants/alternative names including: “Handle/Body”, “Compiler Firewall”, “the Bridge”,
“Opaque Pointers”, and “Cheshire Cat”.

Other patterns/techniques may be used. For example, most of the QPixmap API is implemented
in a common source file, but QPixmap::grabWindow() is implemented in platform specific source
files. This approach is acceptable as long as the platform-specific implementation does not “leak”
into the Qt API.

In addition, there are cases where it is beneficial to expose some platform-specific detail through
platform specific methods (see page 18).

3.1. Pointer to Implementation Pattern

3.1.1. Overview

PIMPL∗ is a variant of the Handle-Body pattern, in which the public API contains a pointer to its
private implementation class. The pointer to the private implementation is forward declared
(instead of #included) in the header file, and is hence opaque to clients of the public API.

If the private class needs to call methods in the public class we pass a pointer/reference to the
public class into its constructor. If it needs to call private methods (e.g., in order to emit signals)
we also make it a friend class. The class diagram in Figure 4 illustrates the relationships, showing
a public class QMyClass which owns a private implementation QMyClassPrivate.

The implementation of the public class constructs (and destroys) the private implementation. In
order to do so it needs to know the definition of the QMyClassPrivate, so it #includes the header
for the current platform based on platform build defines – e.g. Q OS SYMBIAN (from qglobal.h):

Pimpl was popularised by Herb Sutter in Pimpls – Beauty Marks You Can Depend On and The Joy of Pimpls (or,
More About the Compiler-Firewall Idiom).

9
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

Figure 4: PIMPL Class Overview

// qmyclass.cpp
...
#ifdef Q_OS_SYMBIAN
#include ”qmyclass_symbian.h” // Symbian definition of private class
#else
#include ”qmyclass_stub.h” // Stub for all other platforms
#endif
...

Note that the private class header and source files must also be listed in appropriate platform
specific sections of the project file (.pro) – see Project File below.

The definition and implementation of the private class are almost entirely up to the developer.
The only firm constraint is that all platforms must use the same class name (otherwise we would
need to include forward definitions/friend declarations in the public header file for each plat-
form). Typically private classes also have the same functions – this allows us to implement the
public class without platform specific behaviour:

// Implementation of a public class slot


void QMyClass::mySlots()
{
d_ptr->mySlots();
}

The private class can have any derivation. Note: While it is common for private classes to be
QObject derived; this is in no way required (it could be useful if you want your implementa-
tion to have signals or slots). Note that the Qt codeline often uses QObjectPrivate for private
implementations. As this is not part of the public API it should be avoided by third parties.

For Symbian private implementations there are two basic class designs, shown in Figures 7 and
8 respectively. In the first approach the private implementation class is a Symbian class, derived

10
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

Figure 5: Left: QMyClassPrivate is a C class; right: QMyClassPrivate uses a C class.

from CBase. In the second approach, the private class uses one or more Symbian classes that
have a callback into the private class.

The first mechanism requires less developer effort, because you do not need an additional level
of indirection to create/call the Symbian class, or to get notification of completion (the callback
class). However this approach results in more complicated object construction (for reasons given
below) and may make it more difficult to conceptually separate Qt and Symbian C++ code. The
example code, as described in the following sections, uses this approach.

Often it is better to use the second method. This results in a cleaner separation between the two
programming idioms. Its also a better solution if the private class implementation needs to use
a number of Symbian classes. The Callback APIs section explains the second method in more
detail.

11
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

3.1.2. Bluetooth Example Public API

The public BluetoothDiscovery class is shown below. It has a pointer to the friend private im-
plementation class BluetoothDiscoveryPrivate.

The class diagram (Figure 6) shows the private class for both a Symbian implementation and a
stub implementation for other platforms. The private classes share the same name and have a
superset of the public class slots and methods. Note that they don’t re-implement the signals,
as these are implemented by the toolchain; instead the private classes can call the public class
emit through the pointer they get on construction.

// Forward declarations
class BluetoothDiscoveryPrivate;

class BluetoothDiscovery: public QObject


{
Q_OBJECT
public: //enums
enum BluetoothDiscoveryErrors
{ BluetoothNotSupported, BluetoothInUse, BluetoothAlreadyStopped,
BluetoothNotReady, DiscoveryCancelled, UnknownError };
public:
BluetoothDiscovery(QObject *parent = 0);
virtual ~BluetoothDiscovery();

public slots:
void startSearch();
void stopSearch();

signals:
void newDevice(const QBluetoothRemoteDevice remoteDevice);
void discoveryStopped();
void discoveryStarted();
void error(BluetoothDiscovery::BluetoothDiscoveryErrors error);

private: // Data
BluetoothDiscoveryPrivate *d_ptr; //private implementation

private: // Friend class definitions


friend class BluetoothDiscoveryPrivate;
};

12
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

Figure 6: BluetoothDiscovery Class Diagram

13
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

3.1.3. Project File

Platform specific compilation of source files is controlled through the project file.

The public class header and source files are specified in the general section. The platform specific
headers/sources are specified in platform specific blocks, as shown below.

...
HEADERS += qbluetoothaddressdata.h # public header
SOURCES += bluetoothdiscovery.cpp # public class implementation
...

symbian {
...
HEADERS += bluetoothdiscovery_symbian_p.h # Symbian private class header
SOURCES += bluetoothdiscovery_symbian_p.cpp # Symbian private class source code
LIBS += -lesock \
-lbluetooth
TARGET.CAPABILITY = LocalServices NetworkServices ReadUserData UserEnvironment \
WriteUserData
}
else {
HEADERS += bluetoothdiscovery_stub_p.h # private class declaration for other platforms
SOURCES += bluetoothdiscovery_stub_p.cpp # private class source for other platforms
}

The platform specific blocks list the platform libraries the code needs to link against, in this case
esock.dll and bluetooth.dll.

Symbian implementations must specify the capabilities that the executable needs (you can find
out more in the Platform Security (Fundamentals of Symbian C++) document). In this example
we specify just those capabilities that can be granted to a self signed application.

3.1.4. Public Class Implementation

The public class implementation needs the private class header in order to construct the private
class. We conditionally include the header based on the current platform:

// bluetoothdiscovery.cpp
...
#ifdef Q_OS_SYMBIAN
#include ”bluetoothdiscovery_symbian_p.h” // Symbian defn of BluetoothDiscoveryPrivate
#else
#include ”bluetoothdiscovery_stub_p.h” // Stub for all other platforms
#endif

14
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

Construction

The public class creates an instance of the private implementation class in its constructor as
shown below:

BluetoothDiscovery::BluetoothDiscovery(QObject *parent)
: QObject(parent)
{
#ifdef Q_OS_SYMBIAN // Symbian specific compilation
QT_TRAP_THROWING(d_ptr = BluetoothDiscoveryPrivate::NewL(this));
#else
d_ptr = new BluetoothDiscoveryPrivate(this);
#endif
}

Note that because the private class is a Symbian CBase derived class we’ve chosen to use platform
specific construction in the public class implementation. This is OK because the Symbian class
implementation isn’t visible in the public API. However as a rule we’d prefer to push as much
platform specific implementation into the private class.

NewL() is a standard Symbian static factory class for creating objects of type BluetoothDiscov-
eryPrivate, which ensures that either the object is fully allocated, or it will be properly cleaned
up in the event of an exception. As this can Leave, we wrap it in a QT TRAP THROWING macro
to convert the leave into a throw. The section Exceptions and Error Handling explains how you
create exception handling barriers between Symbian and Qt code.

If you implement the private class using a CBase derived class then construction is simpler, and
you can have a common public class implementation.

// Public class
BluetoothDiscovery::BluetoothDiscovery(QObject *parent)
: QObject(parent)
{
d_ptr = new BluetoothDiscoveryPrivate(this);
}

// Private class
BluetoothDiscoveryPrivate::BluetoothDiscoveryPrivate(QObject *parent)
: d_ptr(parent)
{
QT_TRAP_THROWING(symbianMember = CBluetoothDiscovery::NewL(this));
}

There are other ways of allocating the Symbian object. Whatever method you use, the important
things to remember are:

1. Symbian C classes have new overload from CBase that does not throw. If you construct with
new then you need to use q check ptr to check the pointer (and throw on Null).

2. Ensure that the object is cleaned up properly if construction fails.

15
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

The first point might apply if an CBase-derived object need not be initialised until it is first used.
In this case you can construct using new, but you must check the pointer and throw if it is NULL.

The second point is important because its not always obvious, particularly when mixing Qt and
Symbian C++ idioms, whether cleanup will occur properly. Consider the following code:

BluetoothDiscovery::BluetoothDiscovery(QObject *parent)
: QObject(parent)
{
d_ptr = q_check_ptr(new BluetoothDiscoveryPrivate(this)); // from Qt 4.6
#ifdef Q_OS_SYMBIAN
QT_TRAP_THROWING(d_ptr->ConstructL());
#endif
}

If ConstructL() leaves the macro will throw. Following normal C++ rules, the memory for Blue-
toothDiscovery is freed but the destructor is not called. Therefore any partially allocated objects
in BluetoothDiscoveryPrivate will leak. For this to work d ptr would need to be a smart pointer
(QScopedPointer).

The public class must delete the private implementation in its destructor.

Methods

We replicate the public API in the private class.

Public methods call the equivalent private class methods through the pointer to implementation;
for example:

// Called to start searching for new devices


void BluetoothDiscovery::startSearch()
{
d_ptr->startSearch();
}

Note that we haven’t created any barrier between Qt and Symbian C++ exception handling mech-
anisms. This is because we know from the implementation that startSearch() cannot Leave. This
method can throw, which is acceptable because it is never called from Symbian code.

3.1.5. Private Platform Implementation

Class Declaration

The Symbian platform BluetoothDiscoveryPrivate implementation is shown below. It is a Sym-


bian active object. As it is a Symbian C++ class it is important to comply with Symbian coding
standards, in particular:

• Construct it using Symbian mechanisms

• If using multiple inheritance, derive from CBase derived classes first, and only otherwise
derive Mixin (interface) classes.

16
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

Warning: Never derive from both CBase and QObject as the new used for construction is unde-
termined.

class BluetoothDiscoveryPrivate : public CActive


{
public:
//static factory ”constructor” function
static BluetoothDiscoveryPrivate* NewL(BluetoothDiscovery *aPublicAPI = 0);

~BluetoothDiscoveryPrivate(); // Destructor

public:
void startSearch(); // Called to start searching for new devices
void stopSearch(); // Called to stop searching

public: // From CActive


virtual void DoCancel(); // Implements cancellation of an outstanding request.
void RunL(); // Handles an active object’s request completion event.

private:
BluetoothDiscoveryPrivate(BluetoothDiscovery *aPublicAPI = 0); // constructor
void ConstructL(); // 2nd phase ctor: connects to socket server and finds protocol

// Error translator: converts global errors into local format and emits to parent
void ErrorConvertToLocalL(int err);

private: // Data

RSocketServ iSocketServ; // Socket server connection


RHostResolver iHostResolver; // Host resolver
TNameEntry iCurrentDeviceEntry; // The entry of the device just returned.
TInquirySockAddr iInqSockAddr;
TProtocolDesc iProtocolInfo;

BluetoothDiscovery *iPublicBluetoothDiscovery; // ptr to parent object (from ctor)


};

The class has a pointer to its parent BluetoothDiscovery that it uses to emit signals to the Qt
clients when the active object completes/has an error.

The private class replicates the public classes API, reducing the conditional code in the public class
implementation. Additionally it re-implements CActive’s virtual methods RunL() and DoCancel()
to handle completion and cancellation of the active object.

The class has private data members used to get the name and ids of the remote bluetooth devices
from the Symbian’s Bluetooth.dll. iHostResolver is the class that contains the asynchronous
function with which the request is made.

17
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

Construction

As the private implementation is a Symbian class we construct it using the two-phase construc-
tion idiom, as discussed in the preceding sections.

The public static NewL() factory function uses a leaving constructor to create the object, pushes
it on the CleanupStack, initialises it using the leaving second phase constructor, pops it off the
stack and returns it to the user.

BluetoothDiscoveryPrivate* BluetoothDiscoveryPrivate::NewL(BluetoothDiscovery *wrapper)


{
BluetoothDiscoveryPrivate* self = new (ELeave) BluetoothDiscoveryPrivate(wrapper);
// push onto cleanup stack in case self->ConstructL leaves
CleanupStack::PushL(self);
// complete construction with second phase constructor
self->ConstructL();
CleanupStack::Pop(self);
return self;
}

As part of construction we also give the private object a handle to the public class – in this case a
pointer, but should perhaps be a reference. The pointer can then be used to directly call methods,
or to emit signals.

Methods

The implementation of the private class’s methods is dependent on the functionality required. In
general terms the most important thing to keep in mind is correct inter-working of the platform
exception handling systems, as discussed in the Exceptions and Error Handling section.

Note also the use of void ErrorConvertToLocalL(int err); to convert Symbian’s global errors
into local errors for this component.

The example discusses the RunL() and DoCancel() methods in Converting Active Objects to Signals
and Slots .

3.2. Platform Specific Methods

Where possible public APIs should be kept free of platform specific member and functions. Ex-
ceptions to this rule are very rare!

Occasionally platform specific helper functions are made public, where developers would other-
wise need to role out their own methods. For example QPixmap, provides helper functions to
convert to and from CFbsBitmap:

• CFbsBitmap * QPixmap::toSymbianCFbsBitmap () const

• static QPixmap QPixmap::fromSymbianCFbsBitmap ( CFbsBitmap * bitmap )

18
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

Similarly, it is sometimes necessary to reveal platform specific types and publically include plat-
form headers in order to map them to generic typedefs. You can see this in QProcess where
Q PID is typedefed to a TProcessId on the Symbian platform.

In all cases, platform specific artifacts should be defined to be visible only in the specific platform.
For example, from the QPixmap declaration:

#if defined(Q_OS_SYMBIAN)
CFbsBitmap *toSymbianCFbsBitmap() const;
static QPixmap fromSymbianCFbsBitmap(CFbsBitmap *bitmap);
#endif

3.3. File and Class Naming Conventions

In general, if the public class is called QMyClass then:

• The private class will be QMyClassPrivate.

• Public class source and header files share the name of the public class: qmyclass.h, qmy-
class.cpp.

• Private class headers and source file names are terminated with p (e.g. qmyclass p.h)
unless the file is a platform specific implementation.

• Platform-specific implementation headers and source files include the platform in the file
name – e.g. qmyclass symbian.cpp (the p is not necessary as it is implied).

19
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

4. Multi-Tasking

Multi-tasking is the ability to perform more than one task at time. It is important to GUI applica-
tions, because it allows them to perform long running or computationally expensive operations
while still remaining responsive to user input.

There are two types of multitasking: preemptive and co-operative. In preemptive multitasking,
the operating system gives each thread of execution some time in which to run – the thread has no
control over when it will run or how much time it gets. In co-operative multitasking, a scheduler
controls what task is run next, but the current task alone determines when it will complete.

Preemptive multitasking is heavier-weight in terms of RAM and execution speed, and is more dif-
ficult to program because of the need to mediate access to shared resources (and ensure threads
don’t deadlock). Co-operative multitasking is easier – because access to resources is serialized.
However individual tasks need to be short running so that the UI remains responsive.

In multitasking operating systems we use the term process to refer to a set of threads that share
the same global memory space, and which can hence directly access each others variables; all the
threads in an application will typically run in the same process! Note however that it is threads,
not processes, which are scheduled for execution.

A multitasking operating system may also be multiprocessing; multiprocessing is where threads


can run on more than one processor/CPU.

4.1. Multi-Tasking on the Symbian Platform

The Symbian platform is a modern preemptive multitasking operating system.

Applications are created in their own process, running in a single main thread. The kernel pre-
emptively schedules all threads in the system, based on their priority. While it is possible to cre-
ate secondary threads, Symbian strongly encourages applications to co-operatively multi-task
using active objects.

Almost all Symbian services are provided by servers (or “daemons”) running in other processes
(including the file server, window server, font and bitmap server, location server etc). These usu-
ally export an asynchronous API that takes a reference to a TRequestStatus object that the server
uses to signal completion of the request. Active objects provide a consistent and lightweight
way to write code to submit the asynchronous requests and handle its completion.

Active objects are derived from CActive, which either owns or has a handle to an asynchronous
server provider. The active object must add the object to the active scheduler in its construc-
tor and provide a method to set itself as active – first calling the asynchronous method (pass-
ing in the TRequestStatus iStatus member) and then calling CActive::SetActive(). When the
asynchronous service completes it signals the active object’s thread semaphore and changes the
status of the object’s iStatus to show that it is no longer pending. The active scheduler will
later call the object’s RunL() method, which you must implement, to handle completion of the
service. Note that this will not be immediate – active objects are co-operatively multi-tasked
so the scheduler can only run one at a time, and only when the last one has completed. Lastly,
developers must implement the virtual DoCancel() which cancels the asynchronous request, and
ensure that they call Cancel() in the active object’s destructor.

The above summary only touches on the nuances possible with active objects. Developers should

20
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

read the article Active Objects (Fundamentals of Symbian C++) and Co-operative multitasking
(Best Practices in Symbian C++ Design). If you’re interested in using or implementing services
you may also be interested in reading Client-Server Framework (Fundamentals of Symbian C++).

Developers that prefer to use threads and processes can of course do so – in some cases this may
be necessary. Symbian C++ processes and threads can be created and manipulated using the
RProcess and RThread API, respectively. Symbian C++ has the usual synchronization primitives
including mutexes (RMutex), semaphores (RSemaphore), Critical Sections (RCriticalSection) etc.
All of these classes are discussed in Threads, Processes, and IPC (Fundamentals of Symbian C++).
Developers who are interested in how these are implemented can read chapter 3 of the Symbian
OS Internals book (replicated on the Symbian Foundation Wiki). If you’re programming with
standard C++ (through Open C and Open C++) then you can create pthreads as normal.

Threads in the same process can easily share data directly (taking care to serialize access to
shared data). Threads in other processes need to communicate using Symbian’s inter-process
communications mechanisms. These include Client-Server, Publish and Subscribe, Message Queues,
and RPipe. The following documents provide useful discussions of these mechanisms:

• Threads, Processes, and IPC (Fundamentals of Symbian C++)

• New IPC Mechanisms for Symbian OS

• Symbian OS Internals/4. Inter-thread Communication

• Client-Server Framework (Fundamentals of Symbian C++) (and Transient Server Template if


you want to implement your own server!)

Symbian is expecting to add support for symmetric multiprocessing (SMP) from Symbianˆ3.

4.2. Multi-tasking with Qt

Qt applications use both co-operative and preemptive multi-tasking.

Qt’s main application thread runs its own event loop which processes events generated in re-
sponse to user interaction (key presses, mouse events etc) and from timers and the window
system. This event loop is an example of co-operative multitasking – events are queued and
handled synchronously. If too much time is spent on one event then the UI can become unre-
sponsive.

If the computationally intensive operation can be broken into a number of steps – for example
writing to a file, then you can call QApplication::ProcessEvents() at regular intervals during the
operation to give the event loop time to handle other events from the UI. This approach is dis-
cussed in chapter 7 of C++ GUI Programming with Qt 4, Second Edition, Jasmin Blanchette and
Mark Summerfield, Prentice Hall (2006) (the first edition is available free online here).

Multithreading is a more common approach. Developers subclass QThread and re-implement its
run() function to execute code in the new thread. Synchronisation classes include QMutex to
provide mutually exclusive access to a resource, QReadWriteLock which provides unrestricted
access for reading but blocks on writing, QSemaphore which generalises QMutex to allow access
to a specified number of resources, and QWaitCondition which blocks until some condition comes
true. There are also a few helper classes like QMutexLocker, which simplify mixing mutexes
programming and standard C++ exception handling.

21
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

The Thread Support in Qt document provides a good overview to the thread classes, and links on
to topics on Synchronizing Threads, Reentrancy and Thread-Safety, Threads and QObjects,Concurrent
Programming,and Thread-Support in Qt Modules. There are a few good examples here: Mandel-
brot Example, Semaphores Example, and Wait Conditions Example. C++ GUI Programming with
Qt 4, Second Edition, Jasmin Blanchette and Mark Summerfield, Prentice Hall (2006) contains an
excellent discussion of the multithreading (with some duplication of the other links) in chapter
14.

Threads communicate with each other using shared memory and the above synchronization
classes. Threads communicate with the main thread using signals and slots. Note however that
by default the signals are not synchronous as they are within a single thread.

Applications can also multitask using other processes. For example it is possible to create a
QProcess to launch another process, set its command line arguments and to detect its startup,
error and completion status.

You can also use standard C++ threads, processes and inter-process communication mechanisms.

4.3. Converting Active Objects to Signals and Slots

When using Qt and Symbian C++ together, you may need to make use of a Symbian service with
an asynchronous API. The best way to do this is to wrap the request in an active object and then
notify completion of the request to Qt code using a Qt signal.

Converting active objects completion events into signals and slots is straightforward:

• Use the PIMPL idiom to create a public Qt-style API.


• Create the active object as the Symbian private implementation class (or as an object owned
by the private class).
• Map functions/slots in the public class to functions which start the active object.
• Emit signals when the active object completes successfully using its pointer to the public
implementation. Only Qt-style objects should be emitted with the signal(obviously).
• Emit signals on error. Note that Qt uses module-local errors, so you should translate global
(or local) errors from the underlying interface into local errors defined in the module public
interface.

Note: In some cases it may be possible to do a zero-copy transfer from the Symbian object to
the Qt Object: for example its possible to initialise a QString to use a pointer to a buffer you’ve
received from your asynchronous service. As always with C++, care should be taken to ensure that
the object you’re transferring will have a lifetime that exceeds all its users. Given the different
approaches used by Qt and Symbian C++ for memory management you will usually be best off
creating a “fully new” Qt style object.

The BluetoothDiscovery example above is an active object, so you’ve already seen how to con-
struct the object, pass in a pointer to the public class, and start the object (by calling the private
startSearch() through the public startSearch()).

As the active object is otherwise exactly the same as described in Active Objects (Fundamentals
of Symbian C++), all that remains is to talk a little about how you signal completion back to the
Qt API.

22
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

4.3.1. Signalling from the Active Object

You signal either errors or events by calling the public class emit function, using the private
pointer to the parent passed in on construction. The objects that we emit should be Qt-style
objects. Errors that are emitted should be local to the current Qt component (ie defined as enums
in its public header rather than global Symbian errors).

The signal that is emitted can be connected to any number of slots. This has two implications:

• Slots that run inside the context of our non-preemptive active scheduler (RunL) must be
kept short. If the total time spent in all slots is too great the UI may become unresponsive
(or worse block).

• Any of the slots connected the the signal can throw, and this will propagate back to the
emit call. For this reason we use the QT TRYCATCH LEAVING barriers around emit calls that
might propagate into Symbian code!

4.3.2. RunL()

Below is a fragment of the RunL(), which is called when the asynchronous service completes. On
successful completion it creates a new QBluetoothRemoteDevice, populates it with information
from the equivalent Symbian C++ classes, and emits this in our signal. If there is an error, we call
ErrorConvertToLocalL() to convert it to a module specific error.

void BluetoothDiscoveryPrivate::RunL()
{
if (iStatus == KErrNone) {
...
// Create a QBluetoothRemoteDevice and populate it with
// information from the asynchronous service
QBluetoothRemoteDevice remoteDevice(qtBtDeviceAddress);
...
// emit the device as a signal from the public class
QT_TRYCATCH_LEAVING (emit iPublicBluetoothDiscovery->newDevice(remoteDevice) );
// Search for the next device
iHostResolver.Next(iCurrentDeviceEntry,iStatus);
SetActive();
}
else if (iStatus == KErrHostResNoMoreResults) {
// No more devices to detect
QT_TRYCATCH_LEAVING (emit iPublicBluetoothDiscovery->discoveryStopped() );
}
else {
// Error. Emit ”discovery stopped” signal and then translate
// to local error (which is also emitted within the function)
QT_TRYCATCH_LEAVING (emit d_ptr->discoveryStopped() );
ErrorConvertToLocalL(iStatus.Int());
}
}

23
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

4.3.3. SetActive()

The private startSearch() is used to start the active object. It checks if the object is already active,
creates a request on an asynchronous service provider, and then calls CActive::SetActive() to
set the object active.

In addition, the method emits a signal on error (the object is already started) and to notify clients
that its started. In this case we do not need a barrier around the emit calls because although
emit can throw, this function can only be called by the Qt API. The behaviour of stopSearch() is
similar.

void BluetoothDiscoveryPrivate::startSearch()
{
...
emit iPublicBluetoothDiscovery->discoveryStarted();
...
}

4.3.4. DoCancel()

CActive::Cancel() may be called to cancel an asynchronous request, and will in turn call CAc-
tive::DoCancel() (which we need to implement) if it is called when the object is active. Cancel()
must be called from the active object destructor to cancel any outstanding requests – the active
scheduler will panic if a removed object is still waiting on a signal.

In the example below DoCancel() emits a signal that device discovery has stopped, which is a
potentially throwing operation (the signal could be connected to any slot, and any of that con-
nected code might throw). As discussed in the section Mixing Symbian C++ and Qt Exception
Handling , since this method is called in a destructor we should avoid using throwing or leaving
code if at all possible. For the sake of argument we’re assuming this emit is unavoidable and
therefore catching all standard exceptions (only).

void BluetoothDiscoveryPrivate::DoCancel()
{
// Note that must trap any errors here as
// Cancel() is is called in destructor and destructor must not throw.
try {
emit iPublicBluetoothDiscovery->discoveryStopped();
}
catch (std::exception&)

iHostResolver.Cancel();
}

4.4. Callback APIs

Symbian C++ provides APIs that use callbacks to signal completion of an asynchronous service
(under the hood these are implemented as active objects; using a callback simplifies the use
of the API for client code). A good example of this is CMdaAudioPlayerUtility, which takes an

24
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

instance of MMdaAudioPlayerCallback in its static factory constructor, and calls methods on this
to indicate when initialisation and playing are complete.

Wrapping a Symbian C++ callback is even easier than using an active object directly. Again you
can use PIMPL to create a Qt class, with a private implementation. In this case the private imple-
mentation will own an instance of the Symbian class and implement its callback interface.

class MyAudioPlayerPrivate : public QObject, public MMdaAudioPlayerCallback


{
public:
QMyAudioPlayer (MyAudioPlayer *wrapper = 0);
~QMyAudioPlayer ();

public:
// methods, slots intiate play (duplicate API of the public class)

public: // From MMdaAudioPlayerCallback


virtual void MapcInitComplete(TInt aError, const TTimeIntervalMicroSeconds &aDuration);
virtual void MapcPlayComplete(TInt aError);

private:
void ErrorConvertToLocal(int err);

private: // Data
CMdaAudioPlayerUtility *iAudioPlayer; // The audio player object
MyAudioPlayer *d_ptr; // pointer to Qt public API
};

MyAudioPlayerPrivate::MyAudioPlayerPrivate (MyAudioPlayer *wrapper)


: d_ptr(wrapper)
{
QT_TRAP_THROWING(iAudioPlayer=CMdaAudioPlayerUtility::NewL(this));
// note, throws if can’t construct object
}

MyAudioPlayerPrivate::~MyAudioPlayerPrivate()
{
delete iAudioPlayer;
}

We can then emit any needed signals from the callback:

MyAudioPlayerPrivate::initialisationComplete()
{
QT_TRY {
emit d_ptr->initialisationComplete();
}
QT_CATCH (std::exception&) {}
}

25
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

Note that the above code traps exceptions, and does not deal with them or re-throw them: gen-
erally this is considered a bad thing! However we are constrained by the API – we can’t leave
because it’s prototype indicates that it may only be used in a leave-safe way, and we can’t throw,
because this will lead back into Symbian code. Absorbing the errors is our only choice.

Warning: It is important to remember that any callback may be run from within the context of a
RunL(). If you have any Qt code that might throw in a leaving callback, this should be wrapped
in a QT TRYCATCH LEAVING method.

26
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

5. Coding Standards and Conventions

5.1. Symbian Coding Standards

Symbian’s very extensive set of coding standards and conventions are captured the document:
Coding Standards and Conventions. If you’re developing on top of the platform using Symbian
C++ (and not planning to contribute directly to the platform source code) then you will find the
Coding Standards Quick Start is a more useful starting point.

The coding standards cover rules related to code formatting, safety, ease of maintenance and
efficiency. Developers can reasonably ignore the standards related to formatting personal code,
however the other conventions are essential for writing robust and efficient code with Symbian
C++.

5.2. Qt Coding Standards

Qt provides a compact set of coding guidelines that are separated into Coding Style and Cod-
ing Conventions documents. The former defines best-practice for code format/layout, while the
conventions discuss C++ features you shouldn’t use, approaches to including headers, casting,
maintaining binary compatibility and compiler specific issues. As you would expect, there is a
clear focus on coding to ensure cross platform compatibility.

If you are using Carbide.c++, you can use the Qt Code Style.xml template to make your projects
follow the Qt coding style by default. To add it, download and import the file through Carbide.c++
menu: Window > Preferences... > C/C++ > Code Style.

5.3. Coding Standards For Mixed Symbian C++ and Qt Code

Qt applications should typically follow the Qt Coding Style rules with respect to code layout for
both Symbian C++ and Qt code. Otherwise, Qt code should follow Qt conventions and Symbian
C++ code should follow Symbian Coding Standards.

This approach is followed in the example code accompanying this whitepaper. The exception
to this rule is that the BluetoothDiscoveryPrivate class is a Symbian C class (derived from CAc-
tive), and should ideally be named using the C prefix. In this case the class name is necessary to
maintain the “opacity” of the private implementation and the risk of not using the class prefix
is low because BluetoothDiscovery is its only user.

27
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

6. Exceptions and Error Handling

6.1. Symbian Exceptions and Errors

Symbian C++ uses its own exception mechanism consisting of TRAPs (somewhat equivalent to
try), Leaves (a bit like throw), and the Cleanup Stack (which allows locally scoped heap-allocated
objects to be safely deleted in the event of an exception).

Potentially leaving code is run inside a TRAP macro. In the event of a Leave the call stack is
unwound up to the TRAP, automatic variables are deallocated and the Cleanup Stack deletes or
otherwise cleans up any objects on the current TRAP level. The TRAP macro returns the leave error
code (a single integer) and execution continues immediately afterwards (there is no separate
“catch”). Much like catch blocks, the code following a TRAP is expected to handle leave codes
that it understands, and Leave again with any that it doesn’t.

TInt result;
TRAP(result, MayLeaveL());

if (KErrNone!=result) {
// Deal with errors that can be handled at this level
// Leave again with any errors you choose not to handle
}

TRAPs are relatively heavy weight in terms of executable size and RAM consumption. While these
can be nested and used at any level, its usually better to trap at a high level, grouping a number
of Leaving functions.

Symbian uses a naming convention for functions and class types to help users understand how
to safely allocate objects to prevent memory leaks:

• Functions that might Leave are by convention named with a trailing L (or LC to indicate that
the object remains on the Cleanup Stack when the method returns).

• Classes that should be allocated on the heap usually first derive from CBase and are named
with the C prefix.

• CBase provides zero member initialization on construction, an overload of new that Leaves
(new (ELeave)) in the event of allocation failure, and a virtual destructor that the Cleanup
Stack can use to delete the object in the event of a Leave (The cleanup stack also provides
methods to delete other heap based objects that are not derived from CBase – e.g. arrays
and interface classes).

• C classes are constructed using the leave-safe two-phase construction idiom.

• T prefix is used for stack allocated classes that don’t own pointers to heap resources and
don’t have a destructor.

• R prefix is used for stack classes that own resources elsewhere – and hence require explicit
support for cleanup.

Note: In Symbian’s original implementation, the destructors of automatic variables were not
called in the event of a Leave. As a result smart pointers could not be used for local object cleanup.

28
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

The naming conventions allow developers to work out whether objects require explicit cleanup
support via the cleanup stack (R, C classes) or not (T classes).

The document Leaves & The Cleanup Stack (Fundamentals of Symbian C++) provides a more
detailed discussion of how the exception mechanism works, how to write leave-safe code, and
the naming convention. Class Types & Declarations (Fundamentals of Symbian C++) explains the
different class types, and Object Construction (Fundamentals of Symbian C++) explains leave-safe
object construction.

Note: At the time Symbian C++ was created, standard C++ try-catch-throw exception handling
was considered too memory-expensive for an embedded operating system. In addition, there
was limited support in the compilers Psion needed to use. Standard C++ exception support was
added in Symbian OS v9, the precursor operating system to the Symbian platform, used in Nokia
S60 Third Edition.

Symbian’s exception handling mechanism is now implemented in terms of try, catch and throw.
Developers can use try, catch and throw in standard C++ code, and even mix them with Symbian
C++ (with care!). The new exception handling mechanism, and how to interwork with standard
C++ exceptions is discussed in A Comparison of Leaves and Exceptions and the Hybrid Coding Guide.

Symbian functions use either Leaves or error codes to signal an exception condition. Returning
an error is usually preferred to Leaving when the error condition is “expected” – e.g. attempting
to read off the end of a file. However here are no firm rules (other than class construction will
always leave with KErrNoMemory if there is no memory to allocate the object), and you’ll see errors
and Leaves used in an ad-hoc manner within the Symbian C++ API. Note that it is considered bad
programming practice to have a function that both leaves and returns an error, or to convert a
leave to an error (using a TRAP without good reason). As with normal exceptions.

There is a set of global error codes (negative integer values), defined in e32err.h. Other error
codes are defined within individual components – there are a number of lists, including the one
on www.newlc.com.

Developers should also be aware that Symbian defines a second class of exception called a Panic.
A Panic is used to signal programmatic errors, and result in immediate termination of the appli-
cation – this is an appropriate response for misuse of an API.

6.2. Qt Exceptions and Error Codes

Qt supports the standard C++ exception handling mechanism where it is available on the under-
lying platform and compiler.

While Qt has supported the mechanism for third party code since its inception, historically Qt
framework code has itself not used the mechanism (for reasons of cross-platform compatibility).
Qt code instead uses locally defined error codes to notify client code of error conditions. In the
case of failure to allocate memory, framework code simply returned a null pointer and failed
on first pointer dereference (except for some larger objects, where special measures have been
taken.

Qt 4.6 promises basic exception safety (component invariants are preserved and no resources are
leaked) for Qt’s container classes as well as for the QFile class:

• The QScopedPointer smart-pointer has been added to allow locally scoped objects to be
cleaned up in the event of an exception (during use or on construction).

29
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

• Objects now throw std::bad alloc if memory cannot be allocated (using q check ptr() to
check the returned pointer)

• try/catch blocks have been added where appropriate.

Qt does not (at the time of writing) define its own exception class hierarchy, and the framework
code only throws std::bad alloc. Qt itself still uses error codes rather than exceptions to propa-
gate errors other than allocation failure. Developers should assume that almost any Qt class can
throw, and that where it interacts with third party code, it can throw virtually anything (an ex-
ception is QScopedPointer’s constructor, which means that it can safely be used for cleaning up
locally scoped objects). When Qt catches exceptions, it always rethrows if these are not handled.

Developers should use the macros QT TRY, QT CATCH, QT THROW, and QT RETHROW (defined in
qglobal.h) in preference to calling try, catch and throw directly. This ensures that exception
handling code is only compiled on platforms that support exceptions. Note that if you write your
application to be exception safe, it will work whether exceptions are enabled or not.

6.3. Mixing Symbian C++ and Qt Exception Handling

Qt uses standard C++ exceptions while Symbian C++ uses Leaves. While Leaves are implemented
in terms of exceptions, care needs to be taken to interleave the two idioms.

Symbian exception safety succinctly explains the issues, and describes the barrier functions that
you can use to convert between the idioms; A Comparison of Leaves and Exceptions and the Hybrid
Coding Guide are useful additional references.

The barrier functions allow you to catch standard exceptions and convert them to Symbian errors
or leaves for propagation into Symbian code, and similarly to convert Symbian C++ leaves or
errors into standard exceptions. The methods are listed here for convenience:

• qt symbian throwIfError()

• q check ptr()

• QT TRAP THROWING()

• qt symbian exception2Error()

• qt symbian exception2LeaveL()

• QT TRYCATCH ERROR()

• QT TRYCATCH LEAVING()

The barrier methods can only catch and convert standard exceptions – application specific excep-
tions will propagate through, and cause the application to terminate if they reach a TRAP. This is
expected behaviour – by convention code should catch and handle only those exceptions that it
understands – all other exceptions must be re-thrown.

Note: It is tempting to catch all exceptions with catch (...) or QT CATCH (...) to prevent them
propagating to a TRAP and terminating the application. However if you do this the error condi-
tion will still exist – all you’ve done is remove any opportunity for the application to handle the
error appropriately – the code may still leak memory or fail in unpredictable and hard-to-debug
manner.

30
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

There are a few cases that are worth more detailed consideration: where a function must not
fail, and where it can fail but must not throw or leave. In both cases “must not fail” should be
treated as “must not fail with a leave or standard exception” – other exceptions must be allowed
to propagate!

Destructors are a case where a function must not fail (either throw exceptions or Leave). When
implementing a destructor:

• Try to only use functions that will not fail.

• If you must call a function that can Leave, stop it from propagating using a TRAP macro.

• If you must call a function that can throw, stop standard exceptions with a QT CATCH (const
std::exception&). Other exceptions must be allowed to propagate – do not QT CATCH (...)
without rethrowing the exception.

• A logical consequence of this is that a Qt slot connected to the QObject::destroyed() sig-


nal must be implemented as if it were destructor code, following all the rules above (it is
emitted in the QObject destructor).

Some functions can fail, but must not throw or leave. For example Symbian C++ callback methods
that do not have the L suffix cannot leave because the calling code may not have been written
to be leave-safe. Nor can it throw, because there may well be a TRAP at a higher level:

• If the function can return an error code then you can use the Qt barrier functions to convert
leaves or standard exceptions to errors

• If the function prototype does not allow errors to be returned, then you can absorb errors
you understand but must throw others.

• In both cases, non standard exceptions must be allowed to propagate.

31
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

Figure 7: Descriptor class inheritances

7. Converting Between Common Qt and Symbian Types

7.1. Strings

Strings are used in almost every application, and are probably the most common conversion
between Symbian C++ and Qt code.

7.1.1. Symbian Strings

Symbian C++ uses descriptors to handle both text and data. Descriptors are self-describing: they
use the minimum amount of memory to store the string data and information about its length
and memory layout. Descriptors do not resize automatically, and instead will panic if an opera-
tion goes over the buffer length – this promotes robust code on devices that are designed to be
rarely or never rebooted. As mentioned above, they can be used for both text and data because
the length is not determined from the presence of NULL (’\0’) terminators.

Symbian’s descriptor class hierarchy is complicated; providing concrete modifiable and non-
modifiable descriptors that store their data on the stack or heap, and a number of base classes
that are used for function return types and parameters, but which are not intended for instan-
tiation (TDes, TDesC, etc.). There are even some pointer descriptors (TPtr, TPtrC) which simply
point to data stored in other locations.

Every descriptor class comes in a narrow (8 bit) and wide (16 bit) variant (e.g., RBuf8,RBuf16
respectively). The 8 bit variant is primarily used for data, while the 16 bit variant is used for
Unicode text. For string data most developers will actually use the bare version of the descriptors
(e.g. RBuf) shown in the figure: this is a typedef over the 16 bit variant on all current versions
of the Symbian platform.

32
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

The Symbian Foundation Wiki has some excellent documentation on descriptors: Descriptors (Fun-
damentals of Symbian C++) and Descriptors Cookbook, as does the Developer Library: Using
descriptors. Developers should also be aware of TLex, used for lexical parsing and converting nu-
meric strings into number types, the various typdefs classes used for characters: TText, TChar,
and Charconv for conversion between character sets (converts more sets than Qt and is extensi-
ble).

7.1.2. Qt Strings

Qt uses a single class, QString for almost all Unicode string handling. The class provides almost
all functionality developers might need, including string comparison, conversion to and from
numeric types, and conversion between character sets. The class integrates with Qt’s regular ex-
pression classes to provide powerful parsing and string manipulation. Its also works seamlessly
with Qt’s internationalization APIs.

QString will automatically resize if necessary to accommodate a larger string (if the string can not
be reallocated the operation will throw). QString uses implicit sharing (copy-on-write) to reduce
memory usage and to avoid the needless copying of data. This means that while the variable is
stored on the stack, the associated data is stored on the heap.

Note: In terms of memory usage, Symbian C++ developers may find it helpful to think of QStrings
(and any implicitly shared objects) as reference-counted R-Classes. The object data is allocated
on the heap; copy operations that won’t change the data simply increase the count without need
to create a new object. The data is only freed when all objects that use it go out of scope.)

The QString API is much easier to understand than the descriptor hierarchy, and is more powerful
in many respects. QString is less memory efficient than the descriptor classes, but still relatively
efficient and robust.

Qt also provides QByteArray (an array of bytes) which can be used for string operations. QByteAr-
ray is often used for data, and is discussed in the section Qt Binary Data .

7.1.3. Converting a Descriptor to a QString

Use QString::fromUtf16 to create a new QString with a deep copy of the data at a specified ad-
dress and length. The address and length are obtained with TDesC16::Ptr() and TDesC::Length()
respectively:

This approach is used in the example code to copy the device name and address from descriptors
into to QStrings, prior to assigning to the QBluetoothRemoteDevice (see implementation of
BluetoothDiscoveryPrivate::RunL().

In most cases you will use the above method to create a copy of the data. If you need to do
a zero copy transfer of data, and you can guarantee that theDescriptor lifetime exceeds that
of possible Qt variables, then you can use QString::fromRawData() to get a QString pointing at
theDescriptor’s data:

QString::fromRawData(reinterpret_cast<const QChar*>(theDescriptor.Ptr()),
theDescriptor.Length());

33
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

8-bit descriptors containing text may be converted to Unicode within Symbian C++ and then
transferred as above. This has advantages because Symbian’s character conversion classes al-
low auto-detection of the character set, and out-of-the-box support conversion between more
character sets.

However you may find it easier to convert them within Qt code. You can use const TUint8*
TDesC8::Ptr() const; to get a pointer to the data in the descriptor. If you know the character
set you can use QString’s fromAscii(), fromLatin1(), fromUtf8() to do the conversion; if you just
know the data is in the current locale’s default set you can use: QString::fromLocal8Bit().

7.1.4. Converting a QString to a Descriptor

Use QString::utf16() or QString::constData() to get a pointer to the data in the QString myString
and cast this to a TPtrC16 (TPtrC) as shown:

TPtrC myDescriptor (static_cast<const TUint16*>(myString.utf16()),


myString.length());

or

TPtrC myDescriptor (reinterpret_cast<const TText*>(myString.constData()),myString.length());

The pointer descriptor is valid while the original QString (or any shallow copies) are still in scope.
Unless you can guarantee this, you should copy the string into a heap or buffer descriptor. Using
an (RBuf) heap descriptor do:

RBuf buffer;
qt_symbian_throwIfError(buffer.Create(myDescriptor));

For a buffer descriptor you can do either of:

TBuf buffer(myDescriptor);

or

TBuf<KBufLength> buffer(text.utf16());

Note: qcore symbian p.h defines HBufC* qt QString2HBufC(const QString& aString) and TPtrC
qt QString2TPtrC( const QString& string ) which do these conversions. Since this file is not
part of the public API, you may choose to copy the source code from qcore symbian p.cpp into
your own project.)

34
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

7.2. Input/Output and Binary Data

In this section we briefly discuss mechanisms for serializing object data, saving it to files, and
transferring it between devices and threads.

7.2.1. Symbian Binary Data

The Symbian C++ stream classes are used to serialize object’s internal data into a series of bytes
and to initialize them from a series of bytes.

Objects that need an external representation define ExternaliseL() and InternaliseL() methods
as shown below (note that classes which define these methods can use the global >> and <<
streaming operators to externalise/internalize data to/from a stream).

void ExternalizeL(RWriteStream& aStream) const;


void InternalizeL(RReadStream& aStream);

The methods write/read the object’s members in terms of their own ExternalizeL()/InternalizeL()
methods, and ultimately platform-independent representation of the basic types (including de-
scriptors) defined in the RWriteStream/RReadStream classes.

RWriteStream/RReadStream are abstract classes. When we externalize objects we use a con-


crete stream that sends the data to a file, file store, raw memory, or a fixed or dynamic buffer.
Some streams are initialized with other stream objects – for example to compress or encrypt the
data. Native Symbian applications usually store their data as file based “stores” of steams, that
are associated with the application using its unique identifier.

There is good overview of stores and streams in Streams And Stores (Fundamentals of Symbian
C++), and in the application reference here: Streaming and here: Stores. There is a worked
example showing you how to save your application’s data using the streaming mechanisms here:
GUI/Engine Split

Symbian uses the concrete 8-bit variant Descriptors (Fundamentals of Symbian C++)|descriptors
as buffers for non-string data: TBufC8, TBuf8, TPtrC8, TPtr8, RBuf8, HBufC8. For example, they
used as the send and receive buffers to the RSocket APIs. We can also use a stream interfaced
sockets to serialize our objects directly to/from a socket.

Package buffers (aligned 8-bit descriptors) are used for the purpose of transferring objects be-
tween threads and processes. These buffers allow developers to package any value type (a T
class) as a descriptor. Note that this approach is acceptable because we don’t need a platform
independent representation to communicate with another thread.

There are three package buffer variants: TPckgBuf takes a copy of the object data, while TPckgC
and TPckg simply point to existing const and non-const objects (respectively).

7.2.2. Qt Binary Data

Qt’s QIODevice is an abstraction for a “device” capable of reading or writing blocks of data. This
has a number of subclasses (including QTcpSocket, QUdpSocket, QBuffer, QFile, QLocalSocket,
QNetworkReply, and QProcess) that are used for writing to files, processes, sockets, buffers, etc.

35
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

Qt also provides higher level stream classes QDataStream and QTextStream, that can be used to
stream binary and text data (respectively) to any QIODevice. The stream classes serialize data in
a platform independent (but Qt-version specific) manner. Classes that can serialize data overload
the >> and << operators with variants that take a QDataStream argument (or have an associated
method).

In-memory 8-bit text and binary data are usually stored in a QByteArray. This is an array of bytes
which has a very similar API to the QString class. Note that QBuffer class provides a QIODevice
interface for a QByteArray.

The Input/Output and binary classes are well documented in the class documentation. There is
also a very good overview of QByteArray in chapter 11 and Input/Output in chapter 12 of C++
GUI Programming with Qt 4, Second Edition, Jasmin Blanchette and Mark Summerfield, Prentice
Hall (2006) (the first edition is available free online here).

7.2.3. Converting Between Qt and Symbian Binary Data

Symbian and Qt’s classes and approaches for serializing data are fundamentally the same; data
is externalized to a stream in a platform-independent form. In Symbian this stream might be a
file or memory buffer, while in Qt the stream is associated with a QIODevice that is a file, buffer,
or other device-like object.

The main difference between the implementations is that Symbian C++ has a very small set of
platform-independent types (that it has maintained consistently across versions), while Qt has a
richer set of types for which the implementation has varied across versions.

The good news is that there is unlikely to be a reason to convert between the Qt and Symbian
serialization mechanisms. If you do need to transfer data that is serialized in one or the other de-
velopment environments then first import and then convert appropriately (using casts, or some
more complicated method).

If you’re working with raw data then converting between a QByteArray and a descriptor is much
the same as converting between a QString and a descriptor; for example:

TPtrC8 myDataDescriptor( reinterpret_cast<const TText8*> (myQtData.constData()),


myQtData.size());
// Take a copy of the data
HBufC8* buffer = HBufC8::New(myDescriptor.Length());

Q_CHECK_PTR(buffer);
buffer->Des().Copy(myDataDescriptor);

Remember that the data returned by QByteArray::constData() and data() belongs to the QByteAr-
ray, so you may need to take a copy as shown above.

To convert the other way you can use the QByteArray to create a deep copy of the data in the de-
scriptor, or QByteArray::fromRawData() if you know the lifetime of your QByteArray will exceed
that of its users in Symbian C++ code:

QByteArray myQtArray(reinterpret_cast<const char*>(theDescriptor.Ptr()),


theDescriptor.Length());

36
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

7.3. Geometry: Points, Sizes, Rectangles

Qt and Symbian C++ define similar geometry types

TPoint and QPoint are effectively the same. Both store a two-dimensional point in Cartesian
co-ordinates using x and y co-ordinate values (of type TInt (typedefd to signed int) and int
respectively).

TSize and QSize are also the effectively the same. Both store the width and height value, again
using a TInt and int respectively. Converting is straightforward:

TSize myTSize = TSize(myQSize .width(), myQSize .height()); //to TSize

TRect and QRect both define a rectangular area with a particular position within a co-ordinate
system. Both store the top left co-ordinate of the rectangle. TRect stores a TSize for the rectan-
gle, while QRect stores separate values for the width and height. Converting is straightforward:

QRect myQRect = QRect(myTRect.iTl.iX, myTRect.iTl.iY, myTRect.Width(), myTRect.Height());


// to QRect
TRect myTRect = TRect(TPoint(myQRect.left(), myQRect.top()),
TSize(myQRect.width(), myQRect.height())); // to TRect

Note: qcore symbian p.h defines the following inline functions which do these conversions.

• static inline QSize qt TSize2QSize(const TSize& ts)

• static inline TSize qt QSize2TSize(const QSize& qs)

• static inline QRect qt TRect2QRect(const TRect& tr)

• static inline TRect qt QRect2TRect(const QRect& qr)

Since this file is not part of the public API, you may choose to copy the code into your own project.)

Note that QRect also provides mechanisms for getting the bottom and right co-ordinates of the
rectangle. These should not be used for translating to TRects, as for historical reasons they de-
viate from the “true” bottom right of the rectangle. See the QRect documentation for more
information.

7.4. Images

7.4.1. Symbian Images

Symbian’s main class for representing a bitmap to be displayed is CFbsBitmap, a bitmap man-
aged by the font and bitmap server. Its API provides methods to load (and compress) bitmaps
from Symbian’s native image file format (the multibitmap (MBM) file), create and access hard-
ware owned bitmaps, access bitmap data, resize bitmaps, etc. It also provides a mechanism to
duplicate the current bitmap handle so that the bitmap can be shared with other threads. The

37
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

derived CWsBitmap represents a bitmap to which the window server already has a handle, and
provides slightly faster operations than those relying on a CFbsBitmap.

Most of the image handling functionality is defined in the Image Converter Library (ICL). The
ICL provides classes to load image files into a CFbsBitmap. The library can decode a number of
common desktop and mobile formats, and can also encode a subset of these. A comparison of
formats provided by Qt and the Symbian platform is given below.

The ICL also provides classes to perform common image manipulation, including rotation (in 900
steps), flipping, scaling (keeping aspect ratio and stretching). Different classes allow scaling of
CFbsBitmap objects, or on still images held in files or descriptors.

The ICL APIs are asynchronous; clients are signaled on completion of a requested operation. This
allows the user interface to remain responsive while performing computationally expensive op-
erations.

7.4.2. Qt Images

Qt provides four main classes for handling image data: QImage, QPixmap, QBitmap and QPicture.

QImage is a hardware-independent image representation that allows direct pixel access and
manipulation – this is the closest equivalent to a Symbian CFbsBitmap. In addition to accessing
pixel information, the class can directly load a number of common image formats from file or a
buffer, representing them using one of a number of formats – ARGB, RGB32, Mono etc). QImage
provides powerful image manipulation functionality, including rotation, scaling, mirroring, mask
creation, and complex transformations within the 2D co-ordinate system).

QPixmap is designed and optimized for showing images on screen. It does not provide direct
access to the image data, but like QImage, can load common file formats, and do much the
same image manipulation. QPixmap::toImage() and QImage::fromImage() can be used to convert
between the two image classes.

QBitmap is a convenience class that inherits QPixmap, ensuring a depth of 1. Finally, QPicture
is a paint device that records and replays QPainter commands.

Qt supplies a number of additional image classes, including: QImageReader (provides finer grained
control when loading images from files than QImage or QPixmap), and QIcon that provides
scaleable icons you can use in Qt widgets to represent a particular action.

7.4.3. Inter-Working Symbian and Qt Image Formats

If you’re working with images stored in the file system using common file formats, you should
not need to use Symbian’s image handling code directly. QImage and QPixmap work across
platforms, and have a more convenient API for loading and manipulating file-based image data.

You may choose to use Symbian’s APIs if you’re working in file formats that Qt does not support.
A list of the default supported formats on each environment is given below – note that both
architectures allow the supported image formats to be extended with plugins, so this may not
be canonical on a particular platform.

38
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

Format Description Qt support Symbian support


BMP Windows Bitmap Read/write Read/write
EXIF Exchangable Image File format - Read/write
Read (Single and multi frame,
Graphic Interchange Format
GIF Read bitmap mask support)/write
(optional)
(Single frame, no transparency)
ICO Icon - Read (Single and multi frame)
Joint Photographic Experts
JPG/JPEG Read/write Read/write
Group
- Read (Single and multi
MBM Symbian Multi Bitmap
frame)/write (Single frame)
Multiple Image Network
MNG Read Read/write
Graphic
Read (Bitmap mask sup-
PNG Portable Network Graphics Read/write
port)/write (No transparency)
PBM Portable Bitmap Read -
PGM Portable Graymap Read -
PPM Portable Pixmap Read/write -
SMS OTA SMS Over The Air - Read
Read (LittleEndian and BigEn-
TIFF Tagged Image File Format Read/write
dian sub-type support)
WBMP Wireless Bitmap - Read
- Read (Std, apm and clp sub-type
WMF Windows Meta File
support)
XBM X11 Bitmap Read/write -
XPM X11 Pixmap Read/write -

Finally, you may need to use Symbian image classes if that is the format in which the image
becomes available to the platform. For example, the image from a camera becomes available to
the platform in a CFbsBitmap.

Qt provides Platform Specific Methods for converting CFbsBitmap to QPixmap (from which you
can convert to a QImage if necessary).

CFbsBitmap *QPixmap::toSymbianCFbsBitmap() const;


static QPixmap QPixmap::fromSymbianCFbsBitmap(CFbsBitmap *bitmap);

Note: If you need to get the image format (QImage::Format) based on the current Symbian dis-
play mode (TDisplayMode) there is a private function qt TDisplayMode2Format defined in the
Qt 4.6.0 version of q s60 p.h that you can copy into your own project. Note that, as this method
is private, it is subject to change.

39
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

7.5. Containers/Lists

7.5.1. Symbian Containers

Symbian C++ provides a large number of array classes, which should be used in preference to
standard C/C++ arrays because of the protection they provide against memory overruns etc. The
arrays behave much like standard C++ arrays (stl::vector<>); they are indexed by an integer,
elements can be of any type (or pointer to a type), the array does its internal reallocation when
adding or deleting.

The topic Arrays (Fundamentals of Symbian C++) on the Symbian Developer Wiki gives a good
overview of the different array classes. There are four main categories:

• CArrayX (CArrayFixFlat, CArrayVarFlat, CArrayPakFlat, CArrayPtrFlat, CArrayFixSeg, CAr-


rayVarSeg, CArrayPtrSeg)
• RArrays (RArray, RPointerArray)
• Fixed Array (TFixedArray)
• Descriptor Arrays (CDesC16ArrayFlat, CDesC8ArraySeg, CDesC8ArrayFlat, CDesC8ArraySeg,
CPtrC8Array, CPtrC16Array)

The array naming convention uses Fix where the elements of the array all have the same length
and are copied directly into the array buffer (for example, TPoint, TRect) and Var where the
elements of the array are pointers to objects of variable lengths contained elsewhere on the
heap (such as HBufC* or TAny*). Pak (for “packed” array) is used where the elements of the array
are of variable length but are copied into the array buffer with each preceded by its length (for
example, T class objects of variable length). Ptr is used where the elements of the array are
pointers to CBase-derived objects

In general use the Descriptor arrays for storing lists of strings as they have convenient methods
for sorting and finding matching strings. Otherwise use the RArray classes in preference to the
CArrayX. The exception to this rule is when the array is likely resized often – in which case you
might use one of the segmented variants.

Symbian provides a number of templated associative arrays:

• RHashMap – associative array with key type K and value type V, using a probe-sequence
hash table. Both the key and value objects are copied into the table when they are added. A
bitwise binary copy is used here, so neither of the types K and V may implement a nontrivial
copy constructor.
• RPtrHashSet- unordered extensional set of objects of type T using a probe-sequence hash
table. The objects are not copied into the set when they are added; rather the set stores
pointers to the contained objects.
• RPtrHashMap. an associative array with key type K and value type V, using a probe-sequence
hash table. Neither the key nor value objects are copied into the table when they are
added – only pointers are stored.

In addition to the Symbian C++ array classes, you can also use STL containers provided by Open
C and Open C++.

40
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

7.5.2. Qt Containers

Qt allows you to use STL containers, or to use its own general purpose template-based container
classes. The Qt classes are designed to be lighter, safer and easier to use than STL classes. They
are implicitly shared, reentrant, and thread-safe in situations where they are used as read-only
containers by all threads used to access them. Lastly, they are available on all Qt platforms (STL
is not available on Qt Embedded).

Qt provides both sequential containers (QList, QLinkedList, QVector, QStack, and QQueue), and
associative containers (QMap, QMultiMap, QHash, QMultiHash, and QSet). There are also special-
ist classes like QCache and QContiguousCache that provide efficient hash-lookup of objects in a
limited cache storage, and non-template specialisations like QStringList (inherits from QList<QString>)
that make it easier to work with lists of strings.

The containers can be traversed using either java-style or STL-style iterators, or using the conve-
nient foreach keyword. Qt provides a set of Generic Algorithms that you can use to sort, find, fill,
count, delete items in the container (on container types that have an STL iterator).

The template classes store items of a specified type T. The value type T can be a basic type, a
pointer type, a class that has a default constructor, a copy constructor and an assignment oper-
ator, or a container that meets the same criteria as a class.

The Qt container classes are well documented in the Qt reference: Qt container classes. There
is also an excellent discussion in C++ GUI Programming with Qt 4, Second Edition, Jasmin
Blanchette and Mark Summerfield, Prentice Hall (2006) (the first edition is available free online
here).

7.5.3. Converting Between Qt and Symbian Containers

There is no need to convert container/lists if you’re using the standard STL containers, as these
are supported by both Qt and the Symbian platform.

If you do need to convert between Qt and Symbian arrays, this usually a matter of iterating
through one container, converting each contained object into the correct type for the environ-
ment (e.g. QString vs descriptor), and then adding it to the new container.

When converting to Qt, QList is the best “general use” resizable container template if you have
less than 1000 items (its implemented as an array-list but provides very fast prepends and ap-
pends). If you’re working with lists of strings, use QStringList instead. When converting to Sym-
bian C++, use a descriptor array for strings, or RArray for other objects that are greater than 4
bytes in size (unless you are expecting a lot of array resizing).

The following fragment shows conversion of a set of integers from an RArray to a QList:

QList<int> integerList;
...
for (int i = 0; i < count; i++) {
integerList.append(intArrayToList[i]);
}

This fragment shows a QList of integers being imported into an RArray, using foreach to iterate
the QList. Note that a TInt is typdef of signed int:

41
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

RArray<TInt> listToIntArray;
...
foreach (int integerItem, integerList) {
listToIntArray.Append(integerItem);
}

Converting lists of strings uses exactly the same approach, except that you use a QStringList
rather than a QList, a descriptor array instead of an RArray, and you need to convert the con-
tained string to the correct format for the environment, as discussed in the Strings section above.

The following code shows how CDesCArrayFlat is converted to QStringList:

...
QStringList qlistOfStrings;
for (int i = 0; i < count; i++) {
qlistOfStrings.append(QString::fromUtf16(
arrayToStringList->MdcaPoint(i).Ptr(),
arrayToStringList->MdcaPoint(i).Length()));
}

42
Using Qt and Symbian C++ Together © 2009 Nokia Corporation and/or its subsidiary(-ies)

8. Summary

This whitepaper describes techniques and best practices for using Symbian C++ code in Qt appli-
cations, using the PIMPL pattern.

In addition, it describes how to write code that safely mixes the two environment’s exception
handling mechanisms, coding styles and idioms, strings, geometry, containers, images, and data.
Each section gives a high level overview of how a particular task is done in Symbian C++ and Qt,
and how the idioms are mixed – along with links to key documents explaining the approach in
more detail.

The accompanying example code delivers a Qt-style API (and dialog) for discovering remote Blue-
tooth devices. This uses the PIMPL pattern to obtain the information from the underlying plat-
form, and demonstrates how to safely mix the exception handling mechanisms, coding styles
and strings.

43
Nokia, the Nokia logo, Qt, and the Qt logo are trademarks of Nokia Corporation and/or its subsidiary(-ies)
in Finland and other countries. Additional company and product names are the property of their respective
owners and may be trademarks or registered trademarks of the individual companies and are respectfully
acknowledged. For its Qt products, Nokia operates a policy of continuous development. Therefore, we
reserve the right to make changes and improvements to any of the products described herein without
prior notice. All information contained herein is based upon the best information available at the time of
publication. No warranty, express or implied, is made about the accuracy and/or quality of the information
provided herein. Under no circumstances shall Nokia Corporation be responsible for any loss of data or
income or any direct, special, incidental, consequential or indirect damages whatsoever.

Copyright © 2009 Nokia Corporation and/or its subsidiary(-ies).

This document is licensed under the Creative Commons Attribution-Share Alike 2.5 license.
For more information, see http://creativecommons.org/licenses/by-sa/2.5/legalcode for the full terms
of the license.

Das könnte Ihnen auch gefallen