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