堅牢で省資源な文字列“ディスクリプタ”:Symbian OS開発の勘所(5)(3/3 ページ)
Symbian OSでの文字列(ディスクリプタ)は、実装を隠し機能のみを公開することに加えて、リソースを徹底的にケチる設計となっている
ディスクリプタ−Symbian OSの文字列
いよいよSymbian OSにおける文字列にたどり着きました。上述の現代的なロバストな文字列の扱い方からすると、実装は隠ぺいし、インターフェイスには機能群だけを提供することになります。
Symbian OSでは文字列のことをディスクリプタ(descriptor)と呼びます。ディスクリプタの特徴は、
- 文字列を格納する領域のサイズを知っている(バッファオーバーランの防止)
- ターミネータに依存しない
となっており、文字列に対して求められている要求に応えられるようになっています。
しかしSymbian OSは第1回で挙げたとおり、以下の目標を持つOSです。
# | 目標 |
---|---|
1 | 長期間稼働しても問題がないように、メモリのリークがないようにせよ |
2 | リソースの使用を最小限にするようにせよ |
3 | 省電力機構が使用できるように、不要なときには必ずOSに制御を渡すようにせよ |
このうちの2番を実現するために、Symbian OSでは文字列クラスに図2のような階層を導入しています。
図2はディスクリプタのクラスがどのような階層構造を持っているかを示したものですが、数えてみるとクラスの数が9もあります。表1にあるほかの環境の文字列クラスとずいぶん違う構成です。ここにSymbianが省リソースのために行ったデザインを見ることができます。
インターフェイスと実装の分離−インターフェイス編
ディスクリプタの利用者を考えてみましょう。ここでいう利用者とは、どこかで構築されたディスクリプタを使うプログラムを指します。具体的にはディスクリプタを引数として、参照、更新を行うメソッドです(値を確保するのはメソッドの呼び出し元の仕事です)。
利用者がプログラムのうえで気にすることは、どのようにして
- 値を格納、取り出し、複製する
- 検索、置換、編集する
か、ということです。これを逆にすると、ディスクリプタの生成元でメモリや使用状況に合わせたクラスを使い分けていたとしても、利用者に違いを意識させずに済むのであればOKということになります。オブジェクト指向でいう「インターフェイスと実装の分離」です。ディスクリプタの設計方針がまさにこれに該当します。
ディスクリプタでは以下の2つのインターフェイスを利用者に提供します。
class TDesC { public: TInt Length() const; TInt Size() const; …中略… TInt Compare( const TDesC& aDes ) const;// 比較( =, <, > ) TInt Match( const TDesC& aDes ) const; // パターンマッチ TInt Locate( TChar aChar ) const; // キャラクタの位置 TInt Find( const TDesC& aDes ) const; // 部分文字列の検索 TPtrC Left(TInt aLength) const; // 左端からデータを切り出す TPtrC Right(TInt aLength) const; // 右端からデータを切り出す TPtrC Mid(TInt aPos) const; // 途中から一部のデータを切り出す …その他いろいろ… private: TUint iLength; };
書き換え不能(immutable)なインターフェイス |
class TDes : public TDesC { public: inline TInt MaxLength() const; inline TInt MaxSize() const; …中略… void Append( const TDesC& aDes ); // データ追加 void LowerCase(); // 小文字変換 void UpperCase(); // 大文字変換 void Insert( TInt aPos,const TDesC& aDes ); // 指定位置にディスクリプタを追加 void Delete( TInt aPos,TInt aLength ); // 指定位置のデータを削除 …その他いろいろ… protected: TInt iMaxLength; };
利用者は文字列の更新の有無に応じてTDesまたはTDesCを使い分けます。だからSymbian OSの世界では、ディスクリプタを引数に取るメソッド(== ディスクリプタの利用者)は以下のような宣言を持ちます。実体クラスの型を引数に持つわけではないのです。
void DoSomething( const TDesC& aDes );
書き換える気がない場合 |
void DoSomething( TDes& aDes );
書き換える気がある場合 |
インターフェイスと実装の分離−実装編
ではディスクリプタを実際に確保する側は何を意識するのでしょうか。ここがリソースをケチる肝になります。
これらを勘案して、リソース、性能に最適な方法で確保することがディスクリプタのインスタンスを用意する側に要求されます。どうやって? それが図2の中に実装クラス群が複数用意されている理由です。
ディスクリプタの実装クラス群は以下に示すとおり、上記の性質に沿って分類されます。
要求されている性質に応じて上記クラスを使い分けることにより、自然にリソースをケチることができる。それがSymbian OSが行ったデザインです。特にPtr型は強力で、既存領域の中の任意の部分をポイントしますから(注4)、実体がROMにあろうがヒープにあろうが、TDesCで示されるサービスを提供できます。またこの性質に基づき、TDesCの中のLeft()、Right()、Mid()などのメソッドはTPtrCを返すようになっています。
お気付きかもしれませんが、クラスの性質を表す命名規則が存在しています。
上記の実装クラス群はこのルールに従い命名されています。と書くと「え、HBufCは書き換え不能なの? では書き換え可能なヒープディスクリプタはどこに?」という疑問を持たれたでしょうか。Symbian OSに引っ越してきた方は、ほぼ必ずここで戸惑うようです。
実はHBufCクラスは
TPtr Des();
というメソッドを持っています。これを用いることによってTPtrクラス経由でHBufCの実体を書き換えることが可能になります。直感的ではないなあ、と思われるかもしれません。ううむ確かに。いろいろ理由はあるが、そういうものだ。ここではそのように思っておいてください(内部構造に立ち入った説明を展開すると、つじつまが合わないわけではありません。興味がある方は、例えば書籍『Symbian OS C++実践開発技法』のP79あたりで展開されている考察をご覧ください)。
実装を隠ぺいし、機能のみを公開するというのが一般的なソフトウェアの進化の道筋です。しかしSymbian OSではそれに加えて携帯・スマートフォンのようなリソース制限のある環境で性能を発揮するため、「インターフェイスと実装の分離」を用いて
ケチれるところは、利用者に意識させずにこっそりと、しかし徹底的にケチる
という欲張りなデザインを取っています。このあたりが顕著に出ているのがSymbian OSにおける文字列、ディスクリプタです。オブジェクト指向による記述の容易さと、性能の追求、これらのバランスを高度に取っているところにSymbian OSの面白さがあります。
次回はいよいよSymbian OSの3大ポイントの2番目、非同期フレームワークの解説に向かいたいと思います。(次回に続く)
Copyright © ITmedia, Inc. All Rights Reserved.