user3453991
user3453991

Reputation: 31

Using SWIG to pass C++ object pointers to Python, than back to C++ again

I'm using SWIG to wrap 2 C++ objects, and I am embedding the Python interpreter in my application (i.e. calling PyInitialize() etc myself).

The first object is a wrapper for some application data. The second is a "helper" object, also written in C++, which can perform certain operation based on what it finds in the data object.

The python script decides when/how/if to invoke the helper object.

So I pass a pointer to my C++ object to SWIG/Python thus:

swig_type_info *ty = SWIG_MangledTypeQuery("_p_MyDataObject");
if(ty == NULL)
 {
    Py_Finalize();
     return false;
 }

PyObject *data_obj = SWIG_NewPointerObj(PointerToMyDataObject, ty, 0);
if(data_obj == NULL)
{
     Py_Finalize();
     return false;
}

ty = SWIG_MangledTypeQuery("_p_MyHelperObject");
 if(ty == NULL)
{
     Py_Finalize();
    return false;
 }

PyObject *helper_obj = SWIG_NewPointerObj(PointerToMyHelperObject, ty, 0);
if(helper_obj == NULL)
{
    Py_Finalize();
    return false;
}
PyTuple_SetItem(pArgs, 0, data_obj);
PyTuple_SetItem(pArgs, 1, helper_obj);
PyObject *pValue = PyObject_CallObject(pFunc, pArgs);
if(pValue == NULL)
{
     Py_Finalize();
     return false;
}

In Python, we see something like:

def go(dataobj, helperobj):
    ## if conditions are right....
    helperobj.helpme(dataobj)

Now, this largely works except for one thing. In my C++ code when I am preparing my arguments to pass on to the Python script, I observe the pointer value of PointerToMyDataObject.

When I set a breakpoint in the C++ implementation of helperobj.helpme(), I see that the memory address is different, though it seems to be a pointer to a valid instance of MyDataObject.

This is important to me, as "MyDataObject" is in fact a base class for a few possible derived classes. My helper object wants to perform an appropriate (determined by context) dynamic cast on the pointer it receives to point at the appropriate derived class. That's failing for what I think are obvious reasons now.

I've read some things about "shadow" objects in SWIG, which only adds to my confusion (apologies for my tiny brain :-P)

So, is SWIG making a copy of my object for some reason, and then passing a pointer to the copy? If it is, then I can understand why my assumptions about dynamic casts won't work.

I Tried to add this as a comment, but struggled with formatting, so..... more insight follows: The problem has to do with pass-by-reference. Notice I have 2 implementations of the virtual method helpMe():

bool MyHelperObject::helpMe(MyDataObject mydata_obj)
{
    return common_code(&mydata_obj);
}
bool MyHelperObject::helpMe(MyDataObject *mydata_obj)
{
    return common_code(mydata_obj);
}

Although I provided python with a pointer, it is calling the pass-by-reference version. This explains why I'm getting different pointer values. But what can I do to force a call on the version that takes a pointer argument?

Upvotes: 3

Views: 1603

Answers (1)

Based on what you've shown I think you want to make sure SWIG only gets to see the pointer version of helpMe. The non-pointer version will be creating a temporary copy and then passing that into the function and it sounds like that isn't what you want.

SWIG will have a hard time picking which version to use since it abstracts the pointer concept slightly to match Python better.

You can hide the non-pointer version from SWIG with %ignore before the declaration or %import that shows it to SWIG in your interface file:

%ignore MyHelperObject::helpMe(MyDataObject mydata_obj)
%import "some.h"

Upvotes: 1

Related Questions