c++boost.gif (8819 bytes)The Boost Format library

<boost/format.hpp> format クラスは printf に似た、ユーザ定義型も扱える型安全な書式化処理を提供する。
(このライブラリは他の boost ライブラリに依存しない)


Synopsis

format オブジェクトは書式文字列から構築され、その後 operator% を繰り返し呼び出されることで引数を与えられる。
それぞれの引数は文字列に変換され、書式文字列に従って順に一つの文字列へと結合される。

cout << boost::format("writing %1%,  x=%2% : %3%-th try") % "toto" % 40.23 % 50; 
     // "writing toto,  x=40.230 : 50-th try"と表示

How it works

  1. 書式文字列 s を伴って format(s) を呼び出すと、あるオブジェクトが構築される。このオブジェクトは、書式文字列を構文解析してすべての命令を探し、次のステップのために内部構造を準備する。
  2. そして、すぐに
    cout << format("%2% %1%") % 36 % 77 )
    のようにするか、あるいは後で、
    format fmter("%2% %1%");
    fmter % 36; fmter % 77;
    とすることで、フォーマッタに変数を食わせることができる。
    変数は内部のストリームにダンプされる。ストリームの状態は、与えられた書式文字列の書式化オプション(あれば)によってセットされる。 format オブジェクトは最後のステップのための結果文字列を保持する。
  3. すべての引数を与えてしまえば、その format オブジェクトをストリームにダンプしたり、メンバ関数 str() か名前空間 boost::io にある str(const format&) 関数で文字列を取り出すことができる。結果の文字列は、別の引数が与えられて再初期化されるまで、 format オブジェクトの中にアクセス可能な状態で残る。

    
    // 先ほど作って引数を与えた fmter の結果を表示:
    cout << fmter ;  
    
    // 結果の文字列を取り出せる:
    string s  = fmter.str();
    
    // 何度でも:
    s = fmter.str( );
    
    // すべてのステップを一度に行うこともできる:
    cout << boost::format("%2% %1%") % 36 % 77; 
    string s2 = boost::io::str( format("%2% %1%") % 36 % 77 );
    
    
  4. ステップ3の後で format オブジェクトを再利用し、ステップ2からやり直すこともできる: fmter % 18 % 39;
    新しい変数を同じ書式文字列で書式化する際は、こうすることでステップ1で生じる高価な処理を節約できる。
結局のところ、 format クラスは、書式文字列(printf に似た命令を用いる)を内部のストリームへの操作に翻訳する。そして最終的に、その書式化の結果を文字列として、あるいは直接に出力ストリームへと返す。

Examples

using namespace std;
using boost::format;
using boost::io::group;
using boost::io::str;


Sample Files

sample_formats.cpp format の簡単な使い方をデモする。

sample_new_features.cpp は、単純な位置指定命令、中寄せ、そして「桁送り」など、 printf の構文に追加された書式化機能のいくつかを説明する。

sample_advanced.cpp は、 format オブジェクトの 再利用や修飾といった、さらに進んだ機能の使い方をデモする。

そして sample_userType.cpp はユーザ定義型に対する format の振る舞いを示す。


Syntax

boost::format( format-string ) % arg1 % arg2 % ... % argN

format-string は特殊な命令を含むテキストである。これらの命令は、与えられた引数の書式化結果の文字列によって置換される。
C/C++ の世界におけるレガシーな構文は printf で使われているものである。そのため format は printf の書式文字列をそのまま利用でき、同じ結果を生成する。(ほとんどの場合において。詳細は
Incompatibilities with printf を見よ)
この中核となる構文は、新機能を許すだけでなく、 C++ のストリームの文脈に適合するために拡張された。そのため、 format は書式文字列のさまざまな形式の命令を受け付ける :

printf の標準の書式指定子に加えて、中寄せのような新しい機能が実装されている。詳細は new format specification を参照。

printf format specifications

Boost.format でサポートされる printf の書式指定子は、引数の位置指定をサポートしない標準 C の printf よりも、むしろ Unix98Open-group printf の構文に従っている。 (両者の間では共通のフラグは同じ意味を持つので、誰も頭痛に悩まされることはない)
なお、一つの書式文字列に位置指定付きの書式指定子(例. %3$+d)と位置指定なしのもの(例. %+d)を混ぜて使用するのはエラーである。
Open-group の仕様では同じ引数を複数回参照すること(例. "%1$d %1$d")は未定義動作であるが、 Boost.format では各引数を何度でも参照できる。ただ一つの制約は、書式文字列に現れる最大の引数の数が P であるとき、必ず P 個の引数を期待することである。(例. "%1$d %10$d" ならば P == 10)
引数の数が多すぎても少なすぎても例外が起こる。 (そうでないようにセットされていなければ。 exceptions を参照)



書式指定子 spec は次の形式を持つ : [ N$ ] [ flags ] [ width ] [ . precision ] type-char

大括弧で囲われたフィールドは省略可能である。 各フィールドは以下のリストのように説明される :

new format-specifications

Differences of behaviour vs printf

x1, x2 という二つの変数(組み込み型で、 C の printf でサポートされているもの)と書式文字列sがあって、
printf 関数で以下のように使われるとする :

printf(s, x1, x2);

ほとんどすべてのケースで、その結果はこの命令と同じものになる :
cout << format(s) % x1 % x2;
しかしいくつかの printf 書式指定子はストリームの書式化オプションに上手く翻訳されないため、 Boost.format の printf エミュレーションには注意すべき僅かな不完全性がある。
format クラスは、 printf の書式文字列を常に受け付けてほとんど同じ出力を生成するように、どのような場合でもサポートしないオプションを黙って無視する。
以下はそうした相違点のすべての一覧である : 同様に、特殊な 'n' 型指定子 (書式化によって出力された文字数を変数に格納するよう printf に命じるのに用いる) は format では無効である。
そのためこの型指定子を含む書式文字列は printf でも format でも同じ変換文字列を生成する。 printf と format で書式化された文字列に違いは生じない。
Boost.format で書式化された文字数をを得るには以下のようにする :
format formatter("%+5d");
cout << formatter % x;
unsigned int n = formatter.str().size();


User-defined types output

ストリーム状態の修飾に翻訳されたすべてのフラグは、ユーザ定義型にも再帰的に作用する。 ( フラグはアクティブなまま残るので、 ユーザ定義クラスによって呼ばれる各々の '<<' 演算に対しても、期待するオプションが渡される)
例.妥当なクラス Rational なら次のようになる :

Rational ratio(16,9);
cerr << format("%#x \n")  % ratio;  // -> "0x10/0x9 \n"

その他の書式化オプションでは話は異なる。例えば、幅の設定はオブジェクトによって生成される最終出力に適用され、内部の各々の出力には適用されない。これは都合のいい話である :

cerr << format("%-8d")  % ratio;  // -> "16/9    " であって、 "16      /9       " ではない
cerr << format("%=8d")  % ratio;  // -> "  16/9  " であって、 "   16   /    9   " ではない


しかし、 0 や ' ' オプションにも同様に働くため、不自然なことになってしまう。(意地の悪いことに、 '+' が showpos によってストリームの状態へと直接翻訳できるのに対して、 printf のゼロやスペースに当たるオプションはストリームには存在しない) :

cerr << format("%+08d \n")  % ratio;  // -> "+00016/9"
cerr << format("% 08d \n")  % ratio;  // -> "000 16/9"


Manipulators, and internal stream state

format の内部ストリームの状態は、引数を出力する直前に保存され、直後に復帰される。そのため、修飾子の影響は後まで引きづられずに、適用される引数にだけ作用する。
ストリームのデフォルト状態は標準で述べられているように : 精度 6 、幅 0 、右寄せ、そして10進数基数である。

format ストリームの内部ストリームの状態は引数と一緒に渡されるマニピュレータによって変えることができる; group 関数を経由して以下のようにできる :

cout << format("%1% %2% %1%\n") % group(hex, showbase, 40) % 50; // "0x28 50 0x28\n" と表示

'group' の内側にある N 個の項目を渡すとき、 Boost.format はマニピュレータに通常の引数とは異なる処理をする必要がある。そのため、 group の使用には以下の制限がある :
  1. 表示されるオブジェクトは group の最後の項目として渡されなければならない
  2. 先頭の N-1 個の項目はマニピュレータとして扱われるので、出力を生成しても破棄される

マニピュレータは、それが現れるごとに、後に続く引数の直前にストリームに渡される。 書式文字列で指定された書式化オプションは、この方法で渡されたストリーム状態修飾子によって上書きされる点に注意して欲しい。 例えば以下のコードで、 hex マニピュレータは、書式文字列の中で10進数出力を設定している型指定子 d よりも高い優先度を持つ :
cout << format("%1$d %2% %1%\n") % group(hex, showbase, 40) % 50; 
// "0x28 50 0x28\n" と表示

Alternatives


Exceptions

Boost.format は format オブジェクトの使い方にいくつかのルールを強要する。書式文字列は前述の構文に従わなくてはならず、ユーザは最終的な出力までに正しい個数の引数を供給しなければならない。また modify_item や bind_arg を用いるなら、項目や引数のインデックスが範囲外を指してはならない。
ミスが見過ごされたり放置されたりしないように、 format はいずれかのルールが満たされていないことを検出すると対応する例外を発生する。
しかしユーザはこの振る舞いを必要に応じて変えることができる。また、どのエラーの型が発生するかを次の関数を用いて選択できる :


unsigned char exceptions(unsigned char newexcept); // クエリおよび設定
unsigned char exceptions() const;                  // クエリのみ

ユーザは、以下のアトムを2進演算で結合することで引数 newexcept を算出できる :

例えば、 Boost.format が引数の個数をチェックしないようにしたければ、適切な例外設定を施した format オブジェクトを作る特殊なラッパ関数を定義する :

boost::format  my_fmt(const std::string & f_string) {
    using namespace boost::io;
    format fmter(f_string);
    fmter.exceptions( all_error_bits ^ ( too_many_args_bit | too_few_args_bit )  );
    return fmter;
}

すると、必要とされるよりも多くの引数を与えても許される(単に無視される) :

cout << my_fmt(" %1% %2% \n") % 1 % 2 % 3 % 4 % 5;

また、すべての引数が与えられる前に結果を問い合わせると、結果の対応する部分は単に空になる

cout << my_fmt(" _%2%_ _%1%_ \n") % 1 ;
// prints      " __ _1_ \n"


Extract

namespace boost {

template<class charT, class Traits=std::char_traits<charT> > 
class basic_format 
{
public:
  typedef std::basic_string<charT, Traits> string_t,
  basic_format(const charT* str);
  basic_format(const charT* str, const std::locale & loc);
  basic_format(const string_t& s);
  basic_format(const string_t& s, const std::locale & loc);

  string_t str() const;

  // pass arguments through those operators :
  template<class T>  basic_format&   operator%(T& x);  
  template<class T>  basic_format&   operator%(const T& x);

  // dump buffers to ostream :
  friend std::basic_ostream<charT, Traits>& 
  operator<< <> ( std::basic_ostream<charT, Traits>& , basic_format& ); 

// ............  これはただの抜粋である .......
}; // basic_format

typedef basic_format<char >          format;
typedef basic_format<wchar_t >      wformat;


namespace io {
// free function for ease of use :
template<class charT, class Traits> 
std::basic_string<charT,Traits>  str(const basic_format<charT,Traits>& f) {
      return f.str();
}
} //namespace io


} // namespace boost


Rationale

このクラスのゴールは、より良い、 C++ 用の、型安全かつ型拡張性のある printf の等価物が、 ストリームとともに用いられるようにすることである。

正確には、 format は以下の機能を実現するようデザインされた :

デザインの過程で多くの問題に直面し、いくつかの選択をすることになったが、 中には直観的には正しくないものもあった。しかしいずれのケースにも 何らかの意味がある


Credits

Boost format の著者は Samuel Krempp である。   彼は Rüiger Loos と Karl Nelson の両者の format クラスのアイディアを利用した。


February 19, 2002

© Copyright Samuel Krempp 2002. 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
オリジナルの、及びこの著作権表示が全ての複製の中に現れる限り、この文書の複製、利用、変更、販売そして配布を認める。このドキュメントは「あるがまま」に提供されており、いかなる明示的、暗黙的保証も行わない。また、いかなる目的に対しても、その利用が適していることを関知しない。