"Le pourquoi du comment" ( - "どうしてそうなの?")
format は新しいライブラリだ。そのゴールの一つは、 printf の代替物を提供することにある。つまり、 format は printf 用に設計された書式文字列を構文解析することができて、与えられた引数にその書式を適用して printf と同じ結果を生成できる。
この制限の下で、書式文字列の文法には大雑把に3つの選択肢が有り得た :
人々をそれほど混乱させないだろうと期待できる。format(" %s at %s with %s\n") % x % y % z;
演算子の別の恐怖は優先順位の問題だ。
format("%s") % (x+y) と書かずに
format("%s") % x+y を書いた場合どうなるだろう?
これだとコンパイル時に問題が起きるので、エラーはすぐに検出されるだろう。
もちろん、この行は tmp = operator%( format("%s"), x) を呼び、
それから operator+(tmp, y) を呼ぶ。
暗黙の変換が定義されていない限り tmp は format オブジェクトとなるだろう。そのため operator+ の呼び出しは失敗する。 (もちろん、君がそんな演算子を定義した場合は除く)。
だから君は優先順位の間違いはコンパイルの際に知らされると安心して決め込んでいい。
その一方で、関数アプローチには本物の不便さがある。
多くのテンプレート関数を定義する必要があるんだ。こんな感じに :
そして N を 500 まで定義したとしても、 まだ C の printf にはない上限を設けることになる。template <class T1, class T2, .., class TN> string format(string s, const T1& x1, .... , const T1& xN);
いずれにせよ、もし僕らが実際にフォーマルな関数呼び出しテンプレートの仕組みを選択していたら、
が与えられているクラス T しか表示することができなかっただろう。 なぜなら、 const と 非 const の両方を許容すると組み合わせ爆発が生じるからだ - もし 10 個までの引数で行くにしても、 2^10 個の関数が必要になる。operator<< ( stream, const T&)
結論として、コンパイル時に引数の数を知ることができない場合には、専用の二項演算子を用いることが最もシンプルで、ロバストで、かつ制限の少ない引数渡しのメカニズムなんだ。
は、format(fstr) % x1 % x2 % x3;
と同じ構造をしている。後者には優先順位の問題は何も無い。 後者のただ一つの欠点は、演算子を用いるのに比べて、一見してこの行が何をしているのか 把握しづらいということだ。 .with(..) を呼び出すのは、コードのほかの行でやっていることと同じように見える。 だから、好みの問題だけど、この方がより良いな解決方法だろう。 余計な文字を用いる点と、'with(..)'を用いたコードの行の全般的に散らかった側面は、僕に真の演算子を選択させるのに十分だった。format(fstr).with( x1 ).with( x2 ).with( x3 );
cout << format("%s %s ") << x; cout << y ; // うわぁ、 format は本当はストリームマニピュレータじゃないよ
は正確には次のように扱われる :cout << format("%s %s ") % x % y << endl;
だから % を用いることで、 format オブジェクトの寿命が周囲のストリームの文脈を妨げることはない。 これはあり得る振る舞いの中で最も単純なものだ。そのためユーザは format オブジェクトの後でストリームを使いつづけることができる。cout << ( format("%s %s ") % x % y ) << endl;
は次のように理解される :cout << format("%s %s ") << x << y << endl;
代替となる実装の中には << 演算子を選択しているものもあるが、これが働くようにする方法は一つしかない :( ( ( cout << format("%s %s ") ) << x ) << y ) << endl;
呼び出しは プロクシを返す。プロクシは最終的な出力先 (cout) と書式文字列の情報をカプセル化している。operator<<( ostream&, format const&)
僕はいくつか考え得る実装を試してみたけど、どれも完璧には希望に沿っていない。
例えば : ユーザの誤りを捕らえるために、引数が多く渡されすぎたときに例外を発生するのは筋が通っている。
しかしこの文脈では、余分な引数が最終的な出力先に向けられていることはほとんど間違いない。
ここでいくつかの選択肢がある :
endf はプロキシの内部に保持されていた最終的な出力先を返す関数として定義できる。 それで万事解決だ。 endf の後は、ユーザは再び cout に向けて << を呼んでいる。cout << format("%s %s ") << x << y << endf << endl;
だからその解決方法は operator<< をマニピュレータに対してオーバーロードすることだ。 この方法では endf は不要だが、マニピュレータ以外のものを format の引数の後に出力する事はできない。cout << format("%s %s \n") << x << y << flush ;
これと次を比較すると :cout << format("%s %s %s") %x %y %z << "And avg is" << format("%s\n") %avg;
"<<" は、ストリームに渡されているオブジェクトと同じレベルで引数を与えているから、間違いを起こしやすい。cout << format("%s %s %s") << x << y << z << endf <<"And avg is" << format("%s\n") << avg;
operator() には、関数に引数を送る自然な方法であるというメリットがある。
また、 operator[] の意味が format で使うには上手く当てはまると考える人もいる。
技術的にはこれらは operator% と同じくらい良い選択だ。しかしすごく醜い。 (好みの問題だ)
それにそもそも、書式文字列の中の "%" で参照されている引数を operator% を使って渡すことは、それらの演算子を使うよりずっと自然に見える。
July 07, 2001
© Copyright Samuel Krempp 2001. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright notice appears in all copies. This document is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.
Japanese Translation Copyright © 2003 Kent.N
オリジナルの、及びこの著作権表示が全ての複製の中に現れる限り、この文書の複製、利用、変更、販売そして配布を認める。このドキュメントは「あるがまま」に提供されており、いかなる明示的、暗黙的保証も行わない。また、いかなる目的に対しても、その利用が適していることを関知しない。
このドキュメントの対象: Boost Version 1.29.0
最新版ドキュメント (英語)