ctypes приводят c_uint64 к c_char_p - PullRequest
2 голосов
/ 14 июля 2011

from ctypes import * и

In [27]: sizeof(c_char_p)
Out[27]: 8

In [28]: sizeof(c_uint64)
Out[28]: 8

In [29]: cast(c_uint64(0), c_char_p)
---------------------------------------------------------------------------
ArgumentError                             Traceback (most recent call last)

/Users/az/Programmierung/PyCPython/<ipython console> in <module>()

/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/ctypes/__init__.pyc in cast(obj, typ)
    479 _cast = PYFUNCTYPE(py_object, c_void_p, py_object, py_object)(_cast_addr)
    480 def cast(obj, typ):
--> 481     return _cast(obj, obj, typ)
    482 
    483 _string_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_string_at_addr)

ArgumentError: argument 1: <type 'exceptions.TypeError'>: wrong type

Почему cast терпит неудачу?

Есть ли альтернатива ctypes.cast, которая всегда работает, если sizeof оба типа одинаковы

Ответы [ 3 ]

3 голосов
/ 14 июля 2011

Является ли c_uint64 адресом памяти? Если это так, то вы можете сделать это:

>>> n = c_uint64(1234567890) #assume this is a valid memory address...beware of segfaults
>>> p = c_char_p(n.value)
>>> #or..
>>> p.value = n.value #special semantics for c_char_p - accepts both addresses and strings

(см. http://docs.python.org/library/ctypes.html#ctypes.c_char_p)

Или, если вы хотите переосмыслить значение, хранящееся в c_uint64, как 8-байтовый символьный буфер с нулевым символом в конце, то вам нужно привести указатель к c_uint64 как c_char_p ...

>>> n = c_uint64(ord("A"))
>>> n
c_ulong(65L)
>>> p = cast(pointer(n), c_char_p)
>>> p
c_char_p(47101614291216)
>>> p.value
'A'

Похоже, что ctypes защитит вас от переполнения буфера, если он не завершен нулем:

>>> n2 = c_uint64(0xFFFFFFFFFFFFFFFF)
>>> p2 = cast(pointer(n2), c_char_p)
>>> p2.value[0]
'\xff'
>>> p2.value
'\xff\xff\xff\xff\xff\xff\xff\xff'
>>> p2.value[9]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: string index out of range

ОБНОВЛЕНИЕ в ответ на комментарий Альберта:

Я хочу знать, почему здесь не работает приведение

Ответ на вопрос почему в документации и коде - http://docs.python.org/library/ctypes.html#ctypes.cast

ctypes.cast (obj, type) Эта функция аналогична оператору приведения в C. Она возвращает новый экземпляр типа, который указывает на тот же блок памяти, что и obj. тип должен быть указателем, , а объект должен быть объектом, который может быть интерпретируется как указатель.

Договор применяется в коде в следующем виде:

_cast = PYFUNCTYPE(py_object, c_void_p, py_object, py_object)(_cast_addr)
def cast(obj, typ):
    return _cast(obj, obj, typ)

Таким образом, первый аргумент для приведения (объект) должен быть преобразован в c_void_p. В источнике проверьте c_void_p_from_param (). Здесь делается преобразование в c_void_p. Существуют конвертеры для целых чисел Python, строк Python, строк Unicode Python, c_void_p, массива / указателя ctypes, результатов byref, указателей на функции, c_char_p / c_wchar_p и любого объекта, для которого определен метод _as_parameter_ ().

Нет конвертеров для целочисленных объектов ctypes. Я смотрю только на код 2.6 (поскольку это то, что вы используете), так что это может быть не так в 2.7 или 3.x.

Что касается обоснования - этот вопрос должен быть представлен разработчикам.

... если есть универсальная версия, которая всегда работает (во всех случаях не просто c_char_p).

Насколько я знаю, решение такое же, как я показал в первом примере. Создайте указатели с помощью или присвойте элементу значения объектов-указателей целые числа Python (конвертер знает, как преобразовать целые числа Python). cast () не будет работать, потому что это было реализовано.

0 голосов
/ 19 июля 2011

Сейчас я иду по этому пути:

def isPointerType(t):
    if issubclass(t, _ctypes._Pointer): return True
    return False

def getValueOf(obj):
    if isPointerType(obj.__class__):
        return cast(obj, c_void_p).value
    else:
        return obj.value

def static_cast(obj, t):
    newObj = t()
    if isPointerType(t):
        cast(pointer(obj), POINTER(c_void_p)).contents.value = getValueOf(obj)
    else:
        newObj.value = getValueOf(obj)
    return newObj

Выглядит несколько сложно, но я не нашел более простого способа.

0 голосов
/ 14 июля 2011

Вы должны получить указатель на значение, проверьте это:

In [29]: i = c_uint32(0x30313233)

In [30]: cast(pointer(i), c_char_p).value
Out[30]: '3210'
...