STL 和发布/调试库混乱
Posted
技术标签:
【中文标题】STL 和发布/调试库混乱【英文标题】:STL and release/debug library mess 【发布时间】:2011-01-21 21:37:35 【问题描述】:我正在使用一些第 3 方。我正在使用它的共享库版本,因为该库很大(~60MB)并且被多个应用程序使用。
有没有办法在应用程序启动时找出库的发布/调试版本分别用于我的应用程序的发布/调试版本?
详细说明
暴露 C++ 接口的库。 API 方法之一返回std::vector<std::string>
。
当我在调试模式下编译我的应用程序时的问题,应该使用调试版本的库。发布也一样。如果使用了不正确的库版本,应用程序就会崩溃。
根据gcc(见http://gcc.gnu.org/onlinedocs/libstdc++/manual/bk01pt03ch17s04.html)
但使用混合模式标准库 可以使用调试模式 或发布模式 basic_string 对象, 事情变得更复杂了
附: 1
看起来 Timbo 的提议是一个可能的解决方案 - 使用不同的 soname 来调试和发布库。那么,应该将什么传递给 ./configure 脚本来更改库 soname?
附: 2
我的问题不在链接时,而是在运行时。
附: 3
Here 是展示我面临的问题的问题。
【问题讨论】:
Timbo 使用不同库名的解决方案很糟糕。 @VJo 你觉得有什么不好? Libstdc++ 手册的格式已经改变,因为我没有足够的声誉来发表评论,所以我在这里添加了一个更新的链接(如果合适的话)。 gcc.gnu.org/onlinedocs/libstdc++/manual/… 【参考方案1】:引用here 的调试模式与应用程序的调试或发布版本没有任何关系。 STL 调试模式通过-D_GLIBCXX_DEBUG
激活,是一种特殊的检查模式。
第 3 方库实际上不太可能使用 STL 检查模式编译,但如果是,它可能会立即提及您的代码也应该使用 -D_GLIBCXX_DEBUG
编译。
如果第 3 方库不是通过检查 STL 构建的,那么无论您是进行优化构建还是调试构建,它都与您的代码兼容。
由于您声明与优化的 3rd 方库构建相关联的代码调试构建会导致崩溃,因此该崩溃很可能是由您的代码中的错误(或可能由 3rd 方库中的错误)引起的。
Valgrind 和 GDB 是你的朋友。
【讨论】:
实际上,在调试模式下,我的意思是使用 _GLIBCXX_DEBUG 编译的代码。第 3 方默认以发布模式编译。我在发布模式下编译我的应用程序一次,在调试模式下编译一次(使用_GLIBCXX_DEBUG)。因此,我还通过将标志传递给 ./configure 来创建第 3 方的调试(使用 _GLIBCXX_DEBUG)版本。如果不这样做,我的应用程序的调试模式会因第三个库的发布模式而崩溃 @Artyom 请参阅我在问题中提供的链接中的“发布和调试模式组件的链接和运行时共存”部分。它指出:“通常,这不是问题,但是对于可以使用调试模式或发布模式 basic_string 对象的混合模式标准库,事情会变得更加复杂。因为函数的返回值没有编码为损坏的名称,无法指定发布模式或调试模式字符串。实际上,这会导致运行时错误。"。 @Artyom 在使用调试 STL(使用 _GLIBCXX_DEBUG 编译)和普通/发布 STL 时,肯定需要采取一些步骤。也许这个问题可以被称为“STL 调试和发布并存问题”。但无论你怎么称呼它,你都应该承认开发人员需要了解并存的限制。 @Artyom:那么,您能否在***.com/questions/4836344/stdvectorstdstring-crash 解决@dimba 的后续问题?在 std::vector 的情况下,这对我来说肯定像是 ABI 损坏——我希望你能有一个替代解释,因为他有一个可重复的示例,代码非常简单。 @***s 你是对的我错了。删除了我的 cmets。在这种情况下,我不建议使用 GCC 的 STL 的调试模式。【参考方案2】:我认为您误读了您提供的链接中的文档。特别是,您误解了它的目的——该部分的标题是“目标”,并描述了 C++ 调试库的一些假设设计以及这些设计的后果,以解释所做的实际设计选择。您引用的行后面的文字描述了 假设 实现可能导致的混乱,该实现对发布模式和调试模式字符串进行了单独的设计。它继续说:
由于这个原因,我们不能轻易地为 std::basic_string 类模板提供安全的迭代器,因为它存在于整个 C++ 标准库中。
(或者,换个说法,提供一个特殊的“调试”版本的字符串迭代器是不可能的。)
...
通过 libstdc++ 调试模式的设计,我们无法有效地向用户隐藏调试和发布模式字符串之间的差异。未能隐藏差异可能会导致不可预测的行为,因此我们选择仅执行不需要 ABI 更改的 basic_string 更改。预计对用户的影响很小,因为有一些简单的替代方案(例如,__gnu_debug::basic_string),而且我们从混合调试和发布编译的翻译单元中获得的可用性好处是巨大的。
换句话说,GCC 的 libstdc++ 中的调试和发布模式的设计拒绝了这种假设的实现,对字符串进行了单独的设计,特别是为了允许您担心如何避免的那种跨模式链接.
因此,在没有-D_GLIBCXX_DEBUG
(或者如果出于某种原因你喜欢的话)的情况下编译你的库并且将它与应用程序的任一模式链接起来,你应该不会遇到问题。如果你确实有问题,那是由于某个地方的错误。 [但请参阅下面的编辑!这是特定于std::string
,而不是其他容器!]
编辑:这个答案被接受后,我在std::vector<std::string> crash跟进回答了后续问题,发现这个答案的结论是错误的。 GCC 的 libstdc++ 用字符串做了一些聪明的事情来支持“每次使用重新编译”(其中给定容器对象的所有使用都必须使用相同的标志编译,但程序中相同容器类的使用不需要使用相同的编译标志),但这与提供您需要的交叉链接能力的完整“按单元编译”不同。特别是,文档提到了这种交联能力,
我们认为,如果我们打算提供安全的迭代器,保持程序语义不变,并且在发布模式下性能不退步,那么这种级别的重新编译实际上是不可能的......
因此,如果您通过库接口传递容器,您将需要两个独立的库。老实说,对于这种情况,我发现最简单的解决方案就是将两个库安装到不同的目录中(每个变体一个——并且您希望两者都与您的主库目录分开)。或者,您可以重命名调试库文件,然后手动安装。
作为进一步的建议 - 您可能不会经常在调试模式下运行它。只需要将调试版本静态编译并链接到您的应用程序中就值得了,因此您不必担心安装多个动态库并在运行时保持它们正常运行。
【讨论】:
我又读了一遍,你是对的,我误解了。这可能是我的错误 见附注。 3,我在这里演示了 std::vector<:string> 的问题【参考方案3】:为 DLL 的调试和发布版本赋予不同的名称,并通过库依赖项链接正确的版本。除非找到正确的 DLL,否则您的应用程序将无法启动。
【讨论】:
这是一个 apache 库 (activeMQ)。为此,我可能需要弄乱 ./configure。被接受了吗? -1 在 Linux 下不需要像 MSVC 那样的调试/发布混乱。所以我强烈建议不要把这样的“解决方案”放在不属于它们的地方。【参考方案4】:这是您应该在构建系统中进行的检查。在您的构建脚本中,
如果您正在为发布而构建,则链接到发布库。 如果您正在构建调试,则链接到调试库。例如,如果您使用的是 make:
release: $(OBJ)
$(CC) $(CXXFLAGS_RELEASE) $(foreach LIB,$(LIBS_RELEASE),-l$(LIB))
debug: $(OBJ)
$(CC) $(CXXFLAGS_DEBUG) $(foreach LIB,$(LIBS_DEBUG),-l$(LIB))
【讨论】:
以上是关于STL 和发布/调试库混乱的主要内容,如果未能解决你的问题,请参考以下文章