STL和发布/调试库混乱

10

我正在使用第三方库,由于该库很大(约60MB)且被多个应用程序使用,所以我正在使用它的共享库版本。

在应用程序启动时,有没有一种方法可以找出我的应用程序是使用的发布/调试版本的库呢?

更详细的描述

该库提供了C ++接口。 API方法之一返回std :: vector< std :: string >

问题在于,当我以调试模式编译我的应用程序时,应该使用调试版本的库。相同的情况也适用于发布版。如果使用了错误版本的库,则应用程序会崩溃。

根据gcc(请参见http://gcc.gnu.org/onlinedocs/libstdc++/manual/bk01pt03ch17s04.html

但是,在混合模式标准库中, 可以使用调试模式或发布模式基本字符串对象, 事情变得更加复杂

P.S. 1

看起来Timbo的建议是一个可能的解决方案-为调试和发布库使用不同的soname。那么,应该向./configure脚本传递什么来更改库soname?

P.S. 2

我的问题不在链接时,而是在运行时。

P.S. 3

这里有一个展示我正在面对的问题的问题。


Timbo使用不同的库名称的解决方案很糟糕。 - BЈовић
1
@VJo 你认为这是不好的吗? - dimba
Libstdc++手册的格式已经改变,由于我没有足够的声望来发表评论,因此在此添加一个更新的链接(如果合适的话)。https://gcc.gnu.org/onlinedocs/libstdc++/manual/debug_mode_design.html#debug_mode.design.methods.coexistence - Carl
4个回答

7

调试模式 这里 引用的内容与应用程序的调试或发布版本无关。STL 调试模式是通过 -D_GLIBCXX_DEBUG 激活的特殊检查模式。

第三方库很可能没有使用 STL 检查模式进行编译,但如果确实使用了,它很快就会提到您的代码也应该使用 -D_GLIBCXX_DEBUG 进行编译。

如果第三方库没有使用 STL 检查,则无论您是进行优化构建还是调试构建,它都与您的代码兼容。

由于您声明,您的代码的调试构建与第三方库的优化构建链接导致崩溃,因此该崩溃很可能是由您的代码中的错误(或可能是第三方库中的错误)引起的。

Valgrind 和 GDB 是您的好朋友。


实际上,我所说的调试模式是指使用_GLIBCXX_DEBUG编译的代码。第三方默认情况下是以发布模式编译的。我将我的应用程序分别编译为发布模式和调试模式(带有_GLIBCXX_DEBUG)。因此,我通过向./configure传递标志来创建了带有_GLIBCXX_DEBUG的调试版本的第三方库。如果不这样做,我的应用程序的调试模式会与第三方库的发布模式崩溃。 - dimba
@Artyom 请查看我在问题中提供的链接中的“发布模式和调试模式组件的链接和运行时共存”部分。它指出:“通常,这不是问题,但对于可能使用调试模式或发布模式basic_string对象的混合模式标准库,情况会变得更加复杂。由于函数的返回值没有编码到名称中,因此无法指定发布模式或调试模式的字符串。实际上,这会导致运行时错误。” - dimba
@Artyom,当使用debug STL(使用_GLIBCXX_DEBUG编译)和正常/发布版本STL时,需要采取一些步骤。也许问题可以称为“STL调试和发布共存问题”。但无论你如何称呼它,你都应该承认开发人员需要意识到共存的限制。 - dimba
@Artyom:你能回答一下@dimba在https://dev59.com/aFPTa4cB1Zd3GeqPm9Yt的后续问题吗?在std::vector的情况下,这显然看起来像是ABI破坏 - 我希望你有一个替代解释,因为他有一个非常简单的可重复示例。 - Brooks Moses
@Brooks,你是对的,我错了。已删除我的评论。在这种情况下,我不建议使用GCC的STL的调试模式。 - Artyom

5
我认为您误读了您提供的链接中的文档。特别是,您误解了它的目的——该部分标题为“目标”,描述了一些 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++对字符串做了巧妙的处理,以支持“每次使用重新编译”(在其中给定容器对象的所有用途必须使用相同的标志进行编译,但程序中使用相同容器类的用途不需要使用相同的标志进行编译),但这并不等同于完全的“每单元编译”,这将提供您所需的交叉链接能力。特别地,文档对于该交叉链接能力表示:

我们认为,如果我们打算提供安全迭代器,保持程序语义不变,并且在发布模式下不会性能回归,那么实际上不可能达到这种程度的重新编译....

因此,如果您在库接口中传递容器,则 必须 需要两个单独的库。老实说,对于这种情况,我发现最简单的解决方案就是将这两个库安装到不同的目录中(每个变体一个目录--您会希望它们都与主库目录分开)。或者,您可以重命名调试库文件,然后手动安装它。
另一个建议是--您可能不经常在调试模式下运行此程序。将调试版本仅编译并静态链接到应用程序中可能值得考虑,这样您就不必担心安装多个动态库并在运行时保持它们正确。

我再次阅读了它,你是正确的,我误解了它。这很可能是我的错误。 - dimba
请参见P.S. 3,其中我演示了std::vectorstd::string的问题。 - dimba

1
将调试版本和发布版本的DLL命名为不同的名称,并通过库依赖链接到正确的版本。只有找到正确的DLL,您的应用程序才能启动。

这是一个Apache库(ActiveMQ)。为了做到这一点,我可能需要处理./configure。这可以吗? - dimba
1
在Linux下没有像MSVC那样需要调试/发布混乱的情况。因此,我强烈建议不要把这样的“解决方案”带到它们不属于的地方。 - Artyom

0

这是您应该在构建系统中进行的检查类型。在您的构建脚本中,

  • 如果您正在构建发布版本,则链接到发布库。
  • 如果您正在构建调试版本,则链接到调试库。

例如,如果您使用的是make:

release: $(OBJ)
    $(CC) $(CXXFLAGS_RELEASE) $(foreach LIB,$(LIBS_RELEASE),-l$(LIB))
debug: $(OBJ)
    $(CC) $(CXXFLAGS_DEBUG) $(foreach LIB,$(LIBS_DEBUG),-l$(LIB))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接