読者です 読者をやめる 読者になる 読者になる

c++ で println もどきを書く

C/C++

この記事では c++11 の機能を使っています。

参考記事 java - System.out.println function syntax in C++ - Stack Overflow

ちょっとした値を書き出すのも c/c++ だと面倒なことが多いので 他の言語にあるような簡単な出力関数が欲しいと思っていて上の記事を見つけた。
可変引数のテンプレートというのを使えば「もどき」なら書けそうだ。

print という関数を定義して, 可変引数のテンプレートで上書きする。
私はベクトルを良く使うので ostream の << を先に上書きしておく。

#include <iostream>
#include <iomanip>
#include <vector>

int __PRINT_PRECISION__ = 6;

template<typename T> std::ostream& operator<< (std::ostream& os, const std::vector<T>& v) 
{
    os << "{";
    for (auto it = v.begin(); it != v.end(); it++) {
        os << std::setiosflags(std::ios::fixed)
           << std::setprecision(__PRINT_PRECISION__) << *it;
        if (it != v.end()-1) os << ",";
    }
    os << "}";
    return os;
}

template<typename T> inline void print(const T& x)
{
    std::cout << std::setiosflags(std::ios::fixed)
              << std::setprecision(__PRINT_PRECISION__) << x;
}

template<typename T, typename... U> inline void print(const T& x, const U&... args)
{
    print(x);
    std::cout << " ";
    print(args...);
}

細かい数値に興味がなければ __PRINT_PRECISION__ は const でいいと思う。

とりあえず print を動かしてみる。

// ... 上のコード

inline void newline()
{
    std::cout << std::endl;
}

int main()
{
    long long i=1;
    double f = 3.0;
    unsigned int j=4;
    std::vector<double> v{10.0,20.0};

    print(i,2,f,j, "string", 'c', "v =", v);
    newline();

    std::vector< std::vector<double> > w{{1.0,2.0,3.0},{4.0,5.0,6.0}};
    print("w =", w);
    newline();

    return 0;
}

ファイル名を hoge.cc とし コンパイルオプションに -std=c++11 を忘れずに。

g++ hoge.cc -std=c++11 -o hoge

結果は

1 2 3.000000 4 string c v = {10.000000,20.000000}
w = {{1.000000,2.000000,3.000000},{4.000000,5.000000,6.000000}}

要素間の空白の出力も再帰的にやってくれているようで感心した。 ベクトルのベクトルも問題なし。

println はこういう感じで

template<typename... T> inline void println(const T&... args)
{
    print(args...);
    newline();
}

print(args..., "\n") でも問題ないが print の仕様で行末に余分な空白がついてしまうのでこの形にした。

ostream の << をいろんなコンテナやクラスで上書きしておけば print(ln) 自体はいじる必要はないので便利だと思う。

追記 使っているうちにこんなエラーに出くわすこともあるので テンプレートで << を上書きするのは良くないのかも知れない。 c++ は難しい。
cannot bind ‘std::basic_ostream’ lvalue to ‘std::basic_ostream&&’

この辺とか c++ - Overloading operator<<: cannot bind lvalue to ‘std::basic_ostream<char>&&’ - Stack Overflow