以下代码来自必须在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&
和{ {1}}同时强制执行编译时检查,代替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()
原始宏可以做什么我建议的不可以?这些并发症有什么需要?
答案 0 :(得分:5)
也许原作者不知道逗号运算符?这在C / C ++程序员中并非闻所未闻。
答案 1 :(得分:4)
也许原作者不知道功能模板。这个宏乞求被功能模板取代。
显然,CoCreateInstance
的第四个参数是指向IID类型的某个全局对象的指针,该对象属于手头的type
(COMPTR的类型参数)。 CoCreateInstance
的第五个也是最后一个参数应该是type**
指针。
相反,函数CoCreateInstance
将void**
(yech!)指针作为最后一个参数,通过转换假定的type**
指针获得。强制转换为void*
作为中介,因为任何指针都可以转换为void *指针。
在该COMPTR宏中没有保护,可以传递double*
指针,甚至是long long
(不是指针!)作为CoCreateInstance
的第五个参数。当然,如果原作者使用C ++非常擅长,输入安全性就会避免这种混乱。相反,他/她决定采用void *指针路线并将保护放在宏中。
愚蠢行为:sizeof
的参数是指针差异表达式(obj)-(type**)(obj)
。如果obj
是type**
指针,则为0(类型为ptrdiff_t)。如果obj
是其他内容,则此指针差异表达式是错误的。因此,有两种情况,obj
是type**
指针,或者不是。{/ p>
情况1,obj
是type**
指针:指针差异表达式有效,因此CoCreateInstance
的最后一个参数扩展为(void**)(void*)(obj+8-8)
,假设64位机器。 (+ 8-8在32位机器上变为+ 4-4。)无论机器大小如何,都会添加和减去偏移量,留下原始指针。
情况2,obj
不是type**
指针:指针差异表达式格式错误,因此代码无法编译。