How to properly downcast in C# with a SWIG generated interface?

瘦欲@ 提交于 2019-11-29 08:32:14

By default SWIG generates C# and Java code that does not support downcast for polymorphic return types. I found a straightforward way to solve this, provided your C++ code has a way to identify the concrete class of the C++ instances that are returned. That is, my technique will work only if the C++ API you are wrapping with SWIG has something similar to C# object.GetType() or Java Object.getClass().

The solution is to add a C# intermediate class method to instantiate the concrete class that the C++ says it is. Then, use %typemap(out) to tell SWIG to use this intermediate class method when returning the abstract classes.

That's an awfully terse explanation, so refer to my blog article that shows how to generate polymorphic C# and Java that you can downcast. It has all the details.

The third solution in the original post does not work in Swig 2.0 where constructors are private (thus the Activator cannot find the constructor). Therefore different BindingFlags have to be used. Here is a generic variant of the third solution mentioned in the original post:

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
        );
    }
}

Given two SWIG wrappers Foo and Bar where Bar : Foo, you can now try to downcast Foo to Bar like in the following example:

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

We added functions to the interface to get the specific type needed:

// .cpp
class foo : public bar {
}

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

It is a bit tedious, beause it had to be done for every class, but then again, it could be turned into a SWIG macro.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!