如何使用SWIG在python中扩展模板化的c ++类以允许[]运算符

kch*_*462 6 c++ python swig

我有一个模板化的c ++数组类,它使用标准的vector类:

#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];}
};
Run Code Online (Sandbox Code Playgroud)

我的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];
    }
 }
Run Code Online (Sandbox Code Playgroud)

制作模块后,在python中创建一个Array1D,当我输入[2]时,我得到以下错误:

TypeError: 'doubleArray1D' object does not support indexing
Run Code Online (Sandbox Code Playgroud)

我的猜测是我的界面文件的扩展部分出了问题.我不认为它是识别类型T.有关如何使其工作的任何想法?

提前致谢!

Fle*_*exo 5

您可以扩展整个模板,而不必选择特定的类型。例如,如下修改您的代码:

%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>;
Run Code Online (Sandbox Code Playgroud)

它可以按您希望的那样工作,因为SWIG本身会扩展并填充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>;
Run Code Online (Sandbox Code Playgroud)

注意:我size_t从切换到,int是因为它们并非始终是同义词,.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>;
Run Code Online (Sandbox Code Playgroud)

足以(两行更改)获取Python IndexError而不是C ++异常,崩溃或其他UB。


Oli*_*ver 3

您可以单独扩展每种类型,如下所示:

%extend doubleArray1D {
Run Code Online (Sandbox Code Playgroud)

请注意,扩展是虚拟的,因为它只是告诉 SWIG 为将成为导出类一部分的额外函数生成代码,但此类函数只能访问 C++ 类的公共接口。

如果您有一大堆模板实例,您可以定义并使用 SWIG 宏:

%define ArrayExtend(name, T)
%extend name<T> {
    T& __getitem__(int i) {
    return (*self)[i];
    }
 }
%enddef

ArrayExtend(Array1D, double)
ArrayExtend(Array1D, int)
Run Code Online (Sandbox Code Playgroud)