6

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!

3 Answers 3

7

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.

Sign up to request clarification or add additional context in comments.

4 Comments

Perhaps this requires a new question, but what if I want to access "T" from the CSharp wrapper? Say I extend the proxy class, and then want to force one of the wrapped template methods to first call one of the C#-only functions. %typemap(cscode) mytemplateclass%{ public void (T _add){...} %} for example. How do I specify T? I am forced to write a macro and then call it for every specialization?
@andyg not entirely clear why you'd want to do it like that but $typemap(T,cstype) ought to expand to that I think. (but totally guessing right now)
@AndyG that's looking trickier than I expected - probably worth a question with enough motivating factors explained that a solution can be found. I think I have a workaround that might be viable.
Cool, thanks. I'll get one up in a couple hours (on mobile now)
3

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)

5 Comments

I see. So I guess I will just have to extend each individual templated classes. Thanks!
@user1589038 Great. I extended answer to cover case of many same. Don't forget to close a question whenever possible (accept answer that was most useful), and upvote SO answers (any, not just to your questions) that you find useful, and upvote SO questions that you find were worth asking, that's best way to thank people for their contribution. Welcome to SO!
Will do. I had to type in ArrayExtend(Array1D<double>, double) for this to work properly.
@user1589038 Odd. Try the update, if it works it removes some duplication.
@user1589038 Looks like I was wrong about first sentence in my original answer (I learnt something thanks to Flexo's post!), perhaps this is why the -1. I fixed that, the rest of the answer should be fine.
0

To provide a more generic solution:

%define ArrayExtendVal(name, T) %extend name { T getitem(int i) { return (*self)[i]; } } %enddef %define ArrayExtendRef(name, T) %extend name { T& getitem(int i) { return (*self)[i]; } } %enddef %define ArrayExtendConstRef(name, T) %extend name { const T& getitem(int i) { return (*self)[i]; } } %enddef ... %ignore myNamespace::myClass::operator[] %include "my_class.h" ArrayExtendVal(myClass, double); ArrayExtendRef(myClass, double); ArrayExtendConstRef(myClass, double);

Note the %ignore directive that is missing in the other answers.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.