return a ctypes object to C

Arnaud Loonstra arnaud at sphaero.org
Thu Oct 31 10:39:53 EDT 2019


On 31-10-2019 14:44, Thomas Jollans wrote:
> On 31/10/2019 14.13, Arnaud Loonstra wrote:
>> On 30-10-2019 09:32, Arnaud Loonstra wrote:
>>> Hi all,
>>>
>>> I'm trying to wrap my head around the ctypes API. I have a C
>>> structure I wish to create in Python and then return from python to C.
>>>
>>> So a python method is called from C and needs to return an object
>>> which we then process in C again.
>>>
>>> I have a binding to access and create the C methods and structures so
>>> in Python I can call the Zmsg() constructor. I now need to return this.
>>>
>>> My python test method is simply:
>>>
>>> def actor_test( *args, **kwargs):
>>>       print("test")
>>>       msg = Zmsg()
>>>       frame = Zframe(b"Hello", 5)
>>>       msg.prepend(frame)
>>>       return msg
>>>
>>> the method is called from C as follows:
>>>
>>> PyObject *pReturn = PyObject_CallObject(pFunc, NULL);
>>>
>>> This correctly calls the method. However the returned object is of
>>> course a PyObject*. The debugger says it's
>>>
>>> "<czmq._czmq_ctypes.Zmsg object at 0x7ffff5f18e50>"    PyObject
>>>               [class]    "<class 'czmq._czmq_ctypes.Zmsg'>"
>>>               [super class]    "<class 'object'>"
>>>               [meta type]    "<class 'type'>"
>>>               ob_refcnt    1    Py_ssize_t
>>>
>>> However how I can I get it back to the original C type (zmsg_t *)
>>>
>>> Any help really appreciated.
>>>
>>
>> What I've found so far is that I can return the address of the ctypes
>> object.
>>
>> msg = Zmsg()
>> frame = Zframe(b"Hello", 5)
>> msg.prepend(frame)
>> return addressof(msg._as_parameter_.contents)
>>
>> In C I can then cast it back to the original type.
>>
>> PyObject *pReturn = PyObject_CallObject(pFunc, NULL);
>> assert(pReturn);
>> long bla = PyLong_AsLong(pReturn);
>> zmsg_t* test = (zmsg_t *)bla;
>> assert(test);
>> char *hello = zmsg_popstr(test);
>> assert(hello);
>> assert(streq(hello, "Hello"));
>>
>> This works, I'm not sure if this is the right way. It also creates a
>> complicated setup with the garbage collector.
>>
>> Anybody better ideas?
> 
> You've already got a complicated setup with your ctypes objects...
> 
> If you're using the Python C API anyway, why would you use ctypes at
> all? You can create custom Python types in C to wrap your C pointers.
> 
> Alternatively: if you're using ctypes anyway, why use the Python C API
> at all? You can create C function pointers from Python functions with
> ctypes.
> 
> If you're mixing two different ways of interfacing Python and C, the
> result will ALWAYS be messy. Better to stick to one. Personally, I
> prefer cffi or cython depending on the situation, as I find them clearer
> and easier to use than ctypes. Using the Python C API directly is
> usually the hardest to understand and the easiest to get wrong.
> 
> -- Thomas

Hi Thomas,

I have an engine running which can call handlers. These handlers return 
a zmsg_t (a message) which the engine then can process.

In this engine we have Python embedded and I want to use a Python method 
as a handler. To embed Python we need to use the Python C API. To 
construct a zmsg_t type in Python we need to call the corresponding C 
method and we use ctypes to do that.

I'm using a ctypes binding because it already exists, it's here: 
https://github.com/zeromq/czmq/blob/d6283985ba52fd8c3f8fbdc7cd5c08372ff69ca1/bindings/python/czmq/_czmq_ctypes.py#L4392

I know I can use cffi for example but that's IMHO just a ctypes 
alternative.

Are you saying it's better to create some approach to create a zmsg_t 
using the Python C API?

Please enlighten me if this should be done differently.

Rg,

Arnaud


More information about the Python-list mailing list