How to extend C ++ template class in python using SWIG so that the operator []

I have a C ++ template class that uses a 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];} }; 

The 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 creating the module and creating Array1D in python, when I type [2], I get the following error:

 TypeError: 'doubleArray1D' object does not support indexing 

My guess is that something is wrong with the extension of part of my interface file. I don’t think he recognizes type T. Any thoughts on how to make this work?

Thanks in advance!

+4
source share
2 answers

You can expand each type separately, for example:

 %extend doubleArray1D { 

Note that the extension is virtual because it simply tells SWIG to generate code for additional functions that will be part of the exported class, but such a function has access only to the common interface of your C ++ class.

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

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

You can distribute whole patterns without having to select a specific type. For example, changing the 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>; 

It works as you hope, because SWIG itself extends and populates the types for T when it creates a 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 changed to size_t with int , because they are not always synonymous and .at() instead of [] , because the first will cause an invalid index, and not cause undefined behavior. In fact, you can use the default SWIG exception library for 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>; 

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

+3
source

Source: https://habr.com/ru/post/905435/


All Articles