Sign up ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free.

I'm trying to interface a shared c library with a python code. The interface with the library is something like

typedef struct{
    int v1;
    double* v2} input;

There are two other types like this one for configuration and the output type.

I had this structs in python using ctypes Structure like this:

class input(Structure):
    _fields_ = [("v1",c_int),("v2",POINTER(c_double)]

The C code have some functions that receives a pointer to that structure and the argtypes are defined as in:

fun.argtypes = [constraints,input,POINTER(input)]

constraints is another structure with some int fields for configuration.

when I call the function I do in this way: first I update the v2 field in the input struct

input.v2 = generated_array.ctypes.data_as(POINTER(c_double))

Then I call with:

fun(constraints,input,byref(output))

The function prototype asks for struct and * to struct(output struct is similar to input) Then I want to access the results of fun stored in the v2 field of output. But I'm taking unexpected results. Is there a better(perhaps correct) way of do this?

I searched a lot here and read the documentation but i can't find what's wrong. I don't have any error messages, but the warnings that I receive from the shared lib seems to show that there are mistakes on this interfaces.


I guess I had found the problem. When I call the method a numpy array of complex is called. Then I create 4 vectors:

    out_real = ascontiguousarray(zeros(din.size,dtype=c_double))
    out_imag = ascontiguousarray(zeros(din.size,dtype=c_double))
    in_real  = ascontiguousarray(din.real,dtype = c_double)
    in_imag  = ascontiguousarray(din.imag,dtype = c_double) 

where din is the input vector. I had tested the method this way:

    print in_real.ctypes.data_as(POINTER(c_double))    
    print in_imag.ctypes.data_as(POINTER(c_double))
    print out_real.ctypes.data_as(POINTER(c_double))
    print out_imag.ctypes.data_as(POINTER(c_double))

and the result was:

    <model.LP_c_double object at 0x1d81f80>
    <model.LP_c_double object at 0x1d81f80>
    <model.LP_c_double object at 0x1d81f80>
    <model.LP_c_double object at 0x1d81f80>

seems that all of them are pointing to the same place.

After some changes, it work as expected

After several tests I found that the code was nearly correct in the first time. I was creating the Structure instance once and updating it's fields. I change to create a new instance at each call of fun. I also change all arrays types to be the equivalent ctypes type, this seems to make the function work as expected.

The print behavior still holds as in the test above, but the function seems to work even with this strange behavior. It is correct as pointed by @ericsun comment below.

share|improve this question
    
Please post full code, what does the c code do? –  HYRY Mar 15 '13 at 0:34
    
Sadly I can't share the full code since it's a thirdy party library under nda. But it's just a scientific algorithm that takes two vectors and return other 2 vectors. Do you think the question is not clear without the full description? –  Euripedes Rocha Filho Mar 15 '13 at 11:33
1  
the c function interface is not clear, the fun.argtypes = [...,POINTER(input)] and fun(..,input,byref(output)) is not the same interface. –  HYRY Mar 15 '13 at 11:39
    
I see. I'll update to make it clear. –  Euripedes Rocha Filho Mar 15 '13 at 13:08
1  
About the LP_c_double objects, that's the Python object address, not the address in the pointer. You only printed a temporary pointer object; at the next print it was already deallocated and the same address was reused. Look at the contents attribute, or just the [0] index. –  eryksun Mar 15 '13 at 13:48

1 Answer 1

up vote 2 down vote accepted

The struct has an int field that's possibly for the array length, though I'm just guessing without the complete function prototype. If this is indeed the case, here's an example that may help.

First, I need to compile a test function in a shared library. I'll simply multiply the input array by 2:

import os
import numpy as np
from ctypes import *

open('tmp.c', 'w').write('''\
typedef struct {
    int v1; double *v2;
} darray;
int test(darray *input, darray *output) {
    int i;
    /* note: this should first test for compatible size */
    for (i=0; i < input->v1; i++)
        *(output->v2 + i) = *(input->v2 + i) * 2;
    return 0;
}
''')
os.system('gcc -shared -o tmp.so tmp.c')

Next create the ctypes definitions. I added a classmethod to make a darray from a numpy.ndarray:

c_double_p = POINTER(c_double)

class darray(Structure):
    _fields_ = [
      ('v1', c_int),
      ('v2', c_double_p),
    ]
    @classmethod
    def fromnp(cls, a):
        return cls(len(a), a.ctypes.data_as(c_double_p))

lib = CDLL('./tmp.so')
lib.test.argtypes = POINTER(darray), POINTER(darray)

Test:

a1 = np.arange(3) + 1.0
a2 = np.zeros(3)
print 'before:', '\na1 =', a1, '\na2 =', a2 

lib.test(darray.fromnp(a1), darray.fromnp(a2))
print 'after:', '\na1 =', a1, '\na2 =', a2 

Output:

before: 
a1 = [ 1.  2.  3.] 
a2 = [ 0.  0.  0.]
after: 
a1 = [ 1.  2.  3.] 
a2 = [ 2.  4.  6.]
share|improve this answer
    
This code is very close of mine. But instead of creating a new instance each time the function is called I just update the fields I need when I call the method fun(I had the c code encapsulated in a class). I also didn't have c_double_p as in the code. I call POINTER(c_double) when needed. I'll try this modifications. –  Euripedes Rocha Filho Mar 15 '13 at 11:39
1  
@EuripedesRochaFilho: You didn't show how the v1 field is used. This answer sets it as the array length in the fromnp classmethod: cls(len(a), a.ctypes.data_as(c_double_p)). Making that a method and assigning c_double_p were just for convenience. I also showed that it's modifying the ndarray objects' buffers. You only need these Structure instances for the call. Access the data via the NumPy arrays. –  eryksun Mar 15 '13 at 11:49
    
You're right guessing about the v1 field as the size of v2. I'm also using numpy array to have access to results. I was just updating the fields since in my task now I need to call the function several times. I'm testing with creating a new instance of the Structures on each call, but just because I'm on the supernatural tests now :) –  Euripedes Rocha Filho Mar 15 '13 at 13:19

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.