[pypy-commit] pypy default: std::vector performance and handle long double through narrowing
wlav
pypy.commits at gmail.com
Sat Aug 4 00:10:01 EDT 2018
Author: Wim Lavrijsen <WLavrijsen at lbl.gov>
Branch:
Changeset: r94934:cc09dd3d75c4
Date: 2018-08-03 17:44 -0700
http://bitbucket.org/pypy/pypy/changeset/cc09dd3d75c4/
Log: std::vector performance and handle long double through narrowing
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
@@ -8,7 +8,7 @@
.. branch: cppyy-packaging
Main items: vastly better template resolution and improved performance. In
-detail: upgrade to backend 1.3.1, improved handling of templated methods and
+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/_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
@@ -274,8 +289,9 @@
'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),
}
@@ -404,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)]
@@ -658,13 +676,11 @@
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):
@@ -702,6 +718,52 @@
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):
@@ -712,6 +774,9 @@
### std::string
stdstring_c_str,
+ ### std::vector
+ stdvector_iter,
+
### std::vector<bool>
vectorbool_getitem,
vectorbool_setitem,
@@ -730,6 +795,9 @@
_method_alias(space, w_pycppclass, "_cppyy_as_builtin", "c_str")
_method_alias(space, w_pycppclass, "__str__", "c_str")
- if name == "std::vector<bool>":
+ 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
@@ -211,6 +211,9 @@
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
@@ -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):
@@ -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, 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):
@@ -949,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
@@ -984,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:
@@ -1001,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:
@@ -1021,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):
@@ -341,6 +377,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/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);
@@ -220,9 +222,10 @@
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
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
@@ -230,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:
@@ -848,7 +850,7 @@
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 to
+ # 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:
@@ -1221,6 +1223,12 @@
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 = []
@@ -1269,6 +1277,8 @@
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)
@@ -1326,6 +1336,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__),
@@ -1482,6 +1493,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
@@ -57,7 +57,7 @@
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):
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
@@ -333,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
@@ -361,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:
@@ -380,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
@@ -430,8 +435,8 @@
# 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 not (0 <= name.find('vector<bool') <= 5):
+ 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'):
@@ -443,10 +448,12 @@
i.__destruct__()
raise StopIteration
pyclass.__iter__ = __iter__
- # else: rely on numbered iteration
+ 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)):
@@ -468,13 +475,17 @@
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));
@@ -416,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);
}
@@ -520,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");
}
@@ -794,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); \
@@ -966,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("");
}
@@ -1094,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/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/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;
};
+// enum for vector of enums setitem tests
+enum VecTestEnum {
+ EVal1 = 1, EVal2 = 3
+};
+
+namespace VecTestEnumNS {
+ enum VecTestEnum { EVal1 = 5, EVal2 = 42 };
+}
+
+
//- class with lots of std::string handling
class stringy_class {
public:
@@ -31,35 +42,15 @@
class stl_like_class {
public:
no_dict_available* begin() { return 0; }
- no_dict_available* end() { return 0; }
+ no_dict_available* end() { return (no_dict_available*)1; }
int size() { return 4; }
int operator[](int i) { return i; }
std::string operator[](double) { return "double"; }
std::string operator[](const std::string&) { return "string"; }
};
-
-//- instantiations of used STL types
namespace {
-
stl_like_class<int> stlc_1;
-
-} // unnamed namespace
-
-
-// comps for int only to allow testing: normal use of vector is looping over a
-// range-checked version of __getitem__
-#if defined(__clang__) && defined(__APPLE__)
-namespace std {
-#define ns_prefix std::
-#elif defined(__GNUC__) || defined(__GNUG__)
-namespace __gnu_cxx {
-#define ns_prefix
-#endif
-extern template bool ns_prefix operator==(const std::vector<int>::iterator&,
- const std::vector<int>::iterator&);
-extern template bool ns_prefix operator!=(const std::vector<int>::iterator&,
- const std::vector<int>::iterator&);
}
@@ -67,6 +58,8 @@
namespace ArrayTest {
struct Point {
+ Point() : px(0), py(0) {}
+ Point(int x, int y) : px(x), py(y) {}
int px, py;
};
diff --git a/pypy/module/_cppyy/test/stltypes.xml b/pypy/module/_cppyy/test/stltypes.xml
--- a/pypy/module/_cppyy/test/stltypes.xml
+++ b/pypy/module/_cppyy/test/stltypes.xml
@@ -1,6 +1,9 @@
<lcgdict>
<class name="just_a_class" />
+ <enum name="VecTestEnum" />
+ <namespace name="VecTestEnumNS" />
+ <enum name="VecTestEnumNS::VecTestEnum" />
<class name="stringy_class" />
<class pattern="stl_like_class<*>" />
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
@@ -56,10 +56,11 @@
assert round(c.m_double + 77., 11) == 0
assert round(c.get_double_cr() + 77., 11) == 0
assert round(c.get_double_r() + 77., 11) == 0
- #assert round(c.m_ldouble + 88., 24) == 0
- #assert round(c.get_ldouble_cr() + 88., 24) == 0
- #assert round(c.get_ldouble_r() + 88., 24) == 0
- assert round(c.m_double + 77., 8) == 0
+ assert round(c.m_ldouble + 88., 24) == 0
+ assert round(c.get_ldouble_cr() + 88., 24) == 0
+ assert round(c.get_ldouble_r() + 88., 24) == 0
+ assert round(c.get_ldouble_def() -1., 24) == 0
+ assert round(c.get_ldouble_def(2) -2., 24) == 0
"""# complex<double> type
assert type(c.get_complex()) == complex
@@ -187,16 +188,20 @@
assert eval('c.m_%s' % names[i]) == 3*i
# float types through functions
- c.set_float( 0.123 ); assert round(c.get_float() - 0.123, 5) == 0
- c.set_double( 0.456 ); assert round(c.get_double() - 0.456, 8) == 0
+ c.set_float(0.123); assert round(c.get_float() - 0.123, 5) == 0
+ c.set_double(0.456); assert round(c.get_double() - 0.456, 8) == 0
+ c.set_ldouble(0.789); assert round(c.get_ldouble() - 0.789, 8) == 0
# float types through data members
- c.m_float = 0.123; assert round(c.get_float() - 0.123, 5) == 0
- c.set_float(0.234); assert round(c.m_float - 0.234, 5) == 0
- c.set_float_cr(0.456); assert round(c.m_float - 0.456, 5) == 0
- c.m_double = 0.678; assert round(c.get_double() - 0.678, 8) == 0
- c.set_double(0.890); assert round(c.m_double - 0.890, 8) == 0
- c.set_double_cr(0.012); assert round(c.m_double - 0.012, 8) == 0
+ c.m_float = 0.123; assert round(c.get_float() - 0.123, 5) == 0
+ c.set_float(0.234); assert round(c.m_float - 0.234, 5) == 0
+ c.set_float_cr(0.456); assert round(c.m_float - 0.456, 5) == 0
+ c.m_double = 0.678; assert round(c.get_double() - 0.678, 8) == 0
+ c.set_double(0.890); assert round(c.m_double - 0.890, 8) == 0
+ c.set_double_cr(0.012); assert round(c.m_double - 0.012, 8) == 0
+ c.m_ldouble = 0.876; assert round(c.get_ldouble() - 0.876, 8) == 0
+ c.set_ldouble(0.098); assert round(c.m_ldouble - 0.098, 8) == 0
+ c.set_ldouble_cr(0.210); assert round(c.m_ldouble - 0.210, 8) == 0
# arrays; there will be pointer copies, so destroy the current ones
c.destroy_arrays()
@@ -295,10 +300,12 @@
assert CppyyTestData.s_ullong == 404
# floating point types
- assert round(CppyyTestData.s_float + 606., 5) == 0
- assert round(c.s_float + 606., 5) == 0
- assert round(CppyyTestData.s_double + 707., 8) == 0
- assert round(c.s_double + 707., 8) == 0
+ assert round(CppyyTestData.s_float + 606., 5) == 0
+ assert round(c.s_float + 606., 5) == 0
+ assert round(CppyyTestData.s_double + 707., 8) == 0
+ assert round(c.s_double + 707., 8) == 0
+ assert round(CppyyTestData.s_ldouble + 808., 8) == 0
+ assert round(c.s_ldouble + 808., 8) == 0
c.__destruct__()
@@ -363,6 +370,10 @@
assert CppyyTestData.s_double == -math.pi
CppyyTestData.s_double = math.pi
assert c.s_double == math.pi
+ c.s_ldouble = -math.pi
+ assert CppyyTestData.s_ldouble == -math.pi
+ CppyyTestData.s_ldouble = math.pi
+ assert c.s_ldouble == math.pi
c.__destruct__()
@@ -499,6 +510,11 @@
assert gbl.EnumSpace.EnumClass.E1 == -1 # anonymous
assert gbl.EnumSpace.EnumClass.E2 == -1 # named type
+ # typedef enum
+ assert gbl.EnumSpace.letter_code
+ assert gbl.EnumSpace.AA == 1
+ assert gbl.EnumSpace.BB == 2
+
def test11_string_passing(self):
"""Test passing/returning of a const char*"""
diff --git a/pypy/module/_cppyy/test/test_stltypes.py b/pypy/module/_cppyy/test/test_stltypes.py
--- a/pypy/module/_cppyy/test/test_stltypes.py
+++ b/pypy/module/_cppyy/test/test_stltypes.py
@@ -215,6 +215,26 @@
assert len(vb[4:8]) == 4
assert list(vb[4:8]) == [False]*3+[True]
+ def test08_vector_enum(self):
+ """Usability of std::vector<> of some enums"""
+
+ import _cppyy as cppyy
+
+ # TODO: would like to use cppyy.gbl.VecTestEnum but that's an int
+ assert cppyy.gbl.VecTestEnum
+ ve = cppyy.gbl.std.vector['VecTestEnum']()
+ ve.push_back(cppyy.gbl.EVal1);
+ assert ve[0] == 1
+ ve[0] = cppyy.gbl.EVal2
+ assert ve[0] == 3
+
+ assert cppyy.gbl.VecTestEnumNS.VecTestEnum
+ ve = cppyy.gbl.std.vector['VecTestEnumNS::VecTestEnum']()
+ ve.push_back(cppyy.gbl.VecTestEnumNS.EVal1);
+ assert ve[0] == 5
+ ve[0] = cppyy.gbl.VecTestEnumNS.EVal2
+ assert ve[0] == 42
+
class AppTestSTLSTRING:
spaceconfig = dict(usemodules=['_cppyy', '_rawffi', 'itertools'])
@@ -484,10 +504,11 @@
stl_like_class = cppyy.gbl.stl_like_class
a = stl_like_class(int)()
- for i in a:
- pass
+ assert len(a) == 4
+ for i, j in enumerate(a):
+ assert i == j
- assert i == 3
+ assert i == len(a)-1
class AppTestSTLITERATOR:
@@ -594,9 +615,9 @@
def test03_array_of_pointer_to_pods(self):
"""Usage of std::array of pointer to PODs"""
- import cppyy
- from cppyy import gbl
- from cppyy.gbl import std
+ import _cppyy as cppyy
+ gbl = cppyy.gbl
+ std = cppyy.gbl.std
ll = [gbl.ArrayTest.Point() for i in range(4)]
for i in range(len(ll)):
More information about the pypy-commit
mailing list