パスワードを忘れた? アカウント作成
482939 journal

Yak!の日記: printf 風 Boost Format ラッパ

日記 by Yak!

型安全性がない、拡張できないという printf、一方で、書式を変更しようとすると色々と面倒な iostream。これらのいいとこどりをしようというのが Boost Format だが、それでもやっぱり printf のような関数インタフェースの方が単純明快だと思う。

// 以下 #include や namespace なしで記述
// printf
printf("%8d\n", 5);
// iostream
cout << setw(8) << 5 << endl;
// Boost Format
cout << format("%8d\n") % 5;

ということで、printf 風に Boost Format を呼び出せる関数インタフェースラッパを作ってみた。発想は単純。

template<typename T0>
void printf(const char *str, const T0& t0)
{
    cout << format(str) % t0;
}
template<typename T0, typename T1>
void printf(const char* str, const T0& t0, const T1& t1)
{
    cout << format(str) % t0 % t1;
}

全ての引数個の全ての型について overload できればいいわけである。型については template があるわけで、引数の個数について対処できればいい。実用上 10 個もあれば良さそうではあるが、とはいえ全部書き下すのも美しくないし数を増やしたい場合にも面倒だ。こういうときに活躍するのが Boost 中唯一と言っていい C でも利用可能な異端のライブラリ Boost Preprocessor である。

#define BIND(z, n, data) BOOST_PP_CAT(% arg, n)
#define BOOST_PP_LOCAL_MACRO(count) \
template<BOOST_PP_ENUM_PARAMS(count, typename T)> \
void printf(const char* str, BOOST_PP_ENUM_BINARY_PARAMS(count, const T, &arg)) \
{ \
    cout << format(str) BOOST_PP_REPEAT(count, BIND, _); \
} \
#define BOOST_PP_LOCAL_LIMITS (1, 9)
#include BOOST_PP_LOCAL_ITERATE()
 
#undef BOOST_PP_LOCAL_LIMITS
#undef BOOST_PP_LOCAL_MACRO

これで、引数 9 個(書式指定除く)までのコードが生成される。BOOST_PP_LOCAL_ITERATE のところで、BOOST_PP_LOCAL_LIMITS に指定された範囲分、BOOST_PP_LOCAL_MACRO が展開される事になる。↑で見られた繰り返しパターンの部分が、BOOST_PP_ENUM_PARAMS, BOOST_PP_ENUM_BINARY_PARAMS, BOOST_PP_REPEAT に置き換えられているのが見て取れるだろうか。
現状の C++ としてはこれが限界であるが、次期 C++ 規格(いわゆる C++0x)で導入予定の Variadic Templates (可変引数個テンプレート)を用いればプリプロセッサ不要、個数限定なし(テンプレートのネスト、引数の個数等で限界はあるだろうが)で実現が可能である。Variadic Templates は GCC 4.3 あるいは ConceptGCC で利用が可能である。

format& formatter(format &f)
{
    return f;
}
 
template<typename T0, typename ... Tn>
format& formatter(format &f, const T0& t0, const Tn& ... tn)
{
    return formatter((f % t0), tn...);
}
 
template<typename ... Tn>
void printf(const char* spec, const Tn& ... tn)
{
    format f(spec);
    cout << formatter(f, tn...);
}

展開がコンマ区切りのリスト限定になるため % 連結部分を別に再帰で処理しているが、BOOST_PP_ENUM_PARAMS のような繰り返し部分に ... が入っているのがわかると思う。BOOST_PP_REPEAT の繰り返しを再帰で置き換えている、と見なすことも可能だろう。
もう少し整備したコードはこちら

この議論は賞味期限が切れたので、アーカイブ化されています。 新たにコメントを付けることはできません。
typodupeerror

長期的な見通しやビジョンはあえて持たないようにしてる -- Linus Torvalds

読み込み中...