How to extend a templated c++ class in python with SWIG to allow the [] operator
Question: 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!


Answer: 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.

