I have a templated c++ array class which uses the standard vector class:
#include
#include
using namespace std;
template
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)
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.
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.