如何使用SWIG生成的接口在C#中正确地向下转换? 我有一个非常大的成熟的C++代码库,我想用Sigg来生成一个C接口。我不能改变实际的C++代码本身,但是我们可以使用任何Sigg提供的方式来扩展/更新它。我正面临一个问题,其中一个C++函数,如下面所写的,在C语言中引起了问题。

如何使用SWIG生成的接口在C#中正确地向下转换? 我有一个非常大的成熟的C++代码库,我想用Sigg来生成一个C接口。我不能改变实际的C++代码本身,但是我们可以使用任何Sigg提供的方式来扩展/更新它。我正面临一个问题,其中一个C++函数,如下面所写的,在C语言中引起了问题。,c#,c++,swig,downcast,C#,C++,Swig,Downcast,调用方可能会执行以下操作: 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为“下一

调用方可能会执行以下操作:

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自动生成动态转换代码。这似乎是一个比第一个更好的解决方案,但是在深入研究之后,它仍然要求您查找所有方法以及它可能返回的所有可能的派生类型。同样,一个巨大的维护问题。我希望我有一个文档链接,但我找不到。通过查看SWIG附带的示例代码,我发现了这个功能
  • 创建一个C#方法来创建派生对象的实例,并将cPtr传输到新实例。虽然我认为这个笨拙,但它确实有效。请参见下面的示例
  • 公共静态对象castTo(对象fromObj,类型toType) { object retval=null; 基类fromObj2=作为基类的fromObj; HandleRef hr=BaseClass.getCPtr(fromObj2); IntPtr cPtr=hr.Handle; 对象toObj=Activator.CreateInstance(toType,cPtr,false); //确保它确实是我们所认为的 if(fromObj.GetType().IsInstanceOfType(toObj)) { 返回toObj; } 返回返回; }
    这些真的是选择吗?如果我不愿意深入研究所有现有的函数和类派生,那么我只剩下#3?如有任何帮助,我们将不胜感激。

    我们在界面中添加了函数,以获得所需的特定类型:

    // .cpp
    class foo : public bar {
    }
    
    ///////////// part of swig
    // .i (swig)
    %extend foo {
        static foo* GetFoo( bar* iObj ) {
             return (foo*)iObj;
       }
    }
    
    这有点乏味,因为它必须对每个类都执行,但同样,它可以转换为SWIG宏。

    默认情况下,对于多态返回类型。我找到了一个直接的解决方法,<>强>提供了C++代码,它有一个方法来识别返回的C++实例< /强>的具体类。也就是说,只有当你用SWIG包装的C++ API有类似于C对象的.gType()或java对象.GETCype().< /P>时,我的技术才会起作用。

    解决方案是添加一个C类中间类方法来实例化C++所说的具体类。然后,使用%typemap(out)告诉SWIG在返回抽象类时使用这个中间类方法


    这是一个非常简洁的解释,所以请参考我的博客文章。它包含了所有的细节。

    原始文章中的第三个解决方案在Swig 2.0中不起作用,其中构造函数是私有的(因此激活器找不到构造函数)。因此,必须使用不同的BindingFlag。以下是原始帖子中提到的第三种解决方案的通用变体:

    公共类开关
    {
    公共静态T CastTo(对象来自,bool cMemoryOwn)
    {
    System.Reflection.MethodInfo CPtrGetter=from.GetType().GetMethod(“getCPtr”,System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
    返回CPtrGetter==null?默认值(T):(T)System.Activator.CreateInstance
    (
    类型(T),
    System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance,
    无效的
    新对象[]{((HandleRef)CPtrGetter.Invoke(null,新对象[]{from})).Handle,cMemoryOwn},
    无效的
    );
    }
    }
    
    给定两个SWIG包装器
    Foo
    Bar where Bar:Foo
    ,您现在可以尝试将
    Foo
    向下转换为
    Bar
    ,如以下示例所示:

    Foo-Foo=newbar();
    Bar Bar=开关。CastTo(foo,false);
    
    如果我可以问的话,您选择了哪种解决方案?我在另一种目标语言中面临着一个非常类似的问题… 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; }
    // .cpp
    class foo : public bar {
    }
    
    ///////////// part of swig
    // .i (swig)
    %extend foo {
        static foo* GetFoo( bar* iObj ) {
             return (foo*)iObj;
       }
    }