本文转自 http://www.qqread.com/cpp/p397124.html
最近遇到一个挺挠头的技术问题,我们波士顿那边客户公司的代码是既有C++又有C,我们作为外包公司,需要把我们的C++代码与他们的集成起来,原先的集成方案是我们的C++与他们的C++揉在一起,这样不Touch任何C集成的冬冬,我当时在波士顿已经搞定了此问题,但出差回来前客户提出要优化系统效率,其意思就是想让我们更多地重用他们的C代码,这样集成C与C++的任务逐渐提上日程。
网上有很多讨论这方面的中英文文章,但感觉不是很全面,等我使用这些方法搞不定时,没有提供一个全面系统的分析解决问题的思路,我摘选了其中一篇:http://www.pconline.com.cn/pcedu/empolder/gj/c/0508/693175_3.html,文章有四个部分,前三个部分属于科普,可以不看,最后一个部分讲述了C++调用C和C调用C++的两个范例,甚是有用,我总结一下:
(1)C++调用C的问题:
在C++的File中,加入C的头文件,一般情况下需要加extern "C", 但这点不是铁律,后面我会专门讲这个问题,认定这点有时会死得比较惨。建议原文范例中增加__cplusplus宏比较好一些,例如:
#if defined(__cplusplus)
extern "C"
{
#endif
#include "cExample.h" //头文件声明
#if defined(__cplusplus)
}
#endif
加extern "C"的 原因是因为C++在编译函数名时加入了很多其他怪异字符,这点是C++ 语言为了支持重载overload的特性以及其它特性所不得不加的冬冬,但C是朴素型, 编译出来的符号表就是代码里的函数名。加了extern C后, 编译器就会按照C的命名方式将C++引用C代码的地方使用C方式的 函数名,避免引用和定义出现命名的不一致。但有一个问题是这一点取决于特定的 编译器,而不是绝对的,我在.NET的VC7下拿我们的工程做了个试验没有 问题,但在 Linux的g++下就歇了,原因是什么?Window下的nmake在 编译时使用的原则是:C代码按照C的命名方式编译引用点和函数定义,可是g++就是另外一回事了,它对C的 代码采取的是C++的命名方式,怎么印证这个问题呢,下面我来介绍一下 Linux下的两个 工具nm和objdump, 命令如下:
objdump -x (Obj 文件名) , nm (Obj文件名)
下面给出更详尽的测试依据,例如C代码中的PP__sctp函数在g++编译后的符号表就变了
nm mylib.a |grep PP__sctp
结果是:
U _Z8PP__sctpPKcmm
000000c0 B V__PP__sctp
00000040 T _Z8PP__sctpPKcmm
看到了吧!U是引用点的函数符号,T的定义点的函数符号,如果你在调用时加了extern "C",编译后,肯定会有一条错误说:undefined reference "PP__sctp".
这就是我要讲的第一个问题,C,C++集成编译时需要看具体的编译环境和工具,充分利用你手头的工具帮你来分析和定位问题。
(2)C调C++的问题
具体可以参看前面说的文章,非常有用,要点是:C调用C++,通过extern 变量或函数的外部声明,而不用include头文件方式。但前面提到的第一个问题依然会在不同的编译器下重演,你需要定位分析,看是否需要加extern "C",只要耐心一些,就会快速定位和解决问题。
我这里提到的第二个问题是在复杂工程下会出现的问题,我们的工程巨大,六千多个文件,和我们相关的就有7,8百个,最后在链接时,出了问题是几个静态库互相引用,打成了死结,因为g++ 通过-l选项加入库的顺序也是有讲究的,先引用的库可以使用后引用库的函数和变量,但反过来则不行。
这里介绍一个办法,利用拓扑分析,把几个库的引用关系用圈和箭头表示出来,就像分析最短路径那样,如果发现是个环状拓扑,只管把它们加入到一个库中即可,当然这是在 Linux下,Windows下我不Sure是否会出现类似问题。
昨天晚上在家里用VPN连到单位忙到12点才搞定,白天也多亏几位同事的帮助才有了思路,呵呵,解决了问题就是爽啊。