代码之家  ›  专栏  ›  技术社区  ›  Mads Elvheim

对于WIN32 API引入的预处理器命名空间污染,是否有一个简单的一次性解决方案?

  •  5
  • Mads Elvheim  · 技术社区  · 14 年前

    众所周知,包括 <windows.h> 污染 全部的 在C++中的名称空间,通过每个Win32 API函数定义一个预处理器,它可以采用多字节或UTF 16输入。例如:

    #ifdef UNICODE
    #define CreateFont CreateFontW
    #else
    #define CreateFont CreateFontA
    #endif
    

    我已经使用本机Win32 API好几年了,但我几乎要放弃了!在任何非琐碎的项目中,都有足够的名称冲突使您的脸变蓝。 拜托,哦,拜托,能不能有人想出一个解决方案,不需要我在事后逐个定义这些定义宏?

    而且我总是使用Unicode/UTF-16,所以在CreateFont的情况下,我会在代码中直接调用CreateFontW;我不会使用宏定义。有没有人可以解决这个问题,比如一个完整的头文件,其中包含要与windows.h包在一起的undef?

    一个到处蔓延的错误示例是使用了“GetMessage()”这样的通用名称。

    font.cpp(78) : error C2039: 'GetMessageW' : is not a member of 'FontManager'
    

    当你只想让你的类拥有一个名为void GetMessage()的成员函数时。真是令人沮丧。

    2 回复  |  直到 14 年前
        1
  •  11
  •   James McNellis    14 年前

    最干净的解决方案不是直接在程序中调用Windows API函数,而是编写一个抽象层来调用所需的函数。这样,只需在少数源文件中包含Windows API头,宏就更易于管理。

    这还有一个额外的好处,那就是提高代码的可移植性,因为只有一小部分代码依赖于Windows API本身,其余的代码都将调用抽象层。

    既然您正在使用C++,抽象层也可以允许您将Windows API错误代码转换为异常,如果您喜欢使用异常。

    抽象层也有助于使代码更容易测试(模拟抽象层比模拟部分或全部Windows API要容易得多)。

        2
  •  4
  •   Adrian McCarthy    10 年前

    公认的答案是非常好的,我建议尽可能。不幸的是,这样一个抽象层的实现是乏味的,它并不能解决整个问题。

    • pimpl idiom 虔诚地将任何和所有WinAPI类型保留在抽象层的头文件之外。应包括Windows标题 只有 .cpp )不在标题中。
    • 实际上,您最终不得不创建许多自己的类,这些类捕获的信息与底层WinAPI类型(例如LOGFONT)基本相同,因为抽象接口的客户端需要传递和接收它。您最终需要大量样板代码来在抽象层类型和WinAPI类型之间进行转换。
    • 引入抽象层所需的大型重构在遗留代码库中可能需要做很多工作,特别是如果该代码在许多项目中共享的话。

    抽象层的实现需要包含windows头文件。假设您为启用抽象层而引入的一种与平台无关的类型有一个名为 GetFont . 依赖于它的平台无关代码将看到 因为不会有人提起 <windows.h> 在那个翻译单元里。但是实现文件 .cpp公司 在Windows上实现抽象层的 预处理器宏,因为它必须包含 <窗口。h> . 因此,使用pimpl限制了问题的范围,但并不能完全解决问题。

    #include <windows.h> 我控制的代码中的行 #include "apiwrappers/windows.h" ,它是对大致如下所示的文件的引用:

    // Prevents Windows headers from defining macros called min and max, which
    // conflict with identifiers in the C++ standard library.
    #ifndef NOMINMAX
    #define NOMINMAX
    #endif
    
    // Distinguishes between different types of handles so that we get better
    // error checking at compile time.
    #ifndef STRICT
    #define STRICT
    #endif
    
    #include <windows.h>  // the "real" <windows.h>
    
    // Lowercase far and near are perfectly reasonable names for local variables
    // and parameters, so we don't want redefinitions of them.
    #undef far
    #undef near
    
    // But lots of other Win32 headers use FAR and NEAR, which are typically
    // defined to far and near.  We need them to be empty.
    #ifdef FAR
    #undef FAR
    #define FAR
    #endif
    #ifdef NEAR
    #undef NEAR
    #define NEAR
    #endif
    
    // Windows defines macros to select between the "ANSI" and "wide" versions
    // of many API functions (e.g., GetMessage macro expands to GetMessageA or
    // GetMessageW).  We undefine many of these here to avoid naming collisions.
    // Call the specific -W functions explicitly instead of relying on these
    // macros.
    #undef GetFont
    #undef GetMessage
    // ...
    

    #undef 就像我解决碰撞一样。我没有一个全面的。