Namespaces

Variants
Actions

Please note that as of October 24, 2014, the Nokia Developer Wiki will no longer be accepting user contributions, including new entries, edits and comments, as we begin transitioning to our new home, in the Windows Phone Development Wiki. We plan to move over the majority of the existing entries over the next few weeks. Thanks for all your past and future contributions.

Using Qt and Symbian C++ Together/ja

From Wiki
Jump to: navigation, search
Article Metadata
Code ExampleTested with
Devices(s): Nokia 5800, Nokia N95
Compatibility
Platform(s): S60 3rd Edition FP1, Symbian^1
Article
Translated:
By unknown
Last edited: hamishwillee (30 May 2013)

この記事は、PIMPL のパターンを使って、Qt と Symbian C++ コードを綺麗に分ける方法について説明します。

そして、二つの環境の例外、コーディング スタイルとイディオム、文字列、配列、コンテナ、イメージおよびデータを処理する手順について説明します。各セクションは、Symbian C++ と Qt で特定のタスクが実行される方法と、イディオムを混在させる方法の、高度な概要について説明します。 更に、主なドキュメントのリンクで、より詳しい説明へのアプローチを説明します。

付属している、サンプル コード (File:Qtbluetoothdiscoveryexample.zip) は、リモートの Bluetooth デバイスを発見するための、Qt スタイルの API (それと、ダイアログ)を提供します。これは、プラットフォームから情報を取得し、例外処理の仕組み、コーディング スタイルおよび文字列を安全に混在させる方法を説明するために、PIMPL パターンを使用しています。


Contents

前書き

Symbian platform は、モバイル デバイス用のオープン ソースのソフトウェア プラットフォームです。Symbian プラットフォームの初期のバージョン(S60 および Symbian OS として知られています)は、何億台ものデバイスに採用されています。そして、世界中の 100 社以上の通信事業者で利用可能です。

図 1: Symbian プラットフォーム上の Qt
Qt は、クロス プラットフォームのアプリケーションと UI フレームワークです。そして、開発者は、デスクトップ、モバイルおよび組み込みのオペレーティング システムに、ソース コードを書き換える事なく配置する事ができるような、アプリケーションを作成する事ができます。Qt 4.6 から、Qt は、ビルド ターゲットに Symbian プラットフォームを含みました。Qt アプリケーションは、Symbian プラットフォームのデバイスと、S60 第3版 FP1 から初期の S60 デバイス上で動作します。図 1 で説明しているように、Symbian プラットフォーム上の Qt は、ネイティブの Symbian C++ プラットフォーム API と、スタンダード C/C++ 互換レイヤー (Category:Open C/C++) の両方を重ね合わせた上で動作しています。

Qt は、高機能な API のセットと、開発ツールを持っています。しかし、一部の開発者は、一般的な Qt や 標準の C++ API のどちらも提供していない、プラットフォーム レベルの機能にアクセスしていく必要があります。重要なモバイルの機能にアクセスする API (カメラ、Bluetooth、接触部等のような部分)は、まだ利用可能でないので、モバイル デバイスをターゲットにするときは、特にこれらの事が該当します。新しい Qt API は、モバイルのユース ケースに対応し始めました。しかし、開発者の中には、常にネイティブのオペレーティング システムの機能にアクセスする必要があったり、アクセスを望む人もいるかもしれません。

Qt が要求された API を提供していない場合、プラットフォームの特定の機能にアクセスするお勧めの方法は、一般的な クロス プラットフォームのラッパー API を作成し、プライベートなプラットフォームに固有な実装を行う事です。この方法は、他のプラットフォームに、アプリケーションを移植するのが簡単です。これについては、プラットフォーム特有の実装 セクションで説明しています。

デバイス固有な機能のアクセスは、電話に固有の API を呼び出すための、一般的で標準な C++ API およびイディオムを、単純に使用している訳ではありません。Symbian のネイティブなプログラミング言語は、Symbian C++ です。特にデバイスが制約するリソースを取り扱うニーズに対応した、C++ の改良型となっています。Symbian C++ は、堅牢性を高め、メモリーに効率的なコードで、多少ユーザビリティーを犠牲にしている、プログラミング イディオムとフレームワークを使用します。Symbian C++ は、独自の例外処理の仕組みを持っており、作成された時点で、使用可能でなかった(もしくは、処理が重すぎると見なされた)標準の C および C++ ライブラリー は省いています。

異なる C++ のコードは、混在させる事が可能です。しかし、これには注意が必要で、Symbian C++ を多少理解しておく必要があります。この記事では、異なる例外処理の仕組み、文字列、配列、コンテナ、イメージ、データ および、マルチタスクの仕組み等が、内部でどう処理されているか説明します。各セクションは、(主要なリファレンスへのリンクのある)各プラットフォームで必要な事の簡潔な概要を説明しています。そして、これらを混在させる例と詳しい説明に続きます。Qt と Symbian C++ 両方の開発者は、既存の知識の活用と、どこでより多く情報が取得できるか知っておく必要があります。

クロス プラットフォームの互換性の仕組みについて詳しく説明する前に、主要なポイントのいくつかを解説するのに適切な、簡潔なサンプル コードの概要を、次のセクションで説明します。

Bluetooth を発見する例

概要

サンプル コードは、BluetoothLibrary.dll と、それを利用しているテスト アプリケーションの testbluetoothdiscovery.exe を説明します。

BluetoothLibrary.dll は、近くの Bluetooth デバイスを見つけるための Qt API (BluetoothDiscovery) をエクスポートします。BluetoothLibrary.dll は、プライベートな Symbian C++ の実装と、他のプラットフォーム用のスタブを持っています。BluetoothLibrary.dll は、ユーザーが検索可能で、一つ以上のデバイスを選択する事が可能な、Qt の便利なダイアログを出力します。

テスト アプリケーションは、開始すると空のスクリーンを表示する、GUI メインの Window アプリケーションです。シングルとマルチ選択のダイアログを起動するための、ソフトキーのメニュー オプションを持っています。

以下の図は、Nokia 5800 上で、シングルとマルチデバイスを選択するダイアログを表示しています。そして、表は主要なコンポーネントとの関係を表しています。

API

図 5: メイン クラス

BluetoothDiscovery API は、近くのデバイスの検索を開始したり停止するスロットを提供します。そして、検索が開始または停止した時、新しいデバイスが見つかった時、そして、エラーが起きた時に、接続したクライアントに通知するシグナルを提供します。

検索を開始すると、それ以上デバイスが無いと判断するまで、実行されます。その時に、検索が停止したシグナルを発行します。検索は、エラーのイベントでも停止し、定義されたエラー コード列挙型のシグナルをローカルでクライアントに発行します。

ダイアログの QBluetoothRemoteDeviceDialog クラスは、ダイアログを実行し、ユーザーの選択を返す、2 つの static メソッドがあります。最初のメソッドは、一つの選択を返し、二番目のメソッドは、複数デバイスの選択を返します。

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

ダイアログ クラスだけでなく、多くの Qt に関連したクラスは、次のクラスで作成されています。QBluetoothAddress は、デバイスのアドレス用です。QBluetoothRemoteDevice は、リモート デバイスを表します。QBluetooth は、利用可能なデバイスのサービス クラスと動作を記述した、様々な列挙型です。

パブリックな API の簡略化した表は、 図 5 で説明しています。これは、QBluetoothAddress または QBluetoothRemoteDevice クラスを表示していない事に注意してください。

サンプルのダイアログは拡張する事が可能です。表示された(デバイス タイプ、サービス クラス または、SDP プロフィルに基づいた)デバイスまたは、検索に使用するローカルの Bluetooth デバイスに関する詳細情報のフィルタリングは許可していません。コンストラクターを通じた、Windows フラグは無視します。そして、デバイスのタイプを示すアイコンは表示しません。

サンプルのビルドと実行

このサンプルをビルドする前に、Using Qt with Standalone SDKs で説明されたように、開発環境とデバイスを設定しておく必要があります。 (で説明された開発環境の設定は、ハイブリッドの Qt アプリケーションの作成には適していない事に注意してください。)

IDE にサンプルを出力する最も簡単な方法は、PRO ファイルを通した方法です。\QtBluetoothDiscoveryExample\qtbluetoothdiscoveryexample.pro は、サンプルとテスト コードの PRO ファイルと、要求されたビルド 方法を特定します。サンプルは、Symbian プラットフォーム(エミュレーターと携帯デバイス)と Windows の両方でビルドしています。しかし、Bluetooth の実装は提供していないので、Windows 上のデバイスを検索する事はできません。

サンプル DLL と テストの exe は、一般的な方法で、テスト コードのインストール ファイル (testbluetoothdiscovery.sisx) を利用して、デバイスに配置されます。実行ファイルは、Applications フォルダーから testbluetoothdiscovery アイコンを選択する事で、Symbian プラットフォーム上で実行します。

ファイル リスト

パッケージは、次のディレクトリ構造となっています。(\qtbluetoothdiscoveryexample\ フォルダ配下)

フォルダ ファイル 説明
BluetoothLibrary\ bluetoothdiscovery (.cpp/.h) BluetoothDiscovery public API ヘッダーとソース
bluetoothdiscovery_stub (.cpp/.h) Symbian プラットフォームをターゲットとしていない、Private 実装(スタブ)のヘッダーとソースファイル (BluetoothDiscoveryPrivate)
bluetoothdiscovery_symbian (.cpp/.h) Private Symbian プラットフォームの実装ヘッダーとソースファイル (BluetoothDiscoveryPrivate)
BluetoothLibrary.pro BluetoothLibrary.dll 用のプロジェクト ファイル
globalbluetooth.h DLL からインポート/エクスポートするための、エクスポートの定義に使用される、グローバルな #define
qbluetooth.h (他のクラスにより使用されるパブリックな計算を含んだ) QBluetooth ヘッダー ファイル
qbluetoothaddress (.cpp/.h) QBluetoothAddress パブリック ヘッダーとソース ファイル
qbluetoothaddressdata.cpp QBluetoothAddress (暗黙の共有用の)データー実装
qbluetoothremotedevice (.cpp/.h) QBluetoothRemoteDevice パブリック ヘッダーとソース ファイル
QBluetoothRemoteDeviceDialog.cpp QBluetoothRemoteDeviceDialog パブリック ヘッダーとソース ファイル
QBluetoothRemoteDeviceDialog.ui QBluetoothRemoteDeviceDialog Qt デザイナーのファイル
eabi\ bluetoothlibraryu.def オリジナルの定義を凍結
testbluetoothdiscovery\ main.cpp テスト アプリケーションのメインの導入部
testbluetoothdiscovery (.cpp/.h) パブリックなソースとヘッダーファイルのテスト コード
testbluetoothdiscovery.pro プロジェクト ファイルのテスト コード
testbluetoothdiscovery.ui Qt デザイナー ファイルのテスト コード

既知の問題

このサンプルは、いくつかの既知の問題があります。

  • これを書いている時点では、標準のプログレス バーは、アニメーションしません。(Qt のバグ)
  • N95 では、デバイスのリストは、ダイアログが表示された時に、フォーカスがありません。

開発環境

ハイブリッドの Qt/Symbian C++ アプリケーションは、一般的に Carbide.c++ IDE と Symbian アプリケーション開発の SDK に被せられた Qt 開発環境で作成されます。この環境を設定する方法の手順は、スタンドアローン SDK での Qt の利用 の中の記事で説明されており、Qt Carbide.c++ IDE クイック スタート に続いています。Qt Creator IDE も利用可能ですが、より "Symbian を意図している" ので、Carbide.c++ がお勧めです。

スタンドアローン SDK は、アクセスに必要な Qt と Symbian C++ の機能両方のサポートが最も早かったです。(初期の SDK を使用する事は、今後のデバイスのコードと互換性を確実にします。)Qt クイック スタート で説明している、Manufacturer SDKs#Nokia Qt SDK は、Symbian SDK を縮小したバージョンで、ハイブリッドの開発には適していない事に注意してください。そして、シミュレーターで、Symbian C++ コードのデバッグを行う事ができません。

プラットフォーム特有の実装

Qt は リッチな API のセットを提供しますが、これを書いている時点では、Bluetooth または IrDA を利用した通信、カメラ、ロケーション、 加速度、バイオメトリックまたは、他のセンサーのアクセス、SMS または MMS メッセージの送信、またはカレンダーや連絡先のようなユーザー データーの読み書きのための Qt API は、Symbian 開発用にまだありません。Qt 開発フレームワークは、Qt Mobility プロジェクトを通して、多くの クロス プラットフォームの API が提供されています。この機能を使用したい場合、Qt と Symbian C++ の両方を使用する必要があります。これに該当する他の機能も、恐らく将来出る予定です。

Qt が必要な API を提供していない場合、プラットフォームに特有な実装のアクセスを提供するお勧めな方法は、分割したプライベートのプラットフォームに特有な実装をしている、パブリックな Qt スタイルの API を作成する事です。このアプローチは、Qt プログラムのイディオムを使用する事で、コードの記述をかなり簡単にし、他のプラットフォームへの移植を簡単にします。実際に、開発者が各プラットフォーム上のプログラミング イディオム、API、インストールの仕組み、ビルド システム、および、他の制限を気にする必要が無いように、オペレーティング システムに依存する物を取り省くために Qt が使われる事と同じアプローチです。

コードを構造的にするデザイン パターンは沢山あります。続いてのセクションでは、 Pimpl (ポインタの実装)イディオムについて説明します。(プライベートな)プラットフォーム特有の実装は、パブリックな API のポインタの陰に隠れています。このテクニックは、様々な/代わりの多くの名前を持っており、"ハンドル ボディ"、 "コンパイラー ファイヤーウォール"、 "ブリッジ"、 "不透明ポインタ"、そして "チェシャ猫" と言われています。

他のパターン/テクニックを使用しても良いです。例えば、QPixmap API の大部分は、共通のソース ファイルに実装されています。QPixmap::grabWindow() は、プラットフォーム特有のソース ファイルに実装されています。このアプローチは、プラットフォーム特有の実装が、Qt API に 影響を与えない 限り許容する事ができます。

さらに、プラットフォーム特有の方法 を通して、いくつかのプラットフォーム特有な詳細を示す事は、役に立つ場合もあります。

実装パターンのポインタ

概要

図 6: PIMPL クラスの概要
PIMPL は、ハンドル ボディ パターン の一種で、プライベート実装クラスのポインタを含むパブリックな API です。プライベートな実装のポインタは、前に ( #include の代わりに)宣言されています。それ故に、パブリック API のクライアントは不明瞭です。Pimpl は、Pimpls - Beauty Marks You Can Depend On および The Joy of Pimpls (or, More About the Compiler-Firewall Idiom) で有名になりました。

プライベートなクラスが、パブリックなクラスのメソッドを呼ぶ必要がある場合、そのコンストラクタに、パブリックなクラスのポインタ/参照 を渡します。(例えば、シグナルを emit するため)プライベートなメソッドを呼び出す必要がある場合、 friend クラスを作成します。図 6 のクラス図はリレーションを示し、プライベートな実装の QMyClassPrivate を持つ、パブリック クラスの QMyClass を表します。

パブリック クラスの実装は、プライベートな実装を構成(そして破棄)します。それを行うには、 QMyClassPrivate の定義を知る必要があり、プラットフォーム ビルドの定義に基ずく、現在のプラットフォームのヘッダーを、#include する必要があります。 - 例、(qglobal.h からの)Q_OS_SYMBIAN

//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
...

プライベートのクラス ヘッダーとソース ファイルは、プロジェクト ファイル (.PRO) の プラットフォーム特有なセクションに、適切にリスト化される必要があります。 - 以下の、 プロジェクト ファイル を参照してください。

プライベート クラスの定義と実装は、ほぼ完全に開発者に委ねられています。唯一の制限は、全てのプラットフォームは、必ず 同じ名前を使わなくてはなりません。 (そうでなければ、各プラットフォーム用のパブリック ヘッダー ファイルに、definitions/friend 宣言を前もってインクルードしておく必要があります。)一般的に、プライベートなクラスは同じ機能を持っています。これで、プラットフォーム特定の振る舞い無しに、パブリック クラスの実装が可能となります。

// Implementation of a public class slot
void QMyClass::mySlots()
{
d_ptr->mySlots();
}
プライベート クラスは、どんな派生を持つことも可能です。

Note.pngNote: QObject が派生した、プライベート クラスが一般的ですが、これは全く 必要 ありません。(実装した物に、シグナルまたはスロットを持たせたい場合は、役に立ちます。)Qt のコードは、プライベートの実装用に、QObjectPrivate を頻繁に使用する事に注意してください。これはパブリック API の一部ではないので、サード パーティは避けてください。

Symbian のプライベートな実装用に、2 つの基本的なクラス デザインがあり、図 7 と 8 にそれぞれ示します。最初のアプローチの、プライベートなクラスの実装 CBase から派生した、Symbian クラスです。二つ目のアプローチの、プライベートなクラスは、プライベートなクラスにコールバックする、一つ以上の Symbian クラスを 使用 しています。

最初の仕組みの方が、開発者の労力が少なくなります。なぜなら、Symbian クラスを生成/呼び出したり、(コールバック クラスの)完了の通知を取得するために、余計な追加クラスの必要が無いからです。しかし、このアプローチは、(以下の 理由で) オブジェクトの構造がもっと複雑になっています。そして、Qt と Symbian C++ のコードを概念的に分ける事を、より複雑にしています。次に続くセクションでの説明している、サンプル コードは、このアプローチを使用しています。

大抵、二番目の方法を使う方が良いです。これは、二つのプログラムのイディオムを綺麗に分けています。プライベート クラスの実装が、多くのSymbian クラスを必要とする場合にも、良い解決策となります。コールバック API セクションは、二番目の方法を、さらに詳しく説明しています。

パブリック API の Bluetooth サンプル

図 9: BluetoothDiscovery クラス図
パブリックな BluetoothDiscovery クラスを以下に示します。friend プライベート実装クラスの BluetoothDiscoveryPrivate のポインタを持っています。

図 9) のクラス図は、Symbian の実装と、他のプラットフォーム用のスタブ実装両方の、プライベートなクラスを示しています。プライベート クラスは同じ名前を共有し、パブリック クラスのスロットとメソッドのスーパーセットを持っています。それらは、ツールチェーンにより実装されているため、シグナルを再実装しません。その代わりに、プライベート クラスは、それらの構造上のポインタを通して、パブリック クラスの emit を呼び出す事ができます。


//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;
};

プロジェクト ファイル

プラットフォーム特有のソース ファイルのコンパイルは、プロジェクト ファイルでコントロールされています。

パブリック クラスのヘッダーとソース ファイルは、一般的なセクションで明記されています。プラットフォーム特有のヘッダー/ソースは、以下に示すように、プラットフォーム特有のブロックで明記されています。

...
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
}

プラットフォームに特有なブロックは、リンクが必要なコードの、プラットフォーム ライブラリをリスト化します。 このケースでは、esock.dll および bluetooth.dll です。

Symbian の実装は、実行ファイルが必要としている機能を特定する必要があります。(プラットフォームのセキュリティ(Symbian C++ の基礎) に詳しい事が書かれています。)このサンプルでは、自己署名のアプリケーションと仮定した、これらの機能を特定しています。

パブリック クラスの実装

パブリック クラスの実装は、プライベート クラスを構成するために、プライベート クラスのヘッダーが必要です。条件付きで、現在のプラットフォームを基にしたヘッダーを含んでいます。

//bluetoothdiscovery.cpp
...
#ifdef Q_OS_SYMBIAN
#include "bluetoothdiscovery_symbian_p.h" //Symbian definition of BluetoothDiscoveryPrivate
#else
#include "bluetoothdiscovery_stub_p.h" //Stub for all other platforms
#endif
構造

パブリック クラスは、以下で示すようなコンストラクタにプライベート実装のインスタンスを生成します。

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
}

プライベート クラス 、Symbian CBase 派生のクラスなので、パブリック クラスの実装では、プラットフォーム特有の構造を使用する事に注意してください。Symbian クラスの実装は、パブリックな API は見る事が出来ないので、問題ありません。しかし、一般的には、プライベートなクラスのプラットフォーム特有の実装と同じ位、お勧めしたいです。

NewL() は、BluetoothDiscoveryPrivate タイプでオブジェクト生成用の、 標準の static な Symbian ファクトリー クラスです。そして、オブジェクトが完全に割り当てられたか、例外イベントで適切にクリーン アップされるかを明確にします。これは、リーブ が可能なので、リーブをスローに転換するため、QT_TRAP_THROWING 内にラップします。例外とエラー処理 セクションは、Symbian と Qt のコード間の、例外の取り扱い方を説明します。

CBase 派生クラスを使用するプライベート クラスを実装する場合、構造は単純で、共通のパブリック クラスの実装を持つことができます。

//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));
}

Symbian オブジェクトを割り当てる、他の方法もあります。どんなメソッドを使う場合でも、以下の重要な事を覚えておいてください。

  1. Symbian C クラスは、スロー しない CBase からの new オーバーロードを持っています。new で構成するのでしたら、ポインタを確認(そして、Null にスロー)するために q_check_ptr を使う必要があります。
  2. コンストラクションが失敗した場合、オブジェクトが適切にクリーン アップされる事を確実にしてください。

最初に使用されるまで、CBase 派生オブジェクトが初期化される必要がない場合、最初の項目が当てはまるかもしれません。このケースでは、new を用いて作成することができます。しかし、ポインタを確認し、NULL だった時スローする必要があります。

常に明確になっている訳では無く、特に Qt とSymbian C++ のイディオムを混在させる時、クリーン アップが適切に行われるかどうかは、二番目の項目が重要となります。以下のコードを参照してください。

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
}

ConstructL() がリーブする時、マクロがスローします。一般的な C++ のルールに従って、BluetoothDiscovery 用のメモリーは解放されますが、デストラクタは呼び出されません。その結果、BluetoothDiscoveryPrivate に部分的に割り当てられたオブジェクトは、リークします。これを動作させるため、d_ptr は、スマート ポインタ ( QScopedPointer)である必要があります。

パブリック クラスは、デストラクタで プライベートの実装を削除する必要があります。

メソッド

プライベート クラスにパブリックな API を複製します。

パブリック メソッドは、 のような、実装のためのポインタ を通して、プライベートなクラス メソッドと同様な物を呼び出します。

// Called to start searching for new devices
void BluetoothDiscovery::startSearch()
{
d_ptr->startSearch();
}

Qt と Symbian C++ 間の 例外処理の仕組み に、境界を作ることはしない事に注意してください。これは、startSearch()リーブ 出来ない実装がある事を知っているからです。このメソッドは、スロー する事ができ、Symbian のコードから呼び出される事はないので、条件に合っています。

プライベートなプラットフォームの実装

クラスの宣言

Symbian プラットフォームの BluetoothDiscoveryPrivate の実装を以下に示します。それは、Symbian の アクティブ オブジェクト です。それは Symbian C++ クラスなので、Symbian コーディング基準 に従う事は、特に重要です。

  • Symbian の仕組みを使って構成してください。
  • 多重継承を使う場合、最初に CBase 派生クラスから派生させてください。そうでなければ、Mixin (interface) クラスから派生させてください。

Warning.pngWarning: 構造に使用される new が決まっていないので、CBaseQObject 両方からは、決して派生させないでください。


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(); //Second phase constructor - connects to socket server and finds protocol
 
//Error translator - converts global errors into local format then emits to parent object
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; //pointer to parent object (from constructor).
};

アクティブなオブジェクトが終了した/エラーが発生した時に、クラスは、Qt クライアントにシグナルを発行するために使われる、その親の BluetoothDiscovery のポインタをもっています。

パブリック クラスの実装の条件付きコードを減らして、プライベート クラスはパブリック クラス API を複製します。さらに、それは、アクティブ オブジェクトの終了とキャンセルを処理するため、CActive の仮想メソッド RunL() と、 DoCancel() を、再実装します。

クラスは、Symbian の Bluetooth.dll の リモート Bluetooth デバイスの名前と ID を取得するために、プライベートなデータ メンバーを持っています。iHostResolver は、リクエストが作成される非同期の機能を含むオブジェクトです。

構造

プライベートな実装 、Symbian クラスなので、前のセクションで説明したように、2 フェーズ コンストラクション イディオムを用いて構成します。

パブリックの static NewL() ファクトリー関数は、オブジェクトを作成し、CleanupStack にそれをプッシュし、リーブする 2 フェーズのコンストラクタを使用して初期化し、スタックを取り出し、それをユーザーに返すために、リーブするコンストラクターを使用します。

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;
}

コンストラクターの一部として、パブリック クラスを処理するプライベート オブジェクトを提供します。このケースではポインタですが、参照になっているはずです。ポインタは、直接メソッドを呼び出すか、シグナルを発行するために使う事ができます。

メソッド

プライベートなクラスのメソッドの実装は、必要な機能次第です。 一般的に、一番大切な気を付ける事は、例外処理 セクションで説明したように、プラットフォームの例外処理システムの内部処理をただしく行う事です。

void ErrorConvertToLocalL(int err); を使用して、Symbian のグローバル エラーを、このコンポーネントのローカル エラーに変換する事に注意してください。

サンプルは、アクティブ オブジェクトからシグナルとスロットへの変換 RunL()DoCancel() メソッド を説明しています。

プラットフォーム特有のメソッド

パブリック API になる可能性がある場所は、メンバーと関数をプラットフォーム特有にするべきではありません。このルールの例外は極めて稀です!

時々、開発者が別の方法で、彼ら独自の方法を用いる必要がある場所では、プラットフォーム特有のヘルパー関数はパブリックで作られます。 例えば、QPixmap は、 CFbsBitmap のやり取りを変換するヘルパー ファンクションを提供します。

同様に、プラットフォーム特有なタイプを明確にし、一般的な typedef に記すためにプラットフォーム ヘッダーを パブリックでインクルードする事が時々必要となります。 QProcess で、 Q_PID が Symbian プラットフォームで、TProcessIdtypedef にする事を参照することができます。

すべてのケースにおいて、特定のプラットフォームのみで見ることができるために、プラットフォーム特有の作りは、定義されるべきです。例えば、QPixmap の宣言は以下の通りです。

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

ファイルとクラスの名前のつけ方

一般的に、パブリック クラスが QMyClass で呼ばれる場合、以下のようになります。

  • プライベート クラスは、QMyClassPrivate となります。
  • パブリック クラスのソースとヘッダー ファイルは、qmyclass.hqmyclass.cpp パブリック クラスの名前を共有します。
  • プライベート クラスのヘッダーとソース ファイル名は、ファイルがプラットフォーム特有の実装でない場合、 _p で終わります。 (例 qmyclass_p.h
  • プラットフォーム特有の実装のヘッダーとソース ファイルは、ファイル名にプラットフォームを含みます。 - 例、 qmyclass_symbian.cpp ( ファイル名が意味を含む時は、 _p は必要ありません)

マルチタスク

マルチタスクは、一度に一つ以上のタスクを実行する機能です。ユーザーの入力にすぐ反応する状態の間、長い処理や、計算的に高度な命令を可能にするため、GUI にとって重要です。

プリエンプティブとコーポレイティブの、二つのタイプのマルチタスクがあります。プリエンプティブ マルチタスク(または、マルチスレッディング)で、オペレーティング システムは、実行のスレッド を、実行中のものに、各々提供します。スレッドは、いつ実行するのか、何回取得するか管理しません。コーポレイティブ マルチタスクで、スケジューラーはどのタスクが次に実行されるか管理します。しかし、現在のタスクのみだと、いつ完了するかを決定します。

プリエンプティブ マルチタスクは、RAM と実行スピードの点で、より重たいです。そして、(スレッドがデッド ロックしないように)共有リソースのアクセスを調整する必要があるので、より難しいです。コーポレイティブ マルチタスクがより簡単です。理由は、リソースへのアクセスはシリアルだからです。しかし、UI が素早く反応するために、各タスクは短時間に動作する必要があります。

マルチタスク オペレーティング システムでは、同じグローバル メモリー スペースを共有するスレッドのセットを参照するために、プロセス を使用します。従って、お互いの変数に直接アクセスする事ができます。一つのアプリケーション内の全てのスレッドは、一般的に同じプロセスで動作します。しかし、プロセスではなくスレッドなので、実行用にスケジューリングされます。

マルチタスク オペレーティング システムは、マルチプロセスの可能性もあります。マルチプロセスは、スレッドが一つ以上のプロセッサー/CPU で動作します。

Symbian プラットフォームのマルチタスク

Symbian プラットフォームは、最新のプリエンプティブ マルチタスク オペレーティング システムです。

アプリケーションは、一つのメイン スレッドで実行している、各々のプロセスに生成されます。カーネルが先に、優先順位に基づき、システムの全てのスレッドをスケジュール化します。2番目のスレッドを生成する事は可能ですが、Symbian はアクティブ オブジェクトを使用するコーポレーティブ マルチタスクのアプリケーションを強く勧めます。

ほとんど全ての Symbian サービスは、(ファイル サーバー、ウィンドウ サーバー、フロント エンド ビットマップ サーバー、ロケーション サーバー等)他のプロセスで動作するサーバー(または、デーモン)により提供されます。これらは、サーバーがリクエストの完了のシグナルを送るために使われる TRequestStatus オブジェクトを参照する非同期な API をエクスポートします。アクティブ オブジェクトは、非同期のリクエストを投稿し、その完了を取り扱うコードを書く、一貫して軽い処理の方法を提供します。

アクティブ オブジェクトは、所有しているか、非同期のサーバー プロバイダーを処理する、CActive から派生します。アクティブ オブジェクトは、コンストラクタ内のアクティブ スケジューラーにオブジェクトを追加する必要があります。 そして、それ自身をアクティブとして設定するメソッドを提供します。最初に(TRequestStatus iStatus メンバーをパスした)非同期のメソッドを呼び出し、そして CActive::SetActive() を呼び出してください。非同期のサービスが完了する時、アクティブ オブジェクトのスレッド セマフォのシグナルを発行し、待ち状態でない事を示すために、オブジェクトのiStatus のステータスを変更します。アクティブ スケジューラーは、サービスの完了を処理するために、実装が必要なオブジェクトの RunL() メソッドを後で呼び出します。これは直ぐ行われない事に注意してください。 - アクティブ オブジェクトはコーポレーティブ マルチタスクなので、スケジューラーは同時に一つだけ、最後の物が完了したときのみ、実行する事ができます。最後に、開発者は非同期のリクエストをキャンセルする、バーチャル DoCancel() を実装し、オブジェクトのデストラクタに Cancel() を呼び出してください。

上記のサマリーは、アクティブ オブジェクトにより可能となる手法のニュアンスです。開発者は、 アクティブ オブジェクト(Symbian C++ の基礎)コーポレーティブ マルチタスク (Symbian C++ デザインのベスト プラクティス) の記事を読んでください。実装されたサービスの使用に興味があるのでしたら、 クライアント サーバー を読む事にも興味を持つかもしれません。

スレッドとプロセスを使う事を好む開発者は、当然それを行う事が可能です。- 時として、これは必要となるかもしれません。Symbian C++ のプロセスとスレッドは、RProcess および RThread API を各々使用する事で、生成と処理を行う事が可能です。Symbian C++ は、ミューテックス (RMutex)、セマフォ (RSemaphore)、クリティカル セクション (RCriticalSection) 等を含む、一般的な同期基本命令を持っています。これらのすべてのクラスは、スレッド、プロセス、および IPC (Symbian C++ の基礎) で説明しています。これらを実装する方法に興味がある開発者は、Symbian OS Internals の本の、第三章を読んでください。(Symbian ファウンデーションの wiki に複製があります。 Symbian OS Internals/03. スレッド、プロセス、および、ライブラリー)(Open C と Open C++ を用いて)スタンダード C++ でプログラムを書く場合、pthreads を通常通りに書くことができます。

同じプロセスのスレッドは、簡単に直接データを共有する事ができます。(シリアルで、共有データにアクセスする事に注意してください。)他のプロセスのスレッドは、Symbian の内部通信プロセスの仕組みを使って、通信をする必要があります。これらは、クライアント サーバー、パブリッシュ サブスクライブ、メッセージ キュー、および RPipe を含んでいます。以下のドキュメントに、これらの仕組みの役に立つ説明が記載されています。

Symbian は、Symbian^3 から、対称型マルチプロセッシング(SMP)のサポートが追加される予定になっています。

Qt を用いたマルチタスク

Qt アプリケーションは、コーポレーティブ および プリエンプティブ のマルチタスク両方を使用しています。

Qt のメイン アプリケーション スレッドは、ユーザーの操作(キー プレス、マウス イベント等)とタイマー、ウィンドウ システム で生成されたイベントを処理する、それ自身のイベント ループを実行します。このイベント ループは、コーポレーティブ マルチタスクの一つの例です。 - イベントは、待ち行列に入れられ、同期して処理されます。イベントで多くの時間が処理された時は、UI が無反応になる可能性があります。

ファイルの書き込みのような、計算的に集中した操作が、多くのステップに分割する事が出来る場合、UI の他のイベントを処理するための、イベント ループの時間を与える操作の間、規則正しい間隔で、QApplication::ProcessEvents() を呼び出す事ができます。このアプローチは、C++ GUI Programming with Qt 4, Second Edition, Jasmin Blanchette and Mark Summerfield, Prentice Hall (2006) の第七章で説明されています。(初版は、オンラインの こちら で、無料で参照する事ができます。)

マルチスレッド(プリエンプティブ マルチタスク)は、より一般的なアプローチです。開発者のサブクラス QThread と、再実装した run() ファンクションは、新しいスレッドで実行します。同期クラスは、リソースの相互に排他アクセスを提供する QMutex、読み込みの無制限アクセスと、書き込みのブロックを提供する QReadWriteLock、多くの特定なリソースにアクセスする事を許可するための QMutex を一般化する QSemaphore、および、ある条件が true になるまでブロックする QWaitCondition があります。QMutexLocker のようなヘルパー クラスもいくつかあり、ミューテックス プログラムと、スタンダード C++ の例外処理を混在させる事を簡単にします。

Thread Support in Qt は、スレッド クラスの良い概要と、Synchronizing ThreadsReentrancy and Thread-SafetyThreads and QObjectsConcurrent Programming、および Thread-Support in Qt Modules のトピックのリンクを提供します。Mandelbrot ExampleSemaphores Example、および Wait Conditions Example に、良い例があります。C++ GUI Programming with Qt 4, Second Edition, Jasmin Blanchette and Mark Summerfield, Prentice Hall (2006) は、第14章に(他のリンクの複製で)マルチスレッドの素晴らしい説明があります。

スレッドは、共有メモリーと上記の同期クラスを用いて、互いに通信を行います。スレッドは、シグナルとスロットを用いて、メインスレッドと通信を行います。しかし、デフォルトでは、シグナルはシングル スレッド内にあるので、シグナルは同期していない事に注意してください。

アプリケーションは、他のプロセスを使用して、マルチタスクを行うこともできます。例えば、他のプロセスを実行するために QProcess を生成し、そのコマンド ラインの引数を設定し、スタート アップ、エラーおよび完了ステータスを見つける事が可能です。

スタンダード C++ のスレッドと、プロセス、および、内部プロセスの通信の仕組みを利用する事も可能です。

アクティブ オブジェクトからシグナルとスロットへの変換

Qt と Symbian C++ を一緒に使う時、非同期 API の Symbian サービスを利用する必要があるかもしれません。これを行う一番良い方法は、アクティブ オブジェクト にリクエストをラップし、Qt の シグナル を使用している Qt コードへのリクエストの完了を通知する事です。

アクティブ オブジェクトのイベントを、シグナルとスロットに変換する事は簡単です。

  • パブリック Qt スタイルの API を作成するために、PIMPL イディオムを使用してください。
  • Symbian プライベート実装クラスとして、 (または、プライベート クラスに所有されているオブジェクトとして) アクティブ オブジェクトを生成してください。
  • アクティブ オブジェクトを開始するファンクションのパブリック クラスに、ファンクション/スロットをマッピングしてください。
  • パブリック実装のポインタを使用して、アクティブ オブジェクトが正常終了した時、シグナルを発行してください。Qt スタイルのオブジェクトのみ、(明確に)シグナルを発行してください。
  • エラーの時、シグナルを発行してください。Qt は、モジュール ローカルのエラーを使うことに注意してください。従って、インターフェースからのグローバル(またはローカル)エラーを、パブリック インターフェース モジュールに定義されたローカル エラーに変える必要があります。

Note.pngNote: 場合によっては、Symbian オブジェクトから、Qt オブジェクトへのゼロ コピーで転送を行う事ができるかもしれません。例えば、非同期のサービスから受けたバッファーのポインタを使う、QString を初期化する事が可能です。いつものように、 C++ では送信しているオブジェクトが、必ず全てのユーザーを超えたライフタイムを持つ事に注意してください。Qt と Symbian C++ により使用される、完全に新しい Qt スタイル オブジェクトを生成するメモリー管理用の異るアプローチが提供されます。

上記の例の BluetoothDiscovery は、アクティブ オブジェクトなので、オブジェクトの構成方法、パブリック クラスをポインタに渡す方法、(startSearch() を通してプライベートの startSearch() を呼び出す事により)オブジェクトを開始する方法は既に説明しています。

アクティブ オブジェクトは、アクティブ オブジェクト(Symbian C++ の基礎) で説明されている事の他は完全に同じなので、残りは、Qt API にシグナルを返す方法だけです。

アクティブ オブジェクトからのシグナル

親に渡すプライベートなポインタ コンストラクションを使用して、エラーまたはパブリック クラスの発行ファンクションの呼び出しにより、シグナルを発行します。発行したオブジェクトは、Qt スタイルのオブジェクトです。発行されたエラーは、現在の Qt コンポーネントでしか使えません。(例えば、グローバルの Symbian エラーより、パブリック ヘッダーの enum として定義されます。 )

発行されたシグナルは、どんなスロットにでも接続できます。これは 2 つの実装を持っています。

  • プリエンプティブ でないアクティブ スケジューラー (RunL) のコンテキスト内で動作するスロットは、短くする必要があります。全てのスロットで使用されるトータルな時間が多い場合は、UI は反応が無くなる(または、最悪ブロックする)かもしれません。
  • シグナルに繋がる全てのスロットは、スローする事ができます。そして、これは、呼び出しの発行を戻します。従って、Symbian コードに伝える呼び出しの発行の前後で、 QT_TRYCATCH_LEAVING の境界を使用しています!

以下は、RunL() の一部で、非同期 サービスが完了した時に呼び出されます。正常終了して、新しい QBluetoothRemoteDevice を作成し、Symbian C++ クラスと同様の物からの情報を追加し、そして、これをシグナルに 発行 します。もし、エラーだった時、モジュール特有のエラーに変換するため、ErrorConvertToLocalL() を呼び出します。

RunL()

/**
Handles an active object's request completion event.
*/

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());
}
}

SetActive()

プライベートの startSearch() は、アクティブ オブジェクトの開始に使われます。オブジェクトが既にアクティブか、非同期のサービス プロバイダーにリクエストを作成してるか、そして、その時に、オブジェクトをアクティブに設定するために、 CActive::SetActive() を呼び出しているか確認します。

更に、メソッドは、それ開始されていたとクライアントに知らせるために、(オブジェクトが既に開始していた)エラーのシグナルを発行します。このケースでは、発行 はスローする事ができるのですが、呼び出しの発行の境界は必要ありません。このファンクションは、Qt API によってのみ呼び出す事ができます。stopSearch() の挙動は似ています。

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

DoCancel()

CActive::Cancel() は、非同期のリクエストのキャンセルに呼び出されます。そして、オブジェクトがアクティブな時に呼び出された場合は、(実装が必要な) CActive::DoCancel() の呼び出し順に呼び出されます。 未処理のリクエストをすべてキャンセルするため、アクティブ オブジェクトのデストラクタから Cancel() を必ず呼び出してください。 - 取り省いたオブジェクトがシグナルをまだ待っている場合は、アクティブ スケジューラーはパニックを起こします。

以下の DoCancel() の例では、デバイスの発見が止まったシグナルを発行します。それは、潜在的にスローする操作です。(シグナルは、どんなスロットにも接続する事ができ、接続されたコードはスローするかもしれません。)Symbian C++ と Qt 例外処理の混在 のセクションで説明されているように、このメソッドがデストラクタで呼ばれたので、スローの使用、または、コードをリーブする事を、出来る限り避けるべきです。引数のために、この 発行 は避けられず、そういう理由で、全ての標準の例外(だけ)キャッチする事ができると想定しています。

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();
}

コールバック API

Symbian C++ は、非同期サービスのシグナルの完了のコールバックを使用するための API を提供します。(中身は、アクティブ オブジェクトとして実装されています。コールバックの使用は、クライアント コード用の API の使用を簡単にします。)これの良い例は、CMdaAudioPlayerUtility です。 その例は、 MMdaAudioPlayerCallback のスタティック ファクトリー コンストラクタと、インストールと再生が完了した時に示すためのメソッドの呼び出しです。

Symbian C++ コールバックのラッピングは、アクティブ オブジェクトを直接使用するより簡単です。再度説明しますが、プライベート実装の Qt クラスを作成するために、PIMPL を使う事ができます。このケースでは、プライベート実装は、Symbian クラスとそのコールバック インターフェースの実装のインスタンスを持っています。

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;
}

そして、コールバックから必要なシグナルを発行する事ができます。

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

上記のコードは、例外を捕え、それらを処理しないか、再度スローしない事に注意してください。一般的にこれは悪い事だと見なされています!しかし、API に制約されています。- そのプロトタイプは、リーブ セーフの方法のみで使われている事を意味しているので、リーブする事はできません。そして、Symbian コードにリーブ バックするので、スローする事はできません。エラーを取り除く事が、唯一の選択肢です。

Warning.pngWarning: RunL() のコンテキスト内から動作する事を覚えている事は重要です。リーブ のコールバックにスローする Qt コードがある場合、これは QT_TRYCATCH_LEAVING メソッドでラップしてください。

コーディング基準と規約

Symbian コーディング基準と規約

Symbian の広範囲のコーディング基準と規約は、コーディングの基準と規約 のドキュメントに説明されています。Symbian C++ を使用しているプラットフォームのトップを開発している場合(そして、プラットフォームのソース コードに直接コントリビュートする予定が無いとき)、 コーディング基準のクイック スタート はスタート地点として、より役に立ちます。

コーディングの基準は、コードのフォーマット、安全性、メンテナンスの軽減と効率に関するルールをカバーします。開発者は合理的にパーソナル コードのフォーマットに関する基準を無視する事ができます。しかし、他の規約は堅牢で効率的な Symbian C++ コードを書く事に必要不可欠です。

Qt のコーディング基準

Qt はコーディング ガイドラインのコンパクトなセットを備え、Coding Style および Coding Conventions ドキュメントに分けられています。前者は、コーディング フォーマット/レイアウトのベスト プラクティスを定義します。その上、C++ の機能で使用すべきでないもの、ヘッダーのインクルード、キャスト、バイナリー互換性の維持、および、コンパイラー特有の問題のアプローチを説明しています。期待している通り、クロス プラットフォームの互換性を確かにするため、コーディングにはっきりと焦点をあてています。

Tip.pngTip: Carbide.c++ を使用している場合、デフォルトの Qt コーディング スタイルに従ったプロジェクトを作成するために、 Qt_Code_Style.xml テンプレートを使う事ができます。さらに、次の Carbide.c++ メニューでダウンロードとインポートが可能です。 Window > Preferences... > C/C++ > Code Style.

Symbian C++ と Qt コードを混在させたコーディング基準

一般的に Qt アプリケーションは、Symbian C++ と Qt コード両方のコード レイアウト に関する Qt Coding Style ルールに従ってください。そうでなければ、Qt コードは Qt の規約に従ってください。そして、Symbian C++ コードは、Symbian コーディング基準 に従ってください。

このアプローチは、この記事に付属の File:Qtbluetoothdiscoveryexample.zip に従っています。このルールの例外は、BluetoothDiscoveryPrivate クラスが (CActive から派生した)Symbian C クラスであり、C プレフィックスを用いて理想的に命名されています。このケースでは、クラス名はプライベートな実装の 不透明性 を維持する事に必要です。そして、BluetoothDiscovery が、その唯一のユーザーなので、クラスのプレフィックスを使用しないリスクは低いです。

例外とエラーのハンドリング

Symbian の例外とエラー

Symbian C++ は、TRAP (try に多少等しいです)、Leavethrow に少し似ています)そして、(例外のイベントで安全に削除されるための、ヒープが割り当てられたオブジェクトをローカルでスコープする事ができる)Cleanup Stack からなる独自の例外システムを使用します。

潜在的にリーブするコードは、TRAP マクロ内部で実行します。Leave イベントで、コール スタックは TRAP まで戻り、自動変数は割り当てが解除されます。Cleanup Stack は、現在の TRAP レベルのすべてのオブジェクトを削除するか、クリーン アップを行います。TRAP マクロは、Leave のエラー コード(一つの整数)を返します。そして、実行がその後すぐに続きます。("catch" と別れていません)catch ブロックとよく似て、TRAP に続くコードは、それが理解している Leave コードを処理し、Leave してないものを再度、Leave します。

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
}

Tip.pngTip: TRAP は、実行サイズと RAM の消費の点で、比較的重いです。これらはネストが可能で、どのレベルでも使用可能ですが、高いレベルでトラップをし、多くの Leave を行うファンクションでグループ化した方が良いです。

ユーザーが、メモリー リークを防ぐために、安全にオブジェクトを割り当てる方法を理解する事を助けるため、Symbian は、ファンクションとクラス タイプ 用の命名規約を使います。

  • Leave の可能性があるファンクションは規約により、L を後につけて命名します。(または、LC は、メソッドが返って来る時、オブジェクトがクリーンアップ スタック上に残っている事を示します。)
  • ヒープに割り当てられたクラスは、一般的に、最初に CBase から派生し、C プレフィックスで命名されます。
    • CBase は、コンストラクションのゼロ メンバーの初期化、割り当て失敗イベントで Leave (new (ELeave)) する new のオーバーロード、Cleanup Stack が Leave のイベントのオブジェクトを delete するために使う事ができるバーチャル デストラクタを提供します。(クリーンアップ スタックは、CBase から派生していない、他のヒープに基づくオブジェクトを消すためのメソッドも提供します。- 例えば、配列とインターフェース クラスです。)
    • C クラスは、Leave に安全な 2 フェーズ コンストラクション イディオムを使用して、組み立てられています。
  • T プレフィックスは、ヒープ リソースのポインタを持たず、デストラクタを持たない、スタックに割り当てられたクラス用に使用されます。
  • R プレフィックスは、リソースを他に持つ、スタック クラス用に使用されます。- そして、そういう理由で、クリーンアップの明確なサポートが必要です。

Note.pngNote: Symbian のオリジナルな実装で、自動変数のデストラクタは、Leave のイベントでは呼び出されません。結果的に、スマート ポインタは、ローカル オブジェクトのクリーンアップに使うことができません。命名規約は、オブジェクトが明確にクリーンアップするサポートを、クリーンアップ スタックにするか(R、C クラス)、しないか(T クラス)、開発者が解決できるようにします。

Leave と Cleanup Stack (Symbian C++ の基礎) のドキュメントは、例外の仕組みが動く方法、Leave に安全なコードを書く方法、および、命名規約のより詳しい説明を提供します。クラス タイプと宣言(Symbian C++ の基礎) は、異なるクラス タイプを説明し、オブジェクト コンストラクション(Symbian C++ の基礎) は、Leave に安全なオブジェクトのコンストラクションを説明します。

Note.pngNote: Symbian C++ が生成された時、標準の C++ の Try Catch Throw の例外処理は、組み込みオペレーティング システム用には、メモリーが高すぎると考えられています。更に、Psion が使う必要があるコンパイラーのサポートは限定されています。

標準の C++ 例外サポートは、Nokia S60 第三版で使用されている、Symbian プラットフォームの前のオペレーティング システムの、Symbian OS v9 で追加されました。

Symbian の例外処理の仕組みは、trycatch および throw で実装されています。開発者は、標準 C++ コードの trycatch および throw を使用する事ができ、Symbian C++ (注意して!)とそれらを混在して使う事もできます。新しい例外処理の仕組みと、標準 C++ 相互で作用する方法は、Leave と例外の比較 および ハイブリッド コーディング ガイド で説明されています。

Symbian ファンクションは、Leave か例外条件のシグナル発行のエラー コードどちらかを使用します。エラー条件が "expected" の時、エラーを返すのは、Leave する事が好まれます。- 例:ファイルの終わりを読み取ろうと試みた時。しかし、ここには厳格なルールはありません。(クラス コンストラクション以外に、オブジェクトに割り当てるメモリーが無い時は、常に KErrNoMemory で Leave します。)そして、Symbian C++ API 内の特別な方法でエラーと Leave が使われている事を見ることができます。Leave とエラーを返す両方のファンクションを持つ事、または、Leave を(良い理由無しに、TRAP を使用した)エラーに変換するプログラムの練習は悪いと考えられている事に注意してください。通常の例外も同様です。

e32err.h には、グローバル エラー コード(負の整数値)が定義されています。他のエラー コードは、個々のコンポーネントで定義されています。 - Symbian_OS_Error_Codes 上の物も含め、多くのリストがあります。

開発者は、Panic と呼ばれる、二番目の例外クラスを、Symbian が 定義している事も気が付くべきです。Panic は、プログラム的なエラーの発行と、アプリケーションの急な終了の結果に使用されます。これは、API の間違った使用に対しての、適切な反応です。

Qt の例外とエラー コード

プラットフォームとコンパイラー上で利用可能な、標準 C++ の例外処理の仕組みを、Qt はサポートしています。

Qt は開始以来、サード パーティのコードの仕組みをサポートしていますが、歴史的に Qt フレームワーク コードは、(クロス プラットフォームの互換性の理由で)その仕組みを使用していません。Qt のコードは、エラー条件のクライアント コードを通知するため、ローカルで定義されたエラー コードを代わりに使用します。メモリー割り当ての失敗のケースでは、フレームワーク コードは、Null ポインタを単に返し、最初のポインタの指す値の取得に失敗します。

Qt 4.6 は、QFile クラスと同様に、Qt のコンテナ クラス用の基本的な exception safety を約束します。(コンポーネントの不変条件は保護され、リソースはリークしません。)

  • QScopedPointer スマート ポインタは、(使用中もしくは、構造上の)例外のイベントでクリーン アップするための、ローカルにオブジェクトをスコープする事が可能になっています。
  • メモリーが割り当てられない場合、オブジェクトは、std::bad_alloc をスローします。( q_check_ptr() を使用して、返って来たポインタを確認します。)
  • try/catch のブロックは、適切な場所に追加されます。

Qt は、それ自身が持つクラス階層の例外を(これを書いている時点では)定義しません。そして、フレームワーク コードは、std::bad_alloc をスローするだけです。Qt それ自身は、 割り当て失敗以外のエラー コードを伝える例外より、依然としてエラーコードを使用しています。開発者は、殆どすべての Qt クラスが throw する事ができ、実際にすべての物をスローできるサード パーティのコードに影響する事を想定すべきです。(例外は、ローカルにスコープされたオブジェクトのクリーンアップ用に、安全に使用される事を意味する、 QScopedPointer のコンストラクタです。)Qt が例外を捕えた時、処理しない場合は、常に再度スローします。

開発者は、trycatch および throw を直接呼ぶより、 (qglobal.h で定義されている) QT_TRYQT_CATCHQT_THROW、および QT_RETHROW マクロを使用すべきです。例外処理のコードは、例外をサポートするプラットフォームのみに従うことを確実にします。例外に安全なアプリケーションを書く場合、例外が可能かどうか注意してください。

Symbian C++ と Qt 例外処理の混在

Symbian C++ は Leave を使いますが、Qt は標準 C++ の例外を使用します。Leave は例外の観点から実装されていますが、二つのイディオムを交互に取り入れる必要がある事に注意してください。

Symbian exception safety は、問題を簡潔に説明しています。そして、イディオムの間で変換するために使用する事ができる、境界のファンクションを説明します。Leave と例外の比較 および ハイブリッド コーディング ガイド は、追加のリファレンスとして役に立ちます。

境界のファンクションは、標準の例外を捉え、それらを Symbian エラーに変換するか、Symbian コードを伝えるために Leave する事を可能にします。そして、Symbian C++ の Leave またはエラーを、標準の例外に変換する事と似ています。便宜上、メソッドをここにリスト化します。 qt_symbian_throwIfError()q_check_ptr()QT_TRAP_THROWING()qt_symbian_exception2Error()qt_symbian_exception2LeaveL()QT_TRYCATCH_ERROR()QT_TRYCATCH_LEAVING()

境界のメソッドは、標準の例外 を捉え、変換する事ができるだけです。 - アプリケーション特有の例外は、それを通して伝えられます。そして TRAP に到達した場合、アプリケーションを終了させる原因となります。これが、期待された振る舞いです。- 規約によって、コードは、それが理解している例外 のみ 捉え、処理すべきです。 - 他の全ての例外は、再度スローする必要があります。

Note.pngNote: TRAP に伝わり、アプリケーションが終了する事を防ぐため、catch (...) または、 QT_CATCH (...) で、全ての例外を捉えるようにしています。しかし、これを実行した場合は、エラー条件はまだ存在しています。 - アプリケーションがエラーを適切に処理するために、どんな条件も取り除いてください。 - コードは、メモリー リークをするかもしれませんし、また、予測できずに失敗し、デバッグの方法が難しいかもしれません。

もう少し詳しく考慮するのに値するケースがいくつかあります。ファンクションが絶対に失敗しない事と、失敗しても決してスローや Leave をしない事です。両方のケースにおいて、決して失敗しない事 は、Leave または標準の例外で失敗しない事 として扱われるべきです。- 他の例外は、伝える事を許可しなければいけません!

デストラクタは、(例外がスローするか、Leave するかで)ファンクションが 失敗してはいけないケースです。デストラクタを実装した場合:

  • 失敗しないファンクションのみを使うように努めます。
  • Leave の可能性があるファンクションを呼ばなくてはならない場合、TRAP マクロを使い、伝わる事を止めてください。
  • throw の可能性があるファンクションを呼ばなくれはならない場合、QT_CATCH (const std::exception&) で、標準の例外を止めてください。他の例外を伝える事は許可してください。例外を再度 throw しないで、 QT_CATCH (...) しないでください。
  • 上記のルールに従い、Qt のスロットが、デストラクタのコードのように、QObject::destroyed() シグナルと繋がってる、論理的な結果となります。

いくつかのファンクションは失敗する事が可能ですが、throw や Leave をしてはいけません。例えば、呼び出しのコードが Leave に安全に書かれていないかもしれないので、L サフィックスが無い、Symbian C++ コールバック メソッドは、Leave する事ができません。また、より高いレベルで TRAP があるかもしれないので、Throw もできません。

  • ファンクションがエラーコードを返せる場合、Leave または 標準の例外をエラーに変換するために、Qt 境界関数を使用する事ができます。
  • ファンクションのプロトタイプがエラーを返すことを許可しない場合、エラーを取り込むことができますが、他に throw しなくてはなりません。
  • 両方の場合で、標準エラー以外は、伝えられるようにする必要があります。

一般的な Qt と Symbian タイプの変換

このセクションでは、一般的なタイプを変換する方法を、いくつか説明します。

文字列

文字列はほとんど全てのアプリケーションで使われています。恐らく、Symbian C++ と Qt のコード間の、もっとも一般的な変換です。

Symbian の文字列

図 10: ディスクリプタ クラスの継承
Symbian C++ は、テキストとデータ両方を使うために、デスクリプタ を使用します。ディスクリプタは、自分自身を表します。 文字列のデータと、その長さとメモリーのレイアウト情報を格納するために、ディスクリプタは最少の量のメモリーを使用します。ディスクリプタは自動でリサイズを行いません。その代わりに、演算がバッファーの長さを超えた時、panic になります。これは、稀もしくは、決してリブートしない設計での、デバイスの堅牢なコードにしていきます。前に述べたように、NULL ('\0') ターミネーターの存在で長さは決まらないので、これらはテキストとデータの両方に使用されます。

Symbian のディスクリプタ クラスの階層は複雑です。スタックまたはヒープのデータを格納する、明確に変更可能な物と変更不可能な物のディスクリプタと、 ファンクションの返すタイプとパラメーターに使われる、多くのベース クラスを提供します。しかし、(TDes, TDesC 等) のインスタンス化を対象としていません。単に、他の場所でデータが格納されたデーターを示す、いくつかの ポインタ ディスクリプタTPtr, TPtrC) があります。

全てのディスクリプタ クラスは、狭い (8 bit) そして、広い (16 bit) 変数を提供します。(例、 RBuf8, RBuf16 respectively) 8 bit 変数は主にデータ用に使われます。16 bit 変数は、ユニコードのテキストに使用されます。文字列データ用に、多くのデベロッパーは、図に示されたように、実際に様々なバージョンのディスクリプタ (例 RBuf)を使用します。これは、Symbian プラットフォームの、現在の全てのバージョンの 16 bit 変数についての typedef です。

この Wiki には、いくつかの、ディスクリプタの素晴らしいドキュメントがあります。ディスクリプタ(Symbian C++ の基礎)ディスクリプタの詳細説明 です。 開発者のライブラリについては、 Using descriptors があります。開発者は、語彙的な構文解析と、数値型文字列を数値型に変換する TLex を知っておくべきです。様々な typedef のクラスは、文字用に、TTextTChar、そして、キャラクター セットの変換用に Charconv を使用します。(Qt より多くのセットを変換し、拡張可能です。)

Qt の文字列

Qt は、殆ど全ての Unicode 文字列を処理する、一つのクラス、QString を使用します。クラスは、文字列の比較、数値型の相互変換、キャラクター セット間の変換を含む、開発者が必要な機能の殆ど全てを提供します。クラスは、パワフルな構文解析と、文字列操作を提供するため、Qt の正規表現を統合します。それは、Qt の internationalization API でも、シームレスに動作します。

QString は、必要な時に、大きめの文字列に対応するため、自動的にリサイズします。(文字列が、再割り当てできない場合は、throw します。)QString は、メモリー使用量を減らすため、そして、必要ないデーターのコピーを避けるため、implicit sharing (コピー オン ライト) を使用します。これは、変数はスタック上に格納されている間、関連データはヒープ上に格納される事を意味します。

Note.pngNote: メモリー使用量に関して、Symbian C++ の開発者は、QString (そして、間接的な共有オブジェクト)を、参照でカウントした R-Classe と見なすと、役に立つかもしれません。オブジェクトは、ヒープに割り当てられます。データーの変更を行わないコピー操作は、新しいオブジェクトを作成する必要無しに、単にカウントを増やします。使用している全てのオブジェクトがスコープから外れた時、データは解放されます。

QString API は、ディスクリプタの階層を理解するよりもはるかに簡単です。そして、多くの点でよりパワフルです。QString は、ディスクリプタ クラスより、メモリーは効率的ではないですが、比較的効率的で、堅牢です。

Qt は、文字列操作に使う事のできる、 QByteArray (バイトの配列) も提供します。QByteArray は、データによく利用されます。そして、 Qt Binary Data セクションで説明されています。

ディスクリプタを QString に変換

QString::fromUtf16 を使用して、特定のアドレスと長さのデータをディープ コピーして、新しい QString を作成します。アドレスと長さは、各々 TDesC16::Ptr()TDesC::Length() で取得できます。

QString myString = QString::fromUtf16(theDescriptor.Ptr(), theDescriptor.Length());

このアプローチは、QBluetoothRemoteDevice に割り当てる前に、デバイス名とアドレスをディスクリプタから QStrings にコピーするため、サンプルコードで使われています。 ( BluetoothDiscoveryPrivate::RunL() の実装を参照してください。)

Note.pngNote: qcore_symbian_p.h は、この変換を行う、 QString qt_TDesC2QStringL(const TDesC& aDescriptor) で定義されています。このファイルはパブリック API の一部ではないので、 qcore_symbian_p.cpp から、あなたのプロジェクトの中に、ソース コードをコピーする事を選んでも良いです。

多くのケースで、データーの コピー を作成するために、上記の方法を使う事になります。データーのゼロ コピー転送が必要な場合、そして、theDescriptor ライフタイムが、Qt 変数のライフタイムを超える事を保障できる場合、QString::fromRawData() を使用して、theDescriptor のデーターを指す QString を取得する事ができます。

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

テキストを含む、8 bit のディストラクタは、Symbian C++ でユニコードに変更する事ができます。そして、上記のように転送できます。 Symbian のキャラクター変換クラスは、キャラクター セットの自動検出ができ、より多くのキャラクター セットの変換を革新的にサポートしているので、これは利点を持っています。

しかし、Qt コードで変換する方が簡単だと思うかもしれません。ディスクリプタのデータのポインタを取得するために、const TUint8* TDesC8::Ptr() const; を使用する事ができます。キャラクター セットを知っている場合、変換を行うために、QString's fromAscii()fromLatin1()fromUtf8() を使う事ができます。現在のロケールのデフォルト セットのデータをしっている場合、QString::fromLocal8Bit() を使う事ができます。

QString をディスクリプタに変換

QString myString のデータのポインタを取得するため、そして、これを、TPtrC16 (TPtrC) にキャストするため、下記に示すように、QString::utf16() または QString::constData() を使用してください。

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

または、

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

ポインタ ディスクリプターは、オリジナルの QString (または、どんなシャロー コピーも)スコープ内にある間は、有効です。これを保障できない場合は、文字列をヒープまたはバッファー ディスクリプタにコピーすべきです。(RBuf) ヒープ ディスクリプターを使うのは以下となります。

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

バッファー ディスクリプター用には、下記のどちらも使用できます。

TBuf buffer(myDescriptor);

または、

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

Note.pngNote: qcore_symbian_p.h は、これらの変換を行う、HBufC* qt_QString2HBufC(const QString& aString)TPtrC qt_QString2TPtrC( const QString& string ) を定義します。このファイルはパブリック API の一部ではないので、qcore_symbian_p.cpp から、あなたのプロジェクトの中に、ソース コードをコピーしても良いです。

入出力とバイナリー データー

このセクションで、オブジェクト データーのシリアル化、ファイルに保存、および、デバイスとスレッド間に送る仕組みについて、簡潔に説明します。

Symbian バイナリー データー

Symbian C++ ストリーム クラスは、オブジェクトの内部データを連続したバイトにし、連続したバイトからそれらを初期化するのに使用されます。

外部に表す必要のあるオブジェクトは、以下のように、ExternaliseL() および InternaliseL() メソッドを定義します。(これらのクラスが、外部/内部におくデータへ/からのストリームのための、グローバルな >> と << ストリーミングの演算子を用いたメソッドを定義する事に注意してください。)

void ExternalizeL(RWriteStream& aStream) const;
void InternalizeL(RReadStream& aStream);

書き込み/読み込みのメソッド、 これら独自の ExternalizeL()/InternalizeL() メソッドのオブジェクト メンバー、そして、(ディスクリプタを含む)基本タイプの、最終的にプラットフォームに独立した説明は、RWriteStream/RReadStream クラスに定義されています。

RWriteStream/RReadStream は抽象クラスです。オブジェクトを外部化する時、データーを、ファイル、ファイル ストア、ロー メモリー、または固定かダイナミックなバッファーに送る、具体的なストリームを使用します。いくつかのストリームは、他のストリーム オブジェクトで初期化されます。 - 例えば、データーの圧縮または暗号化です。ネイティブの Symbian アプリケーションは、固有の識別子を使うアプリケーションに関連付けられた、ストリームの 格納 に基ずくファイルとして、それらのデータを格納します。

ストアとストリームの概要は次のリンクを参照してください。ストリームとストア(Symbian C++ の基礎) そして、アプリケーション リファレンスは次のリンクを参照してください。 Streaming Stores ストリーミングの仕組みを用いたデータのアプリケーションを保存する方法の、動作例はこちらを参照してください。GUI/Engine Split

Symbian は、文字列のデータでないバッファとして、8 bit 変数の descriptors を使用します。 TBufC8, TBuf8, TPtrC8, TPtr8, RBuf8, HBufC8. 例えば、 RSocket API の送受信バッファとして使用されます。直接ソケット/からのオブジェクトをシリアル化するインターフェース ソケットを使う事もできます。

( 8 bit の記述子で整形した)Package buffers は、スレッドとプロセス間のオブジェクト通信の目的で使われます。開発者は、これらのバッファで、ディスクリプタとして、どんな(T クラスの)値でも、パッケージにする事ができます。このアプローチは、他のスレッドとの通信の、プラットフォーム独自の作りが必要でないので、受け入れることが出来ることに注意してください。

3 つのパッケージ バッファーがあります。 TPckgBuf は、オブジェクト データーのコピーを受取ります。TPckgCTPckg は、単純に(各々) constnon-const オブジェクトを指します。

Qt バイナリー データ

Qt の QIODevice は、デバイス が読み込みと書き込みが可能な、データのブロックを抽象化したものです。これは、ファイルの書き込み、プロセス、ソケット、バッファ等に使用される、多くのサブクラスを持っています。( QTcpSocketQUdpSocketQBufferQFileQLocalSocketQNetworkReply、および QProcess を含みます。)

Qt は、高いレベルのストリーム クラスの QDataStreamQTextStream も提供します。 これは、ストリーム バイナリーとテキスト データを、QIODevice に渡すのに使用されます。ストリーム クラスは、(Qt バージョンに特有な)独自の方法で、データーのシリアル化を行います。データ をシリアル化できるクラスは、QDataStream 引数(または、関連したメソッド)をとる他の物で、>> と << 演算子をオーバーロードします。

メモリー内で、8 bit のテキストとバイナリー データは、一般的に QByteArray に保存されます。これは、QString クラスの API に非常によく似ている、バイトの配列です。QBuffer クラスは、QByteArray に、QIODevice インターフェースを提供する事に注意してください。

入出力とバイナリー クラスは、クラスのドキュメントの中で、十分にドキュメント化されています。C++ GUI Programming with Qt 4, Second Edition, Jasmin Blanchette and Mark Summerfield, Prentice Hall (2006) の、第 11 章 の QByteArrayと、第 12 章の入出力 に、良い概要があります。(初版は、オンラインで無料で参照することができます。 こちらを参照してください。

Qt と Symbian バイナリー データ間の変換

Symbian と Qt のクラスおよび、データ シリアル化のアプローチは、基本的には同じです。データは、プラットフォーム独自のフォームのストリームに作られます。Symbian では、このストリーム、ファイルまたはメモリー バッファーです。Qt では、このストリームは、ファイル、バッファー等の、QIODevice関連付けられています。実装での主な違いは、Symbian C++ は、とても小さなプラットフォーム独自のタイプを持っていることです。一方、Qt は複数のバージョンに渡り、異なる実装用の、リッチなタイプを持っています。

Qt と Symbian 間のシリアル化の仕組みを変換する理由があまり無いと言うのが、良い情報です。異なる環境のシリアル化したデータを、敢えて移す必要がある場合、最初にインポートし、そして適切に変換してください。(キャストか、それより多くの複雑なメソッドを使用してください。)

ロー データーを扱っている場合、QByteArray とディスクリプタ間の変換は、QString とディスクリプタ間の変換とほとんど同じです。 - 例、

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 );

QByteArray に属する QByteArray::constData()data() により、データが返されるので、 上に示したように、コピーをする必要があるかもしれません。

ディスクリプタのディープ コピーを作成するために、QByteArray を用いて変換する別の方法があります。または、Symbian C++ コードのユーザーを上回る、QByteArray のライフタイムを知っている場合は、QByteArray::fromRawData() です。

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

ジオメトリ: ポイント、サイズ、長方形

Qt と Symbian C++ は、よく似たジオメトリ タイプを定義します。

TPointQPoint は事実上同じです。両方は、X と Y の座標値を用いて、デカルト座標の2次元のポイントを格納します。(TInt タイプ ( typedef で signed int と定義されています。 )と int 各々です。)

TSizeQSize も、実質的に同じです。両方は、幅と高さの値を格納します。 TIntint を各々使用します。変換は簡単です。

QSize myQSize = QSize(myTSize.iWidth, myTSize.iHeight);  //To QSize
TSize myTSize = TSize(myQSize .width(), myQSize .height()); //to TSize

TRectQRect 両方は、座標系内の特定の位置を持つ、長方形のエリアを定義します。両方は、長方形の左上の座標を格納します。TRect は長方形の TSize を格納します。一方、QRect は、幅と高さの各々の値を格納します。変換は簡単です

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.pngNote: qcore_symbian_p.h は、これらの変換を行う、以下のインライン関数を定義します。* 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)
このファイルは、パブリック API の一部ではないので、あなたのプロジェクトに、コードをコピーする事もできます。

QRect は、長方形の底と右の座標を取得する仕組みも提供します。これらは、TRect に変換する事に使用しないでください。歴史的な理由で、それらは正確な 長方形の右下から外れます。詳細な情報は、QRect の情報を参照してください。

イメージ

Symbian イメージ

bitmap を表示する、Symbian のメインのクラスは、 CFbsBitmap です。bitmap は、フォントとビットマップ サーバーにより、管理されています。その API は、Symbian のネイティブ イメージのファイル フォーマット ("マルチビットマップ (MBM) ファイル")からの bitmap を読み込み(そして圧縮し)、ハードウェア自身の bitmap を生成およびアクセスをし、bitmap データにアクセスし、bitmat をリサイズする等のメソッドを提供します。それは、bitmap が他のスレッドと共有する事ができるように、 現在の bitmap ハンドルを複製する仕組みを提供します。生成された CWsBitmap は、Window サーバーが既にハンドルを持っている事を表します。そして、CFbsBitmap を利用するこれらより、若干早い処理を提供します。

イメージを処理する機能の多くは、 Image Converter Library (ICL) に定義されています。ICL は、CFbsBitmap にイメージ ファイルを読み込むクラスを提供します。ライブラリーは、共通のデスクトップと、携帯のフォーマットの多くをデコードする事が可能です。そして、これらのサブセットをエンコードする事も可能です。Qt と Symbian プラットフォームにより提供されるフォーマットの比較は、 下記 の通りです。

ICL は、回転(900 ステップ以内)、フリッピング、拡大縮小(アスペクト比を保持した引き伸ばし)を含む、共通なイメージ操作のクラスも提供します。異なるクラスは、CFbsBitmap オブジェクト、または、ファイルやディスクリプタに格納されたイメージの拡大縮小ができます。

ICL API は非同期です。クライアントは、リクエスト操作の完了のシグナルを発行します。これで、計算処理の負荷が高い間でも、ユーザー インターフェースの反応を良く保つ事ができます。

Qt イメージ

Qt は、イメージ データを扱う、4つのメイン クラスを提供します。 QImageQPixmapQBitmap そして、 QPicture です。

QImage は、ハードウェアに独立した表示で、ピクセルのアクセスと操作が直接できます。 - これは、Symbian CFbsBitmap に一番似ています。ピクセルの情報にアクセスする事に加え、QImage クラスは、ファイルやバッファーの、様々な 共通イメージ フォーマット を直接読み込むことができます。 ARGB、 RGB32、 Mono 等の沢山のフォーマットの一つを用いて表示します。QImage は、回転、拡大縮小、ミラーリング、マスク生成、そして、2D 座標系内の複雑な変形を含む機能を操る、パワフルなイメージを提供します。

QPixmap は、スクリーンにイメージを表現するために、設計と最適化がされています。イメージ データへのダイレクト アクセスは提供していません。しかし、QImage のように、共通ファイルフォーマットを読み込む事ができ、殆ど同じイメージ操作を行う事ができます。QPixmap::toImage()QImage::fromImage() は、二つのイメージ クラス間を変換するために使う事ができます。

QBitmap は、QPixmap を継承する便利なクラスで、1 の深さを保障します。最後に、QPicture は、QPainter コマンドを保存し、再生するペイント デバイスです。

Qt は、(ファイルからイメージを読み込む場合に、QImage または QPixmap より素晴らしい、粒状のコントロールを提供する )QImageReader と、特定の動作を表示する Qt の ウィジットに使う事ができ、拡大縮小のアイコンを提供する QIcon を含む、多くの追加イメージ クラスを提供します。

Symbian と Qt イメージ フォーマットの内部動作

共通のファイル フォーマットを用いたファイル システムに格納されたイメージを使う場合、Symbian のイメージ操作用のコードを直接使う必要はありません。QImageQPixmapはプラットフォームを超えて動作する、ファイル を基にしたイメージ データの読み込みと操作に、もっと便利な API があります。

Qt がサポートしていないファイル フォーマットを扱う場合、Symbian の API を選んでも良いです。各々の環境にデフォルトでサポートしているフォーマットのリストは以下の通りです。 - 両方のアーキテクチャで、プラグインでサポートしたフォーマットのイメージが使えるので、これは特定のプラットフォームでは、標準になってないかもしれません。

テーブル: デフォルトの Qt と Symbian のイメージ ファイル フォーマット
フォーマット 説明 Qt のサポート Symbian のサポート
BMP Windows Bitmap Read/write Read/write
EXIF Exchangable Image File format - Read/write
GIF Graphic Interchange Format (optional) Read Read (シングルとマルチフレーム、bitmap マスクのサポート)/write (シングル フレームのサポート、非透過)
ICO Icon - Read (シングルとマルチ フレーム)
JPG/JPEG Joint Photographic Experts Group Read/write Read/write
MBM Symbian Multi Bitmap - Read (シングルとマルチ フレーム)/write (シングル フレーム)
MNG Multiple Image Network Graphic Read Read/write
PNG Portable Network Graphics Read/write Read (Bitmap マスクのサポート)/write (非透過)
PBM Portable Bitmap Read -
PGM Portable Graymap Read -
PPM Portable Pixmap Read/write -
SMS OTA SMS Over The Air - Read
TIFF Tagged Image File Format Read/write Read (リトル エンディアンとビッグ エンディアンのサブタイプをサポート)
WBMP Wireless Bitmap - Read
WMF Windows Meta File - Read (Std, apm と clp サブタイプをサポート)
XBM X11 Bitmap Read/write -
XPM X11 Pixmap Read/write -

最後に、プラットフォームに利用可能になるイメージのフォーマットは、Symbian イメージ クラスを使う必要があるかもしれません。例えば、カメラからのイメージは、 CFbsBitmap のプラットフォームで利用可能になります。

Qt は、CFbsBitmapQPixmap に変換する プラットフォーム特有の方法 を提供します。(必要な場合は、QImage に変換できます。)

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

Note.pngNote: 現在の Symbian の表示モード (TDisplayMode)に基づく、イメージ フォーマット (QImage::Format) が必要な場合、Qt 4.6.0 バージョンの q_s60_p.h に定義された、 プライベート ファンクションの qt_TDisplayMode2Format があります。そして、あなたのプロジェクトにコピーする事が出来ます。このメソッドはプライベートなので、変更に注意してください。

コンテナ/リスト

Symbian コンテナ

Symbian C++ は、多くの配列クラスを提供します。メモリー オーバーラン等の保護を提供するので、標準 C/C++ の配列よりも、これを選択すべきです。配列は、標準 C++ の配列 (stl::vector<>) によく似た振る舞いをします。それらは、整数でインデックス化されています。要素は、どんなタイプ(または、タイプへのポインタ)にもなります。追加や削除の時、配列は内部で再配置を行います。

異なる配列クラスの概要は、 配列(Symbian C++ の基礎) のトピックを参照してください。それには、4 つの主要なカテゴリがあります。

  • CArrayX (CArrayFixFlat, CArrayVarFlat, CArrayPakFlat, CArrayPtrFlat, CArrayFixSeg, CArrayVarSeg, CArrayPtrSeg)
  • RArrays (RArray, RPointerArray)
  • Fixed Array (TFixedArray)
  • Descriptor Arrays (CDesC16ArrayFlat, CDesC8ArraySeg, CDesC8ArrayFlat, CDesC8ArraySeg, CPtrC8Array, CPtrC16Array)

配列の命名規約の Fix は、配列の要素が全て同じ長さで、配列のバッファに直接コピーされています。(例えば、TPointTRect です。)そして、 Var は、ヒープの他の場所に含まれる、可変長のオブジェクトを指すポインタ配列の要素です。 (例えば、HBufC* または TAny* です。)(‘packed’配列用)Pak は、配列の要素は可変長で、その長さだけ各々前に置かれる、配列のバッファにコピーされます。 (例えば、可変長の T クラス オブジェクトです。)Ptr は、配列の要素が、 CBase に生成されたオブジェクトを指すポインタの配列に使われます。

マッチングした文字列をソートし、見つけるための便利なメソッドを持っているので、文字列リスト格納用のディスクリプタ配列を使用します。他の方法では、CArrayX よりも RArray クラスを使用します。このルールの例外は、配列が頻繁にリサイズされる時です。 - このケースでは、セグメント化された変数の一つを使用しても良いです。

Symbian は、多くのテンプレート化された連想配列を提供します。

  • RHashMap - 探索シーケンスのハッシュ テーブルで使用される、キー タイプの K と、値のタイプの V の連想配列です。キーと値両方のオブジェクトは、追加された時にテーブルにコピーされます。ビット単位のバイナリー コピーがこれに使用されるので、タイプ K と V は、単純で無いコピーのコンストラクターを実装しないかもしれません。
  • RPtrHashSet- 探索シーケンスのハッシュ テーブルを用いる、タイプ T のオブジェクトの、整理されていない拡張セットです。追加された時に、オブジェクトはセットにコピーされません。むしろ、セットは含まれたオブジェクトのポインタを格納します。
  • RPtrHashMap. 探索シーケンスのハッシュ テーブルで使用される、キー タイプの K と、値のタイプの V の連想配列です。キーと値両方のオブジェクトは、追加された時にテーブルにコピーされません。- ポインタのみが格納されます。

Symbian C++ の配列クラスに加え、 Category:Open C/C++ より提供されている、STL コンテナを使用する事もできます。

Qt コンテナ

Qt は、STL コンテナか、それ自身の一般のテンプレートを基にしたコンテナ クラスを使用します。Qt クラスは、STL クラスを使用するより、軽く、安全で、簡単になるように設計されています。これらは、implicitly sharedreentrant、そして、これらにアクセスするために使用される全てのスレッドで、読み込み専用として使われるコンテナを使う状況で thread-safe です。最後に、これらは全ての Qt プラットフォームで利用可能です。 (STL は Qt Embedded では利用できません。)

Qt は、シーケンシャルな配列 (QListQLinkedListQVectorQStack, and QQueue)と、連想配列 (QMapQMultiMapQHashQMultiHash、および QSet) 両方を提供します。制限されたキャッシュ ストレージのオブジェクトのハッシュを効率的に調べる、QCacheQContiguousCache のような、特別なクラスもあります。そして、文字列のリストの扱いを簡単にする (QList<QString> から継承した)QStringList のような、テンプレートを使わない特別なクラスがあります。

コンテナは、Java 形式、STL 形式の反復のどちらかを用いて、値を取得する事ができます。もしくは、便利な foreach キーワードを使用してください。Qt は、ソート、検索、カウント、(STL の繰り返しを持つコンテナ タイプの)コンテナ内のアイテムの削除ができる、Generic Algorithms のセットを提供します。

テンプレート クラスは、特定のタイプ T のアイテムを格納します。タイプ T の値は、基本タイプ、ポインタ タイプ、デフォルトのコンストラクターを持つクラス、コピーコンストラクターおよび、割り当ての操作またはクラスと同じ条件を持つコンテナとなります。

Qt コンテナ クラスは、Qt リファレンスに詳しく説明されています。 Qt container classes. C++ GUI Programming with Qt 4, Second Edition, Jasmin Blanchette and Mark Summerfield, Prentice Hall (2006) にも素晴らしい説明があります。(初版は、オンラインの ここ で無料で参照する事ができます。)

Qt と Symbian コンテナ間の変換

これらは、Qt と Symbian プラットフォーム両方でサポートされているので、標準の STL コンテナを使用している場合、コンテナ/リストを変換する必要はありません。

Qt と Symbian の配列を変換する必要がある場合は、一つのコンテナを繰り替えす問題、お互いのオブジェクトを正しい環境に変換する問題(例、 QString とディスクリプタ)、新しいコンテナに追加する問題が良くあります。

Qt に変換する時は、 1000 アイテム以下の時、QList はコンテナをリサイズする、最善の "一般的な用途" です。(配列のリストを実装していますが、プリペンドとアペンドはとても速いです。)文字列のリストを扱っている場合は、QStringList を代わりに使用してください。Symbian C++ に変換する時は、ディスクリプタ配列か、4 byte より大きなサイズ の他のオブジェクト用の RArray を使用してください。(そうでなければ、多くの配列のリサイズをする事になります。)

以下に、RArray から QList に、整数のセットを変換する事を説明します。

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

このコードは、QList を繰り返すために foreach を用いて、RArray にインポートされた QList の整数を説明しています。TInt は、signed inttypdef であることに注意してください。

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

文字列のリストの変換は、QList より QStringList を使い、RArray の代わりにディスクリプタ配列を使い、上記の 文字列 セクションに説明したように、環境に正しい文字列のフォーマットを変換する必要がある事以外、まったく同じアプローチをつかいます。

次のコードは、CDesCArrayFlatQStringList に変換する方法について説明します。

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

サマリー

この記事は、PIMPL パターンを使った、Qt アプリケーションの Symbian C++ コードを使ったテクニックと、ベスト プラクティスを説明します。

そして、二つの環境の例外、コーディング スタイルとイディオム、文字列、配列、コンテナ、イメージおよびデータを処理するメカニズムについて説明します。各セクションは、Symbian C++ と Qt で特定のタスクが実行される方法と、イディオムをミックスする方法の、 高度な概要を説明します。 更に、主要なドキュメントへのリンクは、より詳しい説明へのアプローチを説明します。

付属している、サンプル コード (File:Qtbluetoothdiscoveryexample.zip) は、リモートの Bluetooth デバイスを発見するための、Qt スタイルの API (それと、ダイアログ)を提供します。これは、プラットフォームから情報を取得し、例外を扱う仕組み、コーディング スタイルおよび文字列を安全に混在させる方法を説明するために、PIMPL パターンを使用しています。


CreativeCommons attribution sharealike 2.5 by-sa2.5 88x31.png© 2009 Nokia Corporation and/or its subsidiary(-ies). This document is licensed under the Creative Commons Attribution-Share Alike 2.5 license. See http://creativecommons.org/licenses/by-sa/2.5/legalcode for the full terms of the license.


Licence icon cc-by-sa 3.0-88x31.png© 2010 Symbian Foundation Limited. This document is licensed under the Creative Commons Attribution-Share Alike 2.0 license. See http://creativecommons.org/licenses/by-sa/2.0/legalcode for the full terms of the license.
Note that this content was originally hosted on the Symbian Foundation developer wiki.

This page was last modified on 30 May 2013, at 06:34.
769 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×