extern "C"
前回は、C++ソースで定義された関数を
Cソース内でコールする場合に起こる問題について書きました。
そのキーワードが、「マングリング」でした。
前回:http://d.hatena.ne.jp/debian36/20080226
では、その問題を回避する方法、すなわちマングリングを避ける方法について、今日は記します。
ずばり、その答えが、extern "C" です。
この修飾子を見たことがあるかたも多いのではないでしょうか。
自分も、人のコードを見ていていると、たまに
extern "C"{
....
....
}
なんてコードが登場して、「ん〜、これは、C言語として解釈しろ」ってことなのかなぁってなんとなく思っていた程度でした。
確かに、extern "C" は、C言語として解釈して下さいよって、コンパイラ(g++)にお願いするものなのですが、なんのために、そのようなお願いをするのかというと、、、
「 マングリングを回避するため 」
なんですね。
ケース2:Cソースで定義されている関数をC++ソースから呼ぶ(前回の続き)
前回の例をそのまま使って実験してみます。
test.h
extern void hoge(void);
main.c
#include "./test.h" int main(void){ hoge(); return 0; }
このままビルドすると、前回示したように、
undefined reference error が起こります。
void hoge(void) 関数が、マングリングされたためでした。
では、この問題を回避するために、test.h を以下のように変更します。
test.h
#ifdef __cplusplus extern "C" void hoge(void); #else extern void hoge(void); #endif
この変更を加えることで、ビルドは成功するはずです。
このコードで本質的に行いたいことは、
関数 hoge をマングルしないようにすること
であり、それは、
関数 hoge を extern "C" で囲む
ということです。
ただ、extern "C" という修飾子は、Cコンパイラには解釈不能であるために、#ifdef ガードを加えています。
※ __cplusplus は C++ コンパイラ使用時に定義される識別子です。
test.hが、C++コードから include された場合は、
extern "C" void hoge(void);
が舐められ、 void hoge(void) という関数はこれ以降、extern "C" である関数として扱われます。これによって、 void hoge(void)は、マングルされず、Cからコール可能なシンボルに命名されます。
一方、test.hが、Cコードから include された場合は、
extern void hoge(void);
が舐められ、通常のプロトタイプ宣言として処理されます。
main.cpp
#include "./test.h" int main(void){ hoge(); return 0; }
この場合も結局は、マングルを回避すればよいはずですね。
main.cppの hoge() と関数をコールしている箇所を、C++コンパイラは、hoge(void)をマングルした結果のシンボル名をコールするように解釈してしまいます。
test.c はCコンパイラによって、_hoge のような素直なシンボルとして定義されているにもかかわらず、コールする側は、_Z4hogev などの複雑なシンボル名を期待してしまっています。
なので、コールする側が素直に _hoge を期待するように、
すなわちコールする側のシンボルをマングルしないようにします。