Copyright 1999 Rogue Wave Software
Copyright 1999 Sun Microsystems, Inc.
仮想ストリーム |
5 |
![]() |
しかし、iostream 機能には制限も多数あります。特に、書式指定機能が劣ります。たとえば、double を ostream に挿入する場合、それをバイナリとして挿入するための型保証の方法がありません。さらに、すべてのバイトの送信元と受信先が streambuf モデルに適合しているわけではありません。XDR など多くのプロトコルでは、フォーマットはバイトストリームに組み込み式で結合され、分離することができません。
Rogue Wave の仮想ストリームは、理想的なストリームモデルを提供することによって、これらの制限を克服しています。書式やストリームモデルについて、なんの仮定も設けていません。仮想ストリームのクラス階層の一番下にあるのはクラス RWvios です。これは抽象基底クラスのひとつで、標準ライブラリクラス ios とよく似たインタフェースを備えています。
RWvios の特殊化版が、これらの関数の定義を提供します。class RWvios{
public:
virtual int eof() = 0;
virtual int fail() = 0;
virtual int bad() = 0;
virtual int good() = 0;
virtual int rdstate() = 0;
virtual int clear(int v = 0) = 0;
};
RWvios から継承されるのが抽象基底クラス RWvistream および RWvostream です。これらのクラスは、基本的な組み込み型および組み込み型の配列のすべてについて一式の純粋な仮想関数 (operator<<()、put()、get() など) を宣言します。
RWvistream や RWvostream から継承されるストリームは、これらの組み込み型や組み込み型の配列を、そのクラスのユーザーに対して透過的な形式で特殊化ストリームに格納します。class RWvistream : public RWvios {
public:
virtual Rwvistream& operator>>(char&) = 0;
virtual Rwvistream& operator>>(double&) = 0;
virtual int get() = 0;
virtual Rwvistream& get(char&) = 0;
virtual Rwvistream& get(double&) = 0;
virtual Rwvistream& get(char*, size_t N) = 0;
virtual Rwvistream& get(double*, size_t N) = 0;
.
.
.
};
class RWvostream : public RWvios {
public:
virtual Rwvostream& operator<<(char) = 0;
virtual Rwvostream& operator<<(double) = 0;
virtual Rwvostream& put(char) = 0;
virtual Rwvostream& put(double) = 0;
virtual Rwvostream& put(const char*, size_t N) = 0;
virtual Rwvostream& put(const double*, size_t N) = 0;
.
.
.
};
仮想ストリームの特殊化
Rogue Wave のクラスには、RWvistream および RWvostream を特殊化する 4 種類のクラスが含まれています。それらのクラスは可搬な ASCII 形式、バイナリ形式、エンディアン形式、XDR 形式をそれぞれ使用します。1
| Input class | Output class | |
|---|---|---|
| 抽象基底クラス | RWvistream | RWvostream |
| 可搬 ASCII | RWpistream | RWpostream |
| バイナリ | RWbistream | RWbostream |
| エンディアン | RWeistream | RWeostream |
| XDR | RWXDRistream | RWXDRostream |
可搬な ASCII 形式を使用するクラスは、新しいオペレーティングシステムの下でも正しく復元されるように、タブや復帰改行などの特殊文字をエスケープする ASCII 形式で挿入項目を格納します。バイナリ形式を使用するクラスは、挿入項目を再フォーマットせず、それらの固有の形式で格納します。エンディアンバージョンを使用すると、バイナリ形式の領域の効率と時間的な効率がよくなり、さらに、ビッグエンディアン、リトルエンディアン、ネイティブのどの形式でも情報を格納したり取り出したりできます。XDR 形式を使用するクラスは、項目を XDR ストリームに送り、ネットワークを通じて遠隔地に伝送します。
これらのクラスはいずれも状態を保持しないので、通常のストリーム (XDR も含む) と自由に切り替えて利用できます。これらのクラスを使用するからといって、すべてのファイル入出力をそれぞれの形式に限定するというわけではありません。詳細は、『Tools.h++ 7.0 クラスライブラリ・リファレンスマニュアル』を参照してください。
簡単な例
以下に、RWbostream と RWbistream を、それぞれの抽象基底クラスである RWvostream と RWvistream を通じて実行する簡単な例を示します。
#include <rw/bstream.h>
#include <rw/cstring.h>
#include <fstream.h>
#ifdef __BORLANDC__
# define MODE ios::binary // 1
#else
# define MODE 0
#endif
void save(const RWCString& a, RWvostream& v){
v << a; // 仮想出力ストリームに保存する
}
RWCString recover(RWvistream& v) {
RWCString dupe;
v >> dupe; // 仮想入力ストリームから復元する
return dupe;
}
main(){
RWCString a("A string with\ttabs and a\nnewline.");
{
ofstream f("junk.dat", ios::out|MODE); // 2
RWbostream bostr(f); // 3
save(a, bostr);
} // 4
ifstream f("junk.dat", ios::in|MODE); // 5
RWbistream bistr(f); // 6
RWCString b = recover(bistr); // 7
cout << a << endl; // 2 つの文字列を比較する // 8
cout << b << endl;
return 0;
}
|
A string with tabs and a newline. A string with tabs and a newline. |
| //1, //2 | これらの行では、ファイル出力ストリーム f がファイル junk.dat に対して作成されます。多くの PC コンパイラのデフォルトファイルオープンモードは text であり、DOS(Windows を含む) の自動復帰改行変換を避けるために、明示フラグの ios::binary を使用する必要があります。1 |
| //3 | この行では、f から RWbostream が作成されます。 |
| //4 | この句は中括弧 {...} で囲まれているので、f のデストラクタがここで呼び出されます。これによって、このファイルがクローズされます。 |
| //5 | このファイルが再オープンされます (今回は入力用)。 |
| //6 | このファイルから今度は RWbistream が作成されます。 |
| //7 | このファイルから文字列が復元されます。 |
| //8 | 最後に、元の文字列と復元された文字列の両方が比較のために出力されます。 |
| 1
多くの PC コンパイラでは、ios::binary フラグによってファイルがオープンされていないかぎり、ostream::write() と istream::read() もテキスト変換を実行します。
|
クラス fstream を使用すると、このプログラムを簡略化することができます。このクラスは、出力と入力の両方で ofstream と ifstream を多重継承します。ファイルの始まりのシークは、結果を読み戻す前に起こります。seekg() の初期の実装には、信頼性の低いものがいくつかあるため、この例では、簡単な方法を選択していません。
Windows のクリップボードと DDE Streambufs
前の節では、ストリームに挿入された項目の書式を仮想ストリーム機能がどのように抽象化するかを説明しました。ストリームに挿入された項目の設定も抽象化されています。これは、使用される streambuf の型によって設定されます。
この結果、たとえば、複雑な構造を従来のディスクベースのファイルに保管するときに使用するものと同じコードを使用して、DDE 機能によりその構造を別のアプリケーションに転送することなどができます。
DDE の例
クラス RWDDEstreambuf を使用して、Windows DDE 機能によって RWBinaryTree を交換する方法を示すより複雑な例を次に示します。Windows のクリップボードの場合も、同様に技術を使用します。
#include <rw/bintree.h>
#include <rw/collstr.h>
#include <rw/bstream.h>
#include <rw/winstrea.h>
#include <windows.h>
#include <dde.h>
BOOL
PostCollection(HWND hwndServer, WORD cFormat){
RWBinaryTree sc; // 1
sc.insert(new RWCollectableString("Mary"));
sc.insert(new RWCollectableString("Bill"));
sc.insert(new RWCollectableString("Pierre"));
// RWDDEstreambuf を割り当て、それを使用して
// RWbostream を初期化する
RWDDEstreambuf* sbuf = new RWDDEstreambuf(cFormat, // 2
FALSE, // 3
TRUE, // 4
TRUE); // 5
RWbostream bostr( sbuf ); // 6
// コレクションを RWbostream に格納する
bostr << sc; // 7
// 出力ストリームをロックして、そのハンドルを獲得する
HANDLE hDDEData = sbuf->str(); // 8
// アトムを獲得して、DDE メッセージを識別する
ATOM atom = GlobalAddAtom("SortedNames"); // 9
// DDE 応答を送信する
return PostMessage(0xFFFF, WM_DDE_DATA, hwndServer, //10
MAKELONG(hDDEData, atom));
}
|
前述のコードでは、ラージメモリーモデルが想定されています。次に、行単位の説明を示します。
| //1 | RWBinaryTree が作成されて、そこにいくつかの項目が挿入されます。 |
| //2〜//5 |
RWDDEstreambuf が割り当てられます。コンストラクタは、いくつかの引数をとります。最初の引数は、Windows のクリップボード形式です。この例では、形式の型が引数として渡されていますが、通常は、Windows の RegisterClipboardFormat() を使用して形式を登録し、その形式を使用します。
その他の引数は、DDE データ交換の応答とメモリー管理の複雑さと関係があります。引数のリストについては、『Tools.h++ 7.0 クラスライブラリ・リファレンスマニュアル』を参照してください。また、その意味については、『Petzold (1990), Chapter 17』か『Microsoft Windows Guide to Programming』を参照してください。 |
| //6 | RWbostream が、提供されている RWDDEstreambuf から作成されます。ここで、RWpostream を使用することもできますが、DDE 交換は同じマシンアーキテクチャ内で実行されるため、可搬 ASCII 形式を使用して余分なオーバーヘッドをかける価値はありません。しかし、streambuf の型によって行われるバイト列の配置方法が、RWvostream の型によって設定されるその書式とどれぐらいはっきりと分離されるかには注意してください。 |
| //7 | コレクションが RWbostream に格納されます。RWbostream に関連する streambuf は実際には RWDDEstreambuf であるため、コレクションは実際には、特性 GMEM_DDESHARE を持つ Windows の大域メモリー割り当てに格納されます。この割り当ては、オーバーフローすると自動的にサイズ変更されます。他の strstreambuf と同様、割り当てチャンクのサイズは、メンバー関数 setbuf() を使用して変更することができます。 |
| //8 | RWDDEstreambuf がロックされます。この streambuf は、str() を使用してロックされると、他の strstreambuf と同様、再使用することができません。ただし、RWDDEstreambuf::str() は、char* ではなくハンドルを返すことに注意してください。ハンドルは、ロック解除されてから返されます。 |
| //9 | アトムが作成されてこの DDE データを識別します。 |
| //10 | RWDDEstreambuf::str() によって返されるハンドルが、その識別アトムとともに送信されます。 |
クリップボードの交換では、実際にはこれよりも簡単な類似の技法を使用することができます。
特殊な streambufs である RWCLIPstreambuf と RWDDEstreambuf を Rogue Wave 仮想ストリーム機能だけで使用する場合、制限は何もないことに注意してください。これらは、通常の istreams と ostreams であれば、非常に簡単に使用することができます。実行時にフォーマットを設定できないだけです。
RWAuditStreamBuffer
クラス RWDDEstreambuf と RWCLIPstreambuf は、streambuf を特殊化し、Windows API に従って文字を渡します。ただし、streambuf には、他に役立つ特殊化があります。クラス RWAuditStreamBuffer を使用すると、各文字に選択した関数を任意で呼び出している間、すべてのストリームのバイトをカウントすることができます。『Tools.h++ 7.0 クラスライブラリ・リファレンスマニュアル』のコードの例を参照してください。
まとめ
この章では、ストリームのバイト列の最終的な宛先を考慮せずに、どのようにオブジェクトをストリームに格納し、またストリームから復元するかについて学習しました。宛先はメモリー内であっても、ディスク上であってもかまいません。また、ストリームの最終的な書式を考慮する必要がないということも学習しました。書式は ASCII でもバイナリでもかまいません。
ユーザーが RWpostream と RWpistream のように特殊化された "仮想ストリーム" クラスを独自に記述することも可能です。仮想ストリーム機能の最大の利点は、ユーザーが独自の特殊化仮想ストリームを書くときに、クライアントクラスのコードをまったく修正する必要がないということです。目的のストリームクラスを、以下に示すものへの引数として使用するだけでよいのです。
RWvostream& operator<<(RWvostream&, const ClassName&); RWvistream& operator>>(RWvistream&, ClassName&);