Symbian OSアプリ開発の手引き

Symbian OSアプリ開発の手引き(5)

貴重な情報源“ヘッダファイル”を読んでいますか?

大久保 潤 管理工学研究所 2008/8/27

Symbian OSの全体像を概観した「Symbian OS開発の勘所」の続編となる今回は、“実際にどのようなプログラムを書くのか?”をテーマにSymbian OS向けのアプリケーション開発における心得を分かりやすく伝授する。(編集部)

- PR -

 今回は、イディオム編の第3弾として前回「Symbian流“日本語表示”と“文字列操作”」に引き続き“文字列”について解説します。

 前回は、文字列インスタンスを作る練習で紙幅が尽きてしまったので、今回は「作った文字列インスタンスの具体的な使い方」について見ていきましょう。

Walk, don't run.
− ヘッダファイルをのぞいてみる

 突然ですが、ヘッダファイルを読んでいますか? Javaや.NET系の言語と異なり、由緒正しいコンパイル言語であるC++ではソースコードはいざ知らず、ヘッダに関してはその内容を隠すことができません(注1)。ドキュメントよりも雄弁にクラスやAPIの仕様を語っているものがあるのですから、読まない手はありません。ドキュメントを読んでも分からない部分と出合ったとき、ヘッダファイルは貴重なヒントをワレワレに提供してくれます。

 例えば、Symbian OSに限らずAPIのドキュメンテーションでよく見受けられる文言「××××を行った場合の結果は保証されない」。何をやるべきではないか(Don't)は分かりますが、それがなぜだか(Why)はどこにも書かれていません。なぜダメなのだろうという理由をあれこれ考えるとき(モデリングですね)、ハードの制約や過去の事例、一般論などさまざまな根拠を援用することになりますが、ヘッダファイルほど直接的に役立つものはありません。良い開発者とはできることだけではなく、できないこと(とその理由)を理解している人のことですが、Symbian OSにおいてそうなるためにはヘッダファイルをのぞいてみることが欠かせないように思います(注2)。例えば、前回の最後に出したQUIZ、アレがなぜまずいのかを理解するにはヘッダに書かれている情報がどうしても必要になります。

 というわけで、いつかは必ずやらなきゃならないヘッダファイルのアナリーゼを、文字列クラスとの絡みでやってしまおうというのが今回の企みでアリマス。

注1:JavaDocは確かによくできた仕掛けですが、しかしヘッダファイルが公開できるようになっていたとしたらアレほどまでに熱心に使われたかというとはなはだ疑問です。逆にぞんざいなドキュメントを付けただけのjarファイルが提供されると、殺意に近いものを覚えることがアリマス。C++はヘッダファイルから定義情報が漏れるから言語としては不完全だという意見を耳にすることがありますが、開発を行う立場からすると利用できる情報が多くて困ることはありません。

注2:実行単価や設計者の意図を考えず、APIを呼びたいように呼んでおいて、得られた結果が欲しかったものだ、と開き直る態度のことを“What you get is what you want”といいます。かなりの数のソフトウェアエンジニアが大なり小なりこの病にかかっています。

文字列クラスのヘッダを見てみよう

 Symbian OSにおける文字列クラス群は以下のような構造を持つと、ファーストシーズンの第5回「堅牢で省資源な文字列“ディスクリプタ”」および前回で述べました(図1)

図1 文字列クラス体系図

 確かにそのとおりではあるのですが、前回「Symbian OSでの1文字は16ビットの幅であるUCS-2である場合と、8ビット幅であるCP1252である場合の2通りがあり得ます」(大意)とも書いています。では、図1のクラス構成は2種類の文字コードを許すようなものでしょうか。

 こういうときこそヘッダファイルの出番です。第3回「Symbian OSから見放されないための大事な約束」で説明したとおり、S60の「3rd Ed FP2」をインストールするとC:\S60\devices\S60_3rd_FP2_SDKをルートディレクトリとしてファイルが展開されます。この直下に生成されるepoc32というフォルダに、epoc32(つまりSymbian OS)にとって必要なファイルがすべて格納されます。この中にヘッダファイルを収めているincludeフォルダが存在します。ここがヘッダファイルクエストの起点となるフォルダです(図2)

図2 ヘッダファイルのルートフォルダ

 複数ファイルにまたがって文字列検索ができればどんなツールでも構いません(注3)。C:\S60\devices\S60_3rd_FP2_SDK\epoc32\includeを起点として、TDesCとclassの2つの文字列をANDで含む個所を検索してください(以下、このオペレーションを全検索と呼称します)。これはTDesCクラスの定義を行っている場所を探そうとしてのことです。検索すると以下の2つの行が見つかるはずです(リスト1)(注4)

C:\S60\devices\S60_3rd_FP2_SDK\epoc32\include\e32des16.h 26:    class TDesC16
C:\S60\devices\S60_3rd_FP2_SDK\epoc32\include\e32des8.h 26:     class TDesC8
リスト1 TDesCクラスの定義個所検索結果(抜粋)

注3:筆者の検索ツールは以下の出力形式を取っています。

<ファイル名(絶対パス)> <行番号>: <発見された行の内容>
今回の検索結果例もそのフォーマットに従っています。

注4:C:\S60\devices\S60_3rd_FP2_SDK\epoc32\include\linebreak.h 12: class TDesC16;
のような行も見つかりますが、当該個所を開いてみれば単なるクラス名の宣言のみです。本当のクラス宣言であればクラス名の後に実体が付くので、「;」で文が終わるはずがありません。

 見つかったクラス定義(リスト1)はTDesCそのものではありませんが、文字が16ビットの幅であるUCS-2である場合と、8ビット幅であるCP1252である場合に対応していそうではあります。早速、e32des16.hおよびe32des8.hをエディタで開いてみましょう。それぞれ確かに16ビット文字、8ビット文字に合わせたAPIが定義されています(配列としてアクセスするための[]演算子のオーバーロードも型別に定義されていますね)(リスト2)(リスト3)

inline const TUint16 &operator[](TInt anIndex) const;
inline TInt Length() const;
inline TInt Size() const;
IMPORT_C const TUint16 *Ptr() const;
リスト2 TDesC16抜粋

inline const TUint8 &operator[](TInt anIndex) const;
inline TInt Length() const;
inline TInt Size() const;
IMPORT_C const TUint8 *Ptr() const;
リスト3 TDesC8抜粋

 では、TDesCはどこにいるのでしょうか?

TDesCはどこにいるのか?

 そこでもう一度全検索です。TDesC16およびTDesC8がTDesCの基となりそうなことはヘッダファイルから何となく了解できます。またC++で別名を定義する場合には1)#define、2)typedefのいずれかを用いる必要があります。以上からTDesC16からTDesCが導出されている場所があるはずだと仮定して、

a)TDesC16とdefineのAND条件
b)TDesC16とtypedefのAND条件

でそれぞれ全検索を行ってみます。結論としてはa)に該当する行はなし、b)に該当する行は以下のようになりました(リスト4)

C:\S60\devices\S60_3rd_FP2_SDK\epoc32\include\e32cmn.h 1127:
 typedef TDesC16 TDesC;
C:\S60\devices\S60_3rd_FP2_SDK\epoc32\include\e32des16.h 622:
 typedef TRefByValue<const TDesC16> __TRefDesC16;
リスト4 TDesC16とtypedefのAND条件による全検索結果

 おぉ!! e32cmn.hに見つかったパターンはまさに期待していたもの、TDesC16を使ってTDesCが定義しているコードです。では、どのような条件でTDesC16 == TDesCとなるのか、詳細を調べるために当該行の周辺を眺めてみることにします。すると、下記のような条件コンパイルが行われていることが判明します(リスト5)

#if defined(_UNICODE) && !defined(__KERNEL_MODE__)
リスト5 e32cmn.h内、TDesC定義のチョット上(抜粋)

 _UNICODE環境であり、かつ_KERNEL_MODE_でなければ(注5)、TDesCはTDesC16であるという定義がこの条件コンパイル文で保証されるわけです(当然TDesC8は条件コンパイルの#else側で、TDesC8 == TDesCであるという定義を行っています)。

 というわけで、ワレワレのコンパイル環境においては_UNICODEが定義されており(じゃないと日本語が使えません)、e32cmn.hの条件コンパイルが真になるため、TDesC16 == TDesCが成立することが明らかになりました。あ〜長かったですね。でも、アナリーゼとはこのようなものです。以後、『TDesCとその派生クラスのことを調べるときには、e32des16.hのTDesC16とその派生クラスを見ればよいことが保証された』のですから、それはそれで素晴らしいことなのです。

注5:KERNEL_MODE_とはカーネルで動くコードをコンパイルするときに定義するシンボルです。ここから先に興味がある方は「Symbian OS Internals リアルタイムカーネルプログラミング」という書籍がありますので、まずはそちらをご覧ください。
  • 連載バックナンバー
  • 全記事インデックス
  • 組み込み開発トップ
  • MONOistトップ

ホワイトペーパーTechTargetジャパン

メールマガジン

スキルアップ/キャリアアップ(JOB@IT)

スポンサーからのお知らせ

Windows Embeddedコーナー

Windows Embedded
Windows Embedded専門コーナー。Windows Embedded StandardやWindows Embedded CEをはじめとする「Windows Embedded」ファミリの最新動向や技術情報をお届けします!!

Androidコーナー

Android
Android専門コーナー。組み込みデバイスへの適用からアプリケーション開発、イベントレポート、ニュースなどAndroidに関するさまざまな技術情報がここに集結!!

@IT MONOist 求人情報