1.背景
使用第三方库在编译时出现了链接错误:Error LNK2019 无法解析的外部符号unsigned short* XXXX(XXX);
2.问题分析
使用的第三方库在编译时设置了将wchar_t作为内置类型处理,
而当前开发的程序由于条件限制(依赖的其他库是将wchar_t作为unsigned short处理,并且当前编译环境为某公司在VS上二次开发的编译环境,虽说支持设置编译选项,但开启/Zc:wchar_t后该编译环境却“不认识”),因此要改当前的wchar_t解释是不可行的(当前是作为unsigned short处理),这也是为什么会出现上述问题的原因,当前编译环境将第三方库头文件中的wchar_t认为是unsigned short,等到链接lib文件时发现找不到相应接口的实现(因为lib文件中wchar_t不是unsigned short)。
3.解决方案
先来看下MSDN关于wchar_t的描述,/Zc:wchar_t (wchar_t Is Native Type),
- /Zc:wchar_t开启:wchar_t作为内置类型(C++标准中定义的2个字节的字符,在Microsoft中是__wchar_t的别名);
- /Zc:wchar_t-开启:wchar_t作为unsigned short处理;
- 如果作为C语言编译,wchar_t将作为unsigned short处理,不论当前设置是/Zc:wchar_t还是/Zc:wchar_t-;这也解释了为什么库的导出接口为C(external "C" XXXX)时,在开启/Zc:wchar_t-的程序中使用时没有问题;
- 微软不推荐编译程序时开启/Zc:wchar_t-,因为C++标准是将wchar_t作为内置类型(2个字节的字符)处理的,因此在Visual Studio中默认开启 /Zc:wchar_t,这时wchar_t是__wchar_t的别名;
- 开启了/Zc:wchar_t时将会定义两个预处理器符号:_WCHAR_T_DEFINED和_NATIVE_WCHAR_T_DEFINED;
如何设置wchar_t编译选项 ?
在CMake中
set_target_properties,例如,
set_target_properties(CAAMyAddin PROPERTIES COMPILE_FLAGS "/Zc:wchar_t-")
项目--》属性--》C++ -》语言--》修改 Treat wchar_t as Built-in Type 属性
3.1.开发的库应该怎么设置?
假如开发的库会被旧代码(开启了/Zc:wchar_t-)中使用时该怎么办?微软也给了几种解决方案,
- 对你的库中使用wchar_t的接口 重载两个版本:一个是将wchar_t替换为unsigned short的版本,另一个是将wchar_t替换为__wchar_t的版本(这两个版本的接口实现都会调用wchar_t的版本),这样你的库既可以在新代码(开启/Zc:wchar_t)和旧代码(开启/Zc:wchar_t-)中使用,是不是很完美?
- 或者将你的库编译为两个版本:一个是开启了/Zc:wchar_t的,另一个是开启了/Zc:wchar_t-的,这样就需要管理两个版本,额,代价有点大,但问题可以解决;
- 当然Microsoft建议将旧代码(开启了/Zc:wchar_t-)的编译选项改为新的(即在旧代码中开启/Zc:wchar_t),并且不要在一个程序中混用wchar_t的两种解释(比如当前关于wchar_t的编译选项和库的编译选项不一致);
参考What is __wchar_t (with the leading double underscores) and why am I getting errors about it?
// Something.h bool DoSomething(const __wchar_t* s); bool DoSomething(const unsigned short* s);
// Something.cpp #include <Something.h> bool DoSomethingWorker(const wchar_t* s) { ... implementation ... } bool DoSomething(const __wchar_t* s) { return DoSomethingWorker(reinterpret_cast<const wchar_t*>(s)); } bool DoSomething(const unsigned short* s) { return DoSomethingWorker(reinterpret_cast<const wchar_t*>(s)); }
3.2.你的程序中使用的库时别人开发的该怎么办?
使用的库是别人开发发布的,而这个库关于wchar_t的解释和你当前的程序不一致,如何是好?
- 联系库的作者要一份和你当前应用程序设置一样的编译版本的库(如果可能),当然也可自行去找下有没有这样的版本,或者直接将3.1章节中的建议告诉该库的开发者,他按照那样做的话,就没问题了;
- 如果你的代码是旧的(开启了/Zc:wchar_t-),如果可以的话,那么别再固执了,升级为/Zc:wchar_t吧;
- 大招来了,直接修改库的头文件(注意别乱修改,否则会出更多的错错误!)中关于wchar_t的接口:将wchar_t替换为__wchar_t或者unsigned short(取决于你当前是/Zc:wchar_t还是/Zc:wchar_t-),为什么这样可以?因为wchar_t只是一个别名,它实际上是__wchar_t或者unsigned short,这里你直接将其替换为对应的真实类型是不会有编译错误的。替换完后,就可以开心的使用了~
- 当然如果你觉得上述的大招有点残暴,那么你可以选择将库再封装一层,封装好的库提供两个版本的接口,或者干脆将wchar_t换为wstring或者unsigned short!
- 如果你的程序中使用的不同库的wchar_t设置不一样,那么解决方案和上面的一样~
4.参考
- What is __wchar_t (with the leading double underscores) and why am I getting errors about it?
- /Zc:wchar_t (wchar_t Is Native Type)
- error LNK2019 第3方lib导出函数里有wchar参数时
- Unresolved externals and wchar_t's
- CAA使用boost库的诸多问题
- CAA:故障排除
- C++调用C函数
- extern 和 external“C”的分析
5.总结
正如extern 和 external“C”的分析中所说的那样,我们遇到问题时除了知道怎样解决问题,还应该了解背后的原因,为什么这样就是可以解决的?有没有其他更好的方案?这种问题在谁来处理会更好?知道了原理后,就不仅知道了该问题怎么解决,还知道了这系列问题怎么处理。