You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用SWIG typemap(out)导出C++类,Python调用构造函数生成无效对象

问题1:为何调用MyInt构造函数会生成无效对象?这是SWIG的bug吗?

不是SWIG的bug,是你定义的%typemap(out)覆盖了构造函数的默认处理逻辑,导致Python代理对象内部状态异常。

具体原因:

  • 调用MyInt(6)时,SWIG默认流程是创建C++ MyInt实例,生成对应Python代理对象,代理对象内部持有C++实例的指针,用于后续成员方法调用(比如get())。
  • 但你写的%typemap(out) MyInt, MyInt&和%typemap(out) MyInt*会作用于所有返回对应类型的函数,包括构造函数。对于构造函数,你的typemap把返回值直接转成了Python整数,但SWIG仍会生成代理对象,此时代理对象内部没有持有C++实例指针,而是把整数6当成指针存储。
  • 调用i2.get()时,SWIG尝试把代理对象内部的"指针"(即整数6)转换成MyInt const*,类型完全不匹配,因此抛出TypeError。

问题2:能否让create方法返回原生整数,同时构造函数生成有效代理对象?

可以,只需限制%typemap(out)的应用范围,避免影响构造函数的默认处理。以下是两种简单实现方式:

方法1:调整typemap声明顺序

先导出构造函数(使用SWIG默认处理),再定义typemap,最后导出其他方法。这样typemap只会作用于之后声明的函数(比如create):

修改后的pyhello.i:

%module pyhello

%{
  #include <iostream>
  
  class MyInt
  {
    public:
      MyInt(int n) : _n(n) {}
      
      static MyInt* create(int n) { return new MyInt(n); }
    
      int get() const { return _n; } 

    private:
      int _n;
  };
%}

// 先导出构造函数,使用SWIG默认逻辑
class MyInt
{
  public:
    MyInt(int n);
};

// 定义typemap,仅对后续声明的函数生效
%typemap(out) MyInt, MyInt&
{
  std::cout << "%typemap(out) MyInt, MyInt&" << std::endl;
  $result = PyLong_FromLongLong($1.get());
}

%typemap(out) MyInt*
{
  std::cout << "%typemap(out) MyInt*" << std::endl;
  $result = PyLong_FromLongLong($1->get());
}

// 导出其他方法,应用上述typemap
class MyInt
{
  public:
    static MyInt* create(int n);
    int get() const;
};

方法2:在typemap中排除构造函数

通过$isconstructor宏判断当前函数是否为构造函数,仅对非构造函数应用类型转换:

修改后的typemap部分:

%typemap(out) MyInt, MyInt&
{
  if (!$isconstructor) {
    std::cout << "%typemap(out) MyInt, MyInt&" << std::endl;
    $result = PyLong_FromLongLong($1.get());
  } else {
    // 执行SWIG默认的构造函数处理逻辑
    $result = SWIG_NewPointerObj(SWIG_as_voidptr($1), $descriptor(MyInt), SWIG_POINTER_OWN);
  }
}

%typemap(out) MyInt*
{
  // 构造函数不会返回指针,无需判断
  std::cout << "%typemap(out) MyInt*" << std::endl;
  $result = PyLong_FromLongLong($1->get());
}

验证效果

修改后重新编译执行测试脚本,会得到如下输出:

%typemap(out) MyInt*
<class 'int'>
5
<class 'pyhello.MyInt'>
<pyhello.MyInt; proxy of <Swig Object of type 'MyInt *' at 0x...> >
6

此时i2.get()可正常返回6,i1是原生整数,i2是有效的MyInt代理对象。

内容的提问来源于stack exchange,提问作者McClain

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 20:22:11