首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >c_ulong重移位的C类型重新实现

c_ulong重移位的C类型重新实现
EN

Stack Overflow用户
提问于 2013-03-22 12:41:49
回答 1查看 2.1K关注 0票数 5

我正在通过ctype访问一个C库,并且遇到了以下问题:

我正在使用ctypeslib生成一个“包装器”(用于使用ctype访问库的ctype命令)。C库包含宏,在此步骤中将宏转换为python函数。(为了尽可能地独立于库内部,我想在python中使用其中的一些宏。)

其中一个宏如下所示:

代码语言:javascript
代码运行次数:0
运行
复制
# using the ctypes types
myuint16_t = c_ushort
myuint32_t = c_ulong

def mymacro(x): return (myuint16_t)((myuint32_t)(x) >> 16) # macro

我希望以以下方式(在函数中)使用独立模块中生成的函数:

代码语言:javascript
代码运行次数:0
运行
复制
return wrapper.mymacro(valueToBeConverted) # valueToBeConverted is an int

但是,使用这一行,我得到了以下错误:

代码语言:javascript
代码运行次数:0
运行
复制
....   
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库中发生变化时,我就必须修补生成的包装器。所以我尽量避免这种情况)。

这里似乎不能使用__rshift__的c_ulong实现。

代码语言:javascript
代码运行次数:0
运行
复制
print c_ulong.__rshift__
# throws AttributeError: type object 'c_ulong' has no attribute '__rshift__'

嗯,看起来很奇怪..。因此,我决定重新实现c_ulong的c_ulong方法,以使其正常工作:

代码语言:javascript
代码运行次数:0
运行
复制
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

但这并不能解决问题。我还在犯一个错误:

代码语言:javascript
代码运行次数:0
运行
复制
a >> 16
TypeError: unsupported operand type(s) for >>: 'c_ulong' and 'int'

是否可能__rshift__方法只能用于同一类的两个实例?我尝试了以下几点:

代码语言:javascript
代码运行次数:0
运行
复制
def rshift(self, val):
    print self.value >> int(val.value)

a = c_ulong(1)
a >> c_ulong(16) 

而且起作用了。但这也意味着我仍然需要修补生成的包装器。

有人知道这里的诀窍是什么吗?

更新

@eryksun的解决方案奏效了。我正在使用:

代码语言:javascript
代码运行次数:0
运行
复制
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
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2013-03-22 16:29:32

由于_ctypes._SimpleCData类型没有Py_TPFLAGS_CHECKTYPES标志,所以2.x子类被视为在二进制操作中使用__coerce__的旧样式数字。有关调用方案和函数binary_op1中的实现,请参见binary_op1

出于演示目的,可以将此标志切换到类型对象上,您只需将其定义(使用大量void *模糊地)到tp_flags字段。

黑客攻击PyTypeObject

代码语言:javascript
代码运行次数:0
运行
复制
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的实现细节:

代码语言:javascript
代码运行次数:0
运行
复制
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))

演示

代码语言:javascript
代码运行次数:0
运行
复制
>>> 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'

最后一步如预期的失败了。现在设置旗子:

代码语言:javascript
代码运行次数:0
运行
复制
>>> c_ulong_type.tp_flags |= Py_TPFLAGS_CHECKTYPES

>>> a >> 2
__rshift__ c_ulong(16L) 2
c_ulong(4L)

问题解决了?但那是个黑客。用__coerce__实现再试一次。

实现__coerce__

代码语言:javascript
代码运行次数:0
运行
复制
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

演示

代码语言:javascript
代码运行次数:0
运行
复制
>>> 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,它就会失败。

代码语言:javascript
代码运行次数:0
运行
复制
>>> 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'
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15570610

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档