【问题标题】:What is the meaning of this C++ macro?这个 C++ 宏是什么意思?
【发布时间】:2013-05-16 04:16:52
【问题描述】:

我不知道这个宏是什么意思:

#define DECLARE_HANDLE(n) typedef struct n##__{int i;}*n

DECLARE_HANDLE(HWND);

我从 C 程序中了解到:

“##”表示连接参数。

所以宏等于:

typedef struct HWND__{int i;}*HWND

是这样吗?

如果是对的,那句话是什么意思?

===================

来自 Bombermaaan 游戏的代码(适用于 Windows 和 Linux),
链接http://sourceforge.net/p/bombermaaan/code/HEAD/tree/trunk/src/Bombermaaan/winreplace.h,
第 90 行。

【问题讨论】:

  • 这是什么意思? #define INVALID_SOCKET (SOCKET)(~0)
  • ~logical not 运算符,在这种情况下,它将还原您放入 0 的数字的所有位。char sock = INVALID_SOCKET; 将是 0xFF, int sock = INVALID_SOCKET 将是 0xFFFFFFFF。
  • 感谢@Sean 提出这个问题,一个好问题。并感谢 Xgbi 的详细回答。
  • 这部分意味着有人喜欢创建无效标识符。包含两个连续下划线的名称保留给实现,不应在任何其他代码中使用。
  • @xgbi,我认为! 是逻辑非运算符。 ~ 最好使用按位非运算符。

标签: c++ macros concatenation c-preprocessor


【解决方案1】:

此构造的主要目的是防止误用句柄。如果所有句柄都只是void *intlong long 或其他一些基本类型,那么没有什么可以阻止您使用一个而不是另一个。指向struct HWND__ 的指针和指向struct HBITMAP__ 的指针不是一回事,所以如果你有以下代码:

HWND hwnd;
HBITMAP hbmp;

hbmp = GetBitmap(...);
hwnd = hbmp;    // gives compiler error. 

这是一种相当经典的技术,可确保您获得 API 供应商不想为其提供真实声明的事物的唯一类型。虽然我不完全确定他们为什么需要正确的结构声明,但您可能会侥幸逃脱:

#define DECLARE_HANDLE(n) struct n ## __; struct n ## __ *n;

这也将确保任何取消引用 HWND 都是不可能的,因为编译器将反对“使用不完整类型”。

【讨论】:

  • 如果你想使用智能指针来清理它,这有点烦人,但我不确定持有 decltype(*std::declval<HWND>()>) 是否安全(我打算找个时间问这个)。这样做会产生一个不错的 RAII Handle 类。请记住,STRICT 也在这里发挥作用。
  • @Chris:将整个 HWND 处理包装到一个创建窗口对象的类中,处理它,然后在销毁时关闭它,这可能是一个更好的主意。
  • 好吧,windows 有点挑剔(你不想获得别人的窗口并让它为你做),HWND 只是我想到的第一个句柄。我的计划是创建一个纯Handle 类来保存句柄并使用用户提供的函数进行清理,并拥有代表这些对象的实际类,并且可以使用Handle 类更轻松地管理资源。在理想情况下,用户不必知道 Handle 类的存在。
  • @chris:我想你可以用模板做类似的事情,然后为你知道用什么来关闭它们的类型编写一个“关闭函数”,如果有的话让用户提供一个是多种选择。
  • 当然,我已经这样做了来测试它。智能指针使用该技巧来指向什么,Handle<HDESK> 将是一个使用示例,lambdas 在指定清理代码方面非常出色(我猜如果没有进行错误检查,T(WINAPI *)(HandleType) 的更改可能会很有用)。不过,我 asked 关于整个假设它是一个指针。
【解决方案2】:

你的假设是正确的。

您可以使用以下简单的测试代码进行检查。

DECLARE_HANDLE(HWND);
struct HWND__ s;
HWND p = (HWND) malloc(sizeof(struct HWND__));
s.i = 20;
p->i = 100;
cout << "i valueis " << s.i<<" and " << p->i <<endl;

【讨论】:

    【解决方案3】:

    DECLARE_HANDLE(HWND); 确实扩展为

    typedef struct HWND__{int i;}*HWND;
    

    现在,你要问这意味着什么?只需将 typedef 拆分为两步:

    struct HWND__
    {
        int i;
    };
    
    typedef HWND__* HWND;  // C++ only; for both C++ and C: typedef struct HWND__* HWND;
    

    因此,它定义了一个包含int(名为i)的结构HWND__,并将类型别名HWND声明为指向HWND__的指针

    【讨论】:

      【解决方案4】:

      没错。

      这用于声明一个不透明的指针,指向你不知道的结构。

      我不知道他们为什么不简单地将其声明为

      typedef void* HWND;
      

      可能是对齐问题,因为结构可以对齐,但基本类型不能对齐
      如上所述,将类型声明为结构允许进行一些编译时类型检查。
      聪明!

      【讨论】:

      • 主要是让你在你想要HWND的地方使用HWND,在你想要HBITMAP的地方使用HBITMAP等。如果他们只是void *,你可以使用HWND h = CreateBitmap(...);
      • 正如 Mats 提到的,void* 不允许类型检查(可以混合不同的句柄类型)。但是,我不明白为什么不简单地 #define DECLARE_HANDLE(n) typedef struct n##__ *n; (没有实际的结构定义),这将提供真正的不透明度并防止像 ++myhandle.
      • 在非 STRICT 版本中,我相信是 typedefed 取消 *。见msdn.microsoft.com/en-us/library/windows/desktop/…
      【解决方案5】:

      它将HWND 定义为指向包含int istruct HWND__ 的指针。

      所以,现在您可以在代码中使用类型HWND

      【讨论】:

      • 感谢您对代码含义的解释。
      猜你喜欢
      • 1970-01-01
      • 2015-01-09
      • 2015-11-03
      • 2014-04-25
      • 2011-02-09
      • 1970-01-01
      • 2012-01-05
      • 2011-11-13
      • 1970-01-01
      相关资源
      最近更新 更多