【今更だが】 CComPtrのoperator&でassertで落ちる場合は read目的なら .p()を使うといいぞ
はじめに
なぜいまさらCOMなのか? と言われると DirectXを触っているからである
CComPtrとは?
COMオブジェクトを扱いやすいようにした テンプレート。 いわゆるスマートポインタ
COMは、shared_ptrのような参照カウント方式のスマートポインタだが、shared_ptrと明らかに違うのは 参照カウントのメンバとメソッドが、shared_ptrの場合は shared_ptrが持っているが COMの場合は オブジェクトのベースクラス IUnknownオブジェクトが持っている点である ゆえに COMオブジェクトを shared_ptrで運用するのは あまり効果的でない
boost::intrusive_ptr は、オブジェクトに参照カウントを保持するスマートポインタに対応しているので CComPtrにかえて使うことも出来る
基本的には
void intrusive_ptr_add_ref(IUnknown* p) { p->AddRef(); } void intrusive_ptr_release(IUnknown* p) { p->Release(); }
のように、参照カウンタアクセス関数を置き換えれば良い
CComPtrを使う
基本的には、今まで生ポインタで扱っていたものを置き換えればよい
ID3D11Device *device; ID3D11DeviceContext *context; D3D11CreateDevice( ほげほげ, &device, ほげ, &context ) ... context->Release(); device->Release();
を
CComPtr<ID3D11Device> device; CComPtr<ID3D11DeviceContext> context; D3D11CreateDevice( ほげほげ, &device, ほげ, &context )
と、するだけでよい。
変わったのは CComPtr
の2点。
CComPtrでは、値の代入時に Addref()が呼ばれ参照カウントをインクリメントし スコープから消えると Release()が呼び出され参照カウントがデクリメントされる ので、Releaseをする必要がない。
たとえ例外等でRelease呼ぶ前にスコープが消滅しても Releaseが呼ばれるようになった とても安全!
もちろんこの後 device->ほげ と、通常のポインタと同じように使うことが出来る
operator&問題
上記の CreateDeviceでは、新規にオブジェクトを作成したので問題が生じなかったが たとえば オブジェクトのポインタを必要とする時は問題が発生する
通常は、元々中身はCOMオブジェクトへのポインタであり、Create時以外は ポインタのポインタを使うことがないのだが DirectXでいえば、バッファー、テクスチャ配列、レンダーターゲット配列等で ポインタのポインタが必要となるケースが有る たとえば
ID3D11Buffer *vertexbuffer; ... device->CreateBuffer(&bd, &InitDatta, &vertexbuffer); ... context->IASetVertexBuffers(0, 1, &vertexbuffer, &stride, &offset);
を愚直にCComPtrにすると
CComPtr<ID3D11Buffer> vertexbuffer; ... device->CreateBuffer(&bd, &InitDatta, &vertexbuffer); ... context->IASetVertexBuffers(0, 1, &vertexbuffer, &stride, &offset);
となるが、device->CreateBuffer の部分で、DebugAssertion Failed! が発生する
コードを追っかけると
T** operator&() throw() { ATLASSERT(p==NULL); return &p; }
CComPtrでアドレスを取る時(operator&)は、内部ポインタが 非NULL時は Assertで落ちるようになっている
おそらく、内部ポインタが存在する時に Create等でポインタを書き換えると元のポインタがリークするので そのための警告と思えるが 今回の IASetVertexBuffersは、ポインタを書き換える事はない。単純な参照なので Assertを突破したい
が、スマートポインタ等でよくある get()等がみつからない この場合は ポインタの内部メンバ pを直接参照するといい
CComPtr<ID3D11Buffer> vertexbuffer; ... device->CreateBuffer(&bd, &InitDatta, &vertexbuffer.p); ... context->IASetVertexBuffers(0, 1, &vertexbuffer, &stride, &offset);
これで問題ないコードができた