5

ctypesを介してCライブラリにアクセスしていますが、次の問題が発生します。

ctypeslibを使用して「ラッパー」(ctypesでライブラリにアクセスするためのctypesコマンド)を生成しています。Cライブラリには、このステップでPython関数に変換されるマクロが含まれています。(ライブラリの内部から可能な限り独立させるために、Pythonでこれらのマクロのいくつかを使用したいと思います。)

これらのマクロの1つは次のようになります。

# using the ctypes types
myuint16_t = c_ushort
myuint32_t = c_ulong

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

生成された関数を別のモジュールで次のように使用したい(関数内):

return wrapper.mymacro(valueToBeConverted) # valueToBeConverted is an int

しかし、この行を使用すると、次のエラーが発生しました。

....   
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ライブラリで何かが変更されるたびに、生成されたラッパーにパッチを適用する必要があります。したがって、これは避けようとします)。

ここではc_ulongの__rshift__実装は使用できないようです。

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

うーん、奇妙に思えます...そこで__rshift__、c_ulongのメソッドを再実装して機能させることにしました。

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

しかし、それは問題を解決しません。まだエラーが発生しています:

a >> 16
TypeError: unsupported operand type(s) for >>: 'c_ulong' and 'int'

__rshift__メソッドが同じクラスの2つのインスタンスにのみ使用できる可能性はありますか?私は次のことを試しました:

def rshift(self, val):
    print self.value >> int(val.value)

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

そしてそれは動作します。ただし、生成されたラッパーにパッチを適用する必要があることも意味します。

だから:誰かがここでのトリックを知っていますか?

更新

@eryksunのソリューションは機能しました。使ってます:

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
4

1 に答える 1

4

_ctypes._SimpleCDataタイプにはフラグがないため、 2.xサブクラスは、二項演算Py_TPFLAGS_CHECKTYPESで使用される古いスタイルの数値として扱われます。関数の呼び出しスキームと実装については、Objects/abstract.c__coerce__を参照してください。binary_op1

void *デモンストレーションの目的で、このフラグはタイプオブジェクトで切り替えることができます。このフラグは、フィールドまで(漠然と多くの場合)定義するだけで済みますtp_flags

ハッキング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))

次に、unsigned longサブクラスを作成し、from_addressファクトリを使用してそのサブクラスを作成しますPyTypeObjectidCPythonに固有の実装の詳細である組み込みのアドレスを取得します。

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'

最後のステップは期待どおりに失敗しました。次にフラグを設定します。

>>> c_ulong_type.tp_flags |= Py_TPFLAGS_CHECKTYPES

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

問題が解決しました?しかし、それはハックです。__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

デモ

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

>>> 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'
于 2013-03-22T16:29:32.217 に答える