4

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 ?

Mahonri Moriancumer
  • 5,993
  • 2
  • 18
  • 28
jrsm
  • 1,595
  • 2
  • 18
  • 39

1 Answers1

7

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]
ebarr
  • 7,704
  • 1
  • 29
  • 40
  • Thank You, unfortunately my Structure contains around 100 elements does this still work? I'm suprised that my code works if i dont have the pointer inside the structure any idea why ? – jrsm Apr 28 '14 at 06:09
  • The number of elements in your structure is irrelevant. You just need to set them python side if they are python-side values (like your numpy array) or set them C side if they are C-side values. As to your question, you do have the pointer inside your structure. The structure is created with pointers (which may not point to valid memory), you are just updating those pointers to be valid. – ebarr Apr 28 '14 at 06:14
  • Does this mean test = Test(np.ctypeslib.as_ctypes(array_1d_double)) creates a copy of array_1d_double for each element inside the structure? – jrsm Apr 28 '14 at 06:23
  • 1
    Nope, `test = Test(np.ctypeslib.as_ctypes(array_1d_double))` just allocates a pointer to the array to the first element in the struct – ebarr Apr 28 '14 at 06:25
  • But how does this work if i have more then element in the struct? – jrsm Apr 28 '14 at 07:20
  • Thank you for the answer just one last question, is it possible to put this somehow in the init method of the Struct? – jrsm Apr 28 '14 at 08:07
  • Yes, you can. Just move the numpy array creation into the init if you want an object that encapsulates those arrays. See the final update. – ebarr Apr 28 '14 at 08:19
  • Does this overwrite the inherited init function? isnt it necessary to add something like `super(MyDualArrayStruct, self).__init__()`? Thank you again for your nice answer i will accept it ASAP after I get my script to run :). – jrsm Apr 28 '14 at 08:29
  • Well it works now, I can access the elements, but how is it possible to use numpy functions on the elements ? for example `np.sum(Test.x)` doesn't work. – jrsm Apr 28 '14 at 08:48
  • So you are misunderstanding what ctypes is doing here. Here the numpy array and the ctypes array contain the same data. If you send the ctypes array to a C function which modifies it in place, the corresponding numpy array will also be modified. One more update... – ebarr Apr 28 '14 at 08:51
  • I think I understood this, since I just wanted to use numpy arrays in python I'll probably write a small wrapper class around the Structure. – jrsm Apr 28 '14 at 09:03
  • Wait... why are you using a structure if you just want to use the numpy arrays in python??? – ebarr Apr 28 '14 at 09:05
  • I have some C code working on a structure, from python I want to access the arrays inside the structure to process the results from the c code. Is this not the proper way to do it ? – jrsm Apr 28 '14 at 09:13
  • Ahh, maybe, maybe not. Its impossible to say without knowing the problem. If there is still an issue, you should consider posting a new question. – ebarr Apr 28 '14 at 09:16