调试和发布配置 (c++) 中内置的共享库之间的用法有何区别?



我在使用共享库(Win 10中的.dll)时遇到了问题。

我在两种不同的配置中构建了一个名为xlib的lib,并试图通过CMake在名为xlibtest的项目中测试使用它们。

工作流程是:

步骤A.构建xlib=>xlib.dll+xlib.lib(仅符号)+xlib.pdb[可选]

步骤B.构建xlibtest并链接xlib.lib=>xlibtest.exe

如果步骤A和步骤B中的构建配置不同,那么当我运行xlibtest.exe时,就会出现问题。例如,我有一个功能

//xlib.h    
void foo(std::string input); 

在xlib中,当我调用这个函数时,它会崩溃。

//xlibtest.cpp
int main() {
xlib::foo("test"); // in debug mode I found the string pass to foo is wrong
}

我搜索了一下,找到了关于这个问题的一些解释。这可能是因为调试和发布配置的内存模式不同。建议不要混合这两种配置,即在调试配置中使用调试库,在发布配置中使用发布库。

那么问题来了。我可以建立两个版本的我自己的图书馆。但是,我能为那些外部库做些什么呢?

我在项目中使用了intel MKL库,并在调试和发布配置中链接了相同的库。看起来效果不错。

但当我使用Boost库时,调试版本和发布版本之间可能存在差异,因为我们可以在安装Boost库的时候决定构建的版本。

第页。S.我之所以不链接静态库,是因为存在编译时的错误,并且需要相同的配置(即调试/发布)。

-更新--

我正在使用:

CMake 3.5.2;

编译器:适用于英特尔64 VS2015环境的英特尔icl 16.0更新3;

IDE:VS 2015社区;

操作系统:Windows 10,64位。

CPU:Intel i7 4930K

很抱歉没有提供有关工具链的这些信息。我曾经认为这是一个与我使用的工具链无关的一般问题@Hans Passant

我想@John D的回答可能会结束我的困惑。在我了解到混合调试和发布配置存在问题后,我不明白为什么使用"英特尔MKL库"没有区别,这就是我提出这个问题的原因。"英特尔MKL库"是一个没有std::字符串的C库,这是原因吗?

在调试/发布中编译-对代码的影响

从技术上讲,库的"调试"one_answers"发布"构建是使用不同的编译器开关和预处理器宏构建的。它与编译具有可选功能的库没有本质区别。

  • 编译器开关通常不会影响代码在高级下的工作方式。在低级别上,相关规范没有指定行为(例如,按照独立操作的顺序、内存映射、变量的物理位置、编译器生成的指令插入代码),但如果你依赖这些,那你就错了
  • 另一方面,预处理器宏可以以任何方式更改代码行为通常,DEBUG宏启用具有各种运行时检查和诊断功能的代码块。后者可能包括向数据类型添加成员

因此,您构建的C++代码的调试和发布版本在一般情况下是二进制不兼容的

这代表C++标准库的代码尽管它通常不是每次都从stratch编译的,但插入了为数不多的预编译版本之一(包括委托给DLL的"thunk"版本)。

真正的部分:陷阱和解决方案

如果您有两个模块,问题将发生

  1. 使用不同版本或类型的二进制表示
  2. 该类型的交换对象

这个问题主要发生在动态链接中(但如果预先构建的静态库与其标头不对应,则也可能发生在静态链接中)。

如果模块已针对定义该类型的库的二进制不兼容版本进行编译(使用有问题的类型)和链接(使用处理有问题类型的库代码),则会发生这种情况。

因此,特别是对于C++标准库,为了避免和/或修复这些问题,您需要

  • 请确保所有交换C++类型的模块都已编译并链接到C++运行时的二进制兼容版本(对于正在交换的类型)
    • 这相对容易检查是否所有此类模块都动态链接到它,但如果有些模块静态编译,则不太容易
    • 基本上,在理想情况下,所有模块甚至应该使用运行时的同一实例(在Linux中,它包括libgcc),例如,可能需要在它们之间传递异常。如果您的环境是如此异构以至于无法实现,那么明智的做法可能是完全放弃C++类型作为交换格式

在链接时区分不兼容的库版本

(指定正确的模块并命名自己的模块,以便区分它们)

在链接时(静态或动态),不存在"构建配置"这样的东西。这里有许多模块。链接器看到的只是对其他模块的引用(仅限动态链接)和导出/导入的条目。它所做的就是找到引用的模块(同样,仅限动态;在静态中,模块通常是显式指定的),然后将导出和导入的条目匹配在一起。C++导出/导入条目名称包含编码("损坏")形式的条目的整个签名(特别是,不兼容的编译器必须使用不同的损坏方案,这样它们生成的条目就不会冲突。

链接器区分库的两个版本的唯一方法是:

  1. 提供不同的搜索路径/明确指定不同的模块
  2. 使用不同的模块名称进行链接
  3. 使用不同的条目名称(意味着不同的类型名称/函数签名)

3)通常不实用(需要大量样板代码),并且在动态链接中,如果没有2),则无法使用,因为链接器首先匹配模块名称。1) (搜索路径风格)无法缩放。

总结:

  • 2)是系统范围动态链接的标准做法(_d后缀,版本后缀)
  • 1) 是构建环境(静态和动态)的标准做法
  • 1) (搜索路径风格)用于链接到图书馆的私人副本(正如所说,这不可扩展)

最新更新