1

I have a C library (called clib with header clib.h) that I call through Ctypes. A function get_struct returns a pointer to a structure Foo that is forward-declared in clib.h and that I can use for other function calls as a pointer (like use_struct(Foo* foo)). I would like to use this pointer to call the same function code in Cython.

Since I do not know the content of the structure, I cannot create a duplicate structure in cython and copy over the values from the ctypes ones. So I would like instead to create a cython pointer to the same memory space in order to call some C code that binds the same API.

The code is roughly doing this.

In clib.h:

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

On the python side with 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)

On the Cython side, in 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)

On the Cython side, in cylibc.cpp:

#import "clib.h"

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

This does not build and returns errors either Cannot take address of Python variable or assigning to 'PyObject *' (aka '_object *') from incompatible type 'void' on the line foo_cy = <Foo*>&foo_ct.

Any ideas to go forward?

toine
  • 1,946
  • 18
  • 24
  • Since `foo_ct` is the address of a `Foo_ct` struct, you can do this `bar(size_t foo_ct): cy_use_struct(foo_ct )`. That is, get the C object's address in python, and cast it back to C object in cython – oz1 May 24 '17 at 13:36
  • it's a good idea, but this gives me a segmentation fault.. – toine May 24 '17 at 15:53
  • 2
    I can't test this currently so I'm posting as a comment: I think you do `ctypes.addressof(foo_ct)` to get the address. Cast that to a Cython integer then to the pointer. (I'm pretty sure the 2 step cast is necessary) – DavidW May 24 '17 at 16:30
  • that seems to be doing the trick, this helped too:https://stackoverflow.com/questions/9687002/get-the-memory-address-pointed-to-by-a-ctypes-pointer – toine May 24 '17 at 18:13
  • 1
    If you've got something that works then feel free to post it as an answer yourself (I'm not able to test anything well enough to write a good answer in the near future...) – DavidW May 24 '17 at 21:09

1 Answers1

3

based on @oz1 and @DavidW comments, the following works:

from libc.stdint cimport uintptr_t
cdef uintptr_t adr = <uintptr_t>ctypes.addressof(foo_ct.contents)
cy_use_struct(<Foo*>adr)
toine
  • 1,946
  • 18
  • 24
  • 2
    A pointer as an integer is a `uintptr_t`, not a `long`. That would truncate the address on 64-bit Windows or any other 64-bit platform that has a 32-bit `long` type. – Eryk Sun May 25 '17 at 18:42