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))))开发者_Go百科
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?
Maybe the original author wasn't aware of the comma operator? This is not exactly unheard of among C/C++ programmers.
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.
加载中,请稍侯......
精彩评论