Using Python 3.8.
I'm using a ctypes structure with a field that is a c_char array When I check the type of the field in the class, I see one thing, and when I check the type of the same field in an instance of the class, I get something else. See in the following code:
import ctypes
from builtins import getattr, type
class my_c_struct(ctypes.Structure):
_fields_ = [
('a_floats', ctypes.c_float * 16),
("b_string", ctypes.c_char * 8)
]
ins = my_c_struct()
for field_name, field_type in my_c_struct._fields_:
ins_attr = getattr(ins, field_name)
print(f'Field Name: {field_name}')
print(f'class type is {field_type}')
print(f'Instance type is {type(ins_attr)}')
The output is:
Field Name: a_floats
class type is <class '__main__.c_float_Array_16'>
Instance type is <class '__main__.c_float_Array_16'>
Field Name: b_string
class type is <class '__main__.c_char_Array_8'>
Instance type is <class 'bytes'>
The float field looks good - same type for class and instance. But the 'string' field is inconsistent. Furthermore, if I try to set a char array to the field in the instance as follows:
b_value = ctypes.create_string_buffer(bytes('abc', 'utf8'), 8)
setattr(ins, 'b_string', b_value)
I get a TypeError:
setattr(ins, 'b_string', b_value)
TypeError: expected bytes, c_char_Array_8 found
What am I missing?
CodePudding user response:
ctypes does some conversions to Python standard types for some ctypes types. c_char happens to be one of them. Consider:
import ctypes
class my_c_struct(ctypes.Structure):
_fields_ = (('a_floats', ctypes.c_float * 16),
("b_string", ctypes.c_char * 8))
ins = my_c_struct()
ins.a_floats = (ctypes.c_float * 16)(*range(16)) # needs to be the correct type
ins.b_string = b'abc' # convenience, use byte strings
print(ins.a_floats) # doesn't print the contents of array by default
print(list(ins.a_floats)) # extra work to see the contents
print(ins.b_string) # convenience, byte string output
ins.b_string = b'abcdefghi' # but still limits to array size, generates error
Output:
<__main__.c_float_Array_16 object at 0x000002A2DE2174C0>
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0]
b'abc'
Traceback (most recent call last):
File "C:\test.py", line 15, in <module>
ins.b_string = b'abcdefghi'
ValueError: bytes too long (9, maximum length 8)
If you want the c_float-type behavior, use c_byte or c_ubyte instead.
Other types that convert I can think of are c_char_p, c_wchar_p, and c_wchar. Sometimes you need the real pointer for pointer types. In those cases use, for example, POINTER(c_char).
