如何混合使用C和C++ (转)
来源:互联网 发布:数据变换boxcox 编辑:程序博客网 时间:2024/04/28 10:58
(Part of C++ FAQ Lite, Copyright © 1991-2006, Marshall Cline, cline@parashift.com)
#include <cstdio> // #inlcude行没有什么不寻常的
int main()
{
std::printf("Hello world/n"); // 调用也没什么不寻常的
...
}
#include <stdio.h> /* #inlcude行没有什么不寻常的 */
int main()
{
printf("Hello world/n"); /* 调用也没什么不寻常的 */
...
}
extern "C" {
// 获得声明f(int i, char c, float x)
#include "my-C-code.h"
}
int main()
{
f(7, 'x', 3.14); // 注意:调用没什么特别的
...
}
extern "C" {
#endif
}
#endif
// 获得声明 f(int i, char c, float x)
#include "my-C-code.h" // 注意:#include 行没什么特别的
int main()
{
f(7, 'x', 3.14); // 注意:调用没什么特别的
...
}
void f(int i, char c, float x);
int g(char* s, const char* s2);
double sqrtOfSumOfSquares(double a, double b);
}
{
f(7, 'x', 3.14); // 注意:调用没什么特别的
...
}
// 使用 extern "C"声明 f(int,char,float) :
extern "C" void f(int i, char c, float x);
...
// 在某个C++模块中定义 f(int,char,float):
void f(int i, char c, float x)
{
...
}
#ifndef FRED_H
#define FRED_H
#ifdef __cplusplus
class Fred {
public:
Fred();
void wilma(int);
private:
int a_;
};
#else
typedef
struct Fred
Fred;
#endif
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__STDC__) || defined(__cplusplus)
extern void c_function(Fred*); /* ANSI C prototypes */
extern Fred* cplusplus_callback_function(Fred*);
#else
extern void c_function(); /* K&R style */
extern Fred* cplusplus_callback_function();
#endif
#ifdef __cplusplus
}
#endif
#endif /*FRED_H*/
#include "Fred.h"
Fred::Fred() : a_(0) { }
void Fred::wilma(int a) { }
Fred* cplusplus_callback_function(Fred* fred)
{
fred->wilma(123);
return fred;
}
#include "Fred.h"
int main()
{
Fred fred;
c_function(&fred);
...
}
#include "Fred.h"
void c_function(Fred* fred)
{
cplusplus_callback_function(fred);
}
如何混合使用C和C++(下)
Mixing C and C++ Code in the Same Program
By Stephen Clamage, Sun Microsystems, Sun ONE Studio Solaris Tools Development Engineering
Translator: Qiu Longbin <robin.qiu(at)yeah.net>
C++语言提供了一个混合代码的机制,使得代码可以在同一个程序中被兼容的C和C++编译器编译。在你移植代码到不同的平台和编译器时,你会体验到不同的成功度。本文展示了当你混合使用C,C++时,如何解决出现的一般的问题。文中所有情况,展示了使用Sun C和C++编译器时所要做的事情。(译注:GCC的gcc和g++也是这一对组合。)
- | |
- | |
- | |
- | |
- | |
- | |
- |
使用兼容的编译器
混合代码的第一个要求就是你使用的C和C++编译器必须是兼容的。他们必须以同样的方式,(例如),定义了基本类型如int, float或指针。Solaris操作系统指定了C程序的应用程序的二进制接口(ABI),它包含关于基本类型和函数如何被调用的信息。任何Solaris Os上可用的编译器都必须遵循ABI。
Sun C和C++编译器遵循Solaris OS ABI并且是兼容的。第三方的Solaris OS C编译器也必须遵循ABI。任何与Solaris Os兼容的C编译器也同样与Sun C++编译器兼容。
Sun C和C++编译器使用兼容的头文件,并且使用同样的C运行时库。他们是完全兼容的。
C++语言提供了一个“链接规范(linkage specification)”,用它你可以声明函数或对象遵循特定语言的程序链接约定。对象和函数的默认链接是C++的。所有C++编译器也为兼容的C编译器提供了C链接。
当你需要访问一个用C链接编译的函数(例如,某个函数被C编译器编译),就要声明那个函数具备C链接(译注:在C++代码中)。即使大多数C++编译器对C和C++数据对象的链接没有什么不同,你也需要在你的C++代码中声明C数据对象(data objects)具有C链接。类型(types)没有C或C++链接,除了指向函数的指针(pointer-to-function)类型。
声明链接规范
extern "language_name" declaration ; extern "language_name" { declaration ; declaration ; ... } |
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++代码中包含C头文件
如果你想使用一个C库,它定义的头文件意欲为C编译器所准备,你可以在extern “C”花括号中包含这个头文件:
extern "C" { #include "header.h" } |
Warning-警告- 不用为Solaris OS上的系统头文件使用该技术。Solaris头文件,并且所有赖于Sun C和C++编译器的头文件都已经为C和C++编译器做好了准备。如果你指定了一个链接,你可能使得声明于Solaris头文件中的声明失效。 |
创建混合语言(Mixed-Languge)的头文件
如果你想使得头文件同时适合于C和C++编译器,你可能把所有声明都放置在了extern “C”花括号中,但是C编译器并不认识这些语法。每个C++编译器都预定义了宏__cplusplus,这样你就可以使用这个宏来防卫C++语法扩展:
#ifdef __cplusplus extern "C" { #endif ... /* body of header */ #ifdef __cplusplus } /* closing brace for extern "C" */ #endif |
给C structs增加C++特征
假定你想在你的C++代码中更容易地使用C库。并且假定你不使用C风格的访问方式,你可能想增加成员函数,或许虚函数,也可能从class派生等等。你如何完成这个变换并确保C库函数仍然能识别你的struct?考虑下面这个例子中C的struct buf的使用:
/* buf.h */ struct buf { char* data; unsigned count; }; void buf_clear(struct buf*); int buf_print(struct buf*); /* return status, 0 means fail */ int buf_append(struct buf*, const char*, unsigned count); /* same return */ |
你想把这个struct转变进C++ class,并做下述改变,使它更容易使用:
extern "C" { #include "buf.h" } class mybuf { // first attempt -- will it work? public: mybuf() : data(0), count(0) { } void clear() { buf_clear((buf*)this); } bool print() { return buf_print((buf*)this); } bool append(const char* p, unsigned c) { return buf_append((buf*)this, p, c); } private: char* data; unsigned count; }; |
class mybuf的接口看来更象C++代码,并且更容易整合进面向对象风格的编程 ─ 如果它可行的话。
C++标准对buf和class mybuf的兼容性没有任何保证。这里的代码,没有虚函数,可以工作,但你不能指望这个。如果你增加了虚函数,这个代码会失败,因为编译器增加了额外的数据(比如指向虚表的指针)放在class的开始处。
可移植的方案是把struct buf单独放着不动它,尽管你想保护数据成员并仅仅通过成员函数提供访问。仅当你不改变声明的情况下,你才能保证C和C++的兼容性。
你可以从C struct buf派生出一个C++ class mybuf,并且传递指向基类buf的指针给mybuf的成员函数。当转换mybuf* 到 buf*时,如果指向mybuf的指针没有指向buf数据的起始处,C++编译器会自动调整它。mybuf的布局在C++编译时可能发生改变,但是操纵mybuf和buf对象的C++源代码将到处都可以工作。下面的例子展示了一个可移植方法给C struct增加C++和面向对象特征:
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++函数具有C链接,它就可以在由C编译器编译的函数中被调用。一个声明具有C链接的函数可以使用所有C++的特征,如果你想在C代码中访问它,它的参数和返回值必须是在C中可访问的。例如,如果一个函数声明有一个IOstream类的引用作为参数,就没有(可移植的)方法来解析这个参数类型给C编译器。C语言没有引用,模板,或具备C++特征的class.
#include <iostream> extern "C" int print(int i, double d) { std::cout << "i = " << i << ", d = " << d; } |
#ifdef __cplusplus extern "C" #endif int print(int i, double d); |
你可以至多声明重载集中的一个函数作为extern “C”,因为一个C函数仅仅可以有一个给定的名字。如果你要在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); } |
int g_int(int); double g_double(double); |
你也需要包裹(wrapper)函数来调用template functions,因为template functions不能声明为extern “C”:
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); } |
C++代码仍然可以访问重载函数和template functions。C代码必须使用wrapper functions。
在C中访问C++ class
能够从C代码中访问C++ class吗?你可以声明一个C struct,看上去象一个C++ class并能以某种方式调用成员函数吗?答案是肯定的,虽然你必须为维持可移植性增加一些复杂性。任何对C++ class的定义的修改都要求你重新审查你的C代码。
假定你有一个C++ class如下:
class M { public: virtual int foo(int); // ... private: int i, j; }; |
你不能在C代码中声明class M。你能做的最好的事就是传递指向class M 对象的指针,这类似于在C标准I/O中传递FILE对象。你可以在C++中写extern “C”函数访问class M 对象并在C代码中调用这些函数。下面是一个C++函数,被设计来调用成员函数foo:
extern "C" int call_M_foo(M* m, int i) { return m->foo(i); } |
下面是C代码的一个例子,它使用了class M:
struct M; /* you can supply only an incomplete declaration */ int call_M_foo(struct M*, int); /* declare the wrapper function */ int f(struct M* p, int j) /* now you can call M::foo */ { return call_M_foo(p, j); } |
你可以在C++程序中使用来自于标准C头文件<stdio.h>的C标准I/O,因为C标准I/O是C++的一部分。
Sun C和C++使用同样的C运行时库,这在关于兼容的编译器小节中注明过了。使用Sun编译器,你可以在同一个程序中自由地在C和C++代码中使用标准I/O。
指向函数的指针必须指明是否指向一个C函数或C++函数,因为C和C++函数可能采用不同的调用约定。否则,编译器不知道究竟要产生哪种函数调用的代码。多数系统对C和C++并没有不同的调用约定,但是C++允许存在这种可能性。因此你必须在声明指向函数的指针时要小心,确保类型匹配。考虑下面的例子:
typedef int (*pfun)(int); // line 1 extern "C" void foo(pfun); // line 2 extern "C" int g(int) // line 3 ... foo( g ); // Error! // line 5 |
Line 1声明了pfun指向一个C++函数,因为它缺少链接说明符。
Line 2声明foo为一个C函数,它具有一个指向C++函数的指针。
Line 5试图用指向g的指针调用foo,g是一个C函数,所以类型不匹配。
要确保指向函数的指针的链接规范与它将要指向的函数匹配。在下面这个正确的例子中,所有声明都包含在extern “C”花括号中,确保了类型匹配。
extern "C" { typedef int (*pfun)(int); void foo(pfun); int g(int); } foo( g ); // now OK |
typedef int (*pfn)(int); extern "C" void foo(pfn p) { ... } // definition extern "C" void foo( int (*)(int) ); // declaration |
extern "C" { typedef int (*pfn)(int); void foo(pfn p) { ... } } |
传播(Propagating)异常
从C函数中调用C++函数,并且C++函数抛出了一个异常,将会发生什么?在是否会使得该异常有适当的行为这个问题上C++标准有些含糊,并且在一些系统上你不得不采取特别的预防措施。一般而言,你必须得求诸用户手册来确定代码是否以适当的方式工作。
Sun C++中不需要预防措施。Sun C++中的异常机制不影响函数调用的方式。当C++异常被抛出时,如果一个C函数正处于活动状态,C函数将转交给异常处理过程。
混合异常和set_jmp,long_jmp
最好的建议是在包含C++代码的程序中不要使用long_jmp。C++异常机制和C++关于销毁超出作用域对象的规则可能被long_jmp违反,从而得到不确定的结果。一些编译器整合了异常和long_jmp,允许它们协同工作,但你不能依赖这样的行为。Sun C++使用与C编译器相同的set_jmp和long_jmp。
如果你在混合有C++的C代码中使用long_jmp,要确保long_jmp不要跨越(cross over)活动的C++函数。如果你不能确保这点,查看一下是否你可以通过禁用异常来编译那个C++代码。如果对象的析构器被绕过了,你仍旧可能有问题。
某时,多数C++编译器要求main函数要被C++编译。这个要求今天来说并不常见,Sun C++就不要求这点。如果你的C++编译器需要编译main函数,但你由于某种原因不能这么做,你可以改变C main函数的名字并从一个C++ main的包裹函数中调用它。例如,改变C main函数的名字为C_main,并写如下C++代码:
extern "C" int C_main(int, char**); // not needed for Sun C++ int main(int argc, char** argv) { return C_main(argc, argv); } |
当然,C_main必须是被声明在C代码中,并返回一个int。如上注解,使用Sun C++是不会有这个麻烦。
假定你有C程序文件main.o, f1.o和f2.o,你可以使用C++程序库helper.a。用Sun C++,你要如下引发命令行:
CC -o myprog main.o f1.o f2.o helper.a |
更多信息 |
- | Sun ONE Studio C/C++ Documentation |
Steve Clamage从1994年在Sun至今?。它当前是C++编译器和Sun ONE Studio编译器套件的技术领导。它从1995年开始是ANSI C++委员会的主席。
- 如何混合使用C和C++ (转)
- 如何混合使用C和C++(上)
- 如何混合使用C和C++(下)
- 如何混合使用C和C++(上)
- 如何混合使用C和C++(下)
- 如何混合使用C和C++(上)
- .c和.cpp混合使用
- c和c++混合使用
- 如何混合编译C语言和C++
- C和C++混合编程(__cplusplus使用)
- 汇编语言和C语言的混合使用
- C和C++混合编程(__cplusplus使用)
- C和C++混合编程(__cplusplus使用)
- <c:import>标签的文件混合和代码混合使用
- C和C++混合编程(extern C 和__cplusplus使用)
- 混合使用Objective-C,C++和Objective-C++(转载)
- C和C++的混合编译--extern “C”的使用
- C和C++混合编程之 extern “C”的使用
- 迷茫...
- [2008-11-13]发布 DBATools For PL/SQL Developer 0.8.0.0版
- (5)RegisterClass和RegisterClassEx函数
- Windows Mobile中实现Image.FromFile(string file)这样的方法
- 一个最简单的图的接口的定义
- 如何混合使用C和C++ (转)
- 欲速则不达
- 利用错误学习函数
- 常用CASE工具介绍及比较大全
- 如何在DATE与SYSTEMTIME之间转化
- libnet使用举例(11)
- SaaS时代 国产中间件如何发展?
- java GDK转UTF-8
- 笑话