2014-07-15 65 views
1

我試圖把接口指針到不同類型的着色器在這樣的載體:鑄造的DirectX接口

std::vector<ID3D11DeviceChild*> shaders; 
ID3D11VertexShader* VS; 
// ... instantiate and init VS 
shaders.push_back(static_cast<ID3D11DeviceChild*>(VS)); 
// and when VS is needed again: 
ID3D11VertexShader* VS = static_cast<ID3D11VertexShader*>(shaders[i]); 
// use VS here 

我的問題是這種方法的類型轉換COM接口是否安全。我知道它適用於正常的多態類繼承,但我不確定COM。

回答

2

首先,我建議存儲智能指針代替原料所屬指針 STL容器內等std::vector,例如

std::vector<CComPtr<ID3D11DeviceChild>> shaders; 

使用原始擁有STL容器內指針是泄漏,潛在的錯誤和異常不安全代碼(觀察原始指針是細,雖然)的來源。

向下轉型從ID3D11DeviceChild*ID3D11VertexShader*,可以考慮使用QueryInterface()(再次使用ATL智能指針像CComPtr簡化了代碼):

CComPtr<ID3D11VertexShader> spVertexShader; 
HRESULT hr = (shaders[i])->QueryInterface(IID_PPV_ARGS(&spVertexShader)); 
if (FAILED(hr)) 
    ... 

PS

除非我失蹤因爲ID3D11VertexShader是派生類ID3D11DeviceChild,所以您不需要static_cast位置:

shaders.push_back(static_cast<ID3D11DeviceChild*>(VS)); 

這應該是罰款:

shaders.push_back(VS); 

註上CAdapt

請注意,您可能需要使用CAdaptstd::vector

std::vector<CAdapt<CComPtr<ID3D11DeviceChild>>> shaders; 

一個符合C++ 11 STL實現應該要求CAdapt,但我認爲這是必要的,至少有VS2008和VS2010。
我不知道有關Visual Studio的更現代版本,但似乎他們固定的,例如:
你可能需要閱讀的段落標題爲this Visual C++ blog post「耐超載地址的運營商」

+0

謝謝C64先生。我想我需要閱讀更多的COM接口,以找出爲什麼c + +鑄造將無法正常工作。 – rashmatash

1

ID3D11VertexShaderID3D11DeviceChild繼承,所以你甚至不需要使用static_cast<ID3D11DeviceChild*>推它們爲向量。向下轉型矢量元素着色器更安全的方法是使用QueryInterface

CComPtr<ID3D11VertexShader> vs; 
if (S_OK == shaders[i]->QueryInterface(IID_ID3D11VertexShader, 
     reinterpret_cast<void**>(&vs))) 
{ 
    // Use vs here 
} 
1

它是安全的,只要你絕對肯定矢量只包含ID3D11VertexShader,在這種情況下,你應該只用它來代替的ID3D11DeviceChild

如果你打算包含接口指針的混合載體,每一個共同的祖先ID3D11DeviceChild,你最好還是依靠QueryInterface,不管是存儲中以及從矢量檢索。

請記住,正確的代碼比快速代碼更好,至少在開始時。

當您的個人資料,如果,且僅當你發現QI是一個瓶頸,那麼你應該做的事,但我建議你使用結構的載體,它具有一個指向ID3D11DeviceChild和您存儲哪種接口指針的接口標識符(IID),所以你可以在運行時檢查:

struct DeviceChildPtr { 
    ID3D11DeviceChild *ptr; 
    REFIID riid; 
}; 

std::vector<DeviceChildPtr> shaders; 
// ... instantiate, initialize ... 
DeviceChildPtr shader; 
shader.ptr = static_cast<ID3D11DeviceChild*>(VS); 
shader.iid = IID_ID3D11VertexShader; 
shaders.push_back(static_cast<ID3D11DeviceChild*>(shader)); 

// ... 

DeviceChildPtr shader = shaders[i]; 
// If you only ever use global IIDs, you may replace IsEqualIID with == 
// This is like a half-baked QueryInterface that only checks for a specific IID 
if (IsEqualIID(shader.riid, IID_ID3D11VertexShader) { 
    ID3D11VertexShader* VS = static_cast<ID3D11VertexShader*>(shader.ptr); 
    // ... use ... 
} 

如果你經過這條路,記得要評論爲什麼(性能?),以及如何(鑄造,安全通過運行時接口類型檢查)你這樣做,而不是使用QueryInterface

我的猜測是,由於內部DX本身很可能會使用QueryInterface,所以在這裏不應該找到瓶頸。

+0

謝謝Paulo。我確實打算在同一個向量數組中使用不同類型的着色器。所以使用查詢接口是解決方案。我仍然不明白爲什麼正常的C++鑄造不起作用。 – rashmatash

+0

正如我所展示的那樣,只要您存儲的界面指針的類型與您在使用該類型時所用的類型相同,就會起作用。如果它是不同的類型,它不會在沒有'QueryInterface'的情況下(可靠地)工作,因爲你沒有任何其他的運行時信息。例如。 'IBase','IDerived1:IBase'和另外1個方法,'IDerived2:IBase'和2個額外的方法;如果你將'IDerived1'轉換爲'IDerived2'(不檢查)並且調用第一個額外方法(假設它們具有不兼容的簽名)或調用第二個額外方法,會發生什麼? – acelent

+0

太好了。我現在知道了。謝謝! – rashmatash