These are simple variable declarations in cpp, how do I do it in Python ctypes?
B *C = (B *) A;
D *E = (D *)(A + sizeof(B));
Assume that B and D are structs and A is uint8_t A[42];
. Where do I start from here?
I tried using cast functions but maybe I'm wrong, can you help me?
from ctypes import POINTER, byref, addressof, sizeof, cast
C = cast(byref(A), POINTER(B)).contents
E = cast(addressof(A) + sizeof(B), POINTER(D)).contents
Listing [Python.Docs]: ctypes - A foreign function library for Python.
In short, to:
Dereference a pointer - cts_ptr_var.contents
Reference a variable (to a pointer) - ctypes.pointer(cts_var)
(to later pass it as an argument to a function - ctypes.byref(cts_var)
)
Get its (C) address - ctypes.addressof(cts_var)
Now, regarding your question, a nicer way would be to use (each CTypes type's) from_address (and avoid those pointers).
code00.py:
#!/usr/bin/env python
import ctypes as cts
import sys
I8Arr42 = cts.c_uint8 * 42
class S0(cts.Structure):
if len(sys.argv) > 1: # @TODO - cfati: Lame (for demo purposes only)
_pack_ = 1
_fields_ = (
("ui80", cts.c_uint8),
("ui81", cts.c_uint8),
("ui320", cts.c_uint32),
("ui82", cts.c_uint8),
)
PS0 = cts.POINTER(S0)
class S1(cts.Structure):
if len(sys.argv) > 1: # @TODO
_pack_ = 1
_fields_ = (
("ui80", cts.c_uint8),
("ui320", cts.c_uint32),
("ui81", cts.c_uint8),
)
PS1 = cts.POINTER(S1)
def main(*argv):
size0 = cts.sizeof(S0)
arr = I8Arr42(*range(42))
#arr = I8Arr42()
print(arr, hex(cts.addressof(arr)), hex(id(arr)))
print(f"Initial values: {arr[0]}, {arr[size0]}, {arr[-1]}\n")
print("FromAddress")
s00 = S0.from_address(cts.addressof(arr)) # Initialize structure from memory contents at address
print("First structure:")
for name, _ in s00. _fields_:
print(f" {name}: 0x{getattr(s00, name):02X}")
s10 = S1.from_address(cts.addressof(arr) + size0) # Initialize structure from memory contents at address
print("\nSecond structure:")
for name, _ in s10. _fields_:
print(f" {name}: 0x{getattr(s10, name):02X}")
s00.ui80 = 100
s10.ui80 = 101
print(f"\nModified values: {arr[0]}, {arr[size0]}, {arr[-1]}\n")
print("\nCast")
s01 = cts.cast(cts.addressof(arr), PS0).contents
print("First structure:")
for name, _ in s01. _fields_:
print(f" {name}: 0x{getattr(s01, name):02X}")
s11 = cts.cast(cts.addressof(arr) + size0, PS1).contents
print("\nSecond structure:")
for name, _ in s11. _fields_:
print(f" {name}: 0x{getattr(s11, name):02X}")
s01.ui80 = 200
s11.ui80 = 201
print(f"\nModified values: {arr[0]}, {arr[size0]}, {arr[-1]}\n")
if __name__ == "__main__":
print(
"Python {:s} {:03d}bit on {:s}\n".format(
" ".join(elem.strip() for elem in sys.version.split("\n")),
64 if sys.maxsize > 0x100000000 else 32,
sys.platform,
)
)
rc = main(*sys.argv[1:])
print("\nDone.\n")
sys.exit(rc)
Output:
[cfati@CFATI-5510-0:e:\Work\Dev\StackExchange\StackOverflow\q078494178]> sopr.bat ### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ### [prompt]> :: No argument - default (structure) packing [prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.10_test0\Scripts\python.exe" ./code00.py Python 3.10.11 (tags/v3.10.11:7d4cc5a, Apr 5 2023, 00:38:17) [MSC v.1929 64 bit (AMD64)] 064bit on win32 <__main__.c_ubyte_Array_42 object at 0x000001E9E8C04140> 0x1e9e8fffdb0 0x1e9e8c04140 Initial values: 0, 12, 41 FromAddress First structure: ui80: 0x00 ui81: 0x01 ui320: 0x7060504 ui82: 0x08 Second structure: ui80: 0x0C ui320: 0x13121110 ui81: 0x14 Modified values: 100, 101, 41 Cast First structure: ui80: 0x64 ui81: 0x01 ui320: 0x7060504 ui82: 0x08 Second structure: ui80: 0x65 ui320: 0x13121110 ui81: 0x14 Modified values: 200, 201, 41 Done. [prompt]> :: Dummy argument - #pragma pack(1) [prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.10_test0\Scripts\python.exe" ./code00.py dummy Python 3.10.11 (tags/v3.10.11:7d4cc5a, Apr 5 2023, 00:38:17) [MSC v.1929 64 bit (AMD64)] 064bit on win32 <__main__.c_ubyte_Array_42 object at 0x000001A85B484140> 0x1a85b87ff30 0x1a85b484140 Initial values: 0, 7, 41 FromAddress First structure: ui80: 0x00 ui81: 0x01 ui320: 0x5040302 ui82: 0x06 Second structure: ui80: 0x07 ui320: 0xB0A0908 ui81: 0x0C Modified values: 100, 101, 41 Cast First structure: ui80: 0x64 ui81: 0x01 ui320: 0x5040302 ui82: 0x06 Second structure: ui80: 0x65 ui320: 0xB0A0908 ui81: 0x0C Modified values: 200, 201, 41 Done.
For future tasks, you might want to check [SO]: C function called from Python via ctypes returns incorrect value (@CristiFati's answer) for a common pitfall when working with CTypes (calling functions).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With