SWIG / python array inside structure

I have a structure defined inside header.h that looks like this:

typedef struct { .... int icntl[40]; double cntl[15]; int *irn, *jcn; .... 

When I initialize an object with this structure, I have access to integers / doubles, but not to arrays.

 >> st.icntl <Swig Object of type 'int *' at 0x103ce37e0> >> st.icntl[0] Traceback (most recent call last): File "test_mumps.py", line 19, in <module> print s.icntl[0] TypeError: 'SwigPyObject' object is not subscriptable 

How to access values ​​in read / write?

+6
source share
3 answers

The easiest way to do this is to wrap arrays inside a struct , which can then provide additional methods to satisfy the "signature" requirements .

I put together a small example. It is assumed that you are using C ++, but the equivalent version of C is pretty trivial to build from this, it just requires a little repetition.

First, the C ++ header, which has a struct that we want to wrap, and the template that we use to pack arrays of fixed size:

 template <typename Type, size_t N> struct wrapped_array { Type data[N]; }; typedef struct { wrapped_array<int, 40> icntl; wrapped_array<double, 15> cntl; int *irn, *jcn; } Test; 

Our corresponding SWIG interface looks something like this:

 %module test %{ #include "test.h" #include <exception> %} %include "test.h" %include "std_except.i" %extend wrapped_array { inline size_t __len__() const { return N; } inline const Type& __getitem__(size_t i) const throw(std::out_of_range) { if (i >= N || i < 0) throw std::out_of_range("out of bounds access"); return self->data[i]; } inline void __setitem__(size_t i, const Type& v) throw(std::out_of_range) { if (i >= N || i < 0) throw std::out_of_range("out of bounds access"); self->data[i] = v; } } %template (intArray40) wrapped_array<int, 40>; %template (doubleArray15) wrapped_array<double, 15>; 

The trick is that we used %extend to supply __getitem__ , which Python uses to read substrings and __setitem__ to write. (We could also put __iter__ to make the type iterable). We also gave a specific wraped_array that we want to use unique names to force SWIG to wrap them in the output file.

Using the supplied interface, we can now:

 >>> import test >>> foo = test.Test() >>> foo.icntl[30] = -654321 >>> print foo.icntl[30] -654321 >>> print foo.icntl[40] Traceback (most recent call last): File "<stdin>", line 1, in <module> File "test.py", line 108, in __getitem__ def __getitem__(self, *args): return _test.intArray40___getitem__(self, *args) IndexError: out of bounds access 

You can also find this approach useful / interesting as an alternative.

+6
source

I would do it in python

 ptr = int(st.icntl) import ctypes icntl = ctypes.c_int * 40 icntl = icntl.from_address(ptr) print icntl[0] icntl[0] = 1 for i in icntl: print i 
+3
source

Have you considered using SWIG carriages?

In your header file:

 typedef struct { int icntl[40]; double cntl[15]; } some_struct_t; 

Then in the Swig file:

 %module example %include "carrays.i" // ... %array_class(int, intArray); %array_class(double, doubleArray); 

Python looks like this:

 icntl = example.intArray(40) cntl = example.doubleArray(15) for i in range(0, 40): icntl[i] = i for i in range(0, 15): cntl[i] = i st = example.some_struct_t() st.icntl = icntl st.cntl = cntl 

You still cannot set structures directly. I am writing Python shell code to hide a template.

array_class only works with base types (int, double), if you need something else (like uint8_t), you need to use array_functions, which have even more patterns, but they work.

http://www.swig.org/Doc3.0/SWIGDocumentation.html#Library_carrays

0
source

All Articles