※上記の広告は60日以上更新のないWIKIに表示されています。更新することで広告が下部へ移動します。

C++で書かれたプログラムのシンボル名は、一意の名前となるよう名前マングルされている。
たとえば以下のような関数をgccとg++でコンパイルしてみる。
// sample.c
int test1(){
	return 1;
}
Cでコンパイルし、nmでシンボル名を見てみると、test1が定義されている。
$ gcc -c sample.c 
$ nm sample.o
0000000000000000 T test1
C++だと、以下のように変な名前になる。
$ g++ -c sample.c
$ nm sample.o
0000000000000000 T _Z5test1v             <--名前マングルされたtest1
                 U __gxx_personality_v0
$ nm --demangle sample.o                 <--デマングルして表示させるとたしかにtest1
0000000000000000 T test1()
                 U __gxx_personality_v0

このように、CとC++ではシンボル名が異なるため、CからC++の関数(その逆も)を呼び出そうとすると、そんな関数はないと言われ、エラーになってしまう。
そのため、関数の前に「extern "C"」ディレクティブを付けると、その関数はCのリンケージ形式に従い、名前マングルされなくなる。
上記の例につけると以下のようになる。
extern "C" int test1(){
	return 1;
}
コンパイルしてみると、以下のとおり名前マングルされていない。
$ g++ -c sample.c
$ nm sample.o
                 U __gxx_personality_v0
0000000000000000 T test1

 なので、CからC++の関数を呼ぶ時は、C++で書かれた関数に「extern "C"」をつけて、Cの関数のようにみせかけてやればよい。
 逆にC++からCの関数を呼ぶときは、何もしないと呼び出し元はCの関数を名前マングルしたシンボルで探しにいくので、C関数のプロトタイプ宣言に「extern "C"」をつけて、当該C関数がCのリンケージ形式の関数であることを教えてあげればよい。
 以下がそのサンプル。

■C言語ソース
$ cat csrc.c
#include <stdio.h>

int apmain( int argc, char **argv );

int main( int argc, char **argv ){
	int ret;
	printf("In File:%s Func:%s line:%d \n",__FILE__,__func__,__LINE__); 
	ret = apmain( argc, argv );
	printf("Out File:%s Func:%s line:%d ret:%d \n",__FILE__,__func__,__LINE__,ret); 
	return(30);
}

int cmain( int argc, char **argv )
{
	int i;
	 printf("In File:%s Func:%s line:%d \n",__FILE__,__func__,__LINE__);

	char *strp;
	for( i = 0; i < argc; i++ ){
		strp = argv[i];
		printf("%s\n",strp);
	}
	return(10);
}
■C++言語ソース
$ cat cppsrc.cpp 
#include <stdio.h>
#include <iostream>

#ifdef __cplusplus
extern "C" {
#endif
   int apmain( int argc, char **argv );
   int cmain( int argc, char **argv );
#ifdef __cplusplus
}
#endif

int cppmain( int argc, char **argv )
{
	int ret;
	 printf("In File:%s Func:%s line:%d \n",__FILE__,__func__,__LINE__);

	char *strp;
	for( int i = 0; i < argc; i++ ){
		strp = argv[i];
		std::cout << strp << std::endl;
	}
	
	ret = cmain( argc, argv );
	return(10);
}

int apmain( int argc, char **argv )
{
	int ret;
	printf("In File:%s Func:%s line:%d \n",__FILE__,__func__,__LINE__);
	ret = cppmain( argc, argv);
	printf("Out File:%s Func:%s line:%d ret:%d\n",__FILE__,__func__,__LINE__,ret);
	return(20);
}
■makefile
$ cat makefile 
program = cppfromc
libs = libfc.so \
       libfcpp.so

# 定義済マクロの再定義
CC = gcc
CFLAGS = -g -Wall -fPIC
CXX = g++
CXXFLAGS = -g -Wall -fPIC

# サフィックスルール適用対象の拡張子の定義
.SUFFIXES: .c .o

# プライマリターゲット(c++のライブラリをgccでリンクするのでlibstdc++が必要)
$(program): $(libs)
	$(CC) -o $(program) -lfc -lfcpp -lstdc++

libfc.so: csrc.c
	$(CC) $(CFLAGS) -o libfc.so -c csrc.c

libfcpp.so: cppsrc.cpp
	$(CXX) $(CXXFLAGS) -o libfcpp.so -c cppsrc.cpp

# ファイル削除用ターゲット
.PHONY: clean
clean:
	$(RM) $(program) $(libs)
■実行結果
$ make
gcc -g -Wall -fPIC -o libfc.so -c csrc.c
g++ -g -Wall -fPIC -o libfcpp.so -c cppsrc.cpp
gcc -o cppfromc -lfc -lfcpp -lstdc++
$ cppfromc arg1 arg2
In File:csrc.c Func:main line:7               <--C関数
In File:cppsrc.cpp Func:apmain line:32        <--Cから呼ばれるC++関数
In File:cppsrc.cpp Func:cppmain line:17    <--C++関数
cppfromc
arg1
arg2
In File:csrc.c Func:cmain line:16       <--C++関数から呼ばれるC関数
cppfromc
arg1
arg2
Out File:cppsrc.cpp Func:apmain line:34 ret:10
Out File:csrc.c Func:main line:9 ret:20 


名前:
コメント: