繁体   English   中英

如何解决与使用预处理器重新定义通用函数名称的标头的命名冲突?

[英]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

我通常会采取一些措施来解决此问题:

  • 在Windows特定的层中隔离所有Windows系统调用。 例如,如果我使用文件系统API,则通常将使用win/filesystem.hwin/filesystem.cpp来包装所有调用。 (这也是将Win32错误转换为std::system_error异常,删除不需要的/过时的/保留的参数以及通常使Windows API对C ++更友好的好地方。)
  • 避免使用特定于Windows的类型。 允许DWORDLPTSTRBOOL类的定义渗透到代码的所有级别,使得处理Windows.h变得更加困难。 (并且也要进行移植。) #include <Windows.h>应该唯一的文件应该是包装C ++文件。
  • 避免自己使用Windows重定向宏。 例如,您的包装层应直接调用CreateFileWCreateFileA ,而不要依赖宏。 这样,您就不必依赖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函数都有两种不同的版本,一种采用单字节字符串,另一种采用两字节字符串。 您应该使用通用名称(例如CreateFileLoadLibrary等)编写代码,并让windows.h负责将这些名称映射到实际的API函数。 对于单字节字符,这两个是CreateFileALoadLibraryA 对于两个字节的字符,它们是CreateFileWLoadLibraryW 当然,还有十亿亿。 通过在每个编译单元中定义宏UNICODE ,可以在编译时选择模型。

附带地, 'A'后缀代表“ ANSI”,而'W'后缀代表“宽字符”。

当您编写试图将Windows依赖项隔离到少数源文件中的代码时,这特别隐蔽。 如果编写的类具有名为CreateFile的成员函数,则将在不将windows.h用作CreateFile源文件中以及在将windows.h用作CreateFileACreateFileW源文件中看到该类。 结果:链接器错误。

有几种方法可以解决此问题:

  1. 在每个头文件中始终#include <windows.h> 那是编译器性能的杀手,但是它可以工作。 (windows.h是针对早期针对Windows的C ++编译器中预编译头文件的主要动机)

  2. 始终使用篡改的名称, CreateFileACreateFileW 这将起作用,但是以失去在进行API调用时更改底层字符串模型的灵活性为代价; 这是否重要取决于您。

  3. 不要使用任何Windows API名称; 如果使用与Windows相同的命名约定,可能会很痛苦; 如果使用蛇格,一点也不费劲,即所有小写字母都有下划线来分隔单词。 例如, create_file 或者,使用本地前缀或后缀: MyCreateFileCreateFileMine

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM