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;
}
作られたマクロdcout
はcout
と同じ文法で扱うことができ,デフォルトでは標準出力もなされます.しかし,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だとかなり便利な文法が存在します.
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;
}
こうすると,以下のように出てきます.
データの内容だけ青色で表示されました.些細な工夫ですけれど,これだけでもかなり見やすくなります.
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
こういうコードを書いていたとします.
すると,次のような出力が得られます.100行目を通過すると,赤文字で教えてくれます.
このマクロでは,__LINE__
という定義済みマクロを使用しています.これは,ソースコードの行数を伝えるものです.Microsoft Learnを参照してください.
np.ones
,np.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;
}
全ソース