Author: Armin Rigo <ar...@tunes.org> Branch: py3.5 Changeset: r94973:b9da0b002569 Date: 2018-08-07 23:10 +0200 http://bitbucket.org/pypy/pypy/changeset/b9da0b002569/
Log: hg merge default diff too long, truncating to 2000 out of 2739 lines diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst --- a/pypy/doc/whatsnew-head.rst +++ b/pypy/doc/whatsnew-head.rst @@ -7,7 +7,8 @@ .. branch: cppyy-packaging -Upgrade to backend 1.2.0, improved handling of templated methods and +Main items: vastly better template resolution and improved performance. In +detail: upgrade to backend 1.4, improved handling of templated methods and functions (in particular automatic deduction of types), improved pythonization interface, range of compatibility fixes for Python3, free functions now take fast libffi path when possible, moves for strings (incl. from Python str), diff --git a/pypy/module/_cffi_backend/errorbox.py b/pypy/module/_cffi_backend/errorbox.py --- a/pypy/module/_cffi_backend/errorbox.py +++ b/pypy/module/_cffi_backend/errorbox.py @@ -69,7 +69,10 @@ import sys class FileLike: def write(self, x): - of.write(x) + try: + of.write(x) + except: + pass self.buf += x fl = FileLike() fl.buf = '' diff --git a/pypy/module/_cppyy/capi/loadable_capi.py b/pypy/module/_cppyy/capi/loadable_capi.py --- a/pypy/module/_cppyy/capi/loadable_capi.py +++ b/pypy/module/_cppyy/capi/loadable_capi.py @@ -1,13 +1,18 @@ import os + from rpython.rtyper.lltypesystem import rffi, lltype from rpython.rlib.rarithmetic import intmask from rpython.rlib import jit, jit_libffi, libffi, rdynload, objectmodel from rpython.rlib.rarithmetic import r_singlefloat from rpython.tool import leakfinder -from pypy.interpreter.gateway import interp2app -from pypy.interpreter.error import oefmt +from pypy.interpreter.error import OperationError, oefmt +from pypy.interpreter.argument import Arguments +from pypy.interpreter.gateway import interp2app, interpindirect2app +from pypy.interpreter.typedef import TypeDef +from pypy.objspace.std.iterobject import W_AbstractSeqIterObject +from pypy.module._rawffi.array import W_ArrayInstance from pypy.module._cffi_backend import ctypefunc, ctypeprim, cdataobj, misc from pypy.module._cffi_backend import newtype from pypy.module._cppyy import ffitypes @@ -23,10 +28,11 @@ class _Arg: # poor man's union _immutable_ = True - def __init__(self, tc, h = 0, l = -1, s = '', p = rffi.cast(rffi.VOIDP, 0)): + def __init__(self, tc, h = 0, l = -1, d = -1., s = '', p = rffi.cast(rffi.VOIDP, 0)): self.tc = tc self._handle = h self._long = l + self._double = d self._string = s self._voidp = p @@ -40,6 +46,11 @@ def __init__(self, val): _Arg.__init__(self, 'l', l = val) +class _ArgD(_Arg): + _immutable_ = True + def __init__(self, val): + _Arg.__init__(self, 'd', d = val) + class _ArgS(_Arg): _immutable_ = True def __init__(self, val): @@ -89,6 +100,9 @@ assert obj._voidp != rffi.cast(rffi.VOIDP, 0) data = rffi.cast(rffi.VOIDPP, data) data[0] = obj._voidp + elif obj.tc == 'd': + assert isinstance(argtype, ctypeprim.W_CTypePrimitiveFloat) + misc.write_raw_float_data(data, rffi.cast(rffi.DOUBLE, obj._double), argtype.size) else: # only other use is string assert obj.tc == 's' n = len(obj._string) @@ -182,6 +196,7 @@ 'call_f' : ([c_method, c_object, c_int, c_voidp], c_float), 'call_d' : ([c_method, c_object, c_int, c_voidp], c_double), 'call_ld' : ([c_method, c_object, c_int, c_voidp], c_ldouble), + 'call_nld' : ([c_method, c_object, c_int, c_voidp], c_double), 'call_r' : ([c_method, c_object, c_int, c_voidp], c_voidp), # call_s actually takes an size_t* as last parameter, but this will do @@ -236,6 +251,8 @@ 'method_prototype' : ([c_scope, c_method, c_int], c_ccharp), 'is_const_method' : ([c_method], c_int), + 'get_num_templated_methods': ([c_scope], c_int), + 'get_templated_method_name': ([c_scope, c_index], c_ccharp), 'exists_method_template' : ([c_scope, c_ccharp], c_int), 'method_is_template' : ([c_scope, c_index], c_int), 'get_method_template' : ([c_scope, c_ccharp, c_ccharp], c_method), @@ -272,9 +289,11 @@ 'stdstring2charp' : ([c_object, c_voidp], c_ccharp), 'stdstring2stdstring' : ([c_object], c_object), - 'stdvector_valuetype' : ([c_ccharp], c_ccharp), - 'stdvector_valuesize' : ([c_ccharp], c_size_t), + 'longdouble2double' : ([c_voidp], c_double), + 'double2longdouble' : ([c_double, c_voidp], c_void), + 'vectorbool_getitem' : ([c_object, c_int], c_int), + 'vectorbool_setitem' : ([c_object, c_int, c_int], c_void), } # size/offset are backend-specific but fixed after load @@ -401,7 +420,9 @@ return rffi.cast(rffi.DOUBLE, space.float_w(call_capi(space, 'call_d', args))) def c_call_ld(space, cppmethod, cppobject, nargs, cargs): args = [_ArgH(cppmethod), _ArgH(cppobject), _ArgL(nargs), _ArgP(cargs)] - return rffi.cast(rffi.LONGDOUBLE, space.float_w(call_capi(space, 'call_ld', args))) + #return rffi.cast(rffi.LONGDOUBLE, space.float_w(call_capi(space, 'call_ld', args))) + # call_nld narrows long double to double + return rffi.cast(rffi.DOUBLE, space.float_w(call_capi(space, 'call_nld', args))) def c_call_r(space, cppmethod, cppobject, nargs, cargs): args = [_ArgH(cppmethod), _ArgH(cppobject), _ArgL(nargs), _ArgP(cargs)] @@ -561,16 +582,21 @@ def c_is_const_method(space, cppmeth): return space.bool_w(call_capi(space, 'is_const_method', [_ArgH(cppmeth)])) +def c_get_num_templated_methods(space, cppscope): + return space.int_w(call_capi(space, 'method_is_template', [_ArgH(cppscope.handle)])) +def c_get_templated_method_name(space, cppscope, index): + args = [_ArgH(cppscope.handle), _ArgL(index)] + return charp2str_free(space, call_capi(space, 'method_is_template', args)) def c_exists_method_template(space, cppscope, name): args = [_ArgH(cppscope.handle), _ArgS(name)] return space.bool_w(call_capi(space, 'exists_method_template', args)) def c_method_is_template(space, cppscope, index): args = [_ArgH(cppscope.handle), _ArgL(index)] return space.bool_w(call_capi(space, 'method_is_template', args)) - def c_get_method_template(space, cppscope, name, proto): args = [_ArgH(cppscope.handle), _ArgS(name), _ArgS(proto)] return rffi.cast(C_METHOD, space.uint_w(call_capi(space, 'get_method_template', args))) + def c_get_global_operator(space, nss, lc, rc, op): if nss is not None: args = [_ArgH(nss.handle), _ArgH(lc.handle), _ArgH(rc.handle), _ArgS(op)] @@ -619,7 +645,7 @@ return space.bool_w(call_capi(space, 'is_enum_data', args)) def c_get_dimension_size(space, cppscope, datamember_index, dim_idx): args = [_ArgH(cppscope.handle), _ArgL(datamember_index), _ArgL(dim_idx)] - return space.bool_w(call_capi(space, 'get_dimension_size', args)) + return space.int_w(call_capi(space, 'get_dimension_size', args)) # misc helpers --------------------------------------------------------------- def c_strtoll(space, svalue): @@ -650,24 +676,94 @@ def c_stdstring2stdstring(space, cppobject): return _cdata_to_cobject(space, call_capi(space, 'stdstring2stdstring', [_ArgH(cppobject)])) -def c_stdvector_valuetype(space, pystr): - return charp2str_free(space, call_capi(space, 'stdvector_valuetype', [_ArgS(pystr)])) +def c_longdouble2double(space, addr): + return space.float_w(call_capi(space, 'longdouble2double', [_ArgP(addr)])) +def c_double2longdouble(space, dval, addr): + call_capi(space, 'double2longdouble', [_ArgD(dval), _ArgP(addr)]) -def c_stdvector_valuetype(space, pystr): - return charp2str_free(space, call_capi(space, 'stdvector_valuetype', [_ArgS(pystr)])) -def c_stdvector_valuesize(space, pystr): - return _cdata_to_size_t(space, call_capi(space, 'stdvector_valuesize', [_ArgS(pystr)])) +def c_vectorbool_getitem(space, vbool, idx): + return call_capi(space, 'vectorbool_getitem', [_ArgH(vbool), _ArgL(idx)]) +def c_vectorbool_setitem(space, vbool, idx, value): + call_capi(space, 'vectorbool_setitem', [_ArgH(vbool), _ArgL(idx), _ArgL(value)]) # TODO: factor these out ... # pythonizations def stdstring_c_str(space, w_self): """Return a python string taking into account \0""" - from pypy.module._cppyy import interp_cppyy cppstr = space.interp_w(interp_cppyy.W_CPPInstance, w_self, can_be_None=False) return space.newtext(c_stdstring2charp(space, cppstr._rawobject)) +def vbool_getindex(space, w_vbool, w_idx): + idx = space.getindex_w(w_idx, space.w_IndexError, "std::vector<bool> index") + sz = space.len_w(w_vbool) + if idx < 0: idx += sz + if idx < 0 or idx >= sz: + raise IndexError + return idx + +def vectorbool_getitem(space, w_self, w_idx): + """Index a std::vector<bool>, return the value""" + from pypy.module._cppyy import interp_cppyy + vbool = space.interp_w(interp_cppyy.W_CPPInstance, w_self, can_be_None=False) + idx = vbool_getindex(space, w_self, w_idx) + item = c_vectorbool_getitem(space, vbool._rawobject, idx) + return space.newbool(space.is_true(item)) + +def vectorbool_setitem(space, w_self, w_idx, w_value): + """Index a std::vector<bool>, set the value""" + from pypy.module._cppyy import interp_cppyy + vbool = space.interp_w(interp_cppyy.W_CPPInstance, w_self, can_be_None=False) + idx = vbool_getindex(space, w_self, w_idx) + c_vectorbool_setitem(space, vbool._rawobject, idx, int(space.is_true(w_value))) + +class W_STLVectorIter(W_AbstractSeqIterObject): + # w_seq and index are in base class + _immutable_fields_ = ['converter', 'data', 'len', 'stride'] + + def __init__(self, space, w_vector): + W_AbstractSeqIterObject.__init__(self, w_vector) + # TODO: this should live in rpythonize.py or something so that the + # imports can move to the top w/o getting circles + from pypy.module._cppyy import interp_cppyy + assert isinstance(w_vector, interp_cppyy.W_CPPInstance) + vector = space.interp_w(interp_cppyy.W_CPPInstance, w_vector) + + v_type = c_resolve_name(space, vector.clsdecl.name+'::value_type') + v_size = c_size_of_type(space, v_type) + + if not v_type or not v_size: + raise NotImplementedError # fallback on getitem + + from pypy.module._cppyy import converter + self.converter = converter.get_converter(space, v_type, '') + + # this 'data' is from the decl, so not the pythonized data from pythonify.py + w_arr = space.call_obj_args(vector.clsdecl.get_overload('data'), w_vector, Arguments(space, [])) + arr = space.interp_w(W_ArrayInstance, w_arr, can_be_None=True) + if not arr: + raise OperationError(space.w_StopIteration, space.w_None) + + self.data = rffi.cast(rffi.CCHARP, space.uint_w(arr.getbuffer(space))) + self.len = space.uint_w(space.call_obj_args(vector.clsdecl.get_overload('size'), w_vector, Arguments(space, []))) + self.stride = v_size + + def descr_next(self, space): + if self.w_seq is None: + raise OperationError(space.w_StopIteration, space.w_None) + if self.len <= self.index: + self.w_seq = None + raise OperationError(space.w_StopIteration, space.w_None) + offset = lltype.direct_ptradd(self.data, rffi.cast(rffi.SIZE_T, self.index*self.stride)) + w_item = self.converter.from_memory(space, space.w_None, rffi.cast(rffi.LONG, offset)) + self.index += 1 + return w_item + +def stdvector_iter(space, w_self): + return W_STLVectorIter(space, w_self) + + # setup pythonizations for later use at run-time _pythonizations = {} def register_pythonizations(space): @@ -678,6 +774,12 @@ ### std::string stdstring_c_str, + ### std::vector + stdvector_iter, + + ### std::vector<bool> + vectorbool_getitem, + vectorbool_setitem, ] for f in allfuncs: @@ -692,3 +794,10 @@ space.setattr(w_pycppclass, space.newtext("c_str"), _pythonizations["stdstring_c_str"]) _method_alias(space, w_pycppclass, "_cppyy_as_builtin", "c_str") _method_alias(space, w_pycppclass, "__str__", "c_str") + + if name.find("std::vector<bool", 0, 16) == 0: + space.setattr(w_pycppclass, space.newtext("__getitem__"), _pythonizations["vectorbool_getitem"]) + space.setattr(w_pycppclass, space.newtext("__setitem__"), _pythonizations["vectorbool_setitem"]) + + elif name.find("std::vector", 0, 11) == 0: + space.setattr(w_pycppclass, space.newtext("__iter__"), _pythonizations["stdvector_iter"]) 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 @@ -115,7 +115,7 @@ from pypy.module._cppyy.interp_cppyy import FastCallNotPossible raise FastCallNotPossible - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): self._is_abstract(space) def to_memory(self, space, w_obj, w_value, offset): @@ -142,7 +142,7 @@ state = space.fromcache(ffitypes.State) return state.c_voidp - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): # read access, so no copy needed address_value = self._get_raw_address(space, w_obj, offset) address = rffi.cast(rffi.ULONG, address_value) @@ -184,7 +184,7 @@ ba = rffi.cast(rffi.CCHARP, address) ba[capi.c_function_arg_typeoffset(space)] = 'o' - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): # read access, so no copy needed address_value = self._get_raw_address(space, w_obj, offset) address = rffi.cast(rffi.ULONGP, address_value) @@ -211,10 +211,13 @@ x[0] = self._unwrap_object(space, w_obj) def default_argument_libffi(self, space, address): + if not self.valid_default: + from pypy.module._cppyy.interp_cppyy import FastCallNotPossible + raise FastCallNotPossible x = rffi.cast(self.c_ptrtype, address) x[0] = self.default - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): address = self._get_raw_address(space, w_obj, offset) rffiptr = rffi.cast(self.c_ptrtype, address) return self._wrap_object(space, rffiptr[0]) @@ -224,7 +227,7 @@ rffiptr = rffi.cast(self.c_ptrtype, address) rffiptr[0] = self._unwrap_object(space, w_value) -class ConstRefNumericTypeConverterMixin(NumericTypeConverterMixin): +class ConstRefNumericTypeConverterMixin(object): _mixin_ = True def cffi_type(self, space): @@ -283,7 +286,7 @@ x = rffi.cast(rffi.LONGP, address) x[0] = self._unwrap_object(space, w_obj) - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): address = rffi.cast(rffi.CCHARP, self._get_raw_address(space, w_obj, offset)) if address[0] == '\x01': return space.w_True @@ -308,7 +311,7 @@ x = rffi.cast(self.c_ptrtype, address) x[0] = self._unwrap_object(space, w_obj) - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): address = rffi.cast(rffi.CCHARP, self._get_raw_address(space, w_obj, offset)) return space.newbytes(address[0]) @@ -321,59 +324,92 @@ pass class FloatConverter(ffitypes.typeid(rffi.FLOAT), FloatTypeConverterMixin, TypeConverter): - _immutable_fields_ = ['default'] + _immutable_fields_ = ['default', 'valid_default'] def __init__(self, space, default): - if default: + self.valid_default = False + try: fval = float(rfloat.rstring_to_float(default)) - else: + self.valid_default = True + except Exception: fval = float(0.) - self.default = r_singlefloat(fval) + self.default = rffi.cast(rffi.FLOAT, r_singlefloat(fval)) - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): address = self._get_raw_address(space, w_obj, offset) rffiptr = rffi.cast(self.c_ptrtype, address) return self._wrap_object(space, rffiptr[0]) -class ConstFloatRefConverter(FloatConverter): +class ConstFloatRefConverter(ConstRefNumericTypeConverterMixin, FloatConverter): _immutable_fields_ = ['typecode'] typecode = 'f' - def cffi_type(self, space): - state = space.fromcache(ffitypes.State) - return state.c_voidp - - def convert_argument_libffi(self, space, w_obj, address, scratch): - from pypy.module._cppyy.interp_cppyy import FastCallNotPossible - raise FastCallNotPossible - class DoubleConverter(ffitypes.typeid(rffi.DOUBLE), FloatTypeConverterMixin, TypeConverter): - _immutable_fields_ = ['default'] + _immutable_fields_ = ['default', 'valid_default'] def __init__(self, space, default): - if default: + self.valid_default = False + try: self.default = rffi.cast(self.c_type, rfloat.rstring_to_float(default)) - else: + self.valid_default = True + except Exception: self.default = rffi.cast(self.c_type, 0.) class ConstDoubleRefConverter(ConstRefNumericTypeConverterMixin, DoubleConverter): _immutable_fields_ = ['typecode'] typecode = 'd' -class LongDoubleConverter(ffitypes.typeid(rffi.LONGDOUBLE), FloatTypeConverterMixin, TypeConverter): - _immutable_fields_ = ['default'] +class LongDoubleConverter(TypeConverter): + _immutable_fields_ = ['default', 'valid_default'] + typecode = 'g' def __init__(self, space, default): - if default: - fval = float(rfloat.rstring_to_float(default)) - else: - fval = float(0.) - self.default = r_longfloat(fval) + self.valid_default = False + try: + # use float() instead of cast with r_longfloat + fval = rffi.cast(rffi.DOUBLE, rfloat.rstring_to_float(default)) + self.valid_default = True + except Exception: + fval = rffi.cast(rffi.DOUBLE, 0.) + #self.default = r_longfloat(fval) + self.default = fval + + def convert_argument(self, space, w_obj, address): + x = rffi.cast(rffi.VOIDP, address) + capi.c_double2longdouble(space, space.float_w(w_obj), x) + ba = rffi.cast(rffi.CCHARP, address) + ba[capi.c_function_arg_typeoffset(space)] = self.typecode + + def convert_argument_libffi(self, space, w_obj, address, scratch): + x = rffi.cast(rffi.VOIDP, address) + capi.c_double2longdouble(space, space.float_w(w_obj), x) + + def default_argument_libffi(self, space, address): + if not self.valid_default: + from pypy.module._cppyy.interp_cppyy import FastCallNotPossible + raise FastCallNotPossible + x = rffi.cast(rffi.VOIDP, address) + capi.c_double2longdouble(space, self.default, x) + + def from_memory(self, space, w_obj, offset): + address = self._get_raw_address(space, w_obj, offset) + rffiptr = rffi.cast(rffi.VOIDP, address) + return space.newfloat(capi.c_longdouble2double(space, rffiptr)) + + def to_memory(self, space, w_obj, w_value, offset): + address = self._get_raw_address(space, w_obj, offset) + rffiptr = rffi.cast(rffi.VOIDP, address) + capi.c_double2longdouble(space, space.float_w(w_value), rffiptr) class ConstLongDoubleRefConverter(ConstRefNumericTypeConverterMixin, LongDoubleConverter): _immutable_fields_ = ['typecode'] typecode = 'g' + def convert_argument_libffi(self, space, w_obj, address, scratch): + capi.c_double2longdouble(space, space.float_w(w_obj), rffi.cast(rffi.VOIDP, scratch)) + x = rffi.cast(rffi.VOIDPP, address) + x[0] = scratch + class CStringConverter(TypeConverter): def convert_argument(self, space, w_obj, address): @@ -383,7 +419,7 @@ ba = rffi.cast(rffi.CCHARP, address) ba[capi.c_function_arg_typeoffset(space)] = 'p' - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): address = self._get_raw_address(space, w_obj, offset) charpptr = rffi.cast(rffi.CCHARPP, address) return space.newtext(rffi.charp2str(charpptr[0])) @@ -397,13 +433,15 @@ def __init__(self, space, extra): self.size = extra - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): address = self._get_raw_address(space, w_obj, offset) charpptr = rffi.cast(rffi.CCHARP, address) - strsize = self.size - if charpptr[self.size-1] == '\0': - strsize = self.size-1 # rffi will add \0 back - return space.newtext(rffi.charpsize2str(charpptr, strsize)) + if 0 <= self.size and self.size != 2**31-1: # cling's code for "unknown" (?) + strsize = self.size + if charpptr[self.size-1] == '\0': + strsize = self.size-1 # rffi will add \0 back + return space.newtext(rffi.charpsize2str(charpptr, strsize)) + return space.newtext(rffi.charp2str(charpptr)) class VoidPtrConverter(TypeConverter): @@ -428,7 +466,7 @@ x = rffi.cast(rffi.VOIDPP, address) x[0] = self._unwrap_object(space, w_obj) - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): # returned as a long value for the address (INTPTR_T is not proper # per se, but rffi does not come with a PTRDIFF_T) address = self._get_raw_address(space, w_obj, offset) @@ -540,7 +578,7 @@ from pypy.module._cppyy.interp_cppyy import FastCallNotPossible raise FastCallNotPossible # TODO: by-value is a jit_libffi special case - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): address = rffi.cast(capi.C_OBJECT, self._get_raw_address(space, w_obj, offset)) from pypy.module._cppyy import interp_cppyy return interp_cppyy.wrap_cppinstance(space, address, self.clsdecl, do_cast=False) @@ -560,7 +598,7 @@ return capi.C_NULL_OBJECT raise e - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): address = rffi.cast(capi.C_OBJECT, self._get_raw_address(space, w_obj, offset)) from pypy.module._cppyy import interp_cppyy return interp_cppyy.wrap_cppinstance(space, address, self.clsdecl, do_cast=False) @@ -586,7 +624,7 @@ from pypy.module._cppyy.interp_cppyy import FastCallNotPossible raise FastCallNotPossible - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): address = rffi.cast(capi.C_OBJECT, self._get_raw_address(space, w_obj, offset)) from pypy.module._cppyy import interp_cppyy return interp_cppyy.wrap_cppinstance( @@ -620,6 +658,26 @@ lltype.free(self.ref_buffer, flavor='raw') self.ref_buffer = lltype.nullptr(rffi.VOIDPP.TO) +class InstanceArrayConverter(InstancePtrConverter): + _immutable_fields_ = ['size'] + + def __init__(self, space, clsdecl, array_size, dimensions): + InstancePtrConverter.__init__(self, space, clsdecl) + if array_size <= 0 or array_size == 2**31-1: # cling's code for "unknown" (?) + self.size = sys.maxint + else: + self.size = array_size + # peel one off as that should be the same as the array size + self.dimensions = dimensions[1:] + + def from_memory(self, space, w_obj, offset): + address = rffi.cast(capi.C_OBJECT, self._get_raw_address(space, w_obj, offset)) + return lowlevelviews.W_ArrayOfInstances(space, self.clsdecl, address, self.size, self.dimensions) + + def to_memory(self, space, w_obj, w_value, offset): + self._is_abstract(space) + + class StdStringConverter(InstanceConverter): def __init__(self, space, extra): from pypy.module._cppyy import interp_cppyy @@ -790,7 +848,7 @@ ba = rffi.cast(rffi.CCHARP, address) ba[capi.c_function_arg_typeoffset(space)] = self.typecode - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): address = rffi.cast(capi.C_OBJECT, self._get_raw_address(space, w_obj, offset)) from pypy.module._cppyy import interp_cppyy return interp_cppyy.wrap_cppinstance(space, address, @@ -799,7 +857,7 @@ class SmartPtrPtrConverter(SmartPtrConverter): typecode = 'o' - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): self._is_abstract(space) def to_memory(self, space, w_obj, w_value, offset): @@ -811,7 +869,7 @@ class MacroConverter(TypeConverter): - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): # TODO: get the actual type info from somewhere ... address = self._get_raw_address(space, w_obj, offset) longptr = rffi.cast(rffi.LONGP, address) @@ -844,20 +902,28 @@ pass # match of decorated, unqualified type - compound = helper.compound(name) + cpd = helper.compound(name) clean_name = capi.c_resolve_name(space, helper.clean_type(name)) try: - return _converters[clean_name+compound](space, default) + return _converters[clean_name+cpd](space, default) except KeyError: pass - # arrays + # arrays (array_size may be negative, meaning: no size or no size found) + array_size = -1 + if cpd == "[]": + array_size = helper.array_size(_name) # uses original arg + elif cpd == '*' and ':' in default: + # this happens for multi-dimensional arrays: those are described as pointers + cpd = "[]" + splitpos = default.find(':') + if 0 < splitpos: # always true, but needed for annotator + array_size = int(default[:splitpos]) + try: - # array_index may be negative to indicate no size or no size found - array_size = helper.array_size(_name) # uses original arg # TODO: using clean_name here drops const (e.g. const char[] will # never be seen this way) - return _a_converters[clean_name+compound](space, array_size) + return _a_converters[clean_name+cpd](space, array_size) except KeyError: pass @@ -871,24 +937,28 @@ # check smart pointer type check_smart = capi.c_smartptr_info(space, clean_name) if check_smart[0]: - if compound == '': + if cpd == '': return SmartPtrConverter(space, clsdecl, check_smart[1], check_smart[2]) - elif compound == '*': + elif cpd == '*': return SmartPtrPtrConverter(space, clsdecl, check_smart[1], check_smart[2]) - elif compound == '&': + elif cpd == '&': return SmartPtrRefConverter(space, clsdecl, check_smart[1], check_smart[2]) # fall through: can still return smart pointer in non-smart way # type check for the benefit of the annotator - if compound == "*": + if cpd == "*": return InstancePtrConverter(space, clsdecl) - elif compound == "&": + elif cpd == "&": return InstanceRefConverter(space, clsdecl) - elif compound == "&&": + elif cpd == "&&": return InstanceMoveConverter(space, clsdecl) - elif compound == "**": + elif cpd in ["**", "*[]", "&*"]: return InstancePtrPtrConverter(space, clsdecl) - elif compound == "": + elif cpd == "[]" and array_size > 0: + # default encodes the dimensions + dims = default.split(':') + return InstanceArrayConverter(space, clsdecl, array_size, dims) + elif cpd == "": return InstanceConverter(space, clsdecl) elif "(anonymous)" in name: # special case: enum w/o a type name @@ -900,7 +970,7 @@ return FunctionPointerConverter(space, name[pos+2:]) # void* or void converter (which fails on use) - if 0 <= compound.find('*'): + if 0 <= cpd.find('*'): return VoidPtrConverter(space, default) # "user knows best" # return a void converter here, so that the class can be build even @@ -915,8 +985,8 @@ _converters["const float&"] = ConstFloatRefConverter _converters["double"] = DoubleConverter _converters["const double&"] = ConstDoubleRefConverter -#_converters["long double"] = LongDoubleConverter -#_converters["const long double&"] = ConstLongDoubleRefConverter +_converters["long double"] = LongDoubleConverter +_converters["const long double&"] = ConstLongDoubleRefConverter _converters["const char*"] = CStringConverter _converters["void*"] = VoidPtrConverter _converters["void**"] = VoidPtrPtrConverter @@ -950,7 +1020,12 @@ _immutable_ = True typecode = c_tc def __init__(self, space, default): - self.default = rffi.cast(self.c_type, capi.c_strtoll(space, default)) + self.valid_default = False + try: + self.default = rffi.cast(self.c_type, capi.c_strtoll(space, default)) + self.valid_default = True + except Exception: + self.default = rffi.cast(self.c_type, 0) class ConstRefConverter(ConstRefNumericTypeConverterMixin, BasicConverter): _immutable_ = True for name in names: @@ -967,7 +1042,12 @@ _immutable_ = True typecode = c_tc def __init__(self, space, default): - self.default = rffi.cast(self.c_type, capi.c_strtoll(space, default)) + self.valid_default = False + try: + self.default = rffi.cast(self.c_type, capi.c_strtoll(space, default)) + self.valid_default = True + except Exception: + self.default = rffi.cast(self.c_type, 0) class ConstRefConverter(ConstRefNumericTypeConverterMixin, BasicConverter): _immutable_ = True for name in names: @@ -987,7 +1067,12 @@ _immutable_ = True typecode = c_tc def __init__(self, space, default): - self.default = rffi.cast(self.c_type, capi.c_strtoull(space, default)) + self.valid_default = False + try: + self.default = rffi.cast(self.c_type, capi.c_strtoull(space, default)) + self.valid_default = True + except Exception: + self.default = rffi.cast(self.c_type, 0) class ConstRefConverter(ConstRefNumericTypeConverterMixin, BasicConverter): _immutable_ = True for name in names: 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 @@ -76,9 +76,6 @@ class NumericExecutorMixin(object): _mixin_ = True - #def _wrap_object(self, space, obj): - # return getattr(space, self.wrapper)(obj) - def execute(self, space, cppmethod, cppthis, num_args, args): result = self.c_stubcall(space, cppmethod, cppthis, num_args, args) return self._wrap_object(space, rffi.cast(self.c_type, result)) @@ -100,13 +97,10 @@ self.w_item = w_item self.do_assign = True - #def _wrap_object(self, space, obj): - # return getattr(space, self.wrapper)(rffi.cast(self.c_type, obj)) - def _wrap_reference(self, space, rffiptr): if self.do_assign: rffiptr[0] = rffi.cast(self.c_type, self._unwrap_object(space, self.w_item)) - self.do_assign = False + self.do_assign = False return self._wrap_object(space, rffiptr[0]) # all paths, for rtyper def execute(self, space, cppmethod, cppthis, num_args, args): @@ -119,6 +113,48 @@ return self._wrap_reference(space, rffi.cast(self.c_ptrtype, rffi.cast(rffi.VOIDPP, result)[0])) +class LongDoubleExecutorMixin(object): + # Note: not really supported, but returns normal double + _mixin_ = True + + def execute(self, space, cppmethod, cppthis, num_args, args): + result = self.c_stubcall(space, cppmethod, cppthis, num_args, args) + return space.newfloat(result) + + def execute_libffi(self, space, cif_descr, funcaddr, buffer): + from pypy.module._cppyy.interp_cppyy import FastCallNotPossible + raise FastCallNotPossible + +class LongDoubleExecutor(ffitypes.typeid(rffi.LONGDOUBLE), LongDoubleExecutorMixin, Executor): + _immutable_ = True + c_stubcall = staticmethod(capi.c_call_ld) + +class LongDoubleRefExecutorMixin(NumericRefExecutorMixin): + # Note: not really supported, but returns normal double + _mixin_ = True + + def _wrap_reference(self, space, rffiptr): + if self.do_assign: + capi.c_double2longdouble(space, space.float_w(self.w_item), rffiptr) + self.do_assign = False + return self.w_item + return space.newfloat(capi.c_longdouble2double(space, rffiptr)) + + def execute(self, space, cppmethod, cppthis, num_args, args): + result = capi.c_call_r(space, cppmethod, cppthis, num_args, args) + return self._wrap_reference(space, rffi.cast(self.c_ptrtype, result)) + + def execute_libffi(self, space, cif_descr, funcaddr, buffer): + jit_libffi.jit_ffi_call(cif_descr, funcaddr, buffer) + result = rffi.ptradd(buffer, cif_descr.exchange_result) + return self._wrap_reference(space, + rffi.cast(self.c_ptrtype, rffi.cast(rffi.VOIDPP, result)[0])) + +class LongDoubleRefExecutor(ffitypes.typeid(rffi.LONGDOUBLE), LongDoubleRefExecutorMixin, Executor): + def cffi_type(self, space): + state = space.fromcache(ffitypes.State) + return state.c_voidp + class CStringExecutor(Executor): def execute(self, space, cppmethod, cppthis, num_args, args): @@ -345,6 +381,10 @@ _executors["void*"] = PtrTypeExecutor _executors["const char*"] = CStringExecutor +# long double not really supported: narrows to double +_executors["long double"] = LongDoubleExecutor +_executors["long double&"] = LongDoubleRefExecutor + # special cases (note: 'string' aliases added below) _executors["constructor"] = ConstructorExecutor diff --git a/pypy/module/_cppyy/ffitypes.py b/pypy/module/_cppyy/ffitypes.py --- a/pypy/module/_cppyy/ffitypes.py +++ b/pypy/module/_cppyy/ffitypes.py @@ -296,7 +296,8 @@ _immutable_fields_ = ['c_type', 'c_ptrtype', 'typecode'] c_type = rffi.LONGDOUBLE - c_ptrtype = rffi.LONGDOUBLEP + # c_ptrtype = rffi.LONGDOUBLEP # useless type at this point + c_ptrtype = rffi.VOIDP typecode = 'g' # long double is not really supported ... @@ -304,7 +305,7 @@ return r_longfloat(space.float_w(w_obj)) def _wrap_object(self, space, obj): - return space.wrap(obj) + return space.newfloat(obj) def cffi_type(self, space): state = space.fromcache(State) diff --git a/pypy/module/_cppyy/helper.py b/pypy/module/_cppyy/helper.py --- a/pypy/module/_cppyy/helper.py +++ b/pypy/module/_cppyy/helper.py @@ -117,16 +117,10 @@ # TODO: perhaps absorb or "pythonify" these operators? return cppname -if sys.hexversion < 0x3000000: - CPPYY__div__ = "__div__" - CPPYY__idiv__ = "__idiv__" - CPPYY__long__ = "__long__" - CPPYY__bool__ = "__nonzero__" -else: - CPPYY__div__ = "__truediv__" - CPPYY__idiv__ = "__itruediv__" - CPPYY__long__ = "__int__" - CPPYY__bool__ = "__bool__" +CPPYY__div__ = "__div__" +CPPYY__idiv__ = "__idiv__" +CPPYY__long__ = "__long__" +CPPYY__bool__ = "__nonzero__" # _operator_mappings["[]"] = "__setitem__" # depends on return type # _operator_mappings["+"] = "__add__" # depends on # of args (see __pos__) diff --git a/pypy/module/_cppyy/include/capi.h b/pypy/module/_cppyy/include/capi.h --- a/pypy/module/_cppyy/include/capi.h +++ b/pypy/module/_cppyy/include/capi.h @@ -63,6 +63,8 @@ double cppyy_call_d(cppyy_method_t method, cppyy_object_t self, int nargs, void* args); RPY_EXTERN long double cppyy_call_ld(cppyy_method_t method, cppyy_object_t self, int nargs, void* args); + RPY_EXTERN + double cppyy_call_nld(cppyy_method_t method, cppyy_object_t self, int nargs, void* args); RPY_EXTERN void* cppyy_call_r(cppyy_method_t method, cppyy_object_t self, int nargs, void* args); @@ -151,11 +153,15 @@ RPY_EXTERN char* cppyy_method_signature(cppyy_method_t, int show_formalargs); RPY_EXTERN - char* cppyy_method_prototype(cppyy_scope_t scope, cppyy_method_t idx, int show_formalargs); + char* cppyy_method_prototype(cppyy_scope_t scope, cppyy_method_t, int show_formalargs); RPY_EXTERN int cppyy_is_const_method(cppyy_method_t); RPY_EXTERN + int get_num_templated_methods(cppyy_scope_t scope); + RPY_EXTERN + char* get_templated_method_name(cppyy_scope_t scope, cppyy_index_t imeth); + RPY_EXTERN int cppyy_exists_method_template(cppyy_scope_t scope, const char* name); RPY_EXTERN int cppyy_method_is_template(cppyy_scope_t scope, cppyy_index_t idx); @@ -216,9 +222,14 @@ cppyy_object_t cppyy_stdstring2stdstring(cppyy_object_t ptr); RPY_EXTERN - const char* cppyy_stdvector_valuetype(const char* clname); + double cppyy_longdouble2double(void*); RPY_EXTERN - size_t cppyy_stdvector_valuesize(const char* clname); + void cppyy_double2longdouble(double, void*); + + RPY_EXTERN + int cppyy_vectorbool_getitem(cppyy_object_t ptr, int idx); + RPY_EXTERN + void cppyy_vectorbool_setitem(cppyy_object_t ptr, int idx, int value); #ifdef __cplusplus } diff --git a/pypy/module/_cppyy/interp_cppyy.py b/pypy/module/_cppyy/interp_cppyy.py --- a/pypy/module/_cppyy/interp_cppyy.py +++ b/pypy/module/_cppyy/interp_cppyy.py @@ -24,6 +24,7 @@ INSTANCE_FLAGS_IS_RVALUE = 0x0004 OVERLOAD_FLAGS_USE_FFI = 0x0001 +OVERLOAD_FLAGS_CREATES = 0x0002 FUNCTION_IS_GLOBAL = 0x0001 FUNCTION_IS_STATIC = 0x0001 @@ -229,8 +230,10 @@ if self.converters is None: try: self._setup(cppthis) - except Exception: - pass + except Exception as e: + if self.converters is None: + raise oefmt(self.space.w_SystemError, + "unable to initialize converters (%s)", str(e)) # attempt to call directly through ffi chain if useffi and self._funcaddr: @@ -458,6 +461,36 @@ # need forwarding, which the normal instancemethod does not provide, hence this # derived class. class MethodWithProps(Method): + # set life management of result from the call + def fget_creates(self, space): + f = space.interp_w(W_CPPOverload, self.w_function) + return f.fget_creates(space) + + @unwrap_spec(value=bool) + def fset_creates(self, space, value): + f = space.interp_w(W_CPPOverload, self.w_function) + f.fset_creates(space, value) + + # set ownership policy of arguments (not yet implemented) + def fget_mempolicy(self, space): + f = space.interp_w(W_CPPOverload, self.w_function) + return f.fget_mempolicy(space) + + @unwrap_spec(value=int) + def fset_mempolicy(self, space, value): + f = space.interp_w(W_CPPOverload, self.w_function) + f.fset_mempolicy(space, value) + + # set to release the gil during call (not yet implemented) + def fget_release_gil(self, space): + f = space.interp_w(W_CPPOverload, self.w_function) + return f.fget_release_gil(space) + + @unwrap_spec(value=bool) + def fset_release_gil(self, space, value): + f = space.interp_w(W_CPPOverload, self.w_function) + f.fset_release_gil(space, value) + # allow user to determine ffi use rules per overload def fget_useffi(self, space): f = space.interp_w(W_CPPOverload, self.w_function) @@ -473,19 +506,22 @@ __doc__ = """cpp_instancemethod(function, instance, class) Create an instance method object.""", - __new__ = interp2app(MethodWithProps.descr_method__new__.im_func), - __call__ = interp2app(MethodWithProps.descr_method_call), - __get__ = interp2app(MethodWithProps.descr_method_get), - __func__ = interp_attrproperty_w('w_function', cls=MethodWithProps), - __self__ = interp_attrproperty_w('w_instance', cls=MethodWithProps), + __new__ = interp2app(MethodWithProps.descr_method__new__.im_func), + __call__ = interp2app(MethodWithProps.descr_method_call), + __get__ = interp2app(MethodWithProps.descr_method_get), + __func__ = interp_attrproperty_w('w_function', cls=MethodWithProps), + __self__ = interp_attrproperty_w('w_instance', cls=MethodWithProps), __getattribute__ = interp2app(MethodWithProps.descr_method_getattribute), - __eq__ = interp2app(MethodWithProps.descr_method_eq), - __ne__ = descr_generic_ne, - __hash__ = interp2app(MethodWithProps.descr_method_hash), - __repr__ = interp2app(MethodWithProps.descr_method_repr), - __reduce__ = interp2app(MethodWithProps.descr_method__reduce__), - __weakref__ = make_weakref_descr(MethodWithProps), - __useffi__ = GetSetProperty(MethodWithProps.fget_useffi, MethodWithProps.fset_useffi), + __eq__ = interp2app(MethodWithProps.descr_method_eq), + __ne__ = descr_generic_ne, + __hash__ = interp2app(MethodWithProps.descr_method_hash), + __repr__ = interp2app(MethodWithProps.descr_method_repr), + __reduce__ = interp2app(MethodWithProps.descr_method__reduce__), + __weakref__ = make_weakref_descr(MethodWithProps), + __creates__ = GetSetProperty(MethodWithProps.fget_creates, MethodWithProps.fset_creates), + __mempolicy__ = GetSetProperty(MethodWithProps.fget_mempolicy, MethodWithProps.fset_mempolicy), + __release_gil__ = GetSetProperty(MethodWithProps.fget_release_gil, MethodWithProps.fset_release_gil), + __useffi__ = GetSetProperty(MethodWithProps.fget_useffi, MethodWithProps.fset_useffi), ) MethodWithProps.typedef.acceptable_as_base_class = False @@ -548,7 +584,12 @@ for i in range(len(self.functions)): cppyyfunc = self.functions[i] try: - return cppyyfunc.call(cppthis, args_w, self.flags & OVERLOAD_FLAGS_USE_FFI) + w_result = cppyyfunc.call(cppthis, args_w, self.flags & OVERLOAD_FLAGS_USE_FFI) + if self.flags & OVERLOAD_FLAGS_CREATES: + if isinstance(w_result, W_CPPInstance): + cppinstance = self.space.interp_w(W_CPPInstance, w_result) + cppinstance.fset_python_owns(self.space, self.space.w_True) + return w_result except Exception: pass @@ -561,6 +602,7 @@ for i in range(len(self.functions)): cppyyfunc = self.functions[i] try: + # no need to set ownership on the return value, as none of the methods execute return cppyyfunc.call(cppthis, args_w, self.flags & OVERLOAD_FLAGS_USE_FFI) except OperationError as e: # special case if there's just one function, to prevent clogging the error message @@ -599,6 +641,33 @@ return W_CPPOverload(self.space, self.scope, [f]) raise oefmt(self.space.w_LookupError, "signature '%s' not found", signature) + # set life management of result from the call + def fget_creates(self, space): + return space.newbool(bool(self.flags & OVERLOAD_FLAGS_CREATES)) + + @unwrap_spec(value=bool) + def fset_creates(self, space, value): + if space.is_true(value): + self.flags |= OVERLOAD_FLAGS_CREATES + else: + self.flags &= ~OVERLOAD_FLAGS_CREATES + + # set ownership policy of arguments (not yet implemented) + def fget_mempolicy(self, space): + return space.newint(0) + + @unwrap_spec(value=int) + def fset_mempolicy(self, space, value): + pass + + # set to release the gil during call (not yet implemented) + def fget_release_gil(self, space): + return space.newbool(True) + + @unwrap_spec(value=bool) + def fset_release_gil(self, space, value): + pass + # allow user to determine ffi use rules per overload def fget_useffi(self, space): return space.newbool(bool(self.flags & OVERLOAD_FLAGS_USE_FFI)) @@ -622,11 +691,14 @@ W_CPPOverload.typedef = TypeDef( 'CPPOverload', - __get__ = interp2app(W_CPPOverload.descr_get), - __call__ = interp2app(W_CPPOverload.call_args), - __useffi__ = GetSetProperty(W_CPPOverload.fget_useffi, W_CPPOverload.fset_useffi), - __overload__ = interp2app(W_CPPOverload.mp_overload), - __doc__ = GetSetProperty(W_CPPOverload.fget_doc) + __get__ = interp2app(W_CPPOverload.descr_get), + __call__ = interp2app(W_CPPOverload.call_args), + __creates__ = GetSetProperty(W_CPPOverload.fget_creates, W_CPPOverload.fset_creates), + __mempolicy__ = GetSetProperty(W_CPPOverload.fget_mempolicy, W_CPPOverload.fset_mempolicy), + __release_gil__ = GetSetProperty(W_CPPOverload.fget_release_gil, W_CPPOverload.fset_release_gil), + __useffi__ = GetSetProperty(W_CPPOverload.fget_useffi, W_CPPOverload.fset_useffi), + __overload__ = interp2app(W_CPPOverload.mp_overload), + __doc__ = GetSetProperty(W_CPPOverload.fget_doc) ) @@ -655,11 +727,14 @@ W_CPPStaticOverload.typedef = TypeDef( 'CPPStaticOverload', - __get__ = interp2app(W_CPPStaticOverload.descr_get), - __call__ = interp2app(W_CPPStaticOverload.call_args), - __useffi__ = GetSetProperty(W_CPPStaticOverload.fget_useffi, W_CPPStaticOverload.fset_useffi), - __overload__ = interp2app(W_CPPStaticOverload.mp_overload), - __doc__ = GetSetProperty(W_CPPStaticOverload.fget_doc) + __get__ = interp2app(W_CPPStaticOverload.descr_get), + __call__ = interp2app(W_CPPStaticOverload.call_args), + __creates__ = GetSetProperty(W_CPPStaticOverload.fget_creates, W_CPPStaticOverload.fset_creates), + __mempolicy__ = GetSetProperty(W_CPPStaticOverload.fget_mempolicy, W_CPPStaticOverload.fset_mempolicy), + __release_gil__ = GetSetProperty(W_CPPStaticOverload.fget_release_gil, W_CPPStaticOverload.fset_release_gil), + __useffi__ = GetSetProperty(W_CPPStaticOverload.fget_useffi, W_CPPStaticOverload.fset_useffi), + __overload__ = interp2app(W_CPPStaticOverload.mp_overload), + __doc__ = GetSetProperty(W_CPPStaticOverload.fget_doc) ) @@ -712,6 +787,7 @@ class TemplateOverloadMixin(object): """Mixin to instantiate templated methods/functions.""" + _attrs_ = ['tmpl_args_w'] _mixin_ = True def construct_template_args(self, w_tpArgs, args_w = None): @@ -764,23 +840,37 @@ return cppol def instantiate_and_call(self, name, args_w): - # try to match with run-time instantiations - for cppol in self.master.overloads.values(): - try: - if not self.space.is_w(self.w_this, self.space.w_None): - return self.space.call_obj_args(cppol, self.w_this, Arguments(self.space, args_w)) - return self.space.call_args(cppol, Arguments(self.space, args_w)) - except Exception: - pass # completely ignore for now; have to see whether errors become confusing + method = None + try: + # existing cached instantiations + if name[-1] == '>': # only accept full templated name, to ensure explicit + method = self.master.overloads[name] + else: + # try to match with run-time instantiations + # TODO: logically, this could be used, but in practice, it's proving too + # greedy ... maybe as a last resort? + #for cppol in self.master.overloads.values(): + # try: + # if not self.space.is_w(self.w_this, self.space.w_None): + # return self.space.call_obj_args(cppol, self.w_this, Arguments(self.space, args_w)) + # return self.space.call_args(cppol, Arguments(self.space, args_w)) + # except Exception: + # pass # completely ignore for now; have to see whether errors become confusing + raise TypeError("pre-existing overloads failed") + except (KeyError, TypeError): + # if not known, try to deduce from argument types + w_types = self.space.newtuple([self.space.type(obj_w) for obj_w in args_w]) + proto = self.construct_template_args(w_types, args_w) + method = self.find_method_template(name, proto) - # if all failed, then try to deduce from argument types - w_types = self.space.newtuple([self.space.type(obj_w) for obj_w in args_w]) - proto = self.construct_template_args(w_types, args_w) - method = self.find_method_template(name, proto) - - # only cache result if the name retains the full template - fullname = capi.c_method_full_name(self.space, method.functions[0].cppmethod) - if 0 <= fullname.rfind('>'): + # only cache result if the name retains the full template + # TODO: the problem is in part that c_method_full_name returns incorrect names, + # e.g. when default template arguments are involved, so for now use 'name' if it + # has the full templated name + if name[-1] == '>': + fullname = name + else: + fullname = capi.c_method_full_name(self.space, method.functions[0].cppmethod) try: existing = self.master.overloads[fullname] allf = existing.functions + method.functions @@ -792,9 +882,10 @@ except KeyError: self.master.overloads[fullname] = method - if not self.space.is_w(self.w_this, self.space.w_None): - return self.space.call_obj_args(method, self.w_this, Arguments(self.space, args_w)) - return self.space.call_args(method, Arguments(self.space, args_w)) + if method is not None: + if not self.space.is_w(self.w_this, self.space.w_None): + return self.space.call_obj_args(method, self.w_this, Arguments(self.space, args_w)) + return self.space.call_args(method, Arguments(self.space, args_w)) def getitem_impl(self, name, args_w): space = self.space @@ -808,14 +899,9 @@ fullname = name+'<'+tmpl_args+'>' try: method = self.master.overloads[fullname] - except KeyError: - method = self.find_method_template(fullname) - # cache result (name is always full templated name) - self.master.overloads[fullname] = method - # also cache on "official" name (may include default template arguments) - c_fullname = capi.c_method_full_name(self.space, method.functions[0].cppmethod) - if c_fullname != fullname: - self.master.overloads[c_fullname] = method + except KeyError as e: + # defer instantiation until arguments are known + return self.clone(tmpl_args) return method.descr_get(self.w_this, None) @@ -824,21 +910,29 @@ """App-level dispatcher to allow both lookup/instantiation of templated methods and dispatch among overloads between templated and non-templated method.""" - _attrs_ = ['name', 'overloads', 'master', 'w_this'] - _immutable_fields_ = ['name'] + _attrs_ = ['name', 'tmpl_args', 'overloads', 'master', 'w_this'] + _immutable_fields_ = ['name', 'tmpl_args'] - def __init__(self, space, name, decl_scope, functions, flags = OVERLOAD_FLAGS_USE_FFI): + def __init__(self, space, name, tmpl_args, decl_scope, functions, flags = OVERLOAD_FLAGS_USE_FFI): W_CPPOverload.__init__(self, space, decl_scope, functions, flags) self.name = name + self.tmpl_args = tmpl_args self.overloads = {} self.master = self self.w_this = space.w_None + def clone(self, tmpl_args): + other = W_CPPTemplateOverload(self.space, self.name, tmpl_args, self.scope, self.functions, self.flags) + other.overloads = self.overloads + other.master = self.master + other.w_this = self.w_this + return other + def descr_get(self, w_cppinstance, w_cls=None): # TODO: don't return copy, but bind in an external object (like W_CPPOverload) if self.space.is_w(w_cppinstance, self.space.w_None): return self # unbound, so no new instance needed - cppol = W_CPPTemplateOverload(self.space, self.name, self.scope, self.functions, self.flags) + cppol = W_CPPTemplateOverload(self.space, self.name, self.tmpl_args, self.scope, self.functions, self.flags) cppol.w_this = w_cppinstance cppol.master = self.master return cppol # bound @@ -848,13 +942,18 @@ # direct call: either pick non-templated overload or attempt to deduce # the template instantiation from the argument types - # try existing overloads or compile-time instantiations + # do explicit lookup with tmpl_args if given try: - return W_CPPOverload.call_args(self, args_w) + fullname = self.name + if self.tmpl_args is not None: + fullname = fullname+'<'+self.tmpl_args+'>' + return self.instantiate_and_call(fullname, args_w) except Exception: pass - return self.instantiate_and_call(self.name, args_w) + # otherwise, try existing overloads or compile-time instantiations + # TODO: consolidate errors + return W_CPPOverload.call_args(self, [self.w_this]+args_w) @unwrap_spec(args_w='args_w') def getitem(self, args_w): @@ -868,33 +967,44 @@ W_CPPTemplateOverload.typedef = TypeDef( 'CPPTemplateOverload', - __get__ = interp2app(W_CPPTemplateOverload.descr_get), - __getitem__ = interp2app(W_CPPTemplateOverload.getitem), - __call__ = interp2app(W_CPPTemplateOverload.call_args), - __useffi__ = GetSetProperty(W_CPPTemplateOverload.fget_useffi, W_CPPTemplateOverload.fset_useffi), - __doc__ = GetSetProperty(W_CPPTemplateOverload.fget_doc) + __get__ = interp2app(W_CPPTemplateOverload.descr_get), + __getitem__ = interp2app(W_CPPTemplateOverload.getitem), + __call__ = interp2app(W_CPPTemplateOverload.call_args), + __creates__ = GetSetProperty(W_CPPTemplateOverload.fget_creates, W_CPPTemplateOverload.fset_creates), + __mempolicy__ = GetSetProperty(W_CPPTemplateOverload.fget_mempolicy, W_CPPTemplateOverload.fset_mempolicy), + __release_gil__ = GetSetProperty(W_CPPTemplateOverload.fget_release_gil, W_CPPTemplateOverload.fset_release_gil), + __useffi__ = GetSetProperty(W_CPPTemplateOverload.fget_useffi, W_CPPTemplateOverload.fset_useffi), + __doc__ = GetSetProperty(W_CPPTemplateOverload.fget_doc) ) class W_CPPTemplateStaticOverload(W_CPPStaticOverload, TemplateOverloadMixin): """App-level dispatcher to allow both lookup/instantiation of templated methods and dispatch among overloads between templated and non-templated method.""" - _attrs_ = ['name', 'overloads', 'master', 'w_this'] - _immutable_fields_ = ['name'] + _attrs_ = ['name', 'tmpl_args', 'overloads', 'master', 'w_this'] + _immutable_fields_ = ['name', 'tmpl_args'] - def __init__(self, space, name, decl_scope, funcs, flags = OVERLOAD_FLAGS_USE_FFI): + def __init__(self, space, name, tmpl_args, decl_scope, funcs, flags = OVERLOAD_FLAGS_USE_FFI): W_CPPStaticOverload.__init__(self, space, decl_scope, funcs, flags) self.name = name + self.tmpl_args = tmpl_args self.overloads = {} self.master = self self.w_this = space.w_None + def clone(self, tmpl_args): + other = W_CPPTemplateStaticOverload(self.space, self.name, tmpl_args, self.scope, self.functions, self.flags) + other.overloads = self.overloads + other.master = self.master + other.w_this = self.w_this + return other + def descr_get(self, w_cppinstance, w_cls=None): # TODO: don't return copy, but bind in an external object (like W_CPPOverload) if isinstance(w_cppinstance, W_CPPInstance): cppinstance = self.space.interp_w(W_CPPInstance, w_cppinstance) if cppinstance.clsdecl.handle != self.scope.handle: - cppol = W_CPPTemplateStaticOverload(self.space, self.name, self.scope, self.functions, self.flags) + cppol = W_CPPTemplateStaticOverload(self.space, self.name, self.tmpl_args, self.scope, self.functions, self.flags) cppol.w_this = w_cppinstance cppol.master = self.master return cppol # bound @@ -904,15 +1014,20 @@ def call_args(self, args_w): # direct call: either pick non-templated overload or attempt to deduce # the template instantiation from the argument types + # TODO: refactor with W_CPPTemplateOverload - # try existing overloads or compile-time instantiations + # do explicit lookup with tmpl_args if given try: - return W_CPPStaticOverload.call_args(self, args_w) + fullname = self.name + if self.tmpl_args is not None: + fullname = fullname+'<'+self.tmpl_args+'>' + return self.instantiate_and_call(fullname, args_w) except Exception: pass - # try new instantiation - return self.instantiate_and_call(self.name, args_w) + # otherwise, try existing overloads or compile-time instantiations + # TODO: consolidate errors + return W_CPPStaticOverload.call_args(self, args_w) @unwrap_spec(args_w='args_w') def getitem(self, args_w): @@ -926,11 +1041,18 @@ W_CPPTemplateStaticOverload.typedef = TypeDef( 'CPPTemplateStaticOverload', - __get__ = interp2app(W_CPPTemplateStaticOverload.descr_get), - __getitem__ = interp2app(W_CPPTemplateStaticOverload.getitem), - __call__ = interp2app(W_CPPTemplateStaticOverload.call_args), - __useffi__ = GetSetProperty(W_CPPTemplateStaticOverload.fget_useffi, W_CPPTemplateStaticOverload.fset_useffi), - __doc__ = GetSetProperty(W_CPPTemplateStaticOverload.fget_doc) + __get__ = interp2app(W_CPPTemplateStaticOverload.descr_get), + __getitem__ = interp2app(W_CPPTemplateStaticOverload.getitem), + __call__ = interp2app(W_CPPTemplateStaticOverload.call_args), + __creates__ = GetSetProperty(W_CPPTemplateStaticOverload.fget_creates, + W_CPPTemplateStaticOverload.fset_creates), + __mempolicy__ = GetSetProperty(W_CPPTemplateStaticOverload.fget_mempolicy, + W_CPPTemplateStaticOverload.fset_mempolicy), + __release_gil__ = GetSetProperty(W_CPPTemplateStaticOverload.fget_release_gil, + W_CPPTemplateStaticOverload.fset_release_gil), + __useffi__ = GetSetProperty(W_CPPTemplateStaticOverload.fget_useffi, + W_CPPTemplateStaticOverload.fset_useffi), + __doc__ = GetSetProperty(W_CPPTemplateStaticOverload.fget_doc) ) @@ -950,11 +1072,11 @@ _attrs_ = ['space', 'scope', 'converter', 'offset'] _immutable_fields = ['scope', 'converter', 'offset'] - def __init__(self, space, decl_scope, type_name, offset): + def __init__(self, space, decl_scope, type_name, dimensions, offset): self.space = space self.scope = decl_scope - self.converter = converter.get_converter(self.space, type_name, '') - self.offset = offset + self.converter = converter.get_converter(self.space, type_name, dimensions) + self.offset = rffi.cast(rffi.LONG, offset) def _get_offset(self, cppinstance): if cppinstance: @@ -971,7 +1093,7 @@ raise oefmt(self.space.w_AttributeError, "attribute access requires an instance") offset = self._get_offset(cppinstance) - return self.converter.from_memory(self.space, w_cppinstance, w_pycppclass, offset) + return self.converter.from_memory(self.space, w_cppinstance, offset) def set(self, w_cppinstance, w_value): cppinstance = self.space.interp_w(W_CPPInstance, w_cppinstance, can_be_None=True) @@ -1008,7 +1130,7 @@ return self.offset def get(self, w_cppinstance, w_pycppclass): - return self.converter.from_memory(self.space, self.space.w_None, w_pycppclass, self.offset) + return self.converter.from_memory(self.space, self.space.w_None, self.offset) def set(self, w_cppinstance, w_value): self.converter.to_memory(self.space, self.space.w_None, w_value, self.offset) @@ -1098,6 +1220,21 @@ self.datamembers[name] = new_dm return new_dm + @unwrap_spec(name='text') + def has_enum(self, name): + if capi.c_is_enum(self.space, self.name+'::'+name): + return self.space.w_True + return self.space.w_False + + def _encode_dm_dimensions(self, idata): + # encode dimensions (TODO: this is ugly, but here's where the info is) + dims = [] + sz = capi.c_get_dimension_size(self.space, self, idata, len(dims)) + while 0 < sz: + dims.append(str(sz)) + sz = capi.c_get_dimension_size(self.space, self, idata, len(dims)) + return ':'.join(dims) + @unwrap_spec(name='text', signature='text') def scope__dispatch__(self, name, signature): overload = self.get_overload(name) @@ -1137,13 +1274,16 @@ def _make_datamember(self, dm_name, dm_idx): type_name = capi.c_datamember_type(self.space, self, dm_idx) + if capi.c_is_enum_data(self.space, self, dm_idx): + type_name = capi.c_resolve_enum(self.space, type_name) offset = capi.c_datamember_offset(self.space, self, dm_idx) if offset == -1: raise self.missing_attribute_error(dm_name) + dims = self._encode_dm_dimensions(dm_idx) if capi.c_is_const_data(self.space, self, dm_idx): - datamember = W_CPPConstStaticData(self.space, self, type_name, offset) + datamember = W_CPPConstStaticData(self.space, self, type_name, dims, offset) else: - datamember = W_CPPStaticData(self.space, self, type_name, offset) + datamember = W_CPPStaticData(self.space, self, type_name, dims, offset) self.datamembers[dm_name] = datamember return datamember @@ -1158,10 +1298,10 @@ if capi.c_method_is_template(self.space, self, idx): templated = True if templated: - return W_CPPTemplateStaticOverload(self.space, meth_name, self, cppfunctions[:]) + return W_CPPTemplateStaticOverload(self.space, meth_name, None, self, cppfunctions[:]) return W_CPPStaticOverload(self.space, self, cppfunctions[:]) elif capi.c_exists_method_template(self.space, self, meth_name): - return W_CPPTemplateStaticOverload(self.space, meth_name, self, []) + return W_CPPTemplateStaticOverload(self.space, meth_name, None, self, []) raise self.missing_attribute_error(meth_name) def find_datamember(self, dm_name): @@ -1193,6 +1333,7 @@ get_datamember_names = interp2app(W_CPPNamespaceDecl.get_datamember_names), get_datamember = interp2app(W_CPPNamespaceDecl.get_datamember), is_namespace = interp2app(W_CPPNamespaceDecl.is_namespace), + has_enum = interp2app(W_CPPNamespaceDecl.has_enum), __cppname__ = interp_attrproperty('name', W_CPPNamespaceDecl, wrapfn="newtext"), __dispatch__ = interp2app(W_CPPNamespaceDecl.scope__dispatch__), __dir__ = interp2app(W_CPPNamespaceDecl.ns__dir__), @@ -1253,12 +1394,12 @@ elif ftype & FUNCTION_IS_STATIC: if ftype & FUNCTION_IS_TEMPLATE: cppname = capi.c_method_name(self.space, methods[0].cppmethod) - overload = W_CPPTemplateStaticOverload(self.space, cppname, self, methods[:]) + overload = W_CPPTemplateStaticOverload(self.space, cppname, None, self, methods[:]) else: overload = W_CPPStaticOverload(self.space, self, methods[:]) elif ftype & FUNCTION_IS_TEMPLATE: cppname = capi.c_method_name(self.space, methods[0].cppmethod) - overload = W_CPPTemplateOverload(self.space, cppname, self, methods[:]) + overload = W_CPPTemplateOverload(self.space, cppname, None, self, methods[:]) else: overload = W_CPPOverload(self.space, self, methods[:]) self.overloads[pyname] = overload @@ -1298,14 +1439,15 @@ continue # dictionary problem; raises AttributeError on use is_static = bool(capi.c_is_staticdata(self.space, self, i)) is_const = bool(capi.c_is_const_data(self.space, self, i)) + dims = self._encode_dm_dimensions(i) if is_static and is_const: - datamember = W_CPPConstStaticData(self.space, self, type_name, offset) + datamember = W_CPPConstStaticData(self.space, self, type_name, dims, offset) elif is_static: - datamember = W_CPPStaticData(self.space, self, type_name, offset) + datamember = W_CPPStaticData(self.space, self, type_name, dims, offset) elif is_const: - datamember = W_CPPConstDataMember(self.space, self, type_name, offset) + datamember = W_CPPConstDataMember(self.space, self, type_name, dims, offset) else: - datamember = W_CPPDataMember(self.space, self, type_name, offset) + datamember = W_CPPDataMember(self.space, self, type_name, dims, offset) self.datamembers[datamember_name] = datamember def find_overload(self, meth_name): @@ -1348,6 +1490,7 @@ get_datamember_names = interp2app(W_CPPClassDecl.get_datamember_names), get_datamember = interp2app(W_CPPClassDecl.get_datamember), is_namespace = interp2app(W_CPPClassDecl.is_namespace), + has_enum = interp2app(W_CPPClassDecl.has_enum), __cppname__ = interp_attrproperty('name', W_CPPClassDecl, wrapfn="newtext"), __dispatch__ = interp2app(W_CPPClassDecl.scope__dispatch__) ) diff --git a/pypy/module/_cppyy/lowlevelviews.py b/pypy/module/_cppyy/lowlevelviews.py --- a/pypy/module/_cppyy/lowlevelviews.py +++ b/pypy/module/_cppyy/lowlevelviews.py @@ -3,10 +3,17 @@ # 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.error import OperationError, oefmt from pypy.interpreter.gateway import interp2app, unwrap_spec from pypy.interpreter.typedef import TypeDef, GetSetProperty, interp_attrproperty_w +from pypy.interpreter.baseobjspace import W_Root + +from rpython.rtyper.lltypesystem import rffi +from rpython.rlib.rarithmetic import intmask + from pypy.module._rawffi.array import W_ArrayInstance +from pypy.module._rawffi.interp_rawffi import segfault_exception +from pypy.module._cppyy import capi class W_LowLevelView(W_ArrayInstance): @@ -50,5 +57,45 @@ itemaddress = interp2app(W_LowLevelView.descr_itemaddress), reshape = interp2app(W_LowLevelView.reshape), ) -W_ArrayInstance.typedef.acceptable_as_base_class = False +W_LowLevelView.typedef.acceptable_as_base_class = False + +class W_ArrayOfInstances(W_Root): + _attrs_ = ['converter', 'baseaddress', 'clssize', 'length'] + _immutable_fields_ = ['converter', 'baseaddress', 'clssize'] + + def __init__(self, space, clsdecl, address, length, dimensions): + from pypy.module._cppyy import converter + name = clsdecl.name + self.clssize = int(intmask(capi.c_size_of_klass(space, clsdecl))) + if dimensions: + name = name + '[' + dimensions[0] + ']' + for num in dimensions: + self.clssize *= int(num) + dimensions = ':'.join(dimensions) + self.converter = converter.get_converter(space, name, dimensions) + self.baseaddress = address + self.length = length + + @unwrap_spec(idx=int) + def getitem(self, space, idx): + if not self.baseaddress: + raise segfault_exception(space, "accessing elements of freed array") + if idx >= self.length or idx < 0: + raise OperationError(space.w_IndexError, space.w_None) + itemaddress = rffi.cast(rffi.LONG, self.baseaddress)+idx*self.clssize + return self.converter.from_memory(space, space.w_None, itemaddress) + + def getlength(self, space): + return space.newint(self.length) + + def setlength(self, space, w_length): + self.length = space.int_w(w_length) + +W_ArrayOfInstances.typedef = TypeDef( + 'ArrayOfInstances', + __getitem__ = interp2app(W_ArrayOfInstances.getitem), + __len__ = interp2app(W_ArrayOfInstances.getlength), + size = GetSetProperty(W_ArrayOfInstances.getlength, W_ArrayOfInstances.setlength), +) +W_ArrayOfInstances.typedef.acceptable_as_base_class = False diff --git a/pypy/module/_cppyy/pythonify.py b/pypy/module/_cppyy/pythonify.py --- a/pypy/module/_cppyy/pythonify.py +++ b/pypy/module/_cppyy/pythonify.py @@ -29,7 +29,14 @@ class CPPNamespaceMeta(CPPScopeMeta): def __dir__(self): - return self.__cppdecl__.__dir__() + # For Py3: can actually call base class __dir__ (lives in type) + values = set(self.__dict__.keys()) + values.update(object.__dict__.keys()) + values.update(type(self).__dict__.keys()) + + # add C++ entities + values.update(self.__cppdecl__.__dir__()) + return list(values) class CPPClassMeta(CPPScopeMeta): pass @@ -310,7 +317,7 @@ if not cppitem: try: cppitem = scope.__cppdecl__.get_overload(name) - setattr(scope.__class__, name, cppitem) + setattr(scope, name, cppitem) pycppitem = getattr(scope, name) # binds function as needed except AttributeError: pass @@ -326,6 +333,11 @@ except AttributeError: pass + # enum type + if not cppitem: + if scope.__cppdecl__.has_enum(name): + pycppitem = int + if pycppitem is not None: # pycppitem could be a bound C++ NULL, so check explicitly for Py_None return pycppitem @@ -354,18 +366,19 @@ return '', name # pythonization by decoration (move to their own file?) -def python_style_getitem(self, idx): +def python_style_getitem(self, _idx): # python-style indexing: check for size and allow indexing from the back - try: - sz = len(self) + sz = len(self) + idx = _idx + if isinstance(idx, int): if idx < 0: idx = sz + idx - if idx < sz: + if 0 <= idx < sz: return self._getitem__unchecked(idx) - raise IndexError( - 'index out of range: %d requested for %s of size %d' % (idx, str(self), sz)) - except TypeError: - pass - return self._getitem__unchecked(idx) + else: + raise IndexError( + 'index out of range: %s requested for %s of size %d' % (str(idx), str(self), sz)) + # may fail for the same reasons as above, but will now give proper error message + return self._getitem__unchecked(_idx) def python_style_sliceable_getitem(self, slice_or_idx): if type(slice_or_idx) == slice: @@ -373,8 +386,7 @@ nseq += [python_style_getitem(self, i) \ for i in range(*slice_or_idx.indices(len(self)))] return nseq - else: - return python_style_getitem(self, slice_or_idx) + return python_style_getitem(self, slice_or_idx) def _pythonize(pyclass, name): # general note: use 'in pyclass.__dict__' rather than 'hasattr' to prevent @@ -423,22 +435,25 @@ # map begin()/end() protocol to iter protocol on STL(-like) classes, but # not on vector, which is pythonized in the capi (interp-level; there is # also the fallback on the indexed __getitem__, but that is slower) -# TODO: if not (0 <= name.find('vector') <= 5): - if ('begin' in pyclass.__dict__ and 'end' in pyclass.__dict__): - if _cppyy._scope_byname(name+'::iterator') or \ - _cppyy._scope_byname(name+'::const_iterator'): - def __iter__(self): - i = self.begin() - while i != self.end(): - yield i.__deref__() - i.__preinc__() - i.__destruct__() - raise StopIteration - pyclass.__iter__ = __iter__ - # else: rely on numbered iteration + add_checked_item = False + if name.find('std::vector', 0, 11) != 0: + if ('begin' in pyclass.__dict__ and 'end' in pyclass.__dict__): + if _cppyy._scope_byname(name+'::iterator') or \ + _cppyy._scope_byname(name+'::const_iterator'): + def __iter__(self): + i = self.begin() + while i != self.end(): + yield i.__deref__() + i.__preinc__() + i.__destruct__() + raise StopIteration + pyclass.__iter__ = __iter__ + else: + # rely on numbered iteration + add_checked_item = True # add python collection based initializer - if 0 <= name.find('vector') <= 5: + if name.find('std::vector', 0, 11) == 0: pyclass.__real_init__ = pyclass.__init__ def vector_init(self, *args): if len(args) == 1 and isinstance(args[0], (tuple, list)): @@ -452,20 +467,25 @@ pyclass.__init__ = vector_init # size-up the return of data() - pyclass.__real_data = pyclass.data - def data_with_len(self): - arr = self.__real_data() - arr.reshape((len(self),)) - return arr - pyclass.data = data_with_len + if hasattr(pyclass, 'data'): # not the case for e.g. vector<bool> + pyclass.__real_data = pyclass.data + def data_with_len(self): + arr = self.__real_data() + arr.reshape((len(self),)) + return arr + pyclass.data = data_with_len - # combine __getitem__ and __len__ to make a pythonized __getitem__ - if '__getitem__' in pyclass.__dict__ and '__len__' in pyclass.__dict__: - pyclass._getitem__unchecked = pyclass.__getitem__ - if '__setitem__' in pyclass.__dict__ and '__iadd__' in pyclass.__dict__: - pyclass.__getitem__ = python_style_sliceable_getitem - else: - pyclass.__getitem__ = python_style_getitem + # TODO: must be a simpler way to check (or at least hook these to a namespace + # std specific pythonizor) + if add_checked_item or name.find('std::vector', 0, 11) == 0 or \ + name.find('std::array', 0, 11) == 0 or name.find('std::deque', 0, 10) == 0: + # combine __getitem__ and __len__ to make a pythonized __getitem__ + if '__getitem__' in pyclass.__dict__ and '__len__' in pyclass.__dict__: + pyclass._getitem__unchecked = pyclass.__getitem__ + if '__setitem__' in pyclass.__dict__ and '__iadd__' in pyclass.__dict__: + pyclass.__getitem__ = python_style_sliceable_getitem + else: + pyclass.__getitem__ = python_style_getitem # string comparisons if name == _cppyy._std_string_name(): diff --git a/pypy/module/_cppyy/src/dummy_backend.cxx b/pypy/module/_cppyy/src/dummy_backend.cxx --- a/pypy/module/_cppyy/src/dummy_backend.cxx +++ b/pypy/module/_cppyy/src/dummy_backend.cxx @@ -62,6 +62,7 @@ m_name(name), m_argtypes(argtypes), m_returntype(returntype), m_type(mtype) {} std::string m_name; std::vector<std::string> m_argtypes; + std::vector<std::string> m_argdefaults; std::string m_returntype; EMethodType m_type; }; @@ -376,6 +377,13 @@ PUBLIC_CPPYY_STATIC_DATA(enum, CppyyTestData::EWhat); PUBLIC_CPPYY_STATIC_DATA(voidp, void*); + // default tester for long double + argtypes.clear(); + argtypes.push_back("long double"); + methods.push_back(new Cppyy_PseudoMethodInfo("get_ldouble_def", argtypes, "long double")); + methods.back()->m_argdefaults.push_back("aap_t(1)"); + s_methods["CppyyTestData::get_ldouble_def"] = methods.back(); + // pretend enum values data.push_back(Cppyy_PseudoDatambrInfo( "kNothing", "CppyyTestData::EWhat", (ptrdiff_t)&Pseudo_kNothing, true)); @@ -388,6 +396,16 @@ s_scopes[(cppyy_scope_t)s_scope_id] = info; } // -- class CppyyTest_data + //==================================================================== + + { // namespace pyzables -- + s_handles["pyzables"] = (cppyy_scope_t)++s_scope_id; + s_scopes[(cppyy_scope_t)s_scope_id] = Cppyy_PseudoClassInfo{}; + s_handles["pyzables::SomeDummy1"] = (cppyy_scope_t)++s_scope_id; + s_scopes[(cppyy_scope_t)s_scope_id] = Cppyy_PseudoClassInfo{}; + s_handles["pyzables::SomeDummy2"] = (cppyy_scope_t)++s_scope_id; + s_scopes[(cppyy_scope_t)s_scope_id] = Cppyy_PseudoClassInfo{}; + } // -- namespace pyzables } } _init; @@ -406,6 +424,8 @@ char* cppyy_resolve_name(const char* cppitem_name) { if (cppyy_is_enum(cppitem_name)) return cppstring_to_cstring("internal_enum_type_t"); + else if (strcmp(cppitem_name, "aap_t") == 0) + return cppstring_to_cstring("long double"); return cppstring_to_cstring(cppitem_name); } @@ -510,6 +530,12 @@ } else if (idx == s_methods["CppyyTestData::set_double_cr"]) { assert(self && nargs == 1); ((dummy::CppyyTestData*)self)->set_double_cr(*(double*)&((CPPYY_G__value*)args)[0]); + } else if (idx == s_methods["CppyyTestData::set_ldouble"]) { + assert(self && nargs == 1); + ((dummy::CppyyTestData*)self)->set_ldouble(((CPPYY_G__value*)args)[0].obj.ld); + } else if (idx == s_methods["CppyyTestData::set_ldouble_cr"]) { + assert(self && nargs == 1); + ((dummy::CppyyTestData*)self)->set_ldouble_cr(*(long double*)&((CPPYY_G__value*)args)[0]); } else { assert(!"method unknown in cppyy_call_v"); } @@ -784,6 +810,26 @@ return result; } +double cppyy_call_nld(cppyy_method_t method, cppyy_object_t self, int nargs, void* args) { + double result = 0.; + Cppyy_PseudoMethodInfo* idx = (Cppyy_PseudoMethodInfo*)method; + if (idx == s_methods["CppyyTestData::get_ldouble_def"]) { + if (nargs == 1) + result = (double)((dummy::CppyyTestData*)self)->get_ldouble_def( + ((CPPYY_G__value*)args)[0].obj.ld); + else { + assert(self && nargs == 0); + result = (double)((dummy::CppyyTestData*)self)->get_ldouble_def(); + } + } else if (idx == s_methods["CppyyTestData::get_ldouble"]) { + assert(self && nargs == 0); + result = (double)((dummy::CppyyTestData*)self)->get_ldouble(); + } else { + assert(!"method unknown in cppyy_call_nld"); + } + return result; +} + #define DISPATCH_CALL_R_GET(tpname) \ else if (idx == s_methods["CppyyTestData::get_"#tpname"_r"]) { \ assert(self && nargs == 0); \ @@ -879,7 +925,9 @@ /* scope reflection information ------------------------------------------- */ -int cppyy_is_namespace(cppyy_scope_t /* handle */) { +int cppyy_is_namespace(cppyy_scope_t handle) { + if (handle == s_handles["pyzables"]) + return 1; return 0; } @@ -954,14 +1002,16 @@ } int cppyy_method_req_args(cppyy_method_t method) { - return cppyy_method_num_args(method); + return cppyy_method_num_args(method)-((Cppyy_PseudoMethodInfo*)method)->m_argdefaults.size(); } char* cppyy_method_arg_type(cppyy_method_t method, int idx) { return cppstring_to_cstring(((Cppyy_PseudoMethodInfo*)method)->m_argtypes[idx]); } -char* cppyy_method_arg_default(cppyy_method_t, int /* arg_index */) { +char* cppyy_method_arg_default(cppyy_method_t method, int idx) { + if (idx < (int)((Cppyy_PseudoMethodInfo*)method)->m_argdefaults.size()) + return cppstring_to_cstring(((Cppyy_PseudoMethodInfo*)method)->m_argdefaults[idx]); return cppstring_to_cstring(""); } @@ -1048,6 +1098,11 @@ return 0; } +int cppyy_get_dimension_size( + cppyy_scope_t /* scope */, cppyy_index_t /* idata */, int /* dimension */) { + return -1; // no dimensions +} + /* misc helpers ----------------------------------------------------------- */ #if defined(_MSC_VER) @@ -1077,13 +1132,30 @@ } cppyy_object_t cppyy_charp2stdstring(const char* str, size_t sz) { - void* arena = new char[sz]; - new (arena) std::string(str, sz); - return (cppyy_object_t)arena; + return (cppyy_object_t)new std::string(str, sz); +} + +const char* cppyy_stdstring2charp(cppyy_object_t ptr, size_t* lsz) { + *lsz = ((std::string*)ptr)->size(); + return ((std::string*)ptr)->data(); } cppyy_object_t cppyy_stdstring2stdstring(cppyy_object_t ptr) { - void* arena = new char[sizeof(std::string)]; - new (arena) std::string(*(std::string*)ptr); - return (cppyy_object_t)arena; + return (cppyy_object_t)new std::string(*(std::string*)ptr); } + +double cppyy_longdouble2double(void* p) { + return (double)*(long double*)p; +} + +void cppyy_double2longdouble(double d, void* p) { + *(long double*)p = d; +} + +int cppyy_vectorbool_getitem(cppyy_object_t ptr, int idx) { + return (int)(*(std::vector<bool>*)ptr)[idx]; +} + +void cppyy_vectorbool_setitem(cppyy_object_t ptr, int idx, int value) { + (*(std::vector<bool>*)ptr)[idx] = (bool)value; +} diff --git a/pypy/module/_cppyy/test/conftest.py b/pypy/module/_cppyy/test/conftest.py --- a/pypy/module/_cppyy/test/conftest.py +++ b/pypy/module/_cppyy/test/conftest.py @@ -8,20 +8,25 @@ # run only tests that are covered by the dummy backend and tests # that do not rely on reflex import os + infomsg = 'backend is not installed' tst = os.path.basename(item.location[0]) if not tst in ('test_helper.py', 'test_cppyy.py', 'test_pythonify.py', - 'test_datatypes.py', 'test_pythonization.py'): - py.test.skip("genreflex is not installed") + 'test_cpp11features.py', 'test_datatypes.py', + 'test_pythonization.py'): + py.test.skip(infomsg) import re if tst == 'test_pythonify.py' and \ not re.search("AppTestPYTHONIFY.test0[1-5]", item.location[2]): - py.test.skip("genreflex is not installed") + py.test.skip(infomsg) + elif tst == 'test_cpp11features.py' and \ + not re.search("AppTestCPP11FEATURES.test02", item.location[2]): + py.test.skip(infomsg) elif tst == 'test_datatypes.py' and \ not re.search("AppTestDATATYPES.test0[1-7]", item.location[2]): - py.test.skip("genreflex is not installed") + py.test.skip(infomsg) elif tst == 'test_pythonization.py' and \ not re.search("AppTestPYTHONIZATION.test0[0]", item.location[2]): - py.test.skip("genreflex is not installed") + py.test.skip(infomsg) def pytest_ignore_collect(path, config): if py.path.local.sysfind('genreflex') is None and config.option.runappdirect: diff --git a/pypy/module/_cppyy/test/datatypes.cxx b/pypy/module/_cppyy/test/datatypes.cxx --- a/pypy/module/_cppyy/test/datatypes.cxx +++ b/pypy/module/_cppyy/test/datatypes.cxx @@ -113,6 +113,7 @@ float CppyyTestData::get_float() { return m_float; } double CppyyTestData::get_double() { return m_double; } long double CppyyTestData::get_ldouble() { return m_ldouble; } +long double CppyyTestData::get_ldouble_def(long double ld) { return ld; } CppyyTestData::EWhat CppyyTestData::get_enum() { return m_enum; } void* CppyyTestData::get_voidp() { return m_voidp; } diff --git a/pypy/module/_cppyy/test/datatypes.h b/pypy/module/_cppyy/test/datatypes.h --- a/pypy/module/_cppyy/test/datatypes.h +++ b/pypy/module/_cppyy/test/datatypes.h @@ -32,6 +32,8 @@ enum {E1 = -1}; enum EE {E2 = -1}; }; + + typedef enum { AA = 1, BB, CC, DD } letter_code; } @@ -94,6 +96,8 @@ float get_float(); double get_double(); long double get_ldouble(); + typedef long double aap_t; + long double get_ldouble_def(long double ld = aap_t(1)); EWhat get_enum(); void* get_voidp(); diff --git a/pypy/module/_cppyy/test/datatypes.xml b/pypy/module/_cppyy/test/datatypes.xml --- a/pypy/module/_cppyy/test/datatypes.xml +++ b/pypy/module/_cppyy/test/datatypes.xml @@ -6,6 +6,7 @@ <enum name="EFruit" /> <enum name="EnumSpace::E" /> <class name="EnumSpace::EnumClass" /> + <enum name="EnumSpace::letter_code" /> <function pattern="get_*" /> <function pattern="set_*" /> diff --git a/pypy/module/_cppyy/test/pythonizables.cxx b/pypy/module/_cppyy/test/pythonizables.cxx --- a/pypy/module/_cppyy/test/pythonizables.cxx +++ b/pypy/module/_cppyy/test/pythonizables.cxx @@ -27,3 +27,5 @@ unsigned int pyzables::pass_mine_rp(Countable c) { return c.m_check; } unsigned int pyzables::pass_mine_rp_ref(const Countable& c) { return c.m_check; } unsigned int pyzables::pass_mine_rp_ptr(const Countable* c) { return c->m_check; } + +pyzables::Countable* pyzables::gime_naked_countable() { return new Countable{}; } diff --git a/pypy/module/_cppyy/test/pythonizables.h b/pypy/module/_cppyy/test/pythonizables.h --- a/pypy/module/_cppyy/test/pythonizables.h +++ b/pypy/module/_cppyy/test/pythonizables.h @@ -57,4 +57,6 @@ unsigned int pass_mine_rp_ref(const Countable&); unsigned int pass_mine_rp_ptr(const Countable*); +Countable* gime_naked_countable(); + } // namespace pyzables diff --git a/pypy/module/_cppyy/test/stltypes.h b/pypy/module/_cppyy/test/stltypes.h --- a/pypy/module/_cppyy/test/stltypes.h +++ b/pypy/module/_cppyy/test/stltypes.h @@ -4,12 +4,23 @@ #include <utility> #include <vector> + //- basic example class class just_a_class { public: int m_i; }; _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit