简体   繁体   English

c_ulong的rshift的ctypes重新实现

[英]ctypes reimplementation of rshift for c_ulong

i am accessing a C library via ctypes and i am stuck with the following problem: 我正在通过ctypes访问C库,并且遇到以下问题:

I am generating a "wrapper" (ctypes commands to access the library with ctypes) using ctypeslib. 我正在使用ctypeslib生成一个“包装器”(使用ctypes访问库的ctypes命令)。 The C library contains macros which are converted to python functions in this step. C库包含在此步骤中转换为python函数的宏。 (To be independent of the libraries internals as much as possible i want to use some of these macros in python.) (为了尽可能独立于库的内部,我想在python中使用其中的一些宏。)

One of these macros looks like this: 这些宏之一如下所示:

# 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'

(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的常用方法是c_ulongvar.value >> x但是每次C库中发生任何更改时,我都必须修补生成的包装器。因此,我尝试避免这种情况。)

It seems that the __rshift__ implementation of c_ulong can not be used here. 似乎无法在此处使用c_ulong的__rshift__实现。

print c_ulong.__rshift__
# throws AttributeError: type object 'c_ulong' has no attribute '__rshift__'

Hm, seems strange... So i decided to reimplement the __rshift__ method of c_ulong to get it working: 嗯,似乎很奇怪...所以我决定重新实现__rshift__方法以使其工作:

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'

Is it possible that the __rshift__ method can be only used for two instances of the same class? __rshift__方法是否只能用于同一类的两个实例? 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? 所以:有人知道这有什么窍门吗?

UPDATE : 更新

The solution of @eryksun worked. @eryksun的解决方案有效。 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

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. 由于_ctypes._SimpleCData类型不具有Py_TPFLAGS_CHECKTYPES标志,因此2.x子类被视为在二进制操作中使用__coerce__的旧式数字。 See Objects/abstract.c for the calling scheme and the implementation in the function binary_op1 . 有关调用方案和函数binary_op1的实现,请参见Objects / abstract.c

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. 为了演示起见,可以在类型对象上切换此标志,您只需要在tp_flags字段之前定义(可能有很多void * )。

Hacking the PyTypeObject PyTypeObject

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))

Next, create an unsigned long subclass, and use the from_address factory to create a PyTypeObject for it. 接下来,创建一个unsigned long子类,并使用from_address工厂为其创建PyTypeObject Get the address with built-in id , which is an implementation detail specific to CPython: 获取带有内置id的地址,该id是特定于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))

Demo 演示版

>>> 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)

Problem solved? 问题解决了? But that's a hack. 但这是骇客。 Try again with __coerce__ implemented. 实施__coerce__再试一次。


Implement __coerce__ 实施__coerce__

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

Demo 演示版

>>> 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)

Of course it fails if a c_ulong can't be created, such as for a float : 当然,如果无法创建c_ulong则失败,例如对于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'

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM