使用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
相关产品推荐
相关产品推荐

