Swig typecast to derived class?
-
12-09-2019 - |
Question
I notice that Swig provides a whole host of functions to allow for typecasting objects to their parent classes. However, in C++ one can produce a function like the following:
A * getAnObject()
{
if(someBoolean)
return (A *) new B;
else
return (A *) new C;
}
Where "A" is the parent of classes "B" and "C". One can then typecast the pointer returned into being a "B" type or "C" type at one's convenience like:
B * some_var = (B *) getAnObject();
Is there some way I can typecast an object I've received from a generic-pointer-producing function at run-time in the scripting language using the wrappers? (In my case, Lua?) I have a function that could produce one of about a hundred possible classes, and I'd like to avoid writing an enormous switch structure that I'd have to maintain in C++. At the point where I receive the generic pointer, I also have a string representation of the data type I'd like to cast it to.
Any thoughts? Thanks!
-- EDIT --
I notice that SWIG offers to generate copy constructors for all of my classes. If I had it generate those, could I do something like the following?:
var = myModule.getAnObject(); -- Function that returns an object type-cast down to a pointer of the parent class, as in the function getAnObject() above.
var = myModule.ClassThatExtendsBaseClass(var); -- A copy constructor that SWIG theoretically creates for me
and have var then be an instance of the inheriting class that knows it's an instance of the inheriting class?
Solution
I developed a solution to my problem. I'm new to lua's garbage collection, so I'm not sure if it's memory-leak-proof, but it does what I need it to do. (It's also not fool-proof -- if you pass a valid data type and an object that shouldn't be cast as that data type, bad stuff will result.)
=================================================================================
static int lua_typecast_Object_I(lua_State *L)
{
void * myDataPtr;
void ** ptrToPtr = &myDataPtr;
// Attempt to convert the first parameter to a pointer of
// the lowest parent type from which all other data types
// inherit. e.g. "Object_I"
if (!SWIG_IsOK(SWIG_ConvertPtr(L, 1, ptrToPtr, SWIGTYPE_p_Namespace1__Object_I, 0)))
{
lua_pushnumber(L, -1);
lua_pushstring(L,"Pointer conversion in typecast function failed.");
return 2;
}
const char * type_name = luaL_checkstring(L, 2);
// Search the SWIG module for a swig_type_info that contains the data
// type string that was passed as the second parameter
swig_module_info* module=SWIG_GetModule(L);
swig_type_info *type = SWIG_TypeQueryModule(module,module,type_name);
if(type == NULL)
{
lua_pushnumber(L, -2);
lua_pushstring(L,"Failed to find swig_type_info for specified type.");
return 2;
}
// Using the swig_type_info that we found, create a new object on
// the stack of the desired data type and return.
SWIG_Lua_NewPointerObj(L, myDataPtr, type, 0);
return 1;
}
=================================================================================
Hope that helps someone!
OTHER TIPS
I solved this using a typemap and a class field that holds the SWIG structure swig_type_info of the class type.
For example. Suppose you have a BaseClass that contains basic linked list functionality, yet the actual nodes can be any class derived from BaseClass. Thus you have a polymorphic linked list. In the base class I define a value "stored_swig_info" that holds the result of the call to SWIG_TypeQuery(..). I set this value during initialization. Then at runtime you can use the following:
// The typemap converts a function result from C->Lua.
%typemap(out) BaseClass* {
// stored_swig_info is set by calling SWIG_TypeQuery("DerivedClass *"), done at
// initialization, so it can be used here to read the actual type
swig_type_info* info = $1->stored_swig_info;
SWIG_NewPointerObj(L, $1, info, 0); SWIG_arg++;
};
// base class
class BaseClass {
private:
swig_type_info *stored_swig_info;
public:
BaseClass* next () { ... };
BaseClass* prev () { ... };
};
// derived class
class DerivedClass: public BaseClass {
};
And in the actual class modules the constructor does the following:
BaseClass::BaseClass () {
...
stored_swig_info = SWIG_TypeQuery("BaseClass *");
...
}
...
DerivedClass::DerivedClass () {
...
stored_swig_info = SWIG_TypeQuery("DerivedClass *");
...
}
A note on the implementation. Make sure this initialization is called after the lua module is initialized or else the SWIG typetabel is not yet filled.