2017-05-24 3 views
0

J'ai une bibliothèque C (appelée clib avec l'en-tête clib.h) que j'appelle Ctypes. Une fonction get_struct renvoie un pointeur sur une structure Foo qui est déclarée en avant dans clib.h et que je peux utiliser pour d'autres appels de fonction en tant que pointeur (comme use_struct(Foo* foo)). Je voudrais utiliser ce pointeur pour appeler le même code de fonction dans Cython. Puisque je ne connais pas le contenu de la structure, je ne peux pas créer une structure en double dans Cython et copier sur les valeurs des types ctypes. Je voudrais donc plutôt créer un pointeur cython sur le même espace mémoire pour appeler du code C qui lie la même API.Transmettre la structure ctypes en cython

Le code le fait en gros.

En clib.h:

typedef struct Foo Foo; 
Foo * get_struct(); 
void use_struct(Foo *) 

Du côté python avec ctypes:

# load clib DLL 
clib = ctypes.CDLL('clib.so') 

# empty Ctypes declaration 
class Foo_ct(ctypes.Structure): 
    pass 

# get and use the struct 
clib.get_struct.restype = ctypes.POINTER(Foo_ct) 
foo_ct = clib.get_struct() 
clib.use_struct(foo_ct) 

# call cython code 
bar(foo_ct) 

Du côté Cython, en cylib.pyx:

cdef extern from "clib.h": 
    cdef struct Foo: 
     pass 

    void use_struct(Foo *) 


cpdef bar(foo_ct): 

    cdef Foo* foo_cy 
    foo_cy = <Foo*>&foo_ct # or something equivalent 
    cy_use_struct(foo_cy) 

Sur le Cython côté, dans cylibc.cpp:

#import "clib.h" 

void cy_use_struct(Foo * foo) 
{ 
    use_struct(foo); 
} 

Ceci ne génère pas et renvoie des erreurs Cannot take address of Python variable ou assigning to 'PyObject *' (aka '_object *') from incompatible type 'void' sur la ligne foo_cy = <Foo*>&foo_ct.

Des idées pour aller de l'avant?

+0

Depuis 'foo_ct' est l'adresse d'un struct' Foo_ct', vous pouvez le faire 'bar (size_t foo_ct): cy_use_struct ( foo_ct) '. C'est-à-dire, obtenir l'adresse de l'objet C en python, et la renvoyer à l'objet C dans Cython – oz1

+0

c'est une bonne idée, mais cela me donne une erreur de segmentation .. – toine

+2

Je ne peux pas le tester actuellement, donc je poste en tant que commentaire: Je pense que vous faites 'ctypes.addressof (foo_ct)' pour obtenir l'adresse. Convertissez cela en un entier Cython puis au pointeur. (Je suis assez sûr que la distribution en deux étapes est nécessaire) – DavidW

Répondre

0

basé sur @ et commentaires @DavidW traitées à l'OZ1, les travaux suivants:

from libc.stdint cimport uintptr_t 
cdef uintptr_t adr = <uintptr_t>ctypes.addressof(foo_ct.contents) 
cy_use_struct(<Foo*>adr) 
+2

Un pointeur en tant qu'entier est un 'uintptr_t', pas un' long'. Cela tronquerait l'adresse sur Windows 64 bits ou toute autre plate-forme 64 bits ayant un type 'long' 32 bits. – eryksun