如何在python中使用SWIG扩展模板化的c++类以允许使用[]运算符
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
指令在其他答案中缺失
- 如何创建一个CMake变量,除非显式重写,否则使用默认值
- C++:TypeDef使用元组
- 使用std::multimap迭代器创建std::list
- 从不同线程使用int64的不同字节安全吗
- 比较并显示使用最小值(a,b)和最大值(a、b)升序排列的4个数字
- 为什么在全局范围内使用"extern int a"似乎不行?
- 在C#中处理C++指针而不使用unsafe的最佳方法
- 使用C++库在Android项目中修改gradle中的cmake参数,用于插入指令的测试
- 如何使用Google Mock来模拟gettimeofday()
- 如何使用默认参数等选择模板专业化
- 为什么使用 "this" 指针调用派生成员函数?
- 使用新行和不使用新行读取文件
- 如何使用 < 和 > 命令获取 c++ 中的输入和输出?
- 如何确定我已使用非编码文件到达 EOF?
- 如何在cuSparse中使用cusparseXcoo2csr从coo转换为csc
- 使用CMake创建QML插件
- 使用strcpy将char数组的元素复制到另一个数组
- 在c++中使用nlohmann从类到json的转换
- 使用指针从C++中的数组中获取最大值
- 使用不带参数的函数访问结构元素