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

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

我正在使用 SWIG 创建 Python 某些我无法更改的 C++ 代码的接口。其中一个 C++ 类 有一个构造函数,它创建一个部分初始化的对象,目前还不能使用,必须先调用一个初始化函数。我想在 Python 中通过提供一个替代构造函数来解决这个问题,该构造函数负责同时处理这两件事(获取和初始化)。假设在 C++ 中我有

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

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

X x;
x.init(a);

在Python我愿意做

x = X(a)

我的解决方案是一个 hack,它取决于目标语言和 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
    }
  }
};

效果不错,但不是很满意:

这似乎是一个比较常见的用例,所以有人知道是否有标准方法吗?

你可以试试这样:

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

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

的新构造函数

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

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 文档 here.

中的示例

需要注意的是:

%extend works with both C and C++ code. It does not modify the underlying object in any way---the extensions only show up in the Python interface.

所以这真正做的是创建一个方法(实际上甚至不是 class 方法)来创建 X 的新实例,在其上调用 init(a) 和 returns它。由于语法有点类似于构造函数,SWIG 将这样包装它。