我有一個非常大且成熟的C++代碼庫,我試圖使用SWIG來生成一個C#接口。我不能改變實際的C++代碼本身,但我們可以使用SWIG提供的任何擴展/更新方式。我面臨的一個問題是,如下所述編寫的C++函數在C#中導致問題。如何正確使用SWIG生成的接口在C#中下傳?
A* SomeClass::next(A*)
呼叫者可能做這樣的事情:
A* acurr = 0;
while((acurr = sc->next(acurr)) != 0){
if(acurr isoftype B){
B* b = (B*)a;
...do some stuff with b..
}
elseif(acurr isoftype C)
...
}
從本質上講,通過元素的容器,這取決於他們的真實類型,做不同的東西迭代。該SWIG生成的C#層的「下一個」功能遺憾的是以下幾點:
return new A();
所以在C#調用代碼不能或確定返回的對象實際上是一個派生類不,它實際上似乎永遠是基類(這是有道理的)。我遇到過幾種解決方案:
- 使用%extend SWIG關鍵字在對象上添加方法並最終調用dynamic_cast。正如我所看到的,這種方法的缺點是這需要您瞭解繼承層次結構。在我的情況下,這是相當巨大的,我認爲這是一個維修問題。
- 使用%factory關鍵字來提供方法和派生類型,並讓SWIG自動生成dynamic_cast代碼。這似乎是一個更好的解決方案,但首先,如果深入瞭解,它仍然需要您搜索所有可能返回的方法和所有可能的派生類型。再次,一個巨大的維護問題。我希望我有一個文檔鏈接,但我找不到。通過查看SWIG附帶的示例代碼,我發現了這個功能。
- 創建一個C#方法來創建派生對象的實例並將cPtr傳遞給新實例。雖然我認爲這很笨拙,但確實有效。看下面的例子。
public static object castTo(object fromObj, Type toType) { object retval = null; BaseClass fromObj2 = fromObj as BaseClass; HandleRef hr = BaseClass.getCPtr(fromObj2); IntPtr cPtr = hr.Handle; object toObj = Activator.CreateInstance(toType, cPtr, false); // make sure it actually is what we think it is if (fromObj.GetType().IsInstanceOfType(toObj)) { return toObj; } return retval; }
這些真的是選擇嗎?如果我不願意挖掘所有現有的功能和類派生,那麼我就剩下#3了?任何幫助,將不勝感激。
如果我可能會問,你採用了哪種解決方案?我在另一種目標語言中遇到了類似的問題...... – klickverbot 2011-01-02 21:08:57