const void* pointer in ctypes
Asked Answered
A

2

8

If I have a writable buffer, I can use ctypes.c_void_p.from_buffer function to obtain a C pointer to this buffer.

How to deal with non-writable buffers, however? How to form a const pointer that I can pass to C code that expects a const void* without resorting to making a writable copy of the non-writable buffer?

I considered c_void_p.from_address but buffers (and memoryviews) don't seem to expose their address.


Some clarification:

>>> import ctypes
>>> b = buffer("some data that supports the buffer interface, like a str")
>>> ptr = ctypes.c_void_p.from_buffer(b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: buffer is read-only
>>> ptr = ctypes.c_void_p.from_buffer_copy(b)    # works, but has to copy data
>>> ptr = ctypes.CONST(c_void_p).from_buffer(b)  # (I'm making this one up)
>>> ptr = ctypes.c_void_p.from_address(???)      # could work; how to get addr?

That would work with void some_api(const void* read_only_data) like:

>>> ctypes.cdll.LoadLibrary(some_lib).some_api(ptr)

The method with from_buffer_copy works, but needs to copy the buffer first. I'm looking to way to work around the requirement of the buffer being writable, because nobody is going to write there and I want to avoid redundant copying of data.

Arabist answered 12/12, 2012 at 17:47 Comment(4)
Can you give some example code?Agamemnon
There we go, I hope it's clear nowArabist
So this may be useful #121896 . I have tried playing around with the suggestions therein, but with limited success. The trick is most likely to write a PythonC_API function to take the buffer and return its address. That should be relatively easy to do.Agamemnon
In the case of a void*, a str/unicode (Python 2.x) or bytes/str (Python 3.x) can just be passed directly to the function.Abscise
O
0

You can cast a Python string to char* with ctypes, and that points to actual memory there Python string data is stored.

Feel free to double-cast.

Octonary answered 16/4, 2015 at 9:51 Comment(0)
A
0

In the case of a void*, a str/unicode (Python 2.x) or bytes/str (Python 3.x) can just be passed directly to the function. They will be marshaled as a pointer to the bytes of the value, or in the case of a Unicode string, a pointer to the OS-appropriate UTF-encoded bytes of the string, e.g. UTF-16LE on Windows.

Example (Python 3.x):

import ctypes as ct

crt = ct.CDLL('msvcrt')

# void* memcpy(void* dest, const void* src, size_t count);
memcpy = crt.memcpy
memcpy.argtypes = ct.c_void_p, ct.c_void_p, ct.c_size_t
memcpy.restype = ct.c_void_p

dest = ct.create_string_buffer(10)  # writeable buffer
src = bytes([1,2,3,4,5])  # immutable bytes
print(dest.raw.hex(' '))
memcpy(dest, src, len(src))
print(dest.raw.hex(' '))
src = '马克😉'  # immutable str (will marshal as UTF-16LE bytes on Windows)
memcpy(dest, src, len(src.encode('utf-16le')))
print(dest.raw.hex(' '))
print(dest.raw.decode('utf-16le'))

Output:

00 00 00 00 00 00 00 00 00 00
01 02 03 04 05 00 00 00 00 00
6c 9a 4b 51 3d d8 09 de 00 00
马克😉
Abscise answered 30/9, 2024 at 22:56 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.