C++でプログレスバー表示する関数を作った[はじめてのヘッダ作り]

# はじめに
kerasを使っていて、プログレスバーがかっこいいなと思いました。
pythonでは`tqdm`というパッケージがあり、簡単に表示できましたが、C++では簡単ではありませんでした（`boost/progress.hpp`でできるが）。
また、クラスを作ってみたかったので、経験として自分で作ってみることにしました。

Githubで公開しています。→ https://github.com/ysuzuki19/CppProgressBar

コード全体をここに貼ると長くなる上、今後変更しづらくなってしまうので、コード全体は貼らずに部分だけ紹介していきます。

```plaintext:特徴
・　ラムダ式で自分の処理を与える
・　プログレスバーを表示しながら、自分で出力したいものも出力できる
・　ターミナルサイズに合わせてプログレスバー長を変更する（途中で変更したら崩れる）
```

頑張って調べて作りましたが、自主学習であるため、C++的にマズイ記述がある可能性があり責任は取れません。何かお気づきの方は言及してもらえると助かります！プルリクも歓迎です。

# 完成品
kerasみたいでカッコイイ😋（真似したんですけどね笑）
C++でこれ出したい人いるはず！使ってみてください！

1. `for`ループに合わせてプログレスバーを出力する図
![progressbar_nooutput.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282145/cff6a717-ee82-039d-1fea-abca4b07ebf4.gif)

2. `for`ループに合わせてプログレスバーを出力しつつ好みの情報を出力する図
![progressbar.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282145/813c2ddf-134d-36fb-5ed8-e962f2bb3f14.gif)

## 使い方
クラスを準備したのだが、適宜呼び出して使うのは面倒だと思い、`for_progress()`という関数を準備しておいた。呼び出しは以下で行うことができる。

```cpp:for_progress()の呼び出し方法
for_progress( loop_number, lambda_func );
// loop_number : ループ回数を指定
// lambda_func : 自分の処理をラムダ式にしたもの
```

ここで、ラムダ式の引数に`ｓｔｄ：：string output_string`という出力用の文字列が必要となっている。出力が必要なければ、引数として記述するだけで放置してしまえばよい。また、カウンタによって配列の要素にアクセスしたい場合や処理の結果を保存したい場合には`for_progress()`を呼び出す前に初期化した変数をラムダ式でキャプチャする必要がある。

## 使用例
今回は、使用している様子を分かりしやすくするためにあえて遅延させた。また、処理には、「`0~100`の数値の和」・「`0~10`の数値の積」を準備してそれぞれプログレスバーを出力した。ここで、プログレスバーをみやすくするために`sleep_for()`で遅延させた。

実際に呼び出すために作成したテストコード全文が以下。

```cpp:test.cpp
#include <iostream>
#include <string>
#include <CppProgressBar.hpp>

#include <chrono>
#include <thread>

using namespace std;

int main()
{
  {// for_progress test for sums
    int sums = 0;
    int cnt = 0;
    auto process = [&sums, &cnt](std::string& output_string) {
      output_string = to_string(cnt) + " " + to_string(sums);
      std::this_thread::sleep_for(std::chrono::milliseconds(30));
      sums += cnt;
      cnt ++;
    };
    for_progress(100, process);
  }

  {// for_progress test for times
    double times = 1;
    int cnt = 1;
    auto process_2 = [&times, &cnt](std::string& output_string) {
      output_string = to_string(cnt) + " : " + to_string(times);
      std::this_thread::sleep_for(std::chrono::milliseconds(30));
      times *= cnt;
      cnt ++;
    };
    for_progress(10, process_2);
  }
  return 0;
}
```

# C++で出力した行を上書きする方法
キャリッジリターンというものを用いる。ここで、関数使用時に出力したい内容がプログレスバーより小さい場合、単純にキャリッジリターンを行うだけでは、プログレスバーの残りカスが画面に残って非常に残念なターミナルになってしまう。そこで考えたのが、

```plaintext:overwrite_progressbar
プログレスバーと同じ文字数のスペース埋めされた`std::string`を出力して再びキャリッジリターン
```

というものである。つまり以下の処理を通常の`for`ループに追加した。

- プログレスバーを出力
- 次のプログレスバーに備えてキャリッジリターン
- 出力が{ある, なし}で分岐
    - ある：スペース埋めしてキャリッジリターンして文字出力したのちプログレスバー出力
    - ない：プログレスバーを出力

この処理を加える事で、`for_progress()`使用時に好みの出力が可能となった。

# クラスのメンバ変数の名前の末尾にアンダーバーをつける
Googleのスタイルシートの[変数名項](https://google.github.io/styleguide/cppguide.html#Variable_Names)に書いてあった。
つける前に自分が犯してしまったミスは、「引数とメンバ変数がごっちゃになった」というものである。


# インクルードガード
C++で`#include`をする際に、標準ライブラリの場合は複数回同じヘッダをインクルードしようとしてもコンパイルは成功する。それに対し、インクルードガードを書いていないヘッダを複数回インクルードしようとするとコンパイルは失敗する。それを防ぐためにインクルードガードをする必要がある。（`#pragma once`はよくわかっていない）

自分の書いたヘッダファイルの最上部・最下部に以下のような記述をするとよい。

```cpp:my_header.hpp
#ifndef MY_HEADER_HPP
#define MY_HEADER_HPP
...
#endif
```

こうすることで、`このヘッダは読み込んだな！`とコンパイラが気づいてくれるらしい。複数回同じヘッダをインクルードしてもコンパイルできるようになる。

# アクセサ
クラスのメンバ変数は`Public`にしないのが一般的という事だが、安全性・保守性のためらしい。
クラスのメンバ変数に直にアクセスできるというのはコーディングミスの原因になりそうな上、どこでメンバ変数が書き換えられたか分かりづらいという面がある。
なので`Private`にして、アクセスしたいループカウンタにはアクセサを準備した。また、どれも１行になったので`inline`にした。

```cpp:accessor
Private:
  size_t loop_counter_;
Public:
  inline void cntSet(size_t first) {
    loop_counter_ = first;
  }
  inline size_t cntGet() {
    return loop_counter_;
  }
  inline void cntIncrement() {
    loop_counter_ ++;
  }
```

- `cntSet()` : ループカウンタに引数の値をセットする
- `cntGet()` :　ループカウンタの値をゲットする
- `cntIncrement()` :　ループカウンタの値をインクリメントする

# プログレスバーに関するメンバ関数
アクセサ以外の各メンバ関数は以下である。

```cpp:member_functions
void init_variable(size_t loop_number);
void update_variable();
inline void display_progress_bar();
inline void finish_progress_bar();
template <typename T> inline void stdout_in_for_progress (T& e);
```

基本的には関数名から読み取れるとは思うが、各説明は以下。

- `init_variable()`　：　ループ回数に対して各メンバ変数を初期化
- `update_variable()`　：　プログレスバー等のメンバ変数を更新する
- `display_progress_bar()`　：　プログレスバーを出力する
- `stdout_in_for_progress()`　：　プログレスバーを表示中に標準出力する。

# 追加するかもしれない機能
自分が欲しくなるor需要があれば以下の機能を追加しようかと考えている。

- プログレスバーを表示しながら処理時間も表示する
- 途中でターミナルサイズが変更される場合も対応（`ioctl()`を呼び直すだけだと思ったが今回はバグのため断念）
- 枠線やカーソルを自由に決められる
- イテレータを引数にできるように
- 二重ループに対応

# 参考
初めてクラスを作成したので、いろいろな方の情報を参考にさせていただきました。ありがとうございました。

- https://google.github.io/styleguide/cppguide.html
    - Googleの超いいやつ
    - 書き方に困ったらとりあえずみると安心する
- https://ttsuki.github.io/styleguide/cppguide.ja.html#Braced_Initializer_List
    - Googleの超いいやつの和訳
- https://qiita.com/h6akh/items/fc8674bc2251ff0dfbf3
    - 最初に目を通した
    - どんな風にヘッダを作るのかの参考
- https://www.learncpp.com/cpp-tutorial/header-guards/
    - インクルードガードの参考
- https://qiita.com/yskszk/items/2045ef74396fbaf18f4d
    - メンバ変数の`Private`化とアクセサの準備
- https://ja.wikipedia.org/wiki/キャリッジ・リターン
    - キャリッジリターンとは
- https://en.cppreference.com/w/c/language/escape
    - C++のキャリッジリターン
- https://stackoverflow.com/questions/1022957/getting-terminal-width-in-c/1022961#1022961
    - ターミナルサイズの取得
- https://ja.cppreference.com/w/cpp/keyword
    - C++の予約語の調査

# おわりに
はじめてclass・ヘッダを作ったので、学ばなけばいけないことが多く、大変勉強になりました。
最初にも述べましたが、初心者ですので、おかしい点があれば教えていただけたら幸いです。