はじめに
bits/stdc++.hをプリコンパイル1するくらいなら、ACL2もまとめてプリコンパイルしちゃおうよ!という記事です。AtCoderでC++を使っている人向けです。
ACLなど要らんわ!という方は、下記記事を参考にするとよいです。記事作成に際して参考にした記事でもあります。Big Kansya。
前提
bits/stdc++.hやACLの導入は済んでいることとします。具体的には、
#include <bits/stdc++.h>
#include <atcoder/all>
で始まるコードが正常にコンパイルできればOKです。
また、atcoder/allがどこにあるかを思い出してください。思い出せない場合は、#include <atcoder/all>しているcppファイルを、いつものコンパイルコマンドに-Hオプションをつけてコンパイルすると、atcoder/allがどこにあるか表示されるはずです。標準ライブラリのパスがいっぱい出てきて困ったら、bits/stdc++.hのインクルードを外してみてください。
手順
まず、プリコンパイル済みヘッダーは高々1つしか使用できません3。なので、bits/stdc++.hとatcoder/allをインクルードする別のヘッダーファイルを作り、AtCoderに提出するファイルはそれをインクルードするようにします。
yoniha/all.hの作成
このファイルはatcoder/allと近い場所にあると見通しがいいと思います。
例えばこのようなディレクトリ構造にしたとします。
.
└── kyopro
├── lib
├── atcoder
├── all
├── convolution
└── などなど
└── yoniha ←新規作成フォルダ
└── all.h ←新規作成ファイル
├── abc001
└── などなど
新規作成フォルダの名前はなんでもOKですが、提出ファイルでインクルードするときの名前もこれに合わせて変更する必要があります。
また、プリコンパイルする都合上.hが必要です。コンパイラにヘッダーファイルであることを明示するということですね。
all.hの中身は以下のようにします。
#include <bits/stdc++.h>
#include <atcoder/all>
これで事前準備は完了です。ここから実際にプリコンパイルします。
プリコンパイル
all.hがある場所に移動します。パスは自分が作ったファイルのパスに合わせてください。
cd ~/kyopro/lib/yoniha
コンパイルします。普段使うコンパイルコマンドに合わせてコンパイルしてください。VSCodeを使っている人はtasks.jsonなどを見るといいでしょう。
普段のコンパイルコマンドと違うところは、コンパイル対象ファイルの名前と、-oオプション(実行ファイルの名前)だけです。対象ファイルはもちろんall.hで、-oオプションはつけません。
例えば普段のコンパイルが
g++-14 -std=gnu++2b -O2 -Wall -Wextra -I ~/kyopro/lib -o a.out ./abc001/a/main.cpp
なら、プリコンパイルのコマンドは
g++-14 -std=gnu++2b -O2 -Wall -Wextra -I ~/kyopro/lib all.h
としましょう4。これで晴れてall.h.gchができたはずです!確認してみましょう。
ls
all.hとall.h.gchがあればOKです。
提出ファイルでのall.hのインクルード
ここにちょっとしたトラップがあります。単に
#include <yoniha/all.h>
// 以下コード
としてしまうと、ローカルでのテストが通っても、AtCoder上でCEになります。ジャッジサーバーにはyoniha/all.hがないので当然ですね。
これを回避するために、#ifなどのディレクティブを用います。具体的にはこんな感じです。
#if __has_include(<yoniha/all.h>)
#include <yoniha/all.h>
using namespace atcoder;
#else
#include <bits/stdc++.h>
#if __has_include(<atcoder/all>)
#include <atcoder/all>
using namespace atcoder;
#endif
#endif
using namespace std;
ACLがそもそもない環境でも使えるようにしています。bits/stdc++.hがない環境ではダメなので、そういうジャッジに出す可能性がある場合はそこにも対応した分岐を書いてあげればいいのではないでしょうか(そんなジャッジがあるのかはわからないです)。
#ifdefなどのディレクティブでも同じことができるのですが、コンパイルコマンドのオプションを追加する必要があるのでひと手間増えます。そのへんを問題なくできる人はお好みの方法でやるとよいと思います。C++17より前のC++を使っている人はそもそも__has_includeがないはずなので、頑張ってください。
結果の確認
最後に、いつものコンパイルコマンドに-Hオプションをつけて、先程の呪文を含むコードをコンパイルしてみましょう。プリコンパイルは済んだので、いつものフォルダに戻りましょう。
先程の例を使うと、
g++-14 -std=gnu++2b -O2 -Wall -Wextra -I ~/kyopro/lib -o a.out ./abc001/a/main.cpp -H
という感じです。1行目に、
! /home/yoniha/kyopro/lib/yoniha/all.h.gch
と出ればバッチリです。この!は、プリコンパイル済みヘッダーがあったよ!の!です。
念の為AtCoderのジャッジにも出してやりましょう。CEが出なければOKです。WAが出たら悲しいです。
お疲れ様でした。
おわり
終わりです。コンパイルが速くなったり速くなってなかったりしたら嬉しいです。
ここからは個人的な話ですが、結局-O2が足を引っ張っちゃう感じがあり、外そうかなあとか思ってたりします。でも初期化忘れとかは-O2なしだと通っちゃって-O2ありだとWAみたいなことがあるんだよな~とクネクネしています。皆さんはこの辺のことどうしていますか?
最後まで見てくれてありがとうございました。
-
GCCなどのC++コンパイラに存在する機能です。よく使うヘッダーファイル(拡張子が
.hなどのやつ)をあらかじめコンパイルしておくことで、それをインクルードするファイルのコンパイルを高速にしようというものです。 ↩ -
AtCoderが競技プログラミング用に準備してくれたライブラリです。https://atcoder.jp/posts/517 ↩
-
https://gcc.gnu.org/onlinedocs/gcc/Precompiled-Headers.html ↩
-
sudoが必要だと聞いたって?
bits/stdc++.hは普通usr/以下にありますが、これを直接プリコンパイルしようとするとsudoが必要になります。今回のyoniha/all.hは~/以下にあり、bits/stdc++.hを覗くだけなので、sudoは必要ないんですね。 ↩