Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
387 views
in Technique[技术] by (71.8m points)

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

I've got a very large and mature C++ code base that I'm trying to use SWIG on to generate a C# interface for. I cannot change the actual C++ code itself but we can use whatever SWIG offers in the way of extending/updating it. I'm facing an issue where a C++ function that is written as below is causing issues in C#.

A* SomeClass::next(A*)

The caller might do something like:

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

Essentially, iterating through a container of elements that, depending on their true type, does something different. The SWIG generated C# layer for the "next" function unfortunately does the following:

return new A();

So the calling code in C# cannot determine if the returned object is actually a derived class or not, it actually appears to always be the base class (which does make sense). I've come across several solutions:

  1. Use the %extend SWIG keyword to add a method on an object and ultimately call dynamic_cast. The downside to this approach, as I see it, is that this requires you to know the inheritance hierarchy. In my case it is rather huge and I see this is as a maintenance issue.
  2. Use the %factory keyword to supply the method and the derived types and have SWIG automatically generate the dynamic_cast code. This appears to be a better solution that the first, however upon a deeper look it still requires you to hunt down all the methods and all the possible derived types it could return. Again, a huge maintenance issue. I wish I had a doc link for this but I can't find one. I found out about this functionality by looking through the example code that comes with SWIG.
  3. Create a C# method to create an instance of the derived object and transfer the cPtr to the new instance. While I consider this clumsy, it does work. See an example below.
    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;
    }

Are these really the options? And if I'm not willing to dig through all the existing functions and class derivations, then I'm left with #3? Any help would be appreciated.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...