请解释一下这个进行转换和类型检查的核心宏

发布于 2024-11-17 13:59:44 字数 1474 浏览 3 评论 0原文

以下代码来自必须用 C 和 C++ 编译的现有应用程序。有一个宏:

/* Type-checking macro to provide arguments for CoCreateInstance() etc.
 * The pointer arithmetic is a compile-time pointer type check that 'obj'
 * really is a 'type **', but is intended to have no effect at runtime. */
#define COMPTR(type, obj) &IID_##type, \
(void **)(void *)((obj) + (sizeof((obj)-(type **)(obj))) \
                - (sizeof((obj)-(type **)(obj))))

其用法如下:

ISomeInterface *object;
CoCreateInstance(&CLSID_SomeInterfaceImpl, NULL,
     CLSCTX_INPROC_SERVER, COMPTR(ISomeInterface, &object))));

这里的想法是 CoCreateInstance() 的最后两个参数是 IID&void** 并且该宏获取 ISomeInterface** 并将其转换为 IID&void** 同时强制执行编译时检查传递的地址代替ISomeInterface** 确实是 ISomeInterface* 指针变量的地址。

好吧,但是复杂的表达有什么必要呢

((obj) + (sizeof((obj)-(type **)(obj))) \
     - (sizeof((obj)-(type **)(obj)))

?我看到类型检查是通过 (obj)-(type**)(obj) 子表达式强制执行的。需要先加上然后减去 sizeof() 吗?在转换为 void** 之前,需要先转换为 void* 吗?

我想同样可以这样做:

#define COMPTR(type, obj) &IID_##type, \
(void **)(sizeof((obj)-(type**)(obj)), obj)

这里逗号运算符的第一部分将包含一个 sizeof() ,它将强制进行类型检查并计算为常量,第二部分将产生相同的指针并且指针将被转换为void**

原来的宏可以做哪些我建议的宏不能做的事情?出现这些并发症有什么必要?

The following code is from an existing application that has to be compiled in both C and C++. There's a macro:

/* Type-checking macro to provide arguments for CoCreateInstance() etc.
 * The pointer arithmetic is a compile-time pointer type check that 'obj'
 * really is a 'type **', but is intended to have no effect at runtime. */
#define COMPTR(type, obj) &IID_##type, \
(void **)(void *)((obj) + (sizeof((obj)-(type **)(obj))) \
                - (sizeof((obj)-(type **)(obj))))

that is used as follows:

ISomeInterface *object;
CoCreateInstance(&CLSID_SomeInterfaceImpl, NULL,
     CLSCTX_INPROC_SERVER, COMPTR(ISomeInterface, &object))));

here the idea is that the last two parameters of CoCreateInstance() are IID& and void** and that macro grabs ISomeInterface** and converts it to IID& and void** at the same time enforcing a compile-time check that the address passed in place of ISomeInterface** is indeed an address of ISomeInterface* pointer variable.

Okay, but what's the need for

((obj) + (sizeof((obj)-(type **)(obj))) \
     - (sizeof((obj)-(type **)(obj)))

complex expression? I see that the type checking is enforced with (obj)-(type**)(obj) subexpression. What's the need for adding and then subtracting the sizeof()? And what's the need for casting to void* before casting to void**?

I suppose the same can be done as follows:

#define COMPTR(type, obj) &IID_##type, \
(void **)(sizeof((obj)-(type**)(obj)), obj)

here the first part of comma operator would contain a sizeof() that would enforce the typecheck and evaluate to a constant, the second part would just yield the same pointer and the pointer would be cast to void**.

What can the original macro do what the one I suggest can't? What's the need for those complications?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

蓝眼睛不忧郁 2024-11-24 13:59:44

也许原作者不知道逗号运算符?这在 C/C++ 程序员中并非闻所未闻。

Maybe the original author wasn't aware of the comma operator? This is not exactly unheard of among C/C++ programmers.

梦醒时光 2024-11-24 13:59:44

也许原作者不知道函数模板。该宏请求被函数模板取代。

显然,CoCreateInstance 的第四个参数是指向某个 IID 类型的全局对象的指针,该对象属于当前的 type(COMPTR 的类型参数)。 CoCreateInstance 的第五个也是最后一个参数应该是一个 type** 指针。

相反,函数CoCreateInstance采用一个void**(是的!)指针作为其最后一个参数,该指针是通过转换假定的type**指针获得的。该转换通过 void* 作为中介,因为任何指针都可以转换为 void* 指针或从 void* 指针转换。

如果没有 COMPTR 宏中的保护,我们可以传递一个 double* 指针,甚至一个 long long(不是指针!)作为 CoCreateInstance 的第五个参数。当然,如果原作者使用 C++ 所擅长的类型安全性,那么整个混乱局面就可以避免。相反,他/她决定采用 void* 指针路线并将保护放在宏中。

这个傻瓜在做什么:sizeof 的参数是指针差异表达式 (obj)-(type**)(obj)。如果 obj 是一个 type** 指针,则为 0(作为类型 ptrdiff_t)。如果 obj 是其他内容,则该指针差异表达式的格式不正确。因此,有两种情况,obj 是一个 type** 指针,或者不是。

情况 1,obj 是一个 type** 指针:指针差异表达式有效,因此 CoCreateInstance 的最后一个参数扩展为 (void**)(void*)(obj+8-8),假设是 64 位机器。 (在 32 位机器上,+8-8 变为 +4-4。)无论机器大小如何,都会添加和减去偏移量,留下原始指针。

情况 2,obj 不是 type** 指针:指针差异表达式格式错误,因此代码无法编译。

Maybe the original author wasn't aware of function templates. This macro is begging to be replaced by a function template.

Apparently the fourth argument to CoCreateInstance is a pointer to some global object of type IID that pertains to the type (type argument to COMPTR) at hand. The fifth and final argument to CoCreateInstance is supposed to be a type** pointer.

Instead, the function CoCreateInstance takes a void** (yech!) pointer as its last argument, obtained by casting the supposed type** pointer. The cast goes through void* as an intermediary because any pointer can be cast to/from a void* pointer.

Sans the protection in that COMPTR macro, one could pass a double* pointer, or even a long long (not a pointer!) as the fifth argument to CoCreateInstance. Of course, this whole mess would have been avoided had the original author used C++ what its very good at, type safety. Instead he/she decided to go the void* pointer route and put the protection in the macro.

What the goofiness is doing: The argument to sizeof is the pointer difference expression (obj)-(type**)(obj). If obj is a type** pointer, this is 0 (as type ptrdiff_t). If obj is something else, this pointer difference expression is ill-formed. So, two cases, obj is a type** pointer, or it isn't.

Case 1, obj is a type** pointer: The pointer difference expression is valid, so the last argument to CoCreateInstance expands to (void**)(void*)(obj+8-8), assuming a 64 bit machine. (The +8-8 becomes +4-4 on a 32 bit machine.) Regardless of machine size, the offset is added and subtracted, leaving the original pointer.

Case 2, obj is not a type** pointer: The pointer difference expression is ill-formed, so the code doesn't compile.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文