I have C++ code with a class wrapped with swig. I cannot modify the code or the wrapping. In python, I have, using ctypes, a pointer to an instance of said C++ class. How can I create a swig wrapper around this pointer?
I know that swig objects hold a 'this' attribute which internally points to the wrapped object, but I couldn't find a way to set it to the pointer that I have on hand.
Thanks for the help!
You can do this, but it's rather a lot of work and it would be much simpler to fix the underlying problem of making either the ctypes or the SWIG interface complete and usable than force interchangeability. (It's also worth noting that it's easier to create a ctypes object from a SWIG one than it is to do what you're trying to do, which is to create a SWIG object from a ctypes one).
To illustrate this I've created the following header file that we'll wrap with SWIG:
Then I wrapped it with the following interface:
I also added a test function for us to call from ctypes, that isn't SWIG wrapped:
Your guess about this
this
attribute of SWIG wrapped classes is a good starting point, but it's not as simple as just changing that - the type of the object you slot in has to match what SWIG expects. It's more than just a pointer represented as an int:If we inspect the source code that SWIG generates we can see that there is a function which takes a pointer, some type information and creates these objects for us:
Let's ignore the fact that
SWIGRUNTIME
is defined asstatic
by default for now, to get us started experimenting with this runtime we can redefine it toextern
. Later we'll look at workarounds for when we can't do that.So our goal is to take the output of the "ctypes only" function and pass it, via more ctypes calls into
SwigPyObject_New
to create something we can swap around for with the this attribute of our SWIG module.In order to call that we'd normally call
SWIG_TypeQuery
to lookup the correctswig_type_info
to use. However this is actually a macro, which expands to pass in some static variables that are always static. So instead we'll be using this function:(with the same
SWIGRUNTIME
proviso).At this point we've got enough that we could swap the this attribute of a surrogate object and be done if we were able to construct donors. (Although that would leak). There are two ways we can make this nicer:
Monkey patch
__init__
insidetest.Foo
to work. This is best if you really have%nodefaultctor
inside the SWIG interface you don't want to recompile:Create a new class that only has an
__init__
which sets thethis
attribute before modifying the__class__
attribute and use that instead:this option makes most sense when you don't want to break
test.Foo
's existing__init__
implementation.With that said we can now achieve our initial goal with something like this:
This all compiles and works with:
And gives us:
To work around the problem of having
SWIGRUNTIME
defined asstatic
you'll need to do one more step. Either use debug symbols or reverse engineer the SWIG binary module you've got but can't modify to find the addresses of the two functions we need that aren't exported relative to an exported symbol. You can then use those to construct ctypes function pointers instead of looking them up by name. Of course it would be easier to buy/find/re-write the SWIG module, or add the missing features to the ctypes interface probably.(Finally it's worth noting that although it doesn't seem to apply here if SWIG runs with
-builtin
some substantial changes will need to be made for this answer to work).