std::cerr,std::clogの違いについて

投稿者: Anonymous

2017-04-17-20:00

std::cerrについて調べていたところ以下のようなコード
(http://en.cppreference.com/w/cpp/io/cerr)
に行き当たったのですが、その挙動が理解できずに悩んでいます。

ソースコードは以下です。

#include <thread>
#include <iostream>
#include <chrono>
void f()
{
    std::cout << "Output from thread...";
    std::this_thread::sleep_for(std::chrono::seconds(2));
    std::cout << "...thread calls flush()" << std::endl;
}

int main()
{
    std::thread t1(f);
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::clog << "This output from main is not tie()'d to coutn";
    std::cerr << "This output is tie()'d to coutn";
    t1.join();
    return 0;
}

clang version 3.8.0-2ubuntu4 でコンパイルし、実際に手元の環境(Ubuntu16.04)で動かしてみた所出力は以下のようになりました。

This output from main is not tie()'d to cout
Output from thread...This output is tie()'d to cout
...thread calls flush()

cerrはバッファリングを行わず、clogはバッファリングを行うとの認識だったのですが、この出力結果を見るに、clogへの出力はバッファリングされず、mainスレッドの待機命令を無視して真っ先に出力されているように見えます。また、その後に関数f()の一行目が実行され、t1スレッドも待機に入り、先に待機がとけたmainスレッドでcerrにバッファリングされていたメッセージが出力されているように見えます。
何故このような挙動になり、僕の理解はどのように間違っているのでしょうか。
どなたかご存じの方ご教授願います。

21:20
質問投稿の後にも試行錯誤しつつ調べていたのですが、一部解決したのでここに報告させていただきます。
“Output from thread…”よりも”Thid output …”のほうが早く出力されていたのはf()内一行目ではflushが起きていないためでした。
ここでバッファに溜まったテキストはcerrの手前で吐き出されていますが、これはcerrが呼び出し直後にまずcout.flushを呼び出すためのようです。以下参考URL
(http://en.cppreference.com/w/cpp/io/cerr)

ただバッファリングされるはずのclogがそのまま出力されているのは未だ解決出来ていません。
“n”による行バッファリングの可能性も指摘していただいたのですが、”n”を取り除いても出力は行われるのでこれが原因でもないようです。

21:43
clogですが、試行錯誤の結果、既にcoutにバッファリングされているデータのflushは行わないものの、clog自体のflushはしているということがわかりました。「clogはバッファリングをする」という文言の意味を取り違えていたようです。問題解決に協力してくださったmetropolisさん、ありがとうございました。

21:55
clogについてですが、
cppreference.com(信用度が足りないらしくリンクが投稿できないので、サイト名だけで失礼します)
には勝手にflushされない旨が書いてあるのでやはり不可解な挙動のようです。
追加で調べて進展したらまた追記します。

04-18-12:45
解決しました。詳しくはベストアンサーをご覧ください。Hidekiさんありがとうございました。

解決

isocpp.org にある C++仕様のドラフト (n4618) を見てみましたが、std::clog について書かれているのは、27.4.2 Narrow stream object の

ostream cerr;
The object cerr controls output to a stream buffer associated with the object stderr, declared in (27.11.1).
After the object cerr is initialized, cerr.flags() & unitbuf is nonzero and cerr.tie() returns &cout. Its state is otherwise the same as required for basic_ios::init (27.5.5.2).

ostream clog;
The object clog controls output to a stream buffer associated with the object stderr, declared in (27.11.1).

と、27.5.3.1.6 Class ios_base::Init の

Init();
Effects: Constructs an object of class Init. Constructs and initializes the objects cin, cout, cerr, clog, wcin, wcout, wcerr, and wclog if they have not already been constructed and initialized.

~Init();
Effects: Destroys an object of class Init. If there are no other instances of the class still in existence, calls cout.flush(), cerr.flush(), clog.flush(), wcout.flush(), wcerr.flush(), wclog.flush().

くらいで、他には見つかりませんでした。clog のバッファは、いつ flush されるか、どこにも記述がないので、実装依存と解釈するのが正しいと思います。

つまり

  • cerr については、少なくとも出力ごとには必ず flush される。
  • clog については、いつ flush されるか、わからない。出力ごとかもしれないし、あとで、まとめてかもしれない。

です。cppreference.com の記述は、ちょっと紛らわしいと思います。

回答者: Anonymous

Leave a Reply

Your email address will not be published. Required fields are marked *