2010-03-16 55 views
2

我有一個非常大且成熟的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#調用代碼不能或確定返回的對象實際上是一個派生類不,它實際上似乎永遠是基類(這是有道理的)。我遇到過幾種解決方案:

  1. 使用%extend SWIG關鍵字在對象上添加方法並最終調用dynamic_cast。正如我所看到的,這種方法的缺點是這需要您瞭解繼承層次結構。在我的情況下,這是相當巨大的,我認爲這是一個維修問題。
  2. 使用%factory關鍵字來提供方法和派生類型,並讓SWIG自動生成dynamic_cast代碼。這似乎是一個更好的解決方案,但首先,如果深入瞭解,它仍然需要您搜索所有可能返回的方法和所有可能的派生類型。再次,一個巨大的維護問題。我希望我有一個文檔鏈接,但我找不到。通過查看SWIG附帶的示例代碼,我發現了這個功能。
  3. 創建一個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了?任何幫助,將不勝感激。

+0

如果我可能會問,你採用了哪種解決方案?我在另一種目標語言中遇到了類似的問題...... – klickverbot 2011-01-02 21:08:57

回答

1

我們添加功能到界面獲得所需的特定類型:再次

// .cpp 
class foo : public bar { 
} 

///////////// part of swig 
// .i (swig) 
%extend foo { 
    static foo* GetFoo(bar* iObj) { 
     return (foo*)iObj; 
    } 
} 

這是一個有點乏味,beause它必須爲每類工作要做,但後來,它也可以變成一個SWIG宏。

3

默認情況下SWIG generates C# and Java code that does not support downcast爲多態返回類型。我找到了一個簡單的方法來解決這個問題,提供您的C++代碼有一種方法來識別返回的C++實例的具體類。也就是說,只有用SWIG包裝的C++ API與C#object.GetType()或Java Object.getClass()類似時,我的技術才能工作。

解決方法是添加一個C#中間類方法來實例化C++聲明它的具體類。然後,使用%typemap(out)在返回抽象類時告訴SWIG使用這個中間類方法。

這是一個非常簡潔的解釋,所以請參考我的博客文章,顯示how to generate polymorphic C# and Java that you can downcast。它有所有的細節。

2

原始帖子中的第三個解決方案在Swig 2.0中不適用,其中構造函數是私有的(因此Activator找不到構造函數)。因此必須使用不同的BindingFlags。以下是原帖中提到的第三種解決方案的通用變種:

public class SwigHelper 
{ 
    public static T CastTo<T>(object from, bool cMemoryOwn) 
    { 
     System.Reflection.MethodInfo CPtrGetter = from.GetType().GetMethod("getCPtr", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); 
     return CPtrGetter == null ? default(T) : (T) System.Activator.CreateInstance 
     (
      typeof(T), 
      System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance, 
      null, 
      new object[] { ((HandleRef) CPtrGetter.Invoke(null, new object[] { from })).Handle, cMemoryOwn }, 
      null 
     ); 
    } 
} 

給定兩個SWIG包裝FooBar where Bar : Foo,你現在可以嘗試垂頭喪氣FooBar像下面的例子:

Foo foo = new Bar(); 
Bar bar = SwigHelper.CastTo<Bar>(foo, false); 
相關問題