我有一个非常庞大且成熟的C ++代码库,我正在尝试使用SWIG为其生成C#接口。我不能改变实际的C ++代码本身,但我们可以使用SWIG提供的任何方式来扩展/更新它。我遇到的问题是,如下所示编写的C ++函数会导致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#中的调用代码无法确定返回的对象是否实际上是派生类,它实际上似乎总是基类(这确实有意义)。我遇到了几个解决方案:
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 :(得分: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。它有所有细节。
答案 1 :(得分: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包装Foo
和Bar where Bar : Foo
,您现在可以尝试将Foo
转发给Bar
,如下例所示:
Foo foo = new Bar();
Bar bar = SwigHelper.CastTo<Bar>(foo, false);
答案 2 :(得分:1)
我们在界面中添加了函数以获取所需的特定类型:
// .cpp
class foo : public bar {
}
///////////// part of swig
// .i (swig)
%extend foo {
static foo* GetFoo( bar* iObj ) {
return (foo*)iObj;
}
}
这有点单调乏味,因为必须为每个班级完成,但话说再次,它可以变成一个SWIG宏。