这个题目一看就很欠扁,既然用C++又不想依赖stdlibc++标准库这不是有病吗?其意义究竟
何在?这实际上就是我最近被赋予的光荣使命,神奇的是原来真的可以做到!!!与标准库
斗于编译器斗,其乐无穷~~~收获也不是没有,对C++,对编译器的认识又更近了一步,虽然
本来就没有什么认识。
故事是这样开始的,我们为公司的某产品开发了一个模块,其实就是两动态库。本来这东西
也不是我们开发的,我们只是负责移植到公司的12个平台上。本来一切都很顺利,不幸的事
是在test快要结束的时候发生的,某平台是build机器上与test机器上stdlibc++.so版本不
一致,我们的so竟然不能load,这不是再正常不过的事情么,装一个需要的版本就一了百了
了,但是。。但是可恶的印度人懒得装不说,还指责是我们的问题,说引入了新的依赖,不
行不行就是不行,我的奇妙旅行就此开始了。(这个问题也可以通过静态链接stdlibc++解
决,貌似他们没搞定,我也没有深入研究)
总的来说C++对stdlibc++.so的依赖主要由以下几个部分产生:
1. 对C++标准库的依赖
简单的看就是 include C++的头文件(不以.h结尾)
如果你大量使用的STL...请回吧,不送了。还好我们只使用到了vector和dqueue,简
单实现了一个,凑合着用了
2. new 和 delete 操作符
这两个操作符实际上完成了两部分工作,首先是分配内存,其次是调用相应的构造/析构
方法。而对于gcc来说,分配内存的工作是在stdlibc++.so中完成的,所以会造成依赖,
幸运的是重载这两个操作符就是重载分配内存的部分,所以我们只需要用malloc/free简
单的重载这两个操作符就ok了
3. 纯虚函数
使用继承和虚函数都是没问题的(竟然没有依赖rtti,需要继续研究),但纯虚函数是
不行的,这个估计跟实现有关。最简单的方法就是将所用的纯虚函数改为虚函数再添加
默认实现。
4. exception
这是最麻烦的部分,如果exception用的不多,直接将exception作为返回值返回了结
(原来有返回值的放到输出参数里),每次调用check一下,根据原来代码中的逻辑要么
返回exception(没有catch住该exception),要么做catch中的事。理论上是没问题的
就是麻烦点,代码ugly点,可能再用点goto也没啥大问题。
这里要介绍的是另一种方法,通过使用setjmp和longjmp实现exception机制,再利用宏
替换原来代码中的try throw catch,详细的就不写了,我也是从网上看到的参见:
Exception Handling in C without C++
cexcept:sourceforge上的开源项目
看了就能明白,但要实现完整的exception机制还是有难度的,包括try catch的嵌
套,exception的继承,多catch等。我们现在也没能实现多catch,宏的能力还是有限啊。
5. rtti
这是C++特用的东西,严重依赖于stdlibc++.so,凡是动态的类型判断都是要禁止
的,catch为了判断exception的类型就会使用到rtti,dynmaic_cast也会有同样的问
题,typeid之类的东西更是永不得的。
6. 编译参数
当你完全完成了上面的工作,基本上就大功告成了,不过现代的编译器那是相当的聪明,在
背后为我们默默无闻的做了很多事情,比如对于c++的代码会默认帮你生成处理
exception的代码,所以需要用 -fno-exceptions 来防止这样的事情发生,然而这还不
够,-fno-exceptions 和 -fno-rtti 必须是要同时使用的,只使用 -fno-exceptions只
能得到个半吊子的结果:依然会有rtti的代码生成(不知道gcc那帮人是怎么想的)。
你以为终于可以结束了吗?
if (your_gcc_version < 3.4.6) {
goHome();
happyEnding();
} else {
听我慢慢道来();
}
在gcc 3.4.6 以上版本会为local static生成保证线程安全的代码,不幸的是这些代码
是依赖于stdlibc++.so的,所以我们要禁止他们!!!请使用
-fno-threadsafe-statics,并确保这些static代码是线程安全的
终于写完了,估计没有人会再有这样奇怪的需求了,写在这里留作纪念。
另外提一下模板是可以使用的,因为只是在编译时生成代码,不关标准库的事。
posted on 2008-07-31 00:05
JBahamut 阅读(1046)
评论(0) 编辑 收藏