红联Linux门户
Linux帮助

C与C++在Linux下的集成问题

发布时间:2008-02-17 10:27:44来源:红联作者:tinkage
最近遇到一个挺挠头的技术问题,我们波士顿那边客户公司的代码是既有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点才搞定,白天也多亏几位同事的帮助才有了思路,呵呵,解决了问题就是爽啊。
文章评论

共有 2 条评论

  1. dzj 于 2008-02-24 00:09:20发表:

    :handshake

  2. kigysun 于 2008-02-18 10:44:38发表:

    good! this problem is worth to be think very well.