有时候需要在C程序下调用C++的库,看了很多文章,要么说的太深入和冗余导致不能一下子理解,要么是举例子太简单导致没有理解还是不会使用,所以这篇博客取二者折中,给出简单易懂的解释,并给出实例。
首先,C和C++编译一个函数之后产生的函数名字是不同的,所以二者不能通用,即使用C++去调用C程序编译好的的动态库也是不可以的,反过来就更不可以了。那么C怎么调用C++呢?答案就是写一个接口程序,这个接口程序的interface.cpp
文件负责将C++库的具体函数封装为C函数,头文件interface.h
被main函数调用。看例子最简单,一起来看。
首先假设有C++程序test.cpp
和test.hpp
// test.hpp 文件内容:定义了ClassA类
class ClassA{
public:
void func(int a);
};
// test.cpp 文件内容:实现了ClassA::func(int a)函数
#include <iostream>
#include "test.hpp"
using namespace std;
void ClassA::func(int a){
cout<<"class A: "<<a<<endl;
}
假设这个就是我们已有的一个C++程序,那么怎样在C程序main.c
中调用它呢?答案是写一个接口interface
。
// interface.h文件内容
#ifdef __cplusplus
extern "C"{
#endif
void func(int a);
#ifdef __cplusplus
}
#endif
// interface.cpp 文件内容
#include "test.hpp"
#include "interface.h"
void func(int a){
ClassA c;
c.func(a);
}
上面interface.h
就是连接C++库和C程序的桥梁,这里面extern "C"{}
括起来的部分意思就是用编译C程序的方式进行编译,extern "C"{}
语句是在定义了宏__cplusplus
的情况下启用,而这个宏是在使用g++进行编译时自动会定义的。如果是用以C语言方式进行编译,则不会定义宏__cplusplus
,自然也没有启用extern "C"{}
(C语言不识别extern "C"{}
,所以需要用宏来开关)。Interface.cpp
中将test.cpp
程序中的C++函数封装为C形式的函数,用于main中调用。
这里你可以注意到,Interface
的实现文件是.cpp
,而头文件是.h
,这正是为了用.cpp
连接C++库,而.h
给C程序用。读到这里确实绕,你可以暂时理解个大概,等讲到main函数我们再回头捋一遍就明白了。此时可以将C++库和接口共同编译为libtest.so
动态库
g++ test.cpp interface.cpp -shared -fPIC -o libtest.so
终于到了main函数,如下
// main.c 文件内容
#include "interface.h"
int main()
{
func(1);
}
现在我们要编译main函数需要如下:
gcc main.c ./libtest.so -o main
即实现了C语言程序调用C++程序库。现在我们总结一下,就是要写一个封装接口,接口的具体实现cpp文件用于将C++程序库封装成C程序可读的接口,接口的头文件.h用于被C程序调用。那么读者会疑问,为什么多此一举在interface.h中加上__cplusplus
宏定义控制开关的extern "C"
。这是因为interface.h同时被interface.cpp
和main.c
调用,前者需要按照C++方式编译,后者需要按照C方式编译,需要开关。
别找其他教程了,这篇博客是你能找到最有可能帮你理解extern "C"
的文章了,反复读几遍加上动手实现一下就明白了。
备注:
1.gcc和g++的区别并不是前者用于编译c程序,后者用于编译c++程序,对于简单的.cpp
程序,用gcc也能编译通过。g++的内核仍然是gcc,只是默认增加了一些宏定义和库。总之,不能将gcc简单看为用于编译c程序,g++用于编译c++程序。感兴趣读者可以查阅更详细资料。