[Tutor] Passing Data to .DLL

eryksun eryksun at gmail.com
Wed Oct 22 13:16:17 CEST 2014


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