Различать «фундаментальные» типы данных ctypes и их подклассы? - PullRequest
2 голосов
/ 07 марта 2019

Введение

Я работаю над генератором кода, который будет генерировать функции вокруг ctypes.cdll загруженных функций.Генератор будет получать информацию о ctypes аргументов и возвращаемого значения и генерировать что-то, что ведет себя (и, в некоторой степени, выглядит) следующим образом:

func = getattr(dll, 'UglyLongAndUselessCName')
func.argtypes = [ctypes.c_uint32, ctypes.c_int8, ctypes.c_char_p]
func.restype = ctypes.c_int16

def nice_python_name(handle: int, arg1: int, arg2: str) -> int:
    return func(handle, arg1, arg2)

Обратите внимание, как играют аннотации типа Pythonхорошо с ctypes типами данных аргументов функции.Также обратите внимание, что в функциях nice_python_name и func нет кода преобразования между типами python.Вот о чем мой вопрос.

Знакомство с проблемой

В документах ctypes говорится, что если атрибут argtypes загруженной функции DLL указан с использованием "основных типов данных"msgstr ", то при вызове загруженных функций DLL ctypes выполнит преобразование в типы python.Это здорово, потому что в этом случае мой сгенерированный код будет выглядеть как в примере выше - мне не нужно явно преобразовывать ctypes объекты в значения типа Python для возвращаемых значений и наоборот для аргументов.

Однако в документах также говорится, что для «подклассов основных типов данных» этот трюк не сработает, и вызов загруженной функции DLL потребует ctypes объектов для аргументов, а в результате будет ctypes объект.

Это выдержка из ctypes документов об этом:

Основные типы данных, когда возвращаются как результаты вызова сторонней функции, или, например,путем извлечения членов поля структуры или элементов массива прозрачно преобразуются в собственные типы Python.Другими словами, если сторонняя функция имеет restype из c_char_p, вы всегда получите объект байтов Python, а не экземпляр c_char_p.

Подклассы основных типов данных не наследуют это поведение,Итак, если сторонняя функция restype является подклассом c_void_p, вы получите экземпляр этого подкласса из вызова функции.Конечно, вы можете получить значение указателя, обратившись к атрибуту value.

Итак, я хотел бы обойти это.

Кажется, мне нужно знать,тип является "фундаментальным" или "подклассом".Это поможет мне определить способ генерации кода, то есть для «фундаментальных» типов сгенерированный код будет выглядеть аналогично приведенному выше примеру, а для «подклассов фундаментальных» типов он будет иметь дополнительное преобразование из ctypes объектов в разумныетипы python (или генератор просто выдаст исключение, говорящее «это не поддерживается»).

Вопрос:

Как я могу различить «фундаментальные ctypes типы данных» и «подклассы»фундаментальных ctypes типов данных "?

Я изучил код модуля ctypes python и обнаружил, что c_void_p и c_char_p являются подклассами ctypes._SimpleCData, следовательно, один не является подклассомс другой стороны.

Кроме того, я неправильно понимаю, что эта проблема также относится к входным аргументам, или это только сделка для возвращаемых значений?

1 Ответ

1 голос
/ 08 марта 2019

... я неправильно понимаю, что эта проблема также относится к входным аргументам, или это только сделка для возвращаемых значений?

Это не относится к входным аргументам, как показано в следующей последовательности:

>>> dll=CDLL('msvcrt')
>>> dll.printf.argtypes = c_char_p,
>>> dll.printf(b'abc') # Note: 3 is the return value of printf
abc3
>>> class LPCSTR(c_char_p): # define a subtype
...  pass
...
>>> dll.printf.argtypes = LPCSTR,
>>> dll.printf(b'abc')
abc3

Преобразования все еще работают для входных подтипов; однако выходные подтипы работают иначе, как упоминалось в вашей цитате из документа:

>>> dll.ctime.argtypes = c_void_p,
>>> dll.ctime.restype = c_char_p
>>> dll.ctime(byref(c_int(5)))
b'Wed Dec 31 16:00:05 1969\n'
>>> dll.ctime.restype = LPCSTR
>>> dll.ctime(byref(c_int(5))) # not converted to Python byte string
LPCSTR(1989707373328)
>>> x = dll.ctime(byref(c_int(5))) # but can get the value
>>> x.value
b'Wed Dec 31 16:00:05 1969\n'
...