Oven meets Boost.Spirit.Karma

Boost.Spiritは元々が*1がパーサージェネレータであったこともあり、Qiが一番有名であると思いますが、Boost.Spirit一家にはKarmaという次男坊(?)がいます。

Spirit.KarmaはSpirit.Qiの逆で、データから文字列を生成するコンビネータ郡です。Performance of Numeric Generators - 1.44.0によると、iostreamやsprintfやBoost.Formatよりも高速であるとされています。また、sprintfと違い型安全な設計となっています。難点は書式指定がやや面倒なことと、コンパイル時間がboostされることですが…。

Boost.Karmaは入力に対して柔軟な設計がされており、Ovenのレンジと組合せて使うことができます。

int main() {
    std::vector<int> const xs = { 1, 2, 3, 4, 5 };

    karma::generate(
        std::ostreambuf_iterator<char>(std::cout),
        spirit::karma::int_ % ' ',
        xs | oven::transformed(std::negate<int>()));

    return 0;
}

たとえば、上記のような記述をすれば、std::vectorの中身の符号を反転さしつつ、スペースで区切って出力をすることができます。

また、oven::fuzippedといったアダプタを使えばより複雑なケースに対応することができます。fuzippedはzippedのfusion版です。zippedは型がboost::tupleとなりますが、fuzippedはboost::fusion::vectorとなります。

int main() {
    std::vector<int> xs = { 1, 2, 3, 4, 5 };

    karma::generate(
        std::ostreambuf_iterator<char>(std::cout),
        (karma::lit("negate: ") << karma::int_ << " -> " << karma::int_) % karma::eol,
        egg::pack(
            xs,
            xs | oven::transformed(std::negate<int>())
        ) | oven::fuzipped
    );

    std::cout << std::endl;

    return 0;
}

このプログラムを実行すると、

negate: 1 -> -1
negate: 2 -> -2
negate: 3 -> -3
negate: 4 -> -4
negate: 5 -> -5

という結果が得られます。

Rangeを文字列に変換できるという特徴は、他のsprintfやiostreamによる実装にはない特徴です。

*1:現在のClassic