#include <iostream.h> #include <iomanip.h> #define IOMANIPdeclare(typ) 次の定義に展開 ... class SMANIP(typ) { public: SMANIP(typ)(ios& (*)(ios&, typ), typ); friend istream& operator>>(istream&, SMANIP(typ)&); friend ostream& operator<<(ostream&, SMANIP(typ)&); }; class SAPP(typ) { public: SAPP(typ)(ios& (*)(ios&, typ)); SMANIP(typ) operator()(typ); }; class IMANIP(typ) { public: IMANIP(typ)(istream& (*)(istream&, typ), typ); friend istream& operator>>(istream&, IMANIP(typ)&); }; class IAPP(typ) { public: IAPP(typ)(istream& (*)(istream&, typ)); IMANIP(typ) operator()(typ); }; class OMANIP(typ) { public: OMANIP(typ)(ostream& (*)(ostream&, typ), typ); friend ostream& operator<<(ostream&, OMANIP(typ)&); }; class OAPP(typ) { public: OAPP(typ)(ostream& (*)(ostream&, typ)); OMANIP(typ) operator()(typ); }; class IOMANIP(typ) { public: IOMANIP(typ)(iostream& (*)(iostream&, typ), typ); friend istream& operator>>(iostream&, IOMANIP(typ)&); friend ostream& operator<<(iostream&, IOMANIP(typ)&); }; class IOAPP(typ) { public: IOAPP(typ)(iostream& (*)(iostream&, typ)); IOMANIP(typ) operator()(typ); }; IOMANIPdeclare(int); IOMANIPdeclare(long); smanip_long resetiosflags(long); smanip_int setbase(int); smanip_int setfill(int); smanip_long setiosflags(long); smanip_int setprecision(int); smanip_int setw(int);
マニピュレータは、ストリームに挿入または抽出されるオブジェクトのように見えますが、多くはストリームの状態を変更するだけです。それらの目的は、利便性のため、実際には関数呼び出しであるものを挿入または抽出のシーケンスに組み込むことです。たとえば、次のように書く代わりに:
cout.width(8); cout << val1 << " "; cout.width(4); cout << val2 << '\n'; cout.flush();
次のように書くことができます。
cout << setw(8) << val1 << " " << setw(4) << val2 << endl;
ios(3CC4)、istream(3CC4)、および ostream(3CC4) で説明されているような定義済みのマニピュレータがいくつかあります。これらは、上で使用されているように endl や flush などの引数をとらない簡単なものです。マニピュレータは、上で使用されている setw や setfill などの引数もとることがあります。ヘッダーファイル <iomanip.h> は、これらのマニピュレータの一部を定義し、独自のものを作成するために必要な定義も提供しています。
論理上、マニピュレータはテンプレートとして定義されますが、C++ 言語では、テンプレートが利用可能になる前に導入されました。したがって、それらはテンプレートをシミュレートするマクロとして定義されます。マクロ IOMANIPdeclare(typ) は、型 typ の 1 つのパラメータをとるすべてのマニピュレータの定義に展開されます。マクロの性質上、型パラメータは単純な型名 (識別子だけ) であることが必要です。ヘッダーは、int および long 型の展開された定義を提供します。
引数を持たないマニピュレータは、ストリームへの参照型の 1 つのパラメータを持つ関数で、同じ型の値を返します。ストリームには、そのような関数をパラメータとしてとる多重定義された演算子があらかじめ定義されています。マニピュレータは、ストリーム引数で必要なすべての操作を実行し、同じストリームを返します。たとえば、tab マニピュレータのコードは次のようになります。
ostream& tab(ostream& s) { s << '\t'; return s; }
式 cout<<tab の中で、多重定義された演算子
ostream& operator<< (ostream& (*)(ostream&))
が選択され、これはマニピュレータ関数を呼び出すだけです。結果の呼び出しは、ostream にタブ文字を挿入します。
パラメータを持つマニピュレータは、2 つの部分があります。
manip 部分: ストリームと typ 引数をとり、ストリームを返す関数。
apply 部分: manip 部分によって呼び出され、状態の変更やほかの操作を適用する関数。
指定された型 typ に対し、IOMANIPdeclare マクロによってすべての部分が宣言されます。それらを次で説明します。
以下の説明では、次の宣言を想定しています。
typ - some type name n - an int l - a long s - an ios i - an istream o - an ostream io - an iostream f - an ios& (*) (ios&) if - an istream& (*) (istream&) of - an ostream& (*) (ostream&) iof - an iostream& (*) (iostream&)
s << SMANIP(typ)(f, t) s << SAPP(typ)(f)(t) s >> SMANIP(typ)(f, t) s >> SAPP(typ)(f)(t)
f(s,t) を返します。ストリーム s はほかのストリーム型 i、o、または io のいずれかにもなることがあります。
i >> IMANIP(typ)(if, t) i >> IAPP(typ)(if)(t)
if(i,t) を返します。
o << OMANIP(typ)(of, t) o << OAPP(typ)(of)(t)
of(o,t) を返します。
io << IOMANIP(typ)(iof, t) io << IOAPP(typ)(iof)(t) io >> IOMANIP(typ)(iof, t) io >> IOAPP(typ)(iof)(t)
iof(io,t) を返します。
<iomanip.h> ヘッダーには int または long 引数 (前述の表現では typ) をとるマニピュレータの宣言が含まれます。定義済みのマニピュレータはすべて、次のようにストリームの状態を変更するために使用されます。
ストリーム i または o のフィールド幅を n に設定します。注: 次の書式付き挿入または抽出では、幅が 0 にリセットされます。
ストリーム i または o の充てん文字を n に設定します。
ストリーム i または o の精度変数を n に設定します。
l に設定されている、ストリーム i または o の ios フラグをオンにします。
l に設定されている、ストリーム i または o の ios フラグをオフにします。
独自のパラメータ化されたマニピュレータを作成するには、manip 関数を宣言してから、manip 関数と apply 関数の両方を実装する必要があります。マニピュレータが型 typ の引数をとる場合、その型の引数を 1 つとり、型 Xmanip_typ のオブジェクトを返す manip 関数を宣言します。型 ios、istream、ostream、または iostream のオブジェクトを操作しようとする場合は、X を s、i、o、または io のいずれかにそれぞれ置き換えてください。typ は実際の型名に置き換えます。int および long 型については、すべての宣言がすでに準備できています。ほかの型については、「テンプレート」IOMANIPdeclare(typ) を呼び出す必要があります。
apply 関数は manip 関数によってのみ呼び出されるため、通常は静的です。操作対象のストリームの型への参照を返し、その同じ型の第 1 パラメータ、および型 typ の第 2 パラメータを持つように定義します。
たとえば、setiosflags マニピュレータを考えます。このマニピュレータは ios 上で動作し、long パラメータをとるため、smanip_long 型を返すように宣言されています。これが ostream 上で動作し、int パラメータをとる場合は、戻り型 omanip_int を返すように宣言されます。したがって、マニピュレータ (manip 関数) は、ヘッダーで次のように宣言されます。
smanip_long setiosflags(long);
apply 関数はユーザーコードから直接呼び出されることはないため、ヘッダーには表示されません。実装モジュールでは、これらの両方の関数のコードは次のようになります。
// set the flags bitvector according to the bits set in b static ios& sios(ios& i, long b) // private apply function { i.setf(b); return i; } smanip_long setiosflags(long b) // public manip function { return smanip_long(sios, b); }
ios (3CC4) , ios.intro (3CC4) , istream (3CC4) , ostream (3CC4)
『C++ ライブラリリファレンス』第 3 章「従来型の iostream ライブラリ」