[英]How to extend c++ class in python with operator [], using swig
[英]How to extend a templated c++ class in python with SWIG to allow the [] operator
我有一个使用标准向量类的模板化 c++ 数组类:
#include <vector>
#include <string>
using namespace std;
template<typename T>
class Array1D{
private:
vector<T> data_;
int xsize_;
public:
Array1D(): xsize_(0) {};
// creates vector of size nx and sets each element to t
Array1D(const int& nx, const T& t): xsize_(nx) {
data_.resize(xsize_, t);
}
T& operator()(int i) {return data_[i];}
T& operator[](int i) {return data_[i];}
};
我的 SWIG 接口文件看起来像
%module test
%{
#define SWIG_FILE_WITH_INIT
#include "test.h"
%}
%include "std_vector.i"
// Array 1D Typemaps
// typemaps for standard vector<double>
namespace std{
%template(DoubleVector) vector<double>;
%template(IntVector) vector<int>;
}
%include "test.h"
%template(intArray1D) Array1D<int>;
%template(doubleArray1D) Array1D<double>;
%rename(__getitem__) operator[];
%extend Array1D<T>{
T& __getitem__(int i) {
return (*self)[i];
}
}
制作模块并在python中创建一个Array1D后,当我输入a[2]时,出现以下错误:
TypeError: 'doubleArray1D' object does not support indexing
我的猜测是我的接口文件的扩展部分有问题。 我不认为它正在识别 T 类型。关于如何让它发挥作用的任何想法?
提前致谢!
您可以扩展整个模板,而无需选择特定类型。 例如,修改您的代码如下:
%module test
%{
#include <vector>
%}
%inline %{
template<typename T>
class Array1D{
private:
std::vector<T> data_;
size_t xsize_;
public:
Array1D(): xsize_(0) {};
// creates vector of size nx and sets each element to t
Array1D(const size_t& nx, const T& t): xsize_(nx) {
data_.resize(xsize_, t);
}
T& operator[](const size_t i) {return data_.at(i);}
};
%}
%extend Array1D {
T __getitem__(size_t i) {
return (*$self)[i];
}
}
%template(intArray1D) Array1D<int>;
%template(doubleArray1D) Array1D<double>;
这如您所愿,因为 SWIG 本身在生成包装器时会扩展并填充T
的类型:
In [1]: import test
In [2]: a=test.intArray1D(10,1)
In [3]: a[0]
Out[3]: 1
In [4]: a[10]
terminate called after throwing an instance of 'std::out_of_range'
what(): vector::_M_range_check
zsh: abort ipython
注意:我从int
切换到size_t
因为它们始终不是同义词,而.at()
而不是[]
因为前者会抛出无效索引而不是调用未定义的行为。 实际上,您可以使用 SWIG 的默认异常库免费为异常做“聪明”的事情:
%module test
%{
#include <vector>
%}
%include <std_except.i>
%inline %{
template<typename T>
class Array1D{
private:
std::vector<T> data_;
size_t xsize_;
public:
Array1D(): xsize_(0) {};
// creates vector of size nx and sets each element to t
Array1D(const size_t& nx, const T& t): xsize_(nx) {
data_.resize(xsize_, t);
}
T& operator[](const size_t i) {return data_.at(i);}
};
%}
%extend Array1D {
T __getitem__(size_t i) throw(std::out_of_range) {
return (*$self)[i];
}
}
%template(intArray1D) Array1D<int>;
%template(doubleArray1D) Array1D<double>;
足以(两行更改)获得 Python IndexError
而不是 C++ 异常、崩溃或其他 UB。
您可以单独扩展每种类型,如下所示:
%extend doubleArray1D {
请注意,扩展是虚拟的,因为它只是告诉 SWIG 为将成为导出类的一部分的额外函数生成代码,但此类函数只能访问 C++ 类的公共接口。
如果你有一大堆模板实例,你可以定义和使用 SWIG 宏:
%define ArrayExtend(name, T)
%extend name<T> {
T& __getitem__(int i) {
return (*self)[i];
}
}
%enddef
ArrayExtend(Array1D, double)
ArrayExtend(Array1D, int)
提供更通用的解决方案:
%define ArrayExtendVal(name, T) %extend name { T getitem (int i) { return (*self)[i]; } } %enddef %define ArrayExtendRef(name, T) %extend name { T& getitem (int i) { return (*self)[i]; } } %enddef %define ArrayExtendConstRef(name, T) %extend name { const T& getitem (int i) { return (*self)[i]; } } %enddef ... %ignore myNamespace::myClass::operator[] %include "my_class.h" ArrayExtendVal(myClass, double); ArrayExtendRef(myClass, double); ArrayExtendConstRef(myClass, double);
请注意其他答案中缺少的%ignore
指令。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.