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:
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.
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.