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);

test.cpp

#include 
#include "./test.h"

void hoge(void){
	printf("hoge!\n");
}

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);
が舐められ、通常のプロトタイプ宣言として処理されます。

ケース3 : Cコードで定義されている関数をC++コードから呼ぶ

では、逆に Cで記述された関数をC++のソースファイル内で呼ぶ場合はどうすればよいのか?

$ mv main.c main.cpp

test.h

元に戻します。

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 を期待するように、
すなわちコールする側のシンボルをマングルしないようにします。

main.cpp

extern "C"{
	#include "./test.h"
}

int main(void){
	hoge();
	return 0;
}

#include "./test.h"を extern "C" で囲むことによって、
extern void hoge(void);
のプロトタイプ宣言部分を、マングルしないように解釈します。

これ以降、hoge(void) がコールされたときは、_hoge というシンボルをコールするように解釈するということですね。