Следующий код взят из существующего приложения, которое должно быть скомпилировано как на 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 **
.
Что может делать исходный макрос, а тот, который я предлагаю, не может? Зачем нужны эти осложнения?