我有一个非常庞大且成熟的 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# 中的调用代码无法确定返回的对象是否实际上是派生类,它实际上似乎始终是基类(这确实有意义)。我遇到了几种解决方案:
- 使用 %extend SWIG 关键字在对象上添加方法并最终调用 dynamic_cast。在我看来,这种方法的缺点是需要您了解继承层次结构。就我而言,它相当大,我认为这是一个维护问题。
- 使用 %factory 关键字来提供方法和派生类型,并让 SWIG 自动生成 dynamic_cast 代码。这似乎是一个比第一个更好的解决方案,但是在更深入的研究中,它仍然需要您寻找它可能返回的所有方法和所有可能的派生类型。再次,一个巨大的维护问题。我希望我有一个文档链接,但我找不到。我通过查看 SWIG 附带的示例代码发现了这个功能。
- 创建 C# 方法以创建派生对象的实例并将 cPtr 传输到新实例。虽然我认为这很笨拙,但它确实有效。请参阅下面的示例。
公共静态对象 castTo(object fromObj, Type toType) { 对象 retval = null; BaseClass fromObj2 = fromObj as BaseClass; HandleRef hr = BaseClass.getCPtr(fromObj2); IntPtr cPtr = hr.Handle; 对象 toObj = Activator.CreateInstance(toType, cPtr, false); // 确保它实际上是我们认为的那样 if (fromObj.GetType().IsInstanceOfType(toObj)) { 返回对象; } 返回 retval; }
这些真的是选择吗?如果我不愿意挖掘所有现有的函数和类派生,那么我只剩下#3了吗?任何帮助,将不胜感激。