working with ctypes and complex data structures

eryk sun eryksun at gmail.com
Mon Oct 3 11:53:33 EDT 2016


On Mon, Oct 3, 2016 at 2:35 PM, Michael Felt <michael at felt.demon.nl> wrote:
> On 02-Oct-16 23:44, eryk sun wrote:
>>   On Sun, Oct 2, 2016 at 5:50 PM, Michael Felt <michael at felt.demon.nl>
>> wrote:
>>
>>> b) what I am not understanding - as the basic documentation shows
>>> FOO.value as the way to set/get the value of a _field_
>>
>> You may be surprised when accessing simple-type fields such as c_int
>> and c_char_p. These simple types have getter and setter functions that
>> get called automatically whenever the type is used as a function
>> result, array index, or struct field. For example:
>
> OK - so lucky me - it "does not work" like earlier examples because I am
> referencing, generally, c_ulonglong - and these do not have a automatic
> getter/setter function? If not, how do I go about making one (preferably
> without needing to right a "method" for each and every _field_ in a class.

No, c_ulonglong is a simple (fundamental) type, which behaves just
like other simple types such as c_int or c_char_p.

On platform's with a 64-bit long, c_ulonglong is an alias for c_ulong
(i.e. type "L"). On the other hand, on 64-bit Windows, c_ulonglong is
an unsigned quad word (i.e. type "Q").

All simple types subclass ctypes._SimpleCData and define a `_type_`
code such as "c" for c_char. On 64-bit Linux the simple types are
defined as follows:

    ?: c_bool
    c: c_char
    z: c_char_p
    u: c_wchar
    Z: c_wchar_p
    P: c_void_p

    b: c_int8, c_byte
    B: c_uint8, c_ubyte
    h: c_int16, c_short
    H: c_uint16, c_ushort
    i: c_int32, c_int
    I: c_uint32, c_uint
    l: c_int64, c_long, c_longlong, c_ssize_t
    L: c_uint64, c_ulong, c_ulonglong, c_size_t

    f: c_float
    d: c_double
    g: c_longdouble

For convenience, simple types are automatically converted when
accessed as a function result, struct field, or array index. As I
mentioned previously, the only way around this behavior is to use a
subclass. A subclass doesn't get automatically converted because it
might define custom methods and attributes that need to be preserved.

>> I'd alias the type instead of defining a struct, e.g. `time_t =
>> c_long`. This preserves automatic conversion of the simple type.
>
> The reason for the not using alias is because a) I was trying to be more
> inline with the text of the include file. I will have to check the sizeof
> c_long (i.e., sizeof(long) in both 32 and 64-bit modes

I don't follow. Currently you wrap a c_int or c_long in a struct when
you could just use those types directly. You have to check the pointer
size, but then it's up to you what assumptions to make about the
target platform's integer sizes. Currently on a 64-bit system you're
assuming a Unix-style LP64 data model [1], in which a long is 64-bit.
That should be fine if you're writing Unix-specific code that doesn't
care about LLP64 Windows systems.

Wrapping the type in a struct provides more type safety, but if I
wanted that I'd create my own simple subclass. For example, assuming
time_t should be a signed integer that's the same size as a pointer:

    class time_t(ctypes._SimpleCData):
        if ctypes.sizeof(ctypes.c_void_p) == ctypes.sizeof(ctypes.c_longlong):
            _type_ = ctypes.c_longlong._type_
        elif ctypes.sizeof(ctypes.c_void_p) == ctypes.sizeof(ctypes.c_long):
            _type_ = ctypes.c_long._type_
        elif ctypes.sizeof(ctypes.c_void_p) == ctypes.sizeof(ctypes.c_int):
            _type_ = ctypes.c_int._type_
        # else raise AttributeError for missing _type_

[1]: https://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models



More information about the Python-list mailing list