Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access np.array in ctypes struct

I have a ctypes Structure with dynamic allocated arrays i.e.:

array_1d_double=npct.ndpointer(dtype=np.double,ndim=1,
                               flags='CONTIGUOUS')
class Test(Structure):
    _fields_ = ("x", array_1d_double, ..)

test = Test()
do_some_init_in_c(  for example malloc)

If I print test.x I get the following output:

<ndpointer_<f8_1d_CONTIGUOUS object at 0x7f104dc0c3b0>

The c structure looks roughly like this,

structure Test_s{
    double *x;....
};

How is it possible to access this element like an numpy array? Is it maybe necessary to allocate the arrays as np.arrays instead of using malloc? What would be the proper way to do this ?

like image 936
jrsm Avatar asked Mar 20 '23 18:03

jrsm


1 Answers

One way to do what you are talking about would be to just straight up allocate the numpy array on the python side and behave like it is a straight forward double array on the C side.

import numpy as np
import ctypes as C

# allocate this as a normal numpy array with specified dtype
array_1d_double = np.array([1,2,3,4,5],dtype="float64")

# set the structure to contain a C double pointer
class Test(C.Structure):
    _fields_ = [("x", C.POINTER(C.c_double))]

# Instantiate the structure so it can be passed to the C code
test = Test(np.ctypeslib.as_ctypes(array_1d_double))

# You can also do:
# test = Test()
# test.x = np.ctypeslib.as_ctypes(array_1d_double)

print test.x
# outputs: <__main__.LP_c_double object at 0x1014aa320>

You should now be able to use the struct's x member as a normal double array in the C code.

EDIT:

To clarify: If you instantiate a Structure with no arguments, it provides NULL pointers for all its members.

class Test(C.Structure):
    _fields_ = [("x", C.POINTER(C.c_double)),
                ("y", C.POINTER(C.c_int))]

test = Test()
print test.x
# outputs: <__main__.LP_c_double object at 0x1014aa320>

print test.y
# outputs: <__main__.LP_c_int object at 0x101429320>

print test.x[0]
# raises ValueError: NULL pointer access

print test.y[0]
# raises ValueError: NULL pointer access

If you instantiate the Structure with N arguments, those arguments will be assigned to the first N members of the Structure.

test = Test(np.ctypeslib.as_ctypes(array_1d_double))

print text.x[0]
# outputs: 1.0

print test.y[0]
# raises ValueError: NULL pointer access

EDIT2

If you want to tie the numpy arrays to your struct permanently, you can override the __init__ method:

class MyDualArrayStruct(C.Structure):
    _fields_ = [("c_x", C.POINTER(C.c_double)),
                ("c_y", C.POINTER(C.c_int))]

    def __init__(self,*args,**kwargs):
        super(MyDualArrayStruct,self).__init__(*args,**kwargs)
        self.np_x = np.array([1,2,3,4,5],dtype="float64")
        self.c_x = np.ctypeslib.as_ctypes(self.np_x)
        self.np_y = np.array([5,4,3,2,1],dtype="int32")
        self.c_y = np.ctypeslib.as_ctypes(self.np_y)

test = MyDualArrayStruct()

print test.np_x
print test.c_x[:5]

# Note that here c_x and np_x both contain the same data. Thus modifying one of them
# (inplace) modifies the other. You can use this to do easy inplace modification of 
# numpy arrays in C functions.
# This implies that test.np_x.sum() is also the sum of test.c_x
test.np_x[:] = 1

print test.np_x
print test.c_x[:5]

This outputs:

[ 1.  2.  3.  4.  5.]
[1.0, 2.0, 3.0, 4.0, 5.0]
[ 1.  1.  1.  1.  1.]
[1.0, 1.0, 1.0, 1.0, 1.0]
like image 90
ebarr Avatar answered Mar 22 '23 19:03

ebarr