平时需要接触c和c++,有时会遇到两种语言互相调用动态库的情况,这时就要对代码进行一些处理,做个记录。
前提
两种语言生成的库是无法直接互相调用的,原因在于c++支持重载,因此存在改名机制(也可能是因为有改名机制所以支持重载 ==!)_,即生成的中间文件会对函数名进行改写,会将函数名与参数列表相结合生产新的函数名,这样就可以通过不同的函数名来区分重载函数了。
而C语言没有改名机制,函数名未改动,若两种语言直接调用,便会报错:函数名未定义。
eg:
C++代码 ,cplus.cpp 和 cplus.h
#include<stdio.h>
int sayCplus()
{
printf("Hello, C++\n");
return 0;
}
#ifndef _CPLUS_H_
#define _CPLUS_H_
int sayCplus();
#endif
C 代码,c.c和c.h
#include<stdio.h>
int sayC()
{
printf("HI, This is C program\n");
return 0;
}
#ifndef _C_H_
#define _C_H_
int sayC();
#endif
分别编译成动态库libCplus.so和libC.so
用nm 指令查看,可知函数sayCplus在动态库中被改名了。
这时若用c调用libCplus.so,或c++调用libC.so,会显示对函数xxx未定义的引用。
C调用C++动态库 ——即把C++以C方式编译
要实现C调用C++的动态库,需要使用"extern C"关键字。该关键字是C++之父在设计C++时,为兼容已存在的大量C代码而设定的方法。用该关键字将C++的函数包含起来,即可将C++按C语言的方式进行编译。
改写如上代码cplus.cpp
#include<stdio.h>
extern "C"
{
int sayCplus()
{
printf("Hello, C++\n");
return 0;
}
}
重新编译成动态库libCplus.so,该函数不会被再改名,就可以调用了。
注意:
<1>只在cpp中使用extern “C”,若其他c++代码包含了该头文件并调用动态库仍会报错,因为头文件中的函数是按c++方式(改名机制)寻找。
因此,若c++生成的动态库要供c 和 c++调用,需要将cpp和头文件中的函数都用extern "C"包含起来。
也可只在头文件中用extern "C"括起来,在cpp中包含该头文件,效果如上。
<2>用该方法C++就无法实现重载了。
<3>若C++函数的参数中使用了C中不存在的数据类型(如vector、string),或是包含了代码中的其他结构,则用extern "C"包含并不能编译通过。此时可以通过对该函数再封装一层,入参、出参确保是通用类型即可。
C++调用C动态库
C++动态库也要用到extern "C"关键字,但是用在头文件中。
先将C语言的头文件用extern "C"包起来,c++中include该头文件,即会以c的方式去寻找函数,即可实现c++调用c动态库。
注意:用extern "C"包涵后,该头文件就无法给c语言调用使用了,因此一般会加个宏定义__cplusplus,
判断是用c++的方式编译,则extern “C” 生效。
改后的头文件如下:
#ifndef _C_H_
#define _C_H_
#ifdef __cplusplus
extern "C"
{
#endif
int sayC();
#ifdef __cplusplus
}
#endif
#endif
注意:该种方式,c的动态库并不用改变。