编译静态库遇到的 LNK2019 报错
Posted 一叶扁舟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了编译静态库遇到的 LNK2019 报错相关的知识,希望对你有一定的参考价值。
前文提到了 CMake 学习
文末基本涵盖了我遇到的编译问题,但是在得到一个编译好的 .lib 文件后,还需要放到项目中引用成功后才算真正的完成静态库的编译
嗯,我之所以说这些是因为我在项目中链接静态库时出现了 LNK2019 经典错误
错误如下:
Libraryd.lib(at_exit.obj) : error LNK2019: unresolved external symbol "void __cdecl logging::MakeCheckOpValueString<class base::AtExitManager *>(class std::basic_ostream<char,struct std::char_traits<char> > *,class base::AtExitManager *) Libraryd.lib(rand_util.obj) : error LNK2019: unresolved external symbol "void __cdecl logging::MakeCheckOpValueString<double>(class std::basic_ostream<char,struct std::char_traits<char> > *,double)" Libraryd.lib(rand_util.obj) : error LNK2019: unresolved external symbol "void __cdecl logging::MakeCheckOpValueString<unsigned __int64>(class std::basic_ostream<char,struct std::char_traits<char> > *,unsigned __int64)" Libraryd.lib(file_path.obj) : error LNK2019: unresolved external symbol "void __cdecl logging::MakeCheckOpValueString<wchar_t const *>(class std::basic_ostream<char,struct std::char_traits<char> > *,wchar_t const *)"
我们先看 .h 和 .cc 文件中对 MakeCheckOpValueString 的声明和定义吧
.h 文件
// Provide an overload for functions and function pointers. Function pointers // don\'t implicitly convert to void* but do implicitly convert to bool, so // without this function pointers are always printed as 1 or 0. (MSVC isn\'t // standards-conforming here and converts function pointers to regular // pointers, so this is a no-op for MSVC.) template <typename T> inline typename std::enable_if< std::is_function<typename std::remove_pointer<T>::type>::value, void>::type MakeCheckOpValueString(std::ostream* os, const T& v) (*os) << reinterpret_cast<const void*>(v); // We need overloads for enums that don\'t support operator<<. // (i.e. scoped enums where no operator<< overload was declared). template <typename T> inline typename std::enable_if< std::is_enum<T>::value, void>::type MakeCheckOpValueString(std::ostream* os, const T& v) (*os) << static_cast<typename std::underlying_type<T>::type>(v); // We need an explicit overload for std::nullptr_t. void MakeCheckOpValueString(std::ostream* os, std::nullptr_t p); template <typename T> void MakeCheckOpValueString(std::ostream* os, T p);
.cc 文件
void MakeCheckOpValueString(std::ostream* os, std::nullptr_t p) (*os) << "nullptr"; template <typename T> void MakeCheckOpValueString(std::ostream* os, T p) (*os) << p;
调用的地方:
// Build the error message string. This is separate from the "Impl" // function template because it is not performance critical and so can // be out of line, while the "Impl" code should be inline. Caller // takes ownership of the returned string. template<class t1, class t2> std::string* MakeCheckOpString(const t1& v1, const t2& v2, const char* names) std::ostringstream ss; ss << names << " ("; MakeCheckOpValueString(&ss, v1); ss << " vs. "; MakeCheckOpValueString(&ss, v2); ss << ")"; std::string* msg = new std::string(ss.str()); return msg;
可以看出 MakeCheckOpValueString 的声明和定义都是完整的
MakeCheckOpValueString 有四个重载,有两个 inline 声明的,直接在头文件中定义了,还有两个普通的 void 型,声明和定义是分开的
函数的声明和定义是没有问题的,有问题的话,在编译静态库时就应该出错了
后面研究后,怀疑是没有给外部工程暴露接口,也就是缺少 BASE_EXPROT 宏
这个宏的写法如下:
#ifdef BASE_EXPORT #undef BASE_EXPORT #endif #ifdef _WIN32 // 定义 BASE_EXPORT 宏为适当的导出关键字,以便在 Windows 平台上导出函数 #define BASE_EXPORT __declspec(dllexport) #else // 定义 BASE_EXPORT 宏为适当的导出关键字,以便在其他平台上导出函数 #define BASE_EXPORT __attribute__((visibility("default"))) #endif
顺便看看 dllexport 和 dllimport 的区别,看看 GPT 的回答
dllexport
和 dllimport
是用于在 Windows 平台上定义和使用动态链接库(DLL)中的函数和数据的关键字。
dllexport
用于在 DLL 中定义函数或数据,并指示编译器将这些符号导出到 DLL 中,以便其他模块(如应用程序)可以使用它们。当使用dllexport
修饰符时,编译器会生成导出符号表,并在链接时使其可见。dllimport
用于在应用程序中声明从 DLL 中导入的函数或数据。它告诉编译器该符号将在运行时从 DLL 中导入,而不是在当前模块中定义。使用dllimport
修饰符可以让应用程序在编译时知道要导入的符号的位置和类型。
关键区别在于:
dllexport
用于在 DLL 中定义符号,即导出符号。dllimport
用于在应用程序中声明从 DLL 中导入的符号。
这两个关键字的使用可以帮助实现动态链接库的功能,并支持在不同的模块之间共享函数和数据。在 Windows 平台上,它们是确保 DLL 的正确导出和导入的重要机制。
于是我们添加了 BASE_EXPORT 宏,并在 MakeCheckOpValueString 声明前都添加了该宏,
// We need an explicit overload for std::nullptr_t. BASE_EXPORT void MakeCheckOpValueString(std::ostream* os, std::nullptr_t p); template <typename T> BASE_EXPORT void MakeCheckOpValueString(std::ostream* os, T p);
重新编译静态库,并在项目中链接库,还是出现同样的问题
没办法,BASE_EXPORT 似乎不起作用,项目链接函数时仍然找不到函数对应的符号文件,只能从 inline 下手了,直接在头文件中内联,并删除 .cc 文件中的函数的定义
改成这样:
// We need an explicit overload for std::nullptr_t. inline void MakeCheckOpValueString(std::ostream* os, std::nullptr_t p) (*os) << "nullptr"; template <typename T> inline void MakeCheckOpValueString(std::ostream* os, T p) (*os) << p;
inline 关键词可以保证函数不会出现重复定义的错误,另外 inline 还有命名空间管理的特性
看看 inline 这两个细节的介绍
- 防止重定义(Multiple Definitions):在 C++ 中,如果同一个函数或变量在多个编译单元(源文件)中被定义,会导致重定义错误。为了解决这个问题,可以将函数或变量的定义放在头文件中,并使用
inline
关键字修饰,以便在多个编译单元中使用。这样,每个编译单元中对该函数或变量的定义都会被视为内联定义,避免了重定义错误。 - 命名空间的内联定义:在命名空间中定义的函数和变量,其声明和定义通常是分开的,即声明放在头文件中,定义放在源文件中。为了避免在每个源文件中都包含头文件,可以将命名空间中的函数和变量的定义放在头文件中,并使用
inline
关键字修饰。这样,每个源文件中包含头文件时,就相当于将命名空间中的定义直接内联到该源文件中,避免了多个源文件的重复定义。
最终,在重新编译静态库后,并在项目中链接它,没有出错,一切顺利
LNK2001: 无法解析的外部符号的几种情况
一般来说,我们引用第三方库时,需要进行指定依赖项配置,若没有进行相关配置,则编译器会出现“LNK2001: 无法解析的外部符号”错误。
这个是最常见的问题,具体步骤:
项目、属性、链接器、常规、附加库目录:填写附加依赖库所在目录 分号间隔多项
项目、属性、链接器、输入、附加依赖项:填写附加依赖库的名字.lib 空格或分号间隔多项
若已经按照上述步骤进行配置,但编译器还是报错,可能情况如下:
1.编译lib库和引用lib库平台不一致
lib库采用的编译平台和引用该lib的工程的编译平不兼容导致的。比如项目采用vs2008编译,而该lib是用2010编译的,亦或者项目采用vs2015平台,而lib是vs2008编译的也可能导致链接失败。
2.编译lib库和引用lib库选项不一致情况
1、一个lib库采用“多线程 DLL (/MD)”配置,而另外一个项目采用“多线程(/MT)”编译配置
2、一个lib库采用“使用 Unicode 字符集”配置,而另外一个项目采用“使用多字节字符集”编译配置
3、一个lib库采用选择“release 版本 ”配置,而另外一个项目采用选择 “debug 版本 ”编译配置
4、一个lib库采用“wind32平台 ”配置,而另外一个项目采用“wind64平台 ”编译配置
3.没有添加指定预编译宏
在使用curl静态库时,却遇到了编译链接错误:
1>testcurl.obj : error LNK2001: unresolved external symbol __imp__curl_easy_init
1>testcurl.obj : error LNK2001: unresolved external symbol __imp__curl_easy_setopt
1>testcurl.obj : error LNK2001: unresolved external symbol __imp__curl_easy_perform
1>testcurl.obj : error LNK2001: unresolved external symbol __imp__curl_easy_cleanup
解决方法是,在自己的项目属性中添加一个预编译宏,CURL_STATICLIB,
为了避免遇到以上问题建议:
1、引用第三方库时,确认lib库是否使用相同编译平台
2、确认编译选项是否一致。比如运行时库和字符集
3、若需要则添加指定编译宏
4、添加附加库目录和附加依赖项
---------------------
作者:Jimmy1224
来源:CSDN
原文:https://blog.csdn.net/c_base_jin/article/details/83010745
版权声明:本文为博主原创文章,转载请附上博文链接!
以上是关于编译静态库遇到的 LNK2019 报错的主要内容,如果未能解决你的问题,请参考以下文章
使用 qt creator 和 eigen 库编译程序时出现 lnk2019 错误
Visual Studio 2015编译Lua 5.3.4遇到的坑