Author: Wim Lavrijsen <wlavrij...@lbl.gov> Branch: cppyy-packaging Changeset: r94860:1214d76af168 Date: 2018-07-13 11:44 -0700 http://bitbucket.org/pypy/pypy/changeset/1214d76af168/
Log: support low-level views of arrays that allow user-side updates to shape diff --git a/pypy/module/_cppyy/converter.py b/pypy/module/_cppyy/converter.py --- a/pypy/module/_cppyy/converter.py +++ b/pypy/module/_cppyy/converter.py @@ -1,15 +1,12 @@ import sys from pypy.interpreter.error import OperationError, oefmt - from rpython.rtyper.lltypesystem import rffi, lltype from rpython.rlib.rarithmetic import r_singlefloat, r_longfloat from rpython.rlib import rfloat, rawrefcount - from pypy.module._rawffi.interp_rawffi import letter2tp from pypy.module._rawffi.array import W_ArrayInstance - -from pypy.module._cppyy import helper, capi, ffitypes +from pypy.module._cppyy import helper, capi, ffitypes, lowlevelviews # Converter objects are used to translate between RPython and C++. They are # defined by the type name for which they provide conversion. Uses are for @@ -149,7 +146,8 @@ # read access, so no copy needed address_value = self._get_raw_address(space, w_obj, offset) address = rffi.cast(rffi.ULONG, address_value) - return W_ArrayInstance(space, letter2tp(space, self.typecode), self.size, address) + return lowlevelviews.W_LowLevelView( + space, letter2tp(space, self.typecode), self.size, address) def to_memory(self, space, w_obj, w_value, offset): # copy the full array (uses byte copy for now) @@ -190,7 +188,8 @@ # read access, so no copy needed address_value = self._get_raw_address(space, w_obj, offset) address = rffi.cast(rffi.ULONGP, address_value) - return W_ArrayInstance(space, letter2tp(space, self.typecode), self.size, address[0]) + return lowlevelviews.W_LowLevelView( + space, letter2tp(space, self.typecode), self.size, address[0]) def to_memory(self, space, w_obj, w_value, offset): # copy only the pointer value @@ -438,7 +437,7 @@ from pypy.module._cppyy import interp_cppyy return interp_cppyy.get_nullptr(space) shape = letter2tp(space, 'P') - return W_ArrayInstance(space, shape, sys.maxint/shape.size, ptrval) + return lowlevelviews.W_LowLevelView(space, shape, sys.maxint/shape.size, ptrval) def to_memory(self, space, w_obj, w_value, offset): address = rffi.cast(rffi.VOIDPP, self._get_raw_address(space, w_obj, offset)) diff --git a/pypy/module/_cppyy/executor.py b/pypy/module/_cppyy/executor.py --- a/pypy/module/_cppyy/executor.py +++ b/pypy/module/_cppyy/executor.py @@ -1,14 +1,10 @@ import sys from pypy.interpreter.error import oefmt - from rpython.rtyper.lltypesystem import rffi, lltype from rpython.rlib import jit_libffi - from pypy.module._rawffi.interp_rawffi import letter2tp -from pypy.module._rawffi.array import W_Array, W_ArrayInstance - -from pypy.module._cppyy import helper, capi, ffitypes +from pypy.module._cppyy import helper, capi, ffitypes, lowlevelviews # Executor objects are used to dispatch C++ methods. They are defined by their # return type only: arguments are converted by Converter objects, and Executors @@ -60,7 +56,7 @@ from pypy.module._cppyy import interp_cppyy return interp_cppyy.get_nullptr(space) shape = letter2tp(space, self.typecode) - return W_ArrayInstance(space, shape, sys.maxint/shape.size, ptrval) + return lowlevelviews.W_LowLevelView(space, shape, sys.maxint/shape.size, ptrval) class VoidExecutor(Executor): diff --git a/pypy/module/_cppyy/lowlevelviews.py b/pypy/module/_cppyy/lowlevelviews.py new file mode 100644 --- /dev/null +++ b/pypy/module/_cppyy/lowlevelviews.py @@ -0,0 +1,54 @@ +# Naked C++ pointers carry no useful size or layout information, but often +# such information is externnally available. Low level views are arrays with +# a few more methods allowing such information to be set. Afterwards, it is +# simple to pass these views on to e.g. numpy (w/o the need to copy). + +from pypy.interpreter.error import oefmt +from pypy.interpreter.gateway import interp2app, unwrap_spec +from pypy.interpreter.typedef import TypeDef, GetSetProperty, interp_attrproperty_w +from pypy.module._rawffi.array import W_ArrayInstance + + +class W_LowLevelView(W_ArrayInstance): + def __init__(self, space, shape, length, address): + assert address # if not address, base class will allocate memory + W_ArrayInstance.__init__(self, space, shape, length, address) + + @unwrap_spec(args_w='args_w') + def reshape(self, space, args_w): + # llviews are only created from non-zero addresses, so we only need + # to adjust length and shape + + nargs = len(args_w) + if nargs == 0: + raise oefmt(space.w_TypeError, "reshape expects a tuple argument") + + newshape_w = args_w + if nargs != 1 or not space.isinstance_w(args_w[0], space.w_tuple) or \ + not space.len_w(args_w[0]) == 1: + raise oefmt(space.w_TypeError, + "tuple object of length 1 expected, received %T", args_w[0]) + + w_shape = args_w[0] + + # shape in W_ArrayInstance-speak is somewhat different from what + # e.g. numpy thinks of it: self.shape contains the info (itemcode, + # size, etc.) of a single entry; length is user-facing shape + self.length = space.int_w(space.getitem(w_shape, space.newint(0))) + + +W_LowLevelView.typedef = TypeDef( + 'LowLevelView', + __repr__ = interp2app(W_LowLevelView.descr_repr), + __setitem__ = interp2app(W_LowLevelView.descr_setitem), + __getitem__ = interp2app(W_LowLevelView.descr_getitem), + __len__ = interp2app(W_LowLevelView.getlength), + buffer = GetSetProperty(W_LowLevelView.getbuffer), + shape = interp_attrproperty_w('shape', W_LowLevelView), + free = interp2app(W_LowLevelView.free), + byptr = interp2app(W_LowLevelView.byptr), + itemaddress = interp2app(W_LowLevelView.descr_itemaddress), + reshape = interp2app(W_LowLevelView.reshape), +) +W_ArrayInstance.typedef.acceptable_as_base_class = False + diff --git a/pypy/module/_cppyy/test/test_datatypes.py b/pypy/module/_cppyy/test/test_datatypes.py --- a/pypy/module/_cppyy/test/test_datatypes.py +++ b/pypy/module/_cppyy/test/test_datatypes.py @@ -181,7 +181,7 @@ names = ['uchar', 'short', 'ushort', 'int', 'uint', 'long', 'ulong'] import array a = range(self.N) - atypes = ['B', 'h', 'H', 'i', 'I', 'l', 'L' ] + atypes = ['B', 'h', 'H', 'i', 'I', 'l', 'L'] for j in range(len(names)): b = array.array(atypes[j], a) setattr(c, 'm_'+names[j]+'_array', b) # buffer copies @@ -189,6 +189,7 @@ assert eval('c.m_%s_array[i]' % names[j]) == b[i] setattr(c, 'm_'+names[j]+'_array2', b) # pointer copies + assert 3 < self.N b[3] = 28 for i in range(self.N): assert eval('c.m_%s_array2[i]' % names[j]) == b[i] @@ -210,7 +211,7 @@ a = range(self.N) # test arrays in mixed order, to give overload resolution a workout - for t in ['d', 'i', 'f', 'H', 'I', 'h', 'L', 'l' ]: + for t in ['d', 'i', 'f', 'H', 'I', 'h', 'L', 'l']: b = array.array(t, a) # typed passing @@ -689,9 +690,6 @@ 'get_long_array', 'get_long_array2', 'get_ulong_array', 'get_ulong_array2']: arr = getattr(c, func)() - arr = arr.shape.fromaddress(arr.itemaddress(0), self.N) - - """TODO: interface change ... arr.reshape((self.N,)) assert len(arr) == self.N @@ -703,7 +701,7 @@ l = list(arr) for i in range(self.N): - assert arr[i] == l[i]""" + assert arr[i] == l[i] def test20_voidp(self): """Test usage of void* data""" @@ -756,6 +754,15 @@ def test21_function_pointers(self): """Function pointer passing""" + import os + + # TODO: currently crashes if fast path disabled + try: + if os.environ['CPPYY_DISABLE_FASTPATH']: + return + except KeyError: + pass + import _cppyy as cppyy f1 = cppyy.gbl.sum_of_int _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit