[SciPy-user] numpy array in ctype struct

Paul Kienzle pkienzle@nist....
Tue Jan 15 12:04:29 CST 2008


On Tue, Jan 15, 2008 at 08:59:06AM -0600, Travis E. Oliphant wrote:
> Paul Kienzle wrote:
> > Hi,
> >
> > We are trying to create the following struct in ctypes:
> >
> >   struct {
> >     int n,k;
> >     double A[4][4], B[4][4];
> >   } ;
> >
> > Easy enough:
> >
> >   class embedded_array(Structure):
> >     _fields_ = [("n",c_int),("k",c_int),("A",c_double*16),("B",c_double*16)]
> >   instance = embedded_array()
> >
> > Question:
> >
> >   Is there a way to map the data in A/B into a numpy array, so that we
> >   can use it directly?
> >
> >   
> Yes.   You can create an array from a pointer to memory and a 
> description of the data that is much like the ctypes structure.   At 
> some point, there should be a direct conversion from ctypes objects to 
> NumPy data-types, but nobody has written that yet.  At the moment you 
> have to create a dtype object that is parallel to the ctypes one:
> 
> import numpy as np
> dt = np.dtype([('n',np.intc),('k',np.intc),("A", float, 16), ("B", 
> float, 16)])
> 
> Then,
> 
> arr = np.frombuffer(instance, dtype=dt)
> 
> will create a 1-d array of these structures mapping onto the data 
> pointed to by instance.   Of course, in this case, the 1-d array only 
> has one element.
> 
> Access to the fields of the c-structure is obtained by "dictionary" access:
> 
> arr['n'] = 10
> arr["A"] = 1
> 
> You will notice that this accesses the memory directly:
> 
> print instance.n
> print [x for x in instance.A]

Instead of creating the ctypes struct I just used numpy to create a scalar
of the correct structure.  I wrapped it in a class so that I could reference
the fields directly as e.g., instance.A.  I used a factory function
for generating the class from the structure definition since I will need
to wrap several structures.

I'm attaching cstruct.py and embedarray.c where I demonstrate this.

I'm particularly pleased that I can assign a 4x4 array to instance.A
and it just works!

The ctypes docs talk about possible alignment issues for the structs on
some architectures.  They say it the ctypes follows the conventions of
the compiler which created it.  I haven't checked if this will be a 
problem with the numpy solution you outline above.

	- Paul
-------------- next part --------------
typedef struct {
   int n, k;
   double A[4][4], B[4][4];
} embed_array;
void call(embed_array *pv)
{
  pv->n = 5;
  pv->A[1][1] = 18.652;
}
-------------- next part --------------
import ctypes, numpy, os

# Factory for ctypes/numpy struct classes
def cstruct(*fields):
  class SpecificStruct(object):
    """
    C structure usable from python.

    Use a._pointer to pass the data as a pointer to structure in
    ctypes.
    """
    _dtype = numpy.dtype(list(fields))
    def __init__(self,**kw):
        self.__dict__['_array'] = numpy.zeros((),dtype=self._dtype)
        for k,v in kw.iteritems(): self._array[k] = v

    def _getdata(self): return self._array.ctypes.data
    _pointer = property(_getdata,doc='ctypes data pointer to struct')
    def __getattr__(self,field):
	return self._array[field]
    def __setattr__(self,field,value):
        self._array[field] = value
  return SpecificStruct

if __name__ == "__main__":
    # Create a structure for:
    #    struct {
    #       int n, k;
    #       double A[4][4],B[4][4];
    #    }
    Embed = cstruct(('n',numpy.intc),('k',numpy.intc),
                    ('A',float,(4,4)),('B',float,(4,4)))

    # Initialize the structure and invoke call() in embedarray.dll
    a = Embed(k=6)
    a.A = numpy.array([[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]])
    a.A[0,0] = 12
    lib = numpy.ctypeslib.load_library('embedarray',os.path.dirname(__file__))
    lib.call(a._pointer)
    print a.A,a.k,a.n



More information about the SciPy-user mailing list