C++踩坑指南

869 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

 动态库中的单例出现多例问题

1. 单例模式是最常用的一种设计模式。然而如果一个单例类C在动态库A中,而另一个动态库B中也使用C,此时如果同时加载动态库,A,B;则会出现A,B中各有一个C对象。使得单例不再是单例。

解决方法:单例对象不要定义在.h中,定义在.cpp中。 整个程序要有一个主动态库,其他动态库依赖这个主动态库。这种单例就放在这个主动态库中。

so依赖so,怎么不用设置LD_LIBRARY_APTH

-Wl,-rpath=$ORIGIN

在bazel BUILD文件中:linkopts = ["-lpthread","-lgomp", "-lpython2.7", "-Wl,-rpath=$$ORIGIN"],

rpath可以指定加载时动态库搜索路径。一般加个. 和ORIGIN  ORIGIN  ORIGIN就是动态库自己的路径

bazel中 cc_library打包库时,没用到的变量函数会被strip掉

解决方法:

.bazelrc添加 build --nolegacy_whole_archive

cc_library中添加alwayslink = True,

多动态库符号冲突问题

比如Protobuf, 这相当于“动态库中的单例不出现多例问题”中单例被定义在两个动态库,如果这个单例是factory,那么有可能注册相同的东西,会冲突

解决方法: 这两个动态库都依赖protobuf的.h文件。不要把cc文件打包进去。

最终连接成二进制时,或者main函数所在二进制才依赖protobuf的cc文件。

另一种解法可以尝试使用linux 的dlopen时指定 RTLD_LOCAL模式加载

python以RTLD_GLOBAL方式加载扩展so

有时候想写个so, so里有个单例。 这个单例给其他so用。这时只能以RTLD_GLOBAL加载

而python默认import 是RTLD_LOCAL方式

import sys
import ctypes
old_flags = sys.getdlopenflags()
sys.setdlopenflags(old_flags|ctypes.RTLD_GLOBAL)
import xxx #这是要import的so
sys.setdlopenflags(old_flags)
del old_flags

注意,xxx这个so要尽量简单,不要依赖太多东西,否则和python里的符号冲突就完蛋了。

我一般会搞两个so. 把最核心的单例,factory打成so,用GLOBAL方式加载。其他功能代码,都依赖这个核心so, 并以LOCAL方式加载

依赖bazel大项目

如果你有一个小项目A,想依赖另一个复杂的大项目B(比如tensorflow). 最好把B的WORKSPACE文件和.bazelrc文件复制到A来改。 反过来会更复杂。

BAZEL踩坑

项目不复杂的话在顶层放个BUILD文件就行了,不要在每个目录下放BUILD文件了。

要发布的动态库用cc_library(name=libxxx.so, linkshared=True)来打包。

cc_import 来导入依赖的动态库和.h放在另一个cc_library里。

.bazelrc里注意这两个选项,当编译时找不到文件:

build --spawn_strategy=standalone
build --strategy=Genrule=standalone

.bazelrc里用

ABI问题

依赖动态库,打包库要注意ABI. 默认ABI=1.  如果ABI不同,链接时找不到符号。 undefined reference.