#defineが関係するコンパイルエラーの原因を突き止める


コンパイルエラーは比較的すぐ直せるものが多いのですが
たまに、いくら調べてもコードは間違ってないし何が原因なんだと
四苦八苦することがあります。

いろいろなパターンがあるかとは思いますが
ついさっき、「#define」が引き起こしているコンパイルエラーで苦労したので
その原因を突き止める方法(というほどでもないですが)についてになります。

こんな事がありました。

DirectXのプログラム中にビルドをしたらコンパイルエラーがでました。
コンパイルエラーと言われている該当のコードは以下の部分

xnamathmatrix.inl
XMINLINE BOOL XMMatrixDecompose( XMVECTOR *outScale, XMVECTOR *outRotQuat, XMVECTOR *outTrans, CXMMATRIX M )
{
    // ~省略~
    if(pfScales[b] < XM_DECOMP_EPSILON)
    {
        UINT aa, bb, cc; // ←この行が怒られている!

    // ~省略~

出ているコンパイルエラーはこちら↓

エラー C2143 構文エラー: ';' が '定数' の前にありません。 DirectX11 c:\program files (x86)\microsoft directx sdk (june 2010)\include\xnamathmatrix.inl 847
エラー C2065 'bb': 定義されていない識別子です。 DirectX11 c:\program files (x86)\microsoft directx sdk (june 2010)\include\xnamathmatrix.inl 847
こんな感じのがたくさん。

コードに間違ってるところなんてないし、そもそもSDKの中のプログラムじゃん。。。
と、だいたいこのパターンは#defineによって想定外の置換が起きていると思われる。

とりあえずビルド時のログを見てみる。

SDKのコードが間違っていない以上は、自分で作ったプログラムのどこかに問題があるわけです。
というわけでビルド時の出力を追ってみます。

1>------ ビルド開始: プロジェクト:DirectX11, 構成:Debug Win32 ------
1> main.cpp
1>c:\program files (x86)\microsoft directx sdk (june 2010)\include\xnamath.h(2666): warning C4838: 'unsigned int' から 'INT' への変換には縮小変換が必要です
~省略~
1>c:\program files (x86)\microsoft directx sdk (june 2010)\include\xnamathmatrix.inl(847): error C2143: 構文エラー: ';' が '定数' の前にありません。

問題となっているエラーは、main.cppのコンパイル中に出たようです。

main.cppの処理を見てみる。

xnamathっぽいのは下記のヘッダをインクルードしている部分くらい。

main.cpp
#include "resource.h"
#include <windows.h>
#include <mmsystem.h>
#include <xnamath.h>

この手のエラーはだいたいヘッダを読む順番を変えるとコンパイルが通ります。
下記のように、`resource.h'を下にもっていったらコンパイルが通りました。

main.cpp
#include <windows.h>
#include <mmsystem.h>
#include <xnamath.h>
#include "resource.h"

resource.hの中を見る

resource.h
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ で生成されたインクルード ファイル。
// DirectX11.rc で使用
//
#define IDI_ICON1                       101
#define IDR_MENU1                       102
#define aa                              40001

見るからに怪しい#define aa 40001がいる…
コンパイルエラーで怒られていたコードは以下。

xnamathmatrix.inl
XMINLINE BOOL XMMatrixDecompose( XMVECTOR *outScale, XMVECTOR *outRotQuat, XMVECTOR *outTrans, CXMMATRIX M )
{
    // ~省略~
    if(pfScales[b] < XM_DECOMP_EPSILON)
    {
        UINT aa, bb, cc; // ←この行が怒られている!

    // ~省略~

resource.hを先にインクルードしている場合は
変数名であるaaの部分が40001という定数に置き換わりますね。

resource.hを後に読み込めば、xnamath.hが読み込まれた時点では
コンパイラまだ#define aa 40001を知らないので置換される事なくコンパイルが通るというわけでした。

という感じで、1つずつ追い詰めていくとわりと見つかるのですが
コンパイルエラーの内容を見ただけでは一体どこが原因なのかわかりにくいですね。

というか#define aaってなんだよ…

マクロが展開された後のコードを見る

今回のエラーは上記のように追いかけていけば突き止められましたが
そもそもコードが長すぎてどこがどうなってるのかわからない場合
マクロ(#defineなど)が展開された後のコードを見るのも一つの手です。

マクロが展開された後のコードを作る方法(Visual Studio 2015)

マクロを展開したいプログラムファイル(*.cpp)を右クリック → プロパティ
構成プロパティの C/C++ → プリプロセッサ を選択
ファイルの前処理を「はい」に変更して適用。

設定をしたファイルを右クリック → コンパイルとすると
指定したファイル1つのみがコンパイルされ
拡張子が「i」となっているファイルが作られると思います。

出力先は設定次第ですが、だいたいはDebugとかReleaseとか普段ビルドした結果が作られるところにあります。

一応今回つくられたファイルの中身を確認してみました。

main.i
XMINLINE BOOL XMMatrixDecompose( XMVECTOR *outScale, XMVECTOR *outRotQuat, XMVECTOR *outTrans, CXMMATRIX M )
{
    // ~省略~
    if(pfScales[b] < XM_DECOMP_EPSILON)
    {
        UINT 40001, bb, cc; // ←この行が怒られている!

    // ~省略~

予想通り、aa40001に置換されており
コンパイルエラーの定数の前に「;」がないであるとか
変数のbbが定義されていないとかも納得できるエラーだなとなりますね。

  1. コンパイル時のログからコンパイルが通らないcppファイルを特定する
  2. そのcppの設定を変えて、プリプロセッサ展開後のファイルを作成する
  3. コンパイルエラーと言われている該当箇所を確認する。

という方法でだいたいは解決できるだろうと思います。
が、このファイル、めちゃくちゃでかいです。

コードにもよりますが、だいたいは数百万行とかになったりします。
普通のエディタで開くのが厳しい場合は、無駄な行を省いたり多少工夫はいるかもしれません。

というわけで確実な方法ではないにせよ
今まであてもなく謎のコンパイルエラーの原因を探していた人は
この方法で探してみると、少し楽になるかもしれません。