keisukeのブログ

***乱雑です!自分用のメモです!*** 統計や機械学習の勉強と、読み物を書く練習と、備忘録用のブログ

gccでビルドされたライブラリをg++から呼ぼうとするとundefined reference

またしてもC/C++で落とし穴に落ちた.
Cで書かれたライブラリをC++で普通に使っていて,普通に使えていたわけだが,
そのライブラリのあるヘッダをインクルードした瞬間にg++が

undefined reference to 'function name'

コンパイルエラーで止まる.
gccを使えばコンパイルできるが,C++の標準ライブラリを使いたいので
ぜひともg++でコンパイルしたい.

調べてみると,どうやらCとC++の互換の微妙な部分にいきついた.
「Name Mangling,名前修飾」という機能の有無が関係しているようだ.
簡単に言うと,Cでは重複する関数名は許されないが,
C++では関数名が同じでも引数の型や数が異なれば定義できる(オーバーロード).
ではC++コンパイラが実際どうしているかというと,
func(char) なら__func_c, func(int)なら__func_iみたいに名前を書き換えているらしい.これが名前修飾.
当然,オーバーロードを許さないCのコンパイラはこれをする必要はない.
別にやってもいいんだろうけど.

するとどうなるか.
gccコンパイルされたオブジェクトファイルの関数名と,
g++でコンパイルされたオブジェクトファイルの関数名は,
ソースコードの上では全く同じでも,オブジェクトファイルでは異なる名前になる.
すると,それらをリンクしようとすると,関数を見つけることができずに,めでたく
undefined reference.

名前修飾の標準はないらしく,どのように名前を変えるかはコンパイラ依存らしい.
つまり異なるコンパイラで作られたオブジェクトファイルをリンクしてはいけない
という話になる.

それを解決するための方法が,
extern "C".

解決:
gccコンパイルされたライブラリのヘッダファイルを探します.
関数宣言がずらずら並んでいるところを探します.

#ifdef __cplusplus
extern "C" {
#endif
(見つけた関数宣言)
#ifdef __cplusplus
}
#endif

にします.
おしまい.