われがわログ

最適化アルゴリズムとかプログラミングについて書きたい

プログレスバーを表示するC++ライブラリを作った

成果物:

github.com

https://raw.githubusercontent.com/estshorter/pbar/videos/example1.gif

背景

Pythonで重い処理をする際は、tqdmプログレスバーを表示している。最近、C++でもプログレスバーを表示させたいと思ったのだが、既存のライブラリは微妙なものしかなかった。サーベイの結果は以下の通り。

  • tqdm.cpp: tqdmのC++への公式ポートだがメンテされていない。コンパイルして動かしてみても正常に表示されない。
  • indicators: スター数的には一番人気のライブラリ。だが、Windows + PowerShellの環境だと、バーの後に余分な改行が入り表示が乱れる。また、UTF8対応を謡ってはいるが、日本語環境では文字化けする。Win32 APIを使って文字出力していないことが原因と思われる。将来的に廃止予定のcodecvtクラスを使っているのも微妙に感じた。
  • progressbar: 200行弱のシンプルなライブラリ。これでもよかったのだが、実行中の警告メッセージを表示させる機能がないのが気になった。

pbar

このように、自分好みのプログレスバーC++ライブラリがなかったので自作したのが、最初のpbarライブラリだ。バーの表示の形式はtqdmとほぼ同様である。進捗はUnicode文字"█"で表示しているが、WindowsではWriteConsoleW関数でターミナルに表示しているため、文字化けしない。最初のgif動画のように、複数のバーの同時表示にも対応している。設定しなければ、コンソールの横幅めいっぱいにバーを表示する。なお、本ライブラリはスレッドセーフではないので、バーのアップデートは同期的に行う必要がある。

サンプルは下記の通り。pbar::pbarクラスを作成した後、bar.init()で進捗0%のバーを表示する。ループの終わりでbar++としてバーの表示を更新する。bar.tick()でもよい。 バーを複数表示する場合は、一番上のバー以外にenable_stack()を設定する。 その他いくつか設定はあるが、名前からわかるだろう(説明放棄)。

#include <chrono>
#include <iostream>
#include <pbar.hpp>
#include <thread>

int main(void) {
    using namespace std::this_thread;
    using namespace std::chrono;
    constexpr auto total_ = 100;
    constexpr auto ncols = 100;
    pbar::pbar bar(total_, ncols);
    bar.set_description("[TASK0]");
    bar.init();  // not always necessary
    bar.enable_recalc_console_width(1);  // check console width every 10 ticks
    for (std::int64_t i = 0; i < total_; ++i, ++bar) {
        sleep_for(milliseconds(100));
    }
    std::cout << "done!" << std::endl;
    pbar::pbar bar1(4);
    pbar::pbar bar2(8);
    pbar::pbar bar3(16);
    bar1.set_description("[TASK1]");
    bar2.set_description("[TASK2]");
    bar3.set_description("[TASK3]");

    bar2.enable_stack();
    bar3.enable_stack();

    bar1.enable_recalc_console_width(10);  // check console width every 10 ticks

    bar1.init();
    for (auto i = 0; i < 4; ++i, ++bar1) {
        bar2.init();
        for (auto j = 0; j < 8; ++j, ++bar2) {
            bar3.init();
            for (auto k = 0; k < 16; ++k, ++bar3) {
                sleep_for(milliseconds(10));
            }
            sleep_for(milliseconds(50));
        }
        sleep_for(milliseconds(100));
    }
    std::cout << "done!" << std::endl;

    return 0;
}

210823_追記

スピナーも作った。サンプルプログラムはgithubを参照のこと。 https://raw.githubusercontent.com/estshorter/pbar/videos/example1.gif