在MS Visual C上链接到protobuf 3时出错

jok*_*oon 5 c++ protocol-buffers visual-studio visual-c++ visual-studio-2013

在Visual Studio 2013上遇到,但它可以与任何版本重现.

我从github克隆了协议缓冲库,在它上面运行了CMake-gui(我将所有内容都保留为默认值,因此它是静态版本),只构建libprotobuf(其他项目由于某种原因失败,cmd.exe错误,可能有事情要做有测试,但libprotobuf构建良好).

我的项目使用在mapbox矢量tile规范的github上找到的.proto文件生成的标题.

当我链接时,我首先有这个错误

Error 1 error C4996: 'std::_Copy_impl': Function call with parameters that may be unsafe - this call relies on the caller to check that the passed values are correct. To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ 'Checked Iterators' s:\program files (x86)\microsoft visual studio 12.0\vc\include\xutility

我尝试-D_SCL_SECURE_NO_WARNINGS在其他命令行参数中禁用它,但后来我有其他错误:

Error 1 error LNK2038: mismatch detected for 'RuntimeLibrary': value 'MTd_StaticDebug' doesn't match value 'MDd_DynamicDebug' in main.obj S:\eiogit3\misc-projs\mapload\mapload\libprotobufd.lib(common.obj)

Cri*_*ati 10

你的项目和libprotobuf项目使用VStudio C(和C++)运行时库(VCRTLib - 检查[SO]:如何规避Windows Universal CRT标头依赖于vcruntime.h(@CristiFati的答案))的不匹配.让我详细说明:

假设有一些C代码.该代码的目的是运行.比可以实现:

  • 直接:在VC应用程序类型项目中包含该代码- 这将生成.exe
  • 间接的:包括在代码VC库的项目类型-这将产生一个,从另一个调用时将只能够运行.exe文件(即调用库).该库可以是:
    • static:所有C代码都将被编译并存储在.lib文件中.在链接时,在另一个项目(无论是应用程序还是库)中使用库时,您将需要该文件.请注意,.lib中所有需要的代码将被"复制"到另一个项目中
    • 动态:你现在将有2个文件:一个.dll文件,它将包含已编译(和链接)的代码,一个.lib文件1将包含"指针"(如果你愿意)到.dll文件中的代码.在另一个项目中使用库时,您还需要链接时的.lib文件,但现在它不会包含代码,因此不会在其他库中复制(其他库会更小),但是在运行时,其他库将需要.dll文件

您可以在CLR Windows窗体中检查[SO]:LNK2005错误(@CristiFati的答案),了解如何以可执行格式转换C代码的详细信息.Google还有很多关于静态库和动态库之间差异的文章,何时使用其中一个,可以在[SO]上找到一个例子:何时使用动态库和静态库.

正如您所猜测的,CRTC运行时库(包含使C代码能够运行的底层系统- 一个例子是内存管理函数:malloc,free)也不例外 - 它相当于Uxlibc.a(静态或归档)与libc.so(动态或共享对象) - 但在VStudio中它有点复杂:

  • 静态CRT驻留在libcmt.lib中
  • 动态CRT驻留在msvcrt.lib中,它"指向" msvcr ###.dll 2(适用VStudio 2013的msvcr120.dll)

备注:

  • 库名称末尾的" d "(msvcr d .lib)表示它是使用调试符号编译的
  • C++运行时库是在确切的情况下; 名称有一个额外的 p: libc p mt.lib, msvc p rt.lib, msvc p 120.dll
  • 有关更多详细信息,请查看[MS.Docs]:CRT库功能

现在,VCRTLib部件不像任何其他lib一样包含在项目中(项目属性 - >链接器 - >输入 - >附加依赖项),但由于在编译时需要它们的性质(静态或动态),因此它们配置为:[ MS.Docs]:/ MD,/ MT,/ LD(使用运行时库),其中有4个可用选项:

  1. 多线程(/ MT)
  2. 多线程调试(/ MTd)
  3. 多线程DLL(/ MD)
  4. 多线程调试DLL(/ MDd)

显然,包含"Debug"的是在构建Debug配置时,而其他包含Release ; 关键点是拥有DLL的是使用动态运行时版本,而其他版本是静态版本.

回到你的错误:链接器抱怨main.obj(你的项目的一部分)有MDd_DynamicDebug(链接动态调试版本),而common.obj(libprotobuf项目的一部分)有MTd_StaticDebug(链接静态调试版本),所以你在同一个可执行文件(或.dll)中链接2个运行时- 这是不可能的.

为了解决这个问题,你应该确保libprotobuf和你的主项目都具有相同的VCRTLib值.
当然,更改主项目设置以匹配libprotobuf的设置更简单,但建议使用动态运行时版本(在涉及.dll的大型项目中,事情可能会变得混乱),即使这需要重新编译libprotobuf(以及,如果更改该选项会产生使libprotobuf很难构建的错误,并且您的项目将保持这种简单,您可以使用静态VCRTLib).

注意:不要误解VCRTLib类型(静态/动态)与构建libprotobuf的方式(此时是静态的,但我确信它也可以构建为动态的).

@ EDIT0:

在上面的注释中添加一些额外的信息,就像一些注释所要求的那样,它可能对其他用户有用.

关于库(包括libprotobuf)有两个方面,它们完全不相关:

  1. 库类型(它的构建方式):动态/静态
  2. VCRTLib类型(它使用 VCRTLib的方式):再次,动态 /静态

所以,有4个完全有效的组合:

  1. 使用动态VCRTLib的动态库
  2. 使用静态VCRTLib的动态库
  3. 使用动态VCRTLib的静态库
  4. 静态库使用静态VCRTLib

对于libprotobuf,每个方面都由布尔cmake选项控制:

  1. 库类型:protobuf_BUILD_SHARED_LIBS
  2. VCRTLib类型:protobuf_MSVC_STATIC_RUNTIME

可以通过以下任一方式设置2个标志:

  • cmake的桂
  • cmake的CMDLINE(将它们作为参数-比如:-Dprotobuf_BUILD_SHARED_LIBS=OFF -Dprotobuf_MSVC_STATIC_RUNTIME=OFF)

因此,上述4种组合是可能的(至少在v 3.5中),但是#2. 默认情况下禁用(指定-Dprotobuf_BUILD_SHARED_LIBS=ON -Dprotobuf_MSVC_STATIC_RUNTIME=ON将构建将链接到动态VCRTLib.dll),以避免可能的运行时问题,并启用它需要手动干预.

有关构建指令(通过cmake)的更多详细信息,请检查:[GitHub]:protocolbuffers/protobuf - (master)protobuf/cmake/README.md.



1:只有在库导出符号时才会创建.lib文件,因为它没有意义(链接时不需要,并且会创建.dll,但几乎无法使用)

2:对于较新的VStudio版本(从2015年开始),msvcr(t)部分已被vcruntime取代(或者至少这是入口点,因为它被分成较小的逻辑部分)