c_ulong的rshift的ctypes重新实现 [英] ctypes reimplementation of rshift for c_ulong
问题描述
我正在通过ctypes访问C库,但遇到以下问题:
i am accessing a C library via ctypes and i am stuck with the following problem:
我正在使用ctypeslib生成包装器"(ctypes命令以使用ctypes访问库). C库包含在此步骤中转换为python函数的宏. (为了尽可能独立于库的内部,我想在python中使用其中的一些宏.)
I am generating a "wrapper" (ctypes commands to access the library with ctypes) using ctypeslib. The C library contains macros which are converted to python functions in this step. (To be independent of the libraries internals as much as possible i want to use some of these macros in python.)
其中一个宏如下所示:
# using the ctypes types
myuint16_t = c_ushort
myuint32_t = c_ulong
def mymacro(x): return (myuint16_t)((myuint32_t)(x) >> 16) # macro
我想通过以下方式(在函数内部)在单独的模块中使用生成的函数:
I want to use the generated function in a seperate module in the following way (inside a function):
return wrapper.mymacro(valueToBeConverted) # valueToBeConverted is an int
但是使用这一行,我遇到了以下错误:
But using this line i got the following error:
....
def mymacro(x): return (myuint16_t)((myuint32_t)(x) >> 16) # macro
TypeError: unsupported operand type(s) for >>: 'c_ulong' and 'int'
(我知道移位c_ulong的常见方法是c_ulongvar.value >> x
,但是每次C库中发生某些更改时,我都必须修补生成的包装器.因此,我尝试避免这种情况.)
(I know that the common way to shift a c_ulong is c_ulongvar.value >> x
but i would have to patch the generated wrapper every time something changes in the C library. So i try to avoid this).
似乎无法在此处使用c_ulong的__rshift__
实现.
It seems that the __rshift__
implementation of c_ulong can not be used here.
print c_ulong.__rshift__
# throws AttributeError: type object 'c_ulong' has no attribute '__rshift__'
嗯,似乎很奇怪...所以我决定重新实现c_ulong的__rshift__
方法以使其正常工作:
Hm, seems strange... So i decided to reimplement the __rshift__
method of c_ulong to get it working:
from ctypes import *
from types import MethodType
def rshift(self, val):
print self.value >> val
# create an unbound method which applies to all (even existing) instances
c_ulong.__rshift__ = MethodType(rshift, None, c_ulong)
a = c_ulong(1)
a >> 16
但是它不能解决问题.我仍然遇到错误:
But it does not fix the problem. I am still getting an error:
a >> 16
TypeError: unsupported operand type(s) for >>: 'c_ulong' and 'int'
__rshift__
方法是否可能仅可用于同一类的两个实例?我尝试了以下方法:
Is it possible that the __rshift__
method can be only used for two instances of the same class? I tried the following:
def rshift(self, val):
print self.value >> int(val.value)
a = c_ulong(1)
a >> c_ulong(16)
,并且有效.但这也意味着我仍然必须修补生成的包装器.
and it works. But it would also mean i would still have to patch the generated wrapper.
所以:有人知道这有什么窍门吗?
So: Does anybody know whats the trick here?
更新:
@eryksun的解决方案奏效.我正在使用:
The solution of @eryksun worked. I am using:
from ctypes import *
# from types import MethodType
def _rshift(self, other):
if hasattr(other, 'value'):
other = other.value
return c_ulong(self.value >> other)
def _lshift(self, other):
if hasattr(other, 'value'):
other = other.value
return c_ulong(self.value << other)
def _coerce(self, other):
try:
return self, self.__class__(other)
except TypeError:
return NotImplemented
# Add the functions to the type. A method is created when
# accessed as an attribute of an instance.
c_ulong.__lshift__ = _lshift
c_ulong.__rshift__ = _rshift
c_ulong.__coerce__ = _coerce
推荐答案
由于_ctypes._SimpleCData
类型没有Py_TPFLAGS_CHECKTYPES
标志,因此2.x子类被视为使用__coerce__
的旧式数字二进制运算.请参阅 Objects/abstract.c 进行调用方案和功能binary_op1
中的实现.
Since the _ctypes._SimpleCData
type doesn't have the Py_TPFLAGS_CHECKTYPES
flag, 2.x subclasses are treated as old-style numbers that use __coerce__
in binary operations. See Objects/abstract.c for the calling scheme and the implementation in the function binary_op1
.
出于演示目的,可以在类型对象上切换此标志,您只需要在tp_flags
字段中定义(模糊地包含很多void *
).
For demonstration purposes this flag can be toggled on the type object, which you only need to define (vaguely with a lot of void *
) up to the tp_flags
field.
from ctypes import *
import _ctypes
Py_TPFLAGS_CHECKTYPES = 1 << 4
class PyTypeObject(Structure):
_fields_ = (('ob_refcnt', c_ssize_t),
('ob_type', c_void_p),
('ob_size', c_ssize_t),
('tp_name', c_char_p),
('tp_basicsize', c_ssize_t),
('tp_itemsize', c_ssize_t),
('tp_dealloc', c_void_p),
('tp_print', c_void_p),
('tp_getattr', c_void_p),
('tp_setattr', c_void_p),
('tp_compare', c_void_p),
('tp_repr', c_void_p),
('tp_as_number', c_void_p),
('tp_as_sequence', c_void_p),
('tp_as_mapping', c_void_p),
('tp_hash', c_void_p),
('tp_call', c_void_p),
('tp_str', c_void_p),
('tp_getattro', c_void_p),
('tp_setattro', c_void_p),
('tp_as_buffer', c_void_p),
('tp_flags', c_long))
接下来,创建一个unsigned long
子类,并使用from_address
工厂为其创建一个PyTypeObject
.使用内置的id
获取地址,该地址是特定于CPython的实现细节:
Next, create an unsigned long
subclass, and use the from_address
factory to create a PyTypeObject
for it. Get the address with built-in id
, which is an implementation detail specific to CPython:
class c_ulong(_ctypes._SimpleCData):
_type_ = "L"
def __rshift__(self, other):
print '__rshift__', self, other
if hasattr(other, 'value'):
other = other.value
return c_ulong(self.value >> other)
c_ulong_type = PyTypeObject.from_address(id(c_ulong))
演示
>>> a = c_ulong(16)
>>> b = c_ulong(2)
>>> a >> b
__rshift__ c_ulong(16L) c_ulong(2L)
c_ulong(4L)
>>> a >> 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for >>: 'c_ulong' and 'int'
最后一步按预期失败.现在设置标志:
The last step failed as expected. Now set the flag:
>>> c_ulong_type.tp_flags |= Py_TPFLAGS_CHECKTYPES
>>> a >> 2
__rshift__ c_ulong(16L) 2
c_ulong(4L)
问题解决了吗?但这是骇客.再次尝试实施__coerce__
.
Problem solved? But that's a hack. Try again with __coerce__
implemented.
class c_ulong(_ctypes._SimpleCData):
_type_ = "L"
def __rshift__(self, other):
print '__rshift__', self, other
if hasattr(other, 'value'):
other = other.value
return c_ulong(self.value >> other)
def __coerce__(self, other):
print '__coerce__', self, other
try:
return self, self.__class__(other)
except TypeError:
return NotImplemented
演示
>>> a = c_ulong(16)
>>> b = c_ulong(2)
>>> a >> 2
__coerce__ c_ulong(16L) 2
__rshift__ c_ulong(16L) c_ulong(2L)
c_ulong(4L)
>>> 16 >> b
__coerce__ c_ulong(2L) 16
__rshift__ c_ulong(16L) c_ulong(2L)
c_ulong(4L)
当然,如果无法创建c_ulong
,例如float
,则会失败:
Of course it fails if a c_ulong
can't be created, such as for a float
:
>>> a >> 2.0
__coerce__ c_ulong(16L) 2.0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for >>: 'c_ulong' and 'float'
这篇关于c_ulong的rshift的ctypes重新实现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!