I\'ve got a structure defined inside header.h that looks like :
typedef struct {
....
int icntl[40];
double cntl[15];
int *irn, *jcn;
Have you considered using SWIG carrays?
In your header file:
typedef struct {
int icntl[40];
double cntl[15];
} some_struct_t;
Then, in your swig file:
%module example
%include "carrays.i"
// ...
%array_class(int, intArray);
%array_class(double, doubleArray);
The 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 can't set the structs directly. I write python wrapper code to hide the boilerplate.
array_class only works with basic types (int, double), if you need something else (e.g. uint8_t) you need to use array_functions, which have even more boilerplate, but they work.
http://www.swig.org/Doc3.0/SWIGDocumentation.html#Library_carrays
I would have done this 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
The easiest way to do this is to wrap your arrays inside a struct
, which can then provide extra methods to meet the "subscriptable" requirements.
I've put together a small example. It assumes you're using C++, but the equivalent C version is fairly trivial to construct from this, it just requires a bit of repetition.
First up, the C++ header that has the struct
we want to wrap and a template that we use for wrapping fixed size arrays:
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 then looks something like:
%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 there is that we've used %extend
to supply __getitem__ which is what Python uses for subscript reads and __setitem__ for the writes. (We could also have supplied a __iter__
to make the type iteratable). We also gave the specific wraped_array
s we want to use unique names to make SWIG wrap them in the output.
With the supplied interface we can now do:
>>> 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 might also find this approach useful/interesting as an alternative.