こんにちは、高木です。

毎度おなじみのPHPでC言語の前処理を行う話題です。今回はC言語のインクルードガードマクロをPHPで自動生成してみることにします。

C言語で本格的なプログラミングをしたことがある方なら、ヘッダファイルに記述した構造体の定義などが二重にならないように、インクルードガードが必要になることをご存じかと思います。PHPではインクルードガードが必要ないように、include_onceやrequire_onceといった一度しか他のスクリプトを取り込まない構文がありますね。

インクルードガードは通常、そのヘッダファイルのパス名に基づいた名前のマクロを使います。多くの場合、パス名をすべて大文字にし、英数字以外を下線「_」に置換した名前を使用します。他の名前とたまたま衝突することを避けるため、末尾に下線を付加することも多いでしょう。先頭に下線を付加するケースも見かけますが、予約済み識別子と衝突するので使うべきではありません。

実際には、他の名前と衝突しなければ何でもいいので、もう少しユニークな名前を付けてもいいと思います。手作業でそれを行うのは大変ですが、自動生成なら簡単です。

今回は、前述したように、すべて大文字にして英数字以外を下線にした名前をベースに、末尾に乱数を付加する方法を採用することにしました。本当はUUIDの方が確実なのですが、外部ライブラリを使うことになるので今回はやめました。単純にrandom_int関数で生成した乱数を付加しています。末尾の乱数が不要なら付加しないことにしてもいいでしょう。

少し工夫したのは、パス名にASCII以外が使われている場合にも対応できるようにしたことです。「#line指令等に埋め込むパス名のエンコーディング問題について」の回で作成したescape_path関数を使って\uxxxx形式に変換してから処理するようにしています。ここで登場する逆斜線(または円記号)を単純に下線に置換すると、下線が連続してC++に使おうとしたときに予約済み識別子と衝突します。ですので、今回は\uxxxxの先頭の逆斜線を小文字の「e」に置換することにしました。

これでインクルードガードマクロを自動生成することができるようになりました。

主要なC言語処理系(Visual C++, GCC, Clang)では、いずれも#pragma onceを使ってインクルードガードを実現することもできます。必要であれば、マクロによるインクルードガードと#pragma onceによるインクルードガードを出し分けることもできると思います。もちろん両方を出力することもできるでしょう。