如何在SWIG包装C++代码中向目标语言(特别是Python)添加替代构造函数

How to add an alternative constructor to the target language (specifically Python) in SWIG wrapping C++ code

本文关键字:Python 特别是 添加 构造函数 目标语言 SWIG 包装 C++ 代码      更新时间:2023-10-16

我正在使用SWIG为一些无法更改的C++代码创建Python接口。其中一个C++类有一个构造函数,它创建了一个尚未使用的部分初始化对象,必须首先对其调用初始化函数。我想在Python中通过提供一个替代构造函数来弥补这一点,该构造函数同时处理两件事(获取和初始化)。假设在C++中我有

class X {
 public:
  X() {...}
  void init(T a) {...}
  ...
};

在C++中,我必须将X实例化为

X x;
x.init(a);

在Python中,我想做

x = X(a)

我的解决方案是一个取决于目标语言和SWIG生成包装器代码的特定方式的破解:在我的.i文件中,我有

%inline %{
X* new_X(T a) {
  X* ret = new X();
  ret->init(a);
  return ret;
}
%nodefaultctor X;
class X {
 public:
  ...
  %extend {
    %pythoncode {
      def __init__(self, *args):
          this = _modulename.new_X(*args)
          try:
            self.this.append(this)
          except:
            self.this = this
    }
  }
};

这很好,但不是很令人满意:

  • 这取决于SWIG如何在内部封装构造函数
  • 它完全依赖于目标语言

这似乎是一个相对常见的用例,那么有人知道是否有标准的方法吗?

V-master当前的答案不能正常工作。但它可以工作:

%ignore X::X();
// declaration of class X, e.g. %include X.h
%extend X {
    X(T a) {
        X* newX = new X();
        newX->init(a);
        return newX;
    }
};

诚然,这看起来有点可疑,但它确实有效,本质上是SWIG文档中的一个例子。

需要注意的是:

%extend可以同时使用C和C++代码。它不会以任何方式修改底层对象——扩展只显示在Python接口中。

因此,这实际上是创建一个方法(实际上甚至不是类方法)来创建X的新实例,调用init(a)并返回它。因为语法有点像构造函数,SWIG会将其包装成这样。

您可以尝试以下操作:

%ignore X::X();
%extend X {
  X(T a) {
    init(a);
  }
};

这将隐藏默认的无参数构造函数,并添加采用T 的新构造函数

不利的一面是,如果被忽略的构造函数正在做一些事情,那么您需要将其复制到这个新构造函数中,因为您不能从同一类构造函数中调用其他构造函数(除非您使用的是C++11)