0
0

【自分用メモ】C++競プロ用便利コード

Last updated at Posted at 2024-06-29

C/C++には「プリプロセッサ命令」などの機能があり,これら活用するとコーディングがかなり便利になります.
競プロに限ってですが,プリプロセッサ命令などを最大限活用したC++は,Pythonと同じか,より書きやすいように感じます.

提出時だけ出力されないデバッグ出力

変数の中身を確認しながらコーディングを進めたい時,逐一変数を標準出力するようなコード(デバッグ出力)を組むと思います.しかしこの方法だと,提出する時に,デバッグ出力を全てコメントアウトしなければならず,手間になります.

#define DEBUG
#ifdef DEBUG
#define dcout cout
#else
#define dcout  \
    if (false) \
    cout
int main()
{
    dcout << "Print only when debugging" << endl;
}

作られたマクロdcoutcoutと同じ文法で扱うことができ,デフォルトでは標準出力もなされます.しかし,1行目をコメントアウトするだけで,dcoutは標準出力をしなくなります.

- #define DEBUG
+ //#define DEBUG

dcoutを使ってデバッグ出力をしておきます.そして,提出する時に1行目だけコメントアウトすれば,DEBUGが非定義となり,dcoutは何もしないマクロになります.

問題点 コメントでのご指摘の通り,これには少し問題点があります.例えば次のような例だと,`DEBUG`が定義されているか否かで振る舞いが変わってしまいます.
int main()
{
    if (0)
        dcout << "Hello" << endl;
    else
        cout << "World" << endl;
}

このような状態を"dangling else"といいます.

変数=変数内容という形式でデバッグ出力

デバッグ時,変数名=変数内容という形式でデバッグ出力をしたい時があります.しかし,C++だとかなり面倒くさいです.

dcout << "counter=" << counter << endl;

そこで作るのが次のマクロ.

#define view(x) #x "=" << x

次のように使います.

dcout << view(counter) << endl;
// counter=10

このマクロでは,「文字列化演算子」というプリプロセッサ文法を使用しています.これは,変数名を文字列にする機能です.詳細はMicrosoft Learnを参照してください.

こういったほんの少しのコーディングの効率化が嬉しいのですよ🍰

ちなみに,Pythonだとかなり便利な文法が存在します.
Python
print(f"{counter=}"}

標準出力に色を付けて見やすく

標準出力に特定の文字列を送ることで,文字に色を付けることができます.これを使って,先ほどのviewをさらに見やすくします.

#define NC "\e[0m"
#define RED "\e[0;31m"
#define GRN "\e[0;32m"
#define CYN "\e[0;36m"
#define view(x) #x "=" CYN << x << NC " "
int main()
{
    int count = 3838;
    dcout << view(count) << endl;
}

こうすると,以下のように出てきます.
image.png
データの内容だけ青色で表示されました.些細な工夫ですけれど,これだけでもかなり見やすくなります.

forに関するショートカット構文

$N$回ループを回す系のfor構文は長ったらしくて面倒なので,これも効率化しちゃいます.

#define forrange(i, i0, I) for (int i = i0; i < I; i++)
#define rep(I) forrange(__counter, 0, I)

ベクトルと行列

std::vectorを使ってベクトルと行列を作ることが多々あります.特に,中身がint型になることも多いです.なので,これらの型を呼び出すショートカットを作成します.

template <typename T>
using Vec = vector<T>;
using IntVec = Vec<int>;
template <typename T>
using Mat = Vec<Vec<T>>;
using IntMat = Mat<int>;

ベクトルと行列のデバッグ出力

forを用いてベクトルや行列の要素を逐一デバッグ出力するのは面倒臭いし,その上,提出時に無駄なforループが残ることになります(最適コンパイルとかすると自動的に取り除かれたりする??🤔).
なので,ベクトルや行列のデバッグ出力も専用のマクロを作ります.

#ifdef DEBUG
#define viewvec(vec)          \
    {                         \
        cout << CYN;          \
        for (auto x : vec)    \
            cout << x << " "; \
        cout << NC << endl;   \
    }
#define viewmat(mat)     \
    for (auto vec : mat) \
    {                    \
        viewvec(vec)     \
    }
#else
#define viewvec(vec) 0
#define viewmat(mat) 0
#endif

提出時,つまりDEBUGを非定義にすると,両者のマクロとも何もしないマクロになります.

行通過を知らせるやつ

いつその行を処理が通過するかを出力するマクロmarkを作ります.

#ifdef DEBUG

#define mark dcout << RED << "●" << __LINE__ << NC << endl

#else
#define mark 0
#endif

こういうコードを書いていたとします.
image.png
すると,次のような出力が得られます.100行目を通過すると,赤文字で教えてくれます.
image.png
このマクロでは,__LINE__という定義済みマクロを使用しています.これは,ソースコードの行数を伝えるものです.Microsoft Learnを参照してください.

np.onesnp.zerosみたいなやつ

指定された長さのベクトルや,指定されたサイズの行列を作るメソッドです.

template <typename T>
Vec<T> filledvec(int size, T element)
{
    Vec<T> vec;
    rep(size) vec.push_back(element);
    return vec;
}

Mat<T> filledmat(int height, int width, T element)
{
    Mat<T> mat;
    rep(height) mat.push_back(filledvec(width, element));
    return mat;
}

全ソース

0
0
7

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0