このあと書式付き出力について考えて続けていたんですが。ずるずるとやっているうちに先々月のエントリになってしまった。ま、それはそれとして。
C++のiostreamの書式指定のやり方がなんとも居心地がよくないと感じる理由の一つは、出力するオブジェクト(数値とか)の書式を変更するために出力先(出力相手)のstd::cout
の状態を変化させてしまうところにあるのかもしれないと思いいたった次第。
オブジェクトが出力されるときに、オブジェクト自身が出力形式を知っているという方がやはりオブジェクト指向っぽいと思うわけで。
たとえばC++なら。
std::cout << "x = " << x << ", y = " << y << std::endl;
Rubyなら。
"x = #{x}, y = #{y}"
Javaなら。
"x = " + x + ", y = " + y
x
とかy
が何者かわからなくても出力すること(上のRubyやJavaの例では文字列に変換すること)ができてしまう。
書式を本当に指定する必要があるのは誰か
その一方で。
使われるオブジェクト自身と、そのオブジェクトを利用するコンテクスト(…適切かどうかはひとまずおいていて、ここではこの言葉を使っておきます)のどちらが書式を指定する責任というか権利があるか?と考えてみると、コンテクスト側にその権利がありそうな気がします。というかどういう形式で出力するかというのはオブジェクトの外側の話のような気がするわけです。
で。どのように出力されるかはオブジェクト自身が知っていて、かつ、書式はコンテクストが指定できるようにするにはどうしたらいいか?と考えて、気がつけばひと月が過ぎ。
ベタな解としては。オブジェクトに書式を指定する操作を追加してあげることですが…。
std::cout << "x = " << x. left().width(10).precision(3) << std::endl;
…これはさすがに使い勝手悪すぎ。
オブジェクトを出力するとき(あるいは文字列に変換するとき)に、そのオブジェクトに書式を与えられればいいのではないかと。
たとえば。
std::cout << "x = " << x.format("-10.3") << std::endl;
前回書いたマニュピレータをメンバ関数にしただけと言われればそれまでなんですが。
ただ実際にこうしたしくみを使っているものもすでにあり。たとえばRubyのTime
クラス。
t = Time.now t.strftime("Printed on %m/%d/%Y") #=> "Printed on 04/09/2003" t.strftime("at %I:%M%p") #=> "at 08:56AM"
(class Time (Ruby 1.8.7)のstrftime
のサンプルから)
オブジェクトごとに書式指定の文法を作るのかとか、各々にパーサを作るのかとか、これはこれで面倒で悩み解決とはいかないわけですが、オブジェクトをデザインするときには一考してみるのもいいかも。