1. 非真正意义上的Linux下用C调用C++生成的动态库.so。
编写C++源文件:CPPLIB.h和CPPLIB.cpp。
CPPLIB.h:
#pragma once
#include <iostream>
using namespace std;
class CPPLIB{
public:
CPPLIB();
int add(int x, int y);
~CPPLIB();
};
CPPLIB.cpp:
#include "CPPLIB.h"
CPPLIB::CPPLIB(){
cout << "constrcutor of class CPPLIB!" << endl;
}
int CPPLIB::add(int x, int y){
cout << "function add of class CPPLIB!" << endl;
return x + y;
}
CPPLIB::~CPPLIB(){
cout << "deconstructor of class CPPLIB!" << endl;
}
将其封装(extern “C”)成C可以调用函数,文件CLIB.h和CLIB.cpp如下:
CLIB.h:
#pragma once
#include "CPPLIB.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
int c_add(int x, int y);
#ifdef __cplusplus
}
#endif // __cplusplus
CLIB.cpp:
#include "CLIB.h"
int c_add(int x, int y) {
CPPLIB cpplib;
return cpplib.add(x, y);
}
将其编译成.so的动态库:g++ -shared -o libCLIB.so CPPLIB.cpp CLIB.cpp。
测试源文件main.c如下:
#include <stdio.h>
#include "CLIB.h"
int main() {
printf("c_add in CLIB.h is %d\n", c_add(2, 3));
return 0;
}
编译:g++ -o main.out main.c ./libCLIB.so
(这里一定要用g++,如果用gcc会出错,因为gcc编译C++文件才会自动调用g++,但如果对象直接就是C文件就不会调用g++了)
运行:./main.out
注:这个方法好像没有什么用,因为最终还是用g++编译的,例如用下面的测试方法也是可以成功的
#include <stdio.h>
#include "CPPLIB.h"
int main() {
CPPLIB cpplib;
printf("add in CPPLIB.h is %d\n", cpplib.add(2, 3));
return 0;
}
2.真正意义上的Linux下用C调用C++生成的动态库.so。
上面的方法最终还是g++来编译的,不能算是是真正意义上的用C代码来调用C++代码,但是可以用Linux提供的一组API(dlfcn.h)来完成,先介绍这几个API:
dlopen
函数原型:void *dlopen(const char *libname, int flag);
功能描述:dlopen必须在dlerror,dlsym和dlclose之前调用,表示要将库装载到内存,准备使用。如果要装载的库依赖于其它库,必须首先装载依赖库。如果dlopen操作失败,返回NULL值;如果库已经被装载过,则dlopen会返回同样的句柄。参数中的libname一般是库的全路径,这样dlopen会直接装载该文件;如果只是指定了库名称,在dlopen会按照下面的机制去搜寻:
a.根据环境变量LD_LIBRARY_PATH查找
b.根据/etc/ld.so.cache查找
c.查找依次在/lib和/usr/lib目录查找。
flag参数表示处理未定义函数的方式,可以使用RTLD_LAZY或RTLD_NOW。RTLD_LAZY表示暂时不去处理未定义函数,先把库装载到内存,等用到没定义的函数再说;RTLD_NOW表示马上检查是否存在未定义的函数,若存在,则dlopen以失败告终。
dlerror
函数原型:char *dlerror(void);
功能描述:dlerror可以获得最近一次dlopen,dlsym或dlclose操作的错误信息,返回NULL表示无错误。dlerror在返回错误信息的同时,也会清除错误信息。
dlsym
函数原型:void *dlsym(void *handle,const char *symbol);
功能描述:在dlopen之后,库被装载到内存。dlsym可以获得指定函数(symbol)在内存中的位置(指针)。如果找不到指定函数,则dlsym会返回NULL值。但判断函数是否存在最好的方法是使用dlerror函数,
dlclose
函数原型:int dlclose(void *);
功能描述:将已经装载的库句柄减一,如果句柄减至零,则该库会被卸载。如果存在析构函数,则在dlclose之后,析构函数会被调用。
下面示例仍然使用上面四个文件生成的C++动态库,此次测试文件为:
main.c:
#include <stdio.h>
#include <stdlib.h>
//#include "CLIB.h" //这里好像头文件都用不上了
#include <dlfcn.h> //引用头文件
int main() {
void *handle = dlopen("./libCLIB.so", RTLD_NOW);//加载库到内存
if(!handle){
fprintf(stderr, "%s\n", dlerror());
exit(1);
}
typedef int (*cpf)(int, int); //要用到的函数类型
dlerror();
char *error;
cpf p = (cpf)dlsym(handle, "c_add"); //要调用的函数
if((error = dlerror())!=NULL){
fprintf(stderr, "%s\n", dlerror());
exit(1);
}
printf("c_add in CLIB.h is %d\n", p(2, 3)); //调用
dlclose(handle); //关闭
return 0;
}
编译:gcc -o main.out main.c ./libCLIB.so -ldl
运行:./main.out