由于C编译器与C++编译器之间的区别十分巨大,因此二者之间不可以直接互相调用各自的函数接口。但是,使用extern "C"
可以实现在C代码中调用C++函数的功能,反之亦可。extern "C"
告诉C++编译器,将花括号中的代码按照C语言的规则进行编译与链接。
在本篇文章中,笔者展示了利用C代码调用C++接口的一个示例。实现的功能有:C代码获取C++对象指针,并通过对象指针调用对象函数(包括构造函数和析构函数)。
示例分为5个文件:
CppPrint.cpp
CppPrint.h
CWrapper.cpp
CWrapper.h
CallCpp.c
其中CppPrint.cpp
中是C++代码的实现,其中用iostream
实现了打印Hello world!
的功能。这部分的代码没有需要特殊说明的地方,属于一般的C++实现。
/*
* CppPrint.h
*
*/
#ifndef CPP_PRINT_H
#define CPP_PRINT_H
#include <string>
class Printer {
public:
Printer();
Printer(char* str);
~Printer();
void print();
void print(char* str);
private:
std::string mString;
};
#endif // CPP_PRINT_H
/*
* CppPrint.cpp
*
*/
#include <iostream>
#include <string>
#include "CppPrint.h"
Printer::Printer()
: mString(""){
std::cout << "Printer@[" << this << "] is constructed."<< std::endl;
}
Printer::Printer(char* str)
: mString(str) {
std::cout << "Printer@[" << this << "] is constructed."<< std::endl;
}
Printer::~Printer() {
std::cout << "Printer@[" << this << "] is destructed."<< std::endl;
}
void Printer::print() {
std::cout << mString << std::endl;
}
void Printer::print(char* str) {
if (NULL != str) {
mString = std::string(str);
}
std::cout << mString << std::endl;
}
CWrapper.cpp是利用extern "C"
对C++函数的封装。这里有2点需要特殊说明。首先,C++传递给C的对象指针必须以结构体指针的形式存在,否则C语言的编译器会报错。其次,结构体中只可以包含对象指针,不可以直接包含对象实例。若包含的是对象实例,在C代码执行的时候会发生段错误(Segment Fault)。
/*
* CWrapper.h
*
*/
#ifndef C_WRAPPER_H
#define C_WRAPPER_H
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
struct Printer4C;
typedef struct Printer4C* PrinterRef;
PrinterRef getPrinter(char* str);
void print(PrinterRef ref);
void deletePrinter(PrinterRef ref);
#ifdef __cplusplus
}
#endif
#endif // C_WRAPPER_H
/*
* CWrapper.cpp
*
*/
#include <iostream>
#include <cstring>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "CppPrint.h"
#include "CWrapper.h"
#ifdef __cplusplus
extern "C" {
#endif
struct Printer4C {
Printer* obj;
};
PrinterRef getPrinter(char* str) {
PrinterRef ref = (PrinterRef) malloc(sizeof(struct Printer4C));
if (NULL == str || strlen(str) <= 0) {
ref->obj = new Printer("Hello world!");
} else {
ref->obj = new Printer(str);
}
return ref;
}
void print(PrinterRef ref) {
ref->obj->print();
}
void deletePrinter(PrinterRef ref) {
delete ref->obj;
free(ref);
}
#ifdef __cplusplus
}
#endif
在C文件中获取C++对象指针,并利用对象指针调用成员函数。根据CWrapper的实现,当获取printer时没有传入有效的字符串时,会将打印的内容默认设为“Hello world!”。
/*
* CallCpp.c
*
*/
#include "CWrapper.h"
int main() {
PrinterRef ref1 = getPrinter("");
PrinterRef ref2 = getPrinter("Welcome!");
print(ref1);
print(ref2);
deletePrinter(ref1);
deletePrinter(ref2);
}
编译动态库。
$ g++ -fPIC -shared CppPrint.cpp CppPrint.h CWrapper.cpp CWrapper.h -o libCppPrint.so
将libCppPrint.so复制到/usr/lib下。
$ sudo cp libCppPrint.so /usr/lib
编译C代码
$ gcc CallCpp.c -lCppPrint -o CallCpp
执行程序
$ ./CallCpp
控制台输出
Printer@[0x25b0030] is constructed.
Printer@[0x25b00a0] is constructed.
Hello world!
Welcome!
Printer@[0x25b0030] is destructed.
Printer@[0x25b00a0] is destructed.
PS:
实验环境:
Ubuntu 14.04 LTS 64位
gcc与g++的版本均为4.4.7