i am accessing a C library via ctypes and i am stuck with the following problem:
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.)
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).
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__'
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'
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?
UPDATE:
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
Since the
_ctypes._SimpleCData
type doesn't have thePy_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 functionbinary_op1
.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 thetp_flags
field.Hacking the
PyTypeObject
Next, create an
unsigned long
subclass, and use thefrom_address
factory to create aPyTypeObject
for it. Get the address with built-inid
, which is an implementation detail specific to CPython:Demo
The last step failed as expected. Now set the flag:
Problem solved? But that's a hack. Try again with
__coerce__
implemented.Implement
__coerce__
Demo
Of course it fails if a
c_ulong
can't be created, such as for afloat
: