このブログは、旧・はてなダイアリー「檜山正幸のキマイラ飼育記 メモ編」(http://d.hatena.ne.jp/m-hiyama-memo/)のデータを移行・保存したものであり、今後(2019年1月以降)更新の予定はありません。

今後の更新は、新しいブログ http://m-hiyama-memo.hatenablog.com/ で行います。

gccのpre-compiled headerがらみの謎の現象

対処法は分かったが理由がサッパリ分からない謎の現象。これを詮索している余裕がないので、とりあえず起きたことを記しておく。後で再現できるかどうかは分からない。

まず、2つのソースコードがある。1行しか違ってない!

$ diff -u t_params.cpp t_params2.cpp
--- t_params.cpp        2016-05-13 12:19:14 +0900
+++ t_params2.cpp       2016-05-13 12:19:12 +0900
@@ -1,5 +1,5 @@
 // -*- coding: sjis -*-
-
+#include "stdafx.h"

 #include <stdio.h>
 #include "OJOLparams.h"

stdafx.h は、MSVCと揃えるために、gccでも使っているプリコンパイルヘッダ。次のよう。

// -*- coding: sjis -*-
// gcc/g++向けのPCH(pre-compiled header)の定義
//
#ifndef STDAFX_H
#define STDAFX_H

#include <stdio.h>
#include <iostream>
#include <Windows.h>
#include <vector>

#endif // STDAFX_H

Makefileの関連する場所は、

CXX:=mingw32-g++
CXXFlags:=-std=gnu++0x -MD -DNO_MFC -DMY_DEBUG

# ...

%.o : %.cpp %.h stdafx.h.gch
	$(CXX) $(CXXFlags) -c $<

# pch = pre-compiled header
.PHONY:pch
pch : stdafx.h.gch

stdafx.h.gch : stdafx.h
	$(CXX) $(CXXFlags) $<

mingw32-g++ のバージョンは、 mingw32-g++.exe (GCC) 4.6.2

オブジェクトを作るときは、.gchがあることを前提にしている。

さて、問題の現象。

make -k t_params.exe

Making executable file
mingw32-g++ -std=gnu++0x -MD -DNO_MFC -DMY_DEBUG -o t_params.exe t_params.cpp -L./ -lXYZW -lshlwapi
C:\Users\hiyama\AppData\Local\Temp\cccemmLi.o:t_params.cpp:(.text+0x40): undefined reference to `XXXXyyyyyyZ::SetVolumeLabel(char const*)'
collect2: ld returned 1 exit status
make: *** [t_params.exe] Error 1

Compilation exited abnormally with code 2 at Fri May 13 12:22:01
make -k t_params2.exe

Making executable file
mingw32-g++ -std=gnu++0x -MD -DNO_MFC -DMY_DEBUG -o t_params2.exe t_params2.cpp -L./ -lXYZW -lshlwapi

Compilation finished at Fri May 13 12:24:58

「#include "stdafx.h"」の行がないほうが、リンク時に、とある関数への参照が解決できないのだ。コンパイラ時じゃなくてリンク時!

`XXXXyyyyyyZ::SetVolumeLabel(char const*)'という関数は確実に存在している。その名前が見えなくなる。その名前だけが見えなくなる。

試しに、XXXXyyyyyyZ::SetVolumeLabel00(char const*) という関数を作ると、この名前はどっちでも見える、どっちもリンクが成功する。XXXXyyyyyyZ::SetVolumeLabel という名前だけが「#include "stdafx.h"」の影響を受ける。

ワカラン。

[追記]今日、時間がないが、たぶんこういう事情だろう。

stdafx.h では Windows.h がインクルードされている。問題は、Windows.h だろう。

SetVolumeLabel という名前はWindows APIにある。

かぶったのだ。このカブリは名前のマングリング(Name Mangling)で回避されるが、Windows.hをインクルードしないと、かぶっていることを認識できないので、カブリ回避の処理が走らないのだと思う。

[/追記]
[さらに追記]まず間違いない。[/さらに追記]

[もっと追記 date="翌日"]

かぶっていることを認識できないので、カブリ回避の処理が走らないのだと思う。

これは違った。マングリングではない。

かぶっているのは確かだけど、Windows API名がマクロ展開されるところに問題がある。結局はCプリプロセッサで細工しているからこんなことになる今までのトラブルのほとんどがプリプロセッサがらみだ。

[/もっと追記]