[英]How do I resolve naming conflicts with headers that use the preprocessor to redefine common function names?
编辑:我发现了一个类似的问题,并且答案基本上是windows.h是错误的,您必须重命名函数或#undef宏:Others 的库#define命名冲突
但是,我认为由于LoadLibrary在调试和发行版本下的冲突行为,我的仍然有所不同。
我正在使用Visual Studio在Windows上进行编程,并且遇到了windows.h所使用的预处理器指令及其包含的标头的一些特殊问题。
我们的项目在其自己的名称空间MyProject::FileManager::CreateFile()
具有一个函数。 包含windows.h之后,由于链接器错误指出无法解析MyProject :: FileManager :: CreateFileW(请注意函数名末尾的W),我们的代码无法编译。 这不是静态函数,它是被file_manager.CreateFile(...)
调用的FileManager
对象的成员函数。
在Visual Studio中突出显示该功能时,工具提示显示以下内容:
#define CreateFile CreateFileW
我们感到困惑,但只是将函数重命名为解决方法。 但是后来,我们尝试使用Windows API中的LoadLibrary
函数时遇到了类似的问题。 在Debug模式下进行编译, LoadLibrary
被定义为LoadLibraryW()
,它以LPCWSTR(宽字符串)为参数。 当我尝试以发布模式进行构建时,此函数现在定义为采用正常LPCSTR的LoadLibraryA()
。 这破坏了我们的构建,因为代码是在LoadLibrary
采用LPCWSTR的假设下编写的。
所以,我的问题是,程序员应该如何处理呢? 我是否应该使用#ifdef的Debug或Release模式检查来包装对LoadLibrary的调用? 还是有一个更简单的解决方案?
此外,我在github上发现了一个有趣的头文件,该头文件似乎是为#undef'所有这些函数名而创建的:
https://github.com/waTeim/poco/blob/master/include/Poco/UnWindows.h
我通常会采取一些措施来解决此问题:
win/filesystem.h
和win/filesystem.cpp
来包装所有调用。 (这也是将Win32错误转换为std::system_error
异常,删除不需要的/过时的/保留的参数以及通常使Windows API对C ++更友好的好地方。) DWORD
, LPTSTR
和BOOL
类的定义渗透到代码的所有级别,使得处理Windows.h
变得更加困难。 (并且也要进行移植。) #include <Windows.h>
应该唯一的文件应该是包装C ++文件。 CreateFileW
或CreateFileA
,而不要依赖宏。 这样,您就不必依赖Unicode /多字节项目设置。 例
win/filsystem.h
可能包含以下定义:
namespace win32
{
class FileHandle
{
void* raw_handle_;
public:
// The usual set of constructors, destructors, and accessors (usually move-only)
};
FileHandle CreateNewFile(std::wstring const& file_name);
FileHandle OpenExistingFile(std::wstring const& file_name);
// and so on...
}
您代码的任何部分都可以包含此文件以访问文件系统API。 由于win/filesystem.h
本身并不包含<Windows.h>
,因此客户端代码将不受各种Win32宏的污染。
这里的问题是windows.h尝试支持两种不同的字符串模型:由单字节字符组成的字符串和以unicode编码的字符串(由Microsoft定义为两字节字符)。 几乎所有Windows API函数都有两种不同的版本,一种采用单字节字符串,另一种采用两字节字符串。 您应该使用通用名称(例如CreateFile
, LoadLibrary
等)编写代码,并让windows.h负责将这些名称映射到实际的API函数。 对于单字节字符,这两个是CreateFileA
和LoadLibraryA
; 对于两个字节的字符,它们是CreateFileW
和LoadLibraryW
。 当然,还有十亿亿。 通过在每个编译单元中定义宏UNICODE
,可以在编译时选择模型。
附带地, 'A'
后缀代表“ ANSI”,而'W'
后缀代表“宽字符”。
当您编写试图将Windows依赖项隔离到少数源文件中的代码时,这特别隐蔽。 如果编写的类具有名为CreateFile
的成员函数,则将在不将windows.h用作CreateFile
源文件中以及在将windows.h用作CreateFileA
或CreateFileW
源文件中看到该类。 结果:链接器错误。
有几种方法可以解决此问题:
在每个头文件中始终#include <windows.h>
; 那是编译器性能的杀手,但是它可以工作。 (windows.h是针对早期针对Windows的C ++编译器中预编译头文件的主要动机)
始终使用篡改的名称, CreateFileA
或CreateFileW
; 这将起作用,但是以失去在进行API调用时更改底层字符串模型的灵活性为代价; 这是否重要取决于您。
不要使用任何Windows API名称; 如果使用与Windows相同的命名约定,可能会很痛苦; 如果使用蛇格,一点也不费劲,即所有小写字母都有下划线来分隔单词。 例如, create_file
。 或者,使用本地前缀或后缀: MyCreateFile
或CreateFileMine
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.