同一程序中混合调用C和C++代码

// 觉得这篇文章写的还可以,比较详细有点学究的味道,所以就翻译过来。C++和C混合编码虽然不难理解,但C库、C++库、extern "C"、extern "C++"、#inlcude <stdio.h>、#include <CStdio>等等,区别起来也有点困难。发生误解的根源在于没有把编译和连接理解透彻。一个程序使用了某个函数,不管该函数是在某个头文件中定义的函数,还是通过extern定义的外部函数,还是本地已经定义好的函数,该函数都要经过编译、连接两个步骤。在编译阶段,C++编译器会根据函数返回类型、参数类型等,进行函数名修饰;之后才会根据修饰后的函数名,进行连接。(注意函数名修饰发生在编译阶段)。因此,在定义可同时被C、C++使用的头文件时,要考虑到C、C++编译器的编译过程,综合使用extern "C"、#ifdef __cplusplus(所有C++编译器都会预定义这个头文件)来声明该头文件。

// 本文中:源代码(Source),程序(Program)是指未编译的程序;代码(Code)应该指的是头文件(.H)加库(.LIB / .DLL)的组合。

C++语言提供了这种机制:它允许在同一个程序中有“C编译器”和“C++编译器”编译的代码(程序库)混合存在。本文主要解决由于C和C++代码混合使用所引起的一些通用问题,同时注明了几个容易引起的误区。

主要内容

-使用可兼容的编译器

-C++源程序中调用C代码

-C源程序中调用C++代码

-混合IOstream和C标准I/O

-函数指针的处理

-C++异常的处理

-程序的连接

 

1. 使用可兼容的编译器

 

本文的讨论建立在这样的基础上:所使用C和C++编译器是兼容的;它们都以同种方式定义int、float、pointer等数据类型。

C编译器所使用的C运行时库也要和C++编译器兼容。C++包含了C运行时库,视为它的一个子集。如果C++编译器提供它自己的C版本头文件,这些头文件也要和C编译器兼容。

 

2. 从C++源程序中调用C代码

 

C++语言为了支持重载,提供了一种连接时的“函数名修饰”。对C++文件(.CPP)文件的编译、连接,缺省采用的是这种“C++的方式”,但是所有C++编译器都支持“C连接”(无函数名修饰)。

当需要调用“C连接”(由C编译器编译得到的)时,即便几乎所有C++编译器对“数据”的“连接修饰”与C编译器无任何差异,但还是应该在C++代码中声明“C连接”;指向函数的指针没有“C连接”或“C++连接”。

 

能够对“连接修饰”进行嵌套,如下,这样不会创建一个scope,所有函数都处于同一个全局scope。

 

extern "C" {

void f(); // C linkage

extern "C++" {

void g(); // C++ linkage

extern "C" void h(); // C linkage

void g2(); // C++ linkage

}

extern "C++" void k(); // C++ linkage

void m(); // C linkage

}

 

如果使用C库及其对应的.H头文件,往往可以这样做:

 

extern "C" {

#include "header.h";

}

 

建立支持多语言的.H头文件,如同时支持C和C++的头文件时,需要把所有的声明放在extern "C"的大括号里头,但是“C编译器”却不支持 " extern "C" "这种语法。每一个C++编译器都会预定义__cplusplus宏,可以用这个宏确保C++的语法扩展。

 

#ifdef __cplusplus

extern "C" {

#endif

/* body of header */

#ifdef __cplusplus

}

#endif

 

假如想在C++代码中更加方便的使用C库,例如在C++类的成员函数/虚函数中使用"C库",怎样确保"C库"中的函数能够正确识别出"C++"的类?利用extern "C"可以这样做:

 

struct buf {

char* data;

unsigned count;

};

void buf_clear(struct buf*);

int buf_print(struct buf*);

int buf_append(struct buf*, const char*, unsigned count);

 

在C++中可以方便的使用这个结构,如下:

 

extern "C" {

#include "buf.h";

}

class mybuf {

public:

mybuf() : data(0), count(0) {}

void clear() { buf_clear((buf*)this); }

bool print() { return buf_print((buf*)this); }

bool append()...

private:

char* data;

unsigned count;

} ;

 

提供给class mybuf的接口看起来更像C++的Code,它能够更加容易的被集成到面向对象编程中。但是,这个例子是在没有虚函数、且类的数据区开头没有冗余数据的情况下。

 

另一个可供替代的方案是,保持struct buf的独立性,而从其派生出C++的类。当传递指针到struct buf的成员函数时,即使指向mybuf的指针数据与struct buf位置不完全吻合,C++编译器也会自动调整,把类的类型协变到struct buf。class mybuf的layout可能会随不同的C++编译器而不同,但是这段操作mybuf和buf的C++源代码也能到哪里都工作。如下是这种派生的源代码,它也隐含了struct结构具有的面向对象的特性:

 

extern "C" {

#include "buf.h"

}

class mybuf : public buf { // a portable solution

public:

mybuf() : data(0), count(0) { }

void clear() { buf_clear(this); }

bool print() { return buf_print(this); }

bool append(const char* p, unsigned c)

{ return buf_append(this, p, c); }

};

 

C++代码能够自由地使用mybuf类,传递自身到struct buf的C代码中,能很好的工作,当然,如果给mybuf加入了别的成员变量,C代码是不知道的。这是“派生类”的一种常规设计思路。

 

3. 从C源代码中调用C++代码

 

如果声明C++函数采用“C连接”,那么它就能够被"C代码"引用,前提是这个函数的参数和返回值必须能够被"C代码"所接受。如果该函数接受一个IOStream的类作为参数,那么C将不能使用,因为C编译器没有没有C++的这个模板库。下面是一个C++函数采用“C连接”的例子:

 

#include <iostream>

extern "C" int print(int i, double d)

{

std::cout << "i = " << i << ", d = " << d;

}

 

可以这样定义一个能同时被C和C++使用的头文件:

 

#ifdef __cplusplus

extern "C"

#endif

int print(int i, double d);

 

对于C++同名重载函数,利用extern "C"声明时,最多只能声明“重载函数系列”中的一个函数。如果想引用所有重载的函数,就需要对C++重载的函数外包一个Wrapper。代码实例如下:

 

int g(int);

double g(double);

extern "C" int g_int(int i) { return g(i); }

extern "C" double g_double(double d) { return g(d); }

 

wrapper的头文件可以这样写:

 

int g_int(int);

double g_double(double);

 

模板函数不能用extern "C"修饰,也可以采取wrapper的方式,如下:

 

template<class T> T foo(T t) { ... }

extern "C" int foo_of_int(int t) { return foo(t); }

extern "C" char* foo_of_charp(char* p) { return foo(p); }

 

本文来自: (www.91linux.com) 详细出处参考:http://www.91linux.com/html/article/program/cpp/20100112/18624.html

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值