2011-06-29 6 views
9

次のコードは、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()の最後の2つのパラメータは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) 

コンマ演算子の最初の部分はです。TypeCheckを施行し、定数に評価されますsizeof()を含むことになり、2番目の部分はちょうどもたらすであろう同じポインタとポインタはvoid**にキャストされます。

私の提案したマクロは、元のマクロで何ができますか?これらの合併症の必要性は何ですか?

+0

これが悪いコードです。私は一日中これに苦しんできたと思いますか? – orlp

+0

@ nightcracker:いいえ、私はそのコードを維持していません。私はそれを見つけて、それがどのように機能しているのか興味がありました。 – sharptooth

+2

タイトルのハードコアの場合は+1、タグにする必要があります! –

答えて

5

元の作者がカンマ演算子を認識していない可能性がありますか?これは、C/C++プログラマーの間ではあまり知られていません。

+0

驚いたことに私はコンマ演算子を知っていたCプログラマーはほとんどいませんでした。 – Mike

+0

一部のプロジェクトには、カンマ演算子の使用を禁止するプログラミング標準があります。 禁止された構文を使用するマクロ!天は禁じられている!曖昧な構文を使用し、コンマ演算子または恒等式演算子を使用するよりも、独創的な方法で加算的なゼロ(または乗法的なもの)を書く方がはるかに優れています。

4

元の著者は、機能テンプレートを認識していない可能性があります。このマクロは、関数テンプレートに置き換えられています。

CoCreateInstanceの4番目の引数は、明らかにtype(COMPTRへの型引数)に関連するタイプIIDのグローバルオブジェクトへのポインタです。 CoCreateInstanceの5番目の最後の引数は、type**ポインタになっています。

代わりに、CoCreateInstanceは、ポインタをキャストして得た最後の引数としてvoid**(yech!)ポインタをとります。キャストはvoid*を媒介として実行されるため、どのポインタもvoid *ポインタとの間でキャストできます。

COMPTRマクロ内の保護ではCoCreateInstanceの第5引数にdouble*ポインタ、またはlong long(ポインタではありません)を渡すことができます。もちろん、元の作者がC++を使っていたら、この完全な混乱は避けられました。代わりに、彼/彼女はvoid *ポインタルートに行き、マクロに保護を置くことに決めました。

何をしているのか:sizeofの引数は、ポインタの違いの表現(obj)-(type**)(obj)です。objtype**ポインターの場合、これは0(ptrdiff_tタイプ)です。 objが何か他のものである場合、このポインタの差の式は不正です。したがって、2つの場合、objtype**ポインターであるか、そうではありません。ポインタの差分式が有効であるので、CoCreateInstanceの最後の引数は、64ビットマシンを仮定すると、(void**)(void*)(obj+8-8)に展開:

ケース1は、objtype**ポインタです。 (+ 8-8は32ビットマシンで+ 4-4になります。)マシンのサイズに関係なく、オフセットは加算され、減算され、元のポインタが残ります。

ケース2のobjtype**ではありませんポインタ:ポインタの差分式が正しくないため、コードがコンパイルされません。

+3

C言語にはテンプレートがなく、CとC++の両方でコードをコンパイルすることに注意してください。また、 'CoCreateInstance()'は、どのクラスにも対応できないグローバルクラスファクトリとして機能するWin32関数です。それはまさに 'void ** 'を受け入れる理由です。 – sharptooth

+0

@sharptooth:あなたはダウンボートしましたか?このマクロがどのくらい正確に機能しているかを説明したことに注意してください。 –

+0

いいえ、私はdownvoteしなかった、私はちょうどそれらの2つのかなり重要な点を明確に - テンプレートはここで使用することはできませんと '空の** '理由がある。 – sharptooth

関連する問題