boost::progress_display++

Boost.懇親会 #5 名古屋
http://partake.in/events/e9131e9c-0662-4179-bbd4-b0648909f921
でboost::progress_displayの発表をしてきました。


発表資料
http://www12.ocn.ne.jp/~dante98/progress_display.pptx
ソースコード
http://www12.ocn.ne.jp/~dante98/zip/progress_display.zip


前々からboost::progress_displayが苛められているのを見るたびに、インタフェースでなく実装の問題だと思っていたので、二つの別実装を紹介しました。
以下、実装周りの解説です。


一つ目は、単純なWin32 GUI版です。
boost::progress_displayのインタフェースに合わせるためにはメッセージループを別スレッドで回す必要があります。
また、「ウィンドウの生成」、「メッセージループ」、「ウィンドウの破棄」は同じスレッドで行う必要があり、真面目に実装しようとすると意外と面倒だったりします。

progress_displayからメッセージループへの通知はウィンドウメッセージを投げることで行っています。
スレッドに投げるメッセージは

  • destroy_msg(ウィンドウ破棄)
  • set_expected_msg(最大値設定)
  • set_count_msg(現在値設定)

の三つです。
ウィンドウが生きているかどうかは他のスレッドから確認できないので、ウィンドウハンドルではなくスレッドに対してメッセージを投げています。
progress_display一つに対して一つのメッセージループスレッドを使っているからできる手法ですが。


もう一つの実装は、「Progress display - アンサイクロペディア」に載っている例(?)を実装したものです。
AAが「MS Pゴシック」前提なことと、ソースコードにShift JISで書きたかったことから日本語環境(コードページ932)限定です。
普通に論理フォントを作成してGDIで描いています。
ダブルバッファリングは手抜きでWS_EX_COMPOSITEDスタイルを使っていますが、もうXP以降前提でよいでしょう。

引数は、

boost::progress_display progress(count, std::cout,
    "クソスレ\n"
    "普通\n"
    "優良スレ",
    "スレ評価嬢",
"     88彡ミ8。   /)\r\n"
"     8ノ/ノ^^ヾ8。( i )))\r\n"
"     |(| ∩ ∩|| / /   <ココ!\r\n"
"    从ゝ__▽_.从 /\r\n"
"     /||_、_|| /\r\n"
"     / (___)\r\n"
"    \(ミl_,_(\r\n"
"      /.  _ \\r\n"
"     /_ /  \ _.〉\r\n"
"   / /   / /\r\n"
"   (二/     (二)\r\n"
);

こんな感じで設定します。後ろ三つが「凡例」、「タイトル」、「カーソル」になっています。
カーソルのホットスポットは最上部の右端に決め打ちで、かつ最後の文字は省いて計算しています。(つまり「     88彡ミ8。   /」の幅)
なので、アンサイクロペディアの例以外のAAだと上手く動きません。


ついでに、発表に使ったコマンドプロンプトもどきの解説。

というご要望にお応えして、即興で縞模様のコマンドプロンプトを作りました。
時間がなかったのでコマンドの入力はコマンドラインの引数から、標準入力と標準エラー出力はなしです。
縞模様はGDIでベタ書きして、その上に出力を描画しているだけです。
コマンド実行は、スレッドを起こしてCreateProcess()して、1文字ずつ読んで画面更新してました。(非同期I/O面倒なので)

GetStockObject()で取得できるブラシが白/黒/灰色ぐらいしかなかったので、最初は白と灰色の縞模様だったのですが、色が重要っぽいのでちゃんとブラシを作りました。
あと、よく分かってなくて危うく縦縞にするとこでした。

で、こんな面倒なことしなくてもコンソールの設定変えるだけで出来たらしい、、、。