如何使用SWIG包装调用C++类指针的方法?

3 投票
2 回答
1498 浏览
提问于 2025-04-17 22:01

我正在使用SWIG来包装C++代码,以便在Python测试框架中使用。我的问题是,我得到了一个指向类实例的指针,但我需要在这个实例上调用方法。例如,在我的swig文件example.i中:

iExample* getMyClassInstance();

...

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

现在,在Python中,如果我有这个类,我可以直接调用方法

myClassInstance.somePureVirtualMethod()

但是,实际上我并没有这个类的实例。我只有一个由SWIG生成的不透明指针。我该怎么用它呢?显然在Python中,我不能这样做

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

我尝试在swig中使用cpointer.i或pointer.i来生成指针函数,但这没用,因为它试图创建类的副本。这甚至无法与包含纯虚方法的接口编译,即使我不使用纯虚方法,我也不想创建类的副本,我只是想在上面调用一些方法!

2 个回答

1

我找到的最简单的解决办法是修改你的example.i文件,添加一些辅助函数来进行解引用。在你的swig文件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>;

现在在Python中

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

我想这个方法也应该适用于其他语言,比如Perl,而且你不需要去折腾SWIG的类型映射。

2

SWIG可以很好地处理这个问题。确保你在SWIG中定义接口,这样它就不会变得复杂难懂。下面是一个可以运行的例子:

%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(); }
%}

演示:

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

不过,这种实现会导致一个内部实例的内存泄漏。你可能需要实现一种方法来自动释放它。一个方法是使用%newobject并定义一个虚拟析构函数。当没有更多引用指向这个对象时,Python会自动删除它。

%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(); }
%}

演示:

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

撰写回答