How to write partial of a buffer which was returned from a C function to a file?

eryk sun eryksun at gmail.com
Sat Apr 14 00:34:02 EDT 2018


On Sat, Apr 14, 2018 at 1:57 AM, Jach Fong <jfong at ms4.hinet.net> wrote:
> eryk sun at 2018/4/14 PM 05:27 wrote:
>
>> The simple types c_void_p, c_char_p, and c_wchar_p are pointers.
>> However, since they subclass _SimpleCData instead of _Pointer, they
>> inherit the behavior of simple types.
>
> The ctypes document says:
> "Pointer instances have a contents attribute which returns the object to
> which the pointer points"
>
>>>> buf0 = ctypes.create_string_buffer(b'spam')
>
>>>> pvoid = ctypes.c_void_p(ctypes.addressof(buf0))
>>>> pvoid.contents
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
> AttributeError: 'c_void_p' object has no attribute 'contents'
>>>> pvoid.value
> 35425816
>
>>>> pp = ctypes.pointer(buf0)
>>>> pp.contents
> <ctypes.c_char_Array_5 object at 0x021C8F30>
>>>> pp.value
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
> AttributeError: 'LP_c_char_Array_5' object has no attribute 'value'
>
> It looks like the c_void_p is not of a pointer type:-)

c_void_p is a `void *` pointer type, but it's a simple pointer type
that subclasses _SimpleCData instead of _Pointer, so it doesn't have a
`contents` property but instead has a `value` property.

    >>> hasattr(ctypes._SimpleCData, 'contents')
    False
    >>> hasattr(ctypes._SimpleCData, 'value')
    True
    >>> hasattr(ctypes._Pointer, 'contents')
    True
    >>> hasattr(ctypes._Pointer, 'value')
    False

Don't take the Python class hierarchy so literally that you overlook
what these types ultimately are in C. Just because they don't subclass
_Pointer, that doesn't mean they're not pointer types. c_void_p,
c_char_p, and c_wchar_p are unquestionably pointer types. This is what
the "_p" suffix means in their names. It's just these particular
pointer types were implemented as simple types to get the convenience
of implicit conversion. That said, sometimes the implicit conversion
is a problem, in which case we use, for example, POINTER(c_char)
instead of c_char_p.

Look in Lib/ctypes/__init__.py to review the definitions for yourself.
Here they are without the __repr__ methods:

    class c_void_p(_SimpleCData):
        _type_ = "P"

    class c_char_p(_SimpleCData):
        _type_ = "z"

    class c_wchar_p(_SimpleCData):
        _type_ = "Z"

This doesn't tell you much. You have to go looking for what it means
to be a simple "P" type, and in particular we're concerned with how
conversion to and from native Python types is implemented. You'll find
the get and set C implementations for types "P", "z", and "Z" defined
in Modules/_ctypes/cfield.c.

For example, for type "P", it's P_get() and P_set(). For P_get(), we
use PyLong_FromVoidPtr to convert the pointer value to a Python
integer, except we return None for a NULL pointer. For P_set(), we
require an integer value or None (NULL). Note that the function to
convert a Python integer to an integral address in C depends on the
size of a C `long` or `long long` compared to the size of a C `void *`
pointer. In particular, this design accommodates 64-bit Windows, on
which a `long` is 32-bit and thus too small for a 64-bit pointer
value, so we call PyLong_AsUnsignedLongLongMask instead of
PyLong_AsUnsignedLongMask.


More information about the Python-list mailing list