How do I invoke a method on a C++ class pointer with swig wrappers?

I'm using SWIG to wrap C++ code for use within a Python testing framework. My problem is that I'm getting a pointer to an instance of a class that I need to then invoke methods on. Eg, within my swig file example.i:

iExample* getMyClassInstance();

...

class iExample
{
    public:
        virtual void somePureVirtualMethod() = 0;
// ...
};

Now, in python, if I had the class, I could just call the method

myClassInstance.somePureVirtualMethod()

However, I don't actually have an instance of the class, of course. I have an opaque pointer generated from SWIG. How do I use it? Obviously in Python I can't do

myClassInstancePtr = example.getMyClassInstance()
myClassInstancePtr->somePureVirtualMethod()

I tried using cpointer.i or pointer.i in swig to generate pointer functions, but that's no good, because it's trying to create copies of the class. This can't even compile with an interface with pure virtual methods, and even if I wasn't using pure virtual methods, I don't want to create a copy of the class, I just want to invoke something on it!

SWIG can handle this just fine. Make sure you define the interface in SWIG and then it won't be opaque. Here's a working example:

%module x

%inline %{

// Define the interface.
struct iExample
{
    virtual int somePureVirtualMethod() = 0;
};

iExample* getMyClassInstance();

%}

// Implementation, not exposed to Python
%{
struct Internal : public iExample
{
    int somePureVirtualMethod() { return 5; }
};
iExample* getMyClassInstance() { return new Internal(); }
%}

Demo:

>>> import x
>>> i = x.getMyClassInstance()
>>> i.somePureVirtualMethod()
5

However, this implementation will leak an Internal Instance. You may want to implement a way to free it automatically. One way is to use%newobjectand define a virtual destructor. Python will delete the object when there are no more references to it.

%module x

%newobject getMyClassInstance;

%inline %{
struct iExample
{
    virtual ~iExample() {};
    virtual int somePureVirtualMethod() = 0;
};
iExample* getMyClassInstance();
%}

// Implementation
%{
#include <iostream>
struct Internal : public iExample
{
    int somePureVirtualMethod() { return 5; }
    ~Internal() { std::cout << "destroyed" << std::endl; }
};
iExample* getMyClassInstance() { return new Internal(); }
%}

Demo:

>>> import x
>>> i = x.getMyClassInstance()
>>> i.somePureVirtualMethod()
5
>>> i=2       # reassign i
destroyed     # garbage-collected

The simplest answer I've found is to edit your example.i to add in some helper functions to do dereferencing. In your swig file example.i:

{%
...
// Helper function to dereference pointers within python
template <typename T>
T& dereference(T* ptr)
{
    return *ptr;
}
...  
%}
...
// Make every version of the templated functions we'll need
template <typename T> T& dereference(T* ptr);
%template(dereferencePtr_iExample) dereference<iExample>;

Now in python

myClassInstance = example.dereferencePtr_iExample(example.getMyClassInstance())
myClassInstance.somePureVirtualMethod()

I imagine this method should work generically for other languages like perl as well, and you don't have to screw around with SWIG typemaps.

Tags: python, swig
What Others Are Reading