How to extend a templated c++ class in python with SWIG to allow the [] operator

[亡魂溺海] 提交于 2019-12-18 04:54:30

问题


I have a templated c++ array class which uses the standard vector class:

#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];}
};

My SWIG interface file looks like

%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];
    }
 }

After making the module, and create an Array1D in python, when I type in a[2] I get the following error:

TypeError: 'doubleArray1D' object does not support indexing

My guess is something is wrong with the extend part of my interface file. I don't think it is recognizing the type T. Any thoughts on how to get this to work?

Thanks in advance!


回答1:


You can extend whole templates, without having to pick a specific type. For example, modifying your code as follows:

%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>;

Which works as you'd hope because SWIG itself expands and fills in the types for T when it is generating the wrapper:

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

Note: I swapped to size_t from int because they're not synonyms always and .at() instead of [] because the former will throw for an invalid index rather than invoke undefined behaviour. You can actually use SWIG's default exception library to do "smart" things with the exception for free:

%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>;

Is sufficient (two lines of changes) to get a Python IndexError instead of a C++ exception, crash or other UB.




回答2:


You can extend each type separately, like this:

%extend doubleArray1D {

Note that extension is virtual in that it just tells SWIG to generate code for extra functions that will be part of exported class but such function only has access to public interface of your c++ class.

If you have a whole bunch of template instances, you could define and use a SWIG macro:

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

ArrayExtend(Array1D, double)
ArrayExtend(Array1D, int)


来源:https://stackoverflow.com/questions/22736700/how-to-extend-a-templated-c-class-in-python-with-swig-to-allow-the-operator

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!