开发者

Please explain this hardcore macro that does casting and typechecking

开发者 https://www.devze.com 2023-03-15 23:28 出处:网络
The following code is from an existing application that has to be compiled in both C and C++. There\'s a macro:

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号