Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c++

python

swig

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!

like image 747
Bluegreen17 Avatar asked Mar 29 '14 20:03

Bluegreen17


People also ask

How to extend a class in Python?

Extending a class is the same as inheriting a class. Let us see how inheritance works in Python. To inherit a class in Python, we pass the name of that class as a parameter while creating the child class. Let us understand this with an example. We first create a parent class, Desserts, with two methods - init and intro.

Is it possible to extend Python using C?

Still, there may be other lesser-used system calls that are only accessible through C. The os module in Python is one example. This is not an exhaustive list, but it gives you the gist of what can be done when extending Python using C or any other language.

What is Swig in C++?

What is SWIG In a nutshell, SWIG is a compiler that takes C/C++ declarations and creates a wrapper needed to access those declarations from other languages like Python, Tcl, Ruby etc. It normally required no changes in existing code and create an interface within a minute. Reasons for creating wrapper

Why should you use C in Python?

So, why should you use C? Here are a few reasons why you might decide to build a Python C extension module: To implement new built-in object types: It’s possible to write a Python class in C, and then instantiate and extend that class from Python itself.


1 Answers

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.

like image 121
Flexo Avatar answered Oct 02 '22 09:10

Flexo