[Tutor] Passing Data to .DLL

Wilson, Pete Pete.Wilson at atmel.com
Wed Oct 22 22:50:13 CEST 2014


It's working!

I don't understand the line
rx_buf = (c_uint8 * rx_buf_size).from_buffer_copy(string_buf)
or where .from_buffer_copy() came from but it works...

I promise I will not knowingly pass Python strings to C. 

Thanks.

> -----Original Message-----
> From: eryksun [mailto:eryksun at gmail.com]
> Sent: Wednesday, October 22, 2014 4:16 AM
> To: Wilson, Pete
> Cc: tutor at python.org
> Subject: Re: [Tutor] Passing Data to .DLL
> 
> On Tue, Oct 21, 2014 at 7:04 PM, Wilson, Pete <Pete.Wilson at atmel.com>
> wrote:
> >
> > ProcessIncomingSerialData_t = CFUNCTYPE(None, POINTER(c_uint8),
> > c_uint16) process_incoming_serial_data =
> > pt_dll.ProcessIncomingSerialData
> process_incoming_serial_data.argtypes
> > = [ProcessIncomingSerialData_t]
> 
> ProcessIncomingSerialData takes two parameters: uint8_t *rx_buf and
> uint16_t size.
> 
>     process_incoming_serial_data.argtypes = [POINTER(c_uint8),
> c_uint16]
>     process_incoming_serial_data.restype = None
> 
> The buffer parameter isn't const, so pass a copy of string_buf.
> 
>     size = len(string_buf)
>     rx_buf = (c_uint8 * size).from_buffer_copy(string_buf)
>     process_incoming_serial_data(rx_buf, size)
> 
> I'm going to meander off topic a bit to give you a warning...
> 
> Python strings are immutable by contract, so don't pass them directly
> to C functions that might modify them. String immutability makes
> interning possible, sometimes just locally in the code object and
> sometimes globally in the interpreter. Here's an example of both in
> CPython 2.7:
> 
>     import abc
>     from ctypes import *
> 
>     def test():
>         s = 'abc'
>         a = cast(s, POINTER(c_char * 3))[0]
>         a[:] = 'cba'
>         print 'abc'
> 
>     >>> test.__code__.co_consts
>     (None, 'abc', 3, 0, 'cba')
> 
> Notice that the 2nd constant in co_consts is 'abc'. This gets stored to
> s and later printed. In between I use ctypes to reverse it. So what
> gets printed? If you guessed "cba", you're right.
> 
>     >>> test()
>     cba
> 
> Look at the constants now:
> 
>     >>> test.__code__.co_consts
>     (None, 'cba', 3, 0, 'cba')
> 
> So how about referencing the abc module?
> 
>     >>> abc
>     Traceback (most recent call last):
>       File "<stdin>", line 1, in <module>
>     NameError: name 'abc' is not defined
> 
> OK, but do you think we can use cba instead?
> 
>     >>> cba
>     Traceback (most recent call last):
>       File "<stdin>", line 1, in <module>
>     NameError: name 'cba' is not defined
> 
> This is yet another problem with mucking with the internal state of an
> immutable object. Equal objects are supposed to hash the same.  'cba'
> is now equal to the interned 'abc' string that I reversed, but it
> probes to a different slot in the dict's hash table.
> 
>     >>> test.__code__.co_consts[1] == 'cba'
>     True
>     >>> hash(test.__code__.co_consts[1]) == hash('cba')
>     False
> 
> OTOH, a new 'abc' string probes to the same slot in the hash table, but
> it's no longer equal (i.e. it gets handled as a hash collision).
> 
>     >>> test.__code__.co_consts[1] == 'abc'
>     False
>     >>> hash(test.__code__.co_consts[1]) == hash('abc')
>     True
> 
> This breaks dict lookup unless we use the interned string itself:.
> 
>     >>> globals()[test.__code__.co_consts[1]]
>     <module 'abc' from '/usr/lib/python2.7/abc.pyc'>
> 
> So the moral is only pass Python strings to C functions that promise
> (scout's honor) to not modify them. If the parameter isn't const, err
> on the side of caution; copy the string to a ctypes buffer.


More information about the Tutor mailing list