Swig封装C++容器时__getitem__触发StopIteration致脚本终止求正解
解决方法
你的问题出在SWIG生成的Python包装函数无法正确处理C++层直接设置的PyExc_StopIteration错误,导致脚本异常终止。以下是两种正确的实现方式:
方式一:在Python层处理迭代终止(推荐)
通过拆分底层获取逻辑和Python层的异常抛出逻辑,避免C++层直接操作Python错误状态:
- 修改SWIG接口文件,将原
__getitem__重命名为底层调用函数,再用%pythoncode实现符合Python规范的__getitem__:
%template(vectorModelDescriptor) com_cae::physiology::vector<::IModelDescriptor*>; %extend com_cae::physiology::vector<::IModelDescriptor*> { // 底层获取元素的函数,仅返回NULL表示越界 IModelDescriptor* _getitem(unsigned int i) { if (i >= self->size()) { return NULL; } return (*self)[i]; } } // 用Python代码重写__getitem__,处理迭代终止逻辑 %pythoncode %{ def __getitem__(self, i): item = self._getitem(i) if item is None: raise StopIteration return item %}
- 原理:Python迭代器机制会自动捕获
StopIteration异常来终止迭代,将异常抛出逻辑放在Python层,完全符合Python迭代规范,不会触发SWIG的错误检测。
方式二:通过C++异常映射实现
利用SWIG的异常映射功能,将C++异常转换为Python的StopIteration:
- 包含SWIG标准异常头,添加异常映射规则,修改
__getitem__抛出C++异常:
%include <std_except.i> %template(vectorModelDescriptor) com_cae::physiology::vector<::IModelDescriptor*>; // 配置异常映射:将C++的out_of_range转换为Python的StopIteration %exception { try { $action } catch (const std::out_of_range&) { PyErr_SetNone(PyExc_StopIteration); return NULL; } } %extend com_cae::physiology::vector<::IModelDescriptor*> { IModelDescriptor* __getitem__(unsigned int i) { if (i >= self->size()) { throw std::out_of_range("Index out of bounds"); } return (*self)[i]; } }
- 原理:SWIG会自动捕获C++抛出的
std::out_of_range异常,将其转换为Python的StopIteration,Python迭代器可正确识别并终止迭代。
额外优化:实现迭代器模式(更贴合Python习惯)
如果希望容器的迭代行为完全遵循Python标准,可实现__iter__方法返回专属迭代器:
%include <std_except.i> %template(vectorModelDescriptor) com_cae::physiology::vector<::IModelDescriptor*>; // 定义迭代器类 %inline %{ class VectorModelDescriptorIterator { private: com_cae::physiology::vector<::IModelDescriptor*>* _vec; size_t _pos; public: VectorModelDescriptorIterator(com_cae::physiology::vector<::IModelDescriptor*>* vec) : _vec(vec), _pos(0) {} IModelDescriptor* __next__() { if (_pos >= _vec->size()) { throw std::out_of_range("End of iteration"); } return (*_vec)[_pos++]; } }; %} // 为vector添加__iter__方法返回迭代器 %extend com_cae::physiology::vector<::IModelDescriptor*> { VectorModelDescriptorIterator* __iter__() { return new VectorModelDescriptorIterator(self); } } // 兼容Python2,将__next__重命名为next %rename(next) VectorModelDescriptorIterator::__next__; // 配置迭代器的异常映射 %exception VectorModelDescriptorIterator::__next__ { try { $action } catch (const std::out_of_range&) { PyErr_SetNone(PyExc_StopIteration); return NULL; } }
这种方式支持for item in container:的原生遍历,也能兼容Python的各类迭代工具。
内容的提问来源于stack exchange,提问作者sgibeau
相关产品推荐
相关产品推荐

