太长不看版: -fno-rtti
问题
有个朋友求助,她的 C++ 项目 Project A 引入了一个以静态链接库形式提供的 SDK B,在编译链接自己的项目时,链接器报错:
Undefined Symbols: typeinfo for BaseClassInSDKB
分析
看到这个问题,我们当然第一时间意识到是链接时静态库中的某个 symbol 丢失了,但具体是什么问题呢。通过翻阅她的代码发现,在 Project A 中,有一个 class DerivedClassInProjectA: public BaseClassInSDKB 继承了 BaseClassInSDKB,报错就报在了这一行。
熟练使用 C++ 的朋友知道,typeinfo 是 C++ 的运行时类型信息,用于实现 C++ 的 运行时类型信息(RTTI) 功能。简单说,RTTI 类似于 Java 的 instanceOf 运算符,用于在运行时(而非静态编译期)实现类型的识别和检查。C++ 的 typeid 运算符和 dynamic_cast 运算符是依赖 typeinfo 的,异常捕获也会涉及到 RTTI 能力。
如果我们的代码在编译时启用了 RTTI(默认是启用的),那么我们代码中 符合特定条件的 class 是会被编译器生成 typeinfo 的。而我们的类 DerivedClassInProjectA 的 typeinfo,会引用到它父类的 typeinfo。
这时编译器会在我们项目的 object 文件中产生对父类 BaseClassInSDKB 的 typeinfo 的引用。而 BaseClassInSDKB 所在的静态库 libsdkb.a 中不存在这个 symbol,因此链接器链接我们代码产生的 object 文件和静态库时,会报找不到 typeinfo 的错误。
而之所以 libsdkb.a 中没有对应的 symbol,是因为 libsdkb.a 在生成的时候关掉了 RTTI 支持。
到这里解决方案就比较简单了:
解决方法一:关闭项目的 RTTI
如果你整个项目都用不到 RTTI,那么就在你的 makefile 或者 cmake 等文件中配置关闭 RTTI 即可。方法就是给 gcc/clang 的参数中增加 -fno-rtti。
解决方法二:关闭特定文件的 RTTI
如果你的项目中其他地方用到 RTTI,那可以单独把 DerivedClassInProjectA 这个类所在文件的 RTTI 关掉。只要在 makefile 或者 cmake 等配置中单独针对该文件配置编译器参数即可。
例如在 cmake 中:
set_source_files_properties(某个文件.cpp PROPERTIES COMPILE_FLAGS -fno-rtti)
解决方法三:打开 SDK B 的 RTTI
如果你的整个项目都需要 RTTI 支持,且你所引用的 SDK 有支持 RTTI 的版本(或者是你有源码可以自己编译),那么你可以选择支持 RTTI 的 SDK。