Author: Wim Lavrijsen <wlavrij...@lbl.gov> Branch: cppyy-packaging Changeset: r94742:c68cd6b1c308 Date: 2018-06-08 22:26 -0700 http://bitbucket.org/pypy/pypy/changeset/c68cd6b1c308/
Log: further support for templated methods and for sfinae 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 @@ -69,7 +69,8 @@ space = self.space cif_descr = self.cif_descr size = cif_descr.exchange_size - raw_string = rffi.cast(rffi.CCHARP, 0) # only ever have one in the CAPI + raw_string1 = rffi.cast(rffi.CCHARP, 0) + raw_string2 = rffi.cast(rffi.CCHARP, 0) # have max two in any CAPI buffer = lltype.malloc(rffi.CCHARP.TO, size, flavor='raw') try: for i in range(len(args)): @@ -91,11 +92,15 @@ else: # only other use is string assert obj.tc == 's' n = len(obj._string) - assert raw_string == rffi.cast(rffi.CCHARP, 0) - # XXX could use rffi.get_nonmovingbuffer_final_null() - raw_string = rffi.str2charp(obj._string) data = rffi.cast(rffi.CCHARPP, data) - data[0] = raw_string + if raw_string1 == rffi.cast(rffi.CCHARP, 0): + # XXX could use rffi.get_nonmovingbuffer_final_null() + raw_string1 = rffi.str2charp(obj._string) + data[0] = raw_string1 + else: + assert raw_string2 == rffi.cast(rffi.CCHARP, 0) + raw_string2 = rffi.str2charp(obj._string) + data[0] = raw_string2 jit_libffi.jit_ffi_call(cif_descr, rffi.cast(rffi.VOIDP, funcaddr), @@ -106,8 +111,10 @@ # immediate unwrapping, the round-trip is removed w_res = self.ctitem.copy_and_convert_to_object(resultdata) finally: - if raw_string != rffi.cast(rffi.CCHARP, 0): - rffi.free_charp(raw_string) + if raw_string1 != rffi.cast(rffi.CCHARP, 0): + rffi.free_charp(raw_string1) + if raw_string2 != rffi.cast(rffi.CCHARP, 0): + rffi.free_charp(raw_string2) lltype.free(buffer, flavor='raw') return w_res @@ -218,6 +225,7 @@ 'get_method' : ([c_scope, c_index], c_method), 'method_name' : ([c_method], c_ccharp), + 'method_full_name' : ([c_method], c_ccharp), 'method_mangled_name' : ([c_method], c_ccharp), 'method_result_type' : ([c_method], c_ccharp), 'method_num_args' : ([c_method], c_int), @@ -528,6 +536,8 @@ def c_method_name(space, cppmeth): return charp2str_free(space, call_capi(space, 'method_name', [_ArgH(cppmeth)])) +def c_method_full_name(space, cppmeth): + return charp2str_free(space, call_capi(space, 'method_full_name', [_ArgH(cppmeth)])) def c_method_mangled_name(space, cppmeth): return charp2str_free(space, call_capi(space, 'method_mangled_name', [_ArgH(cppmeth)])) def c_method_result_type(space, cppmeth): @@ -558,8 +568,8 @@ 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): - args = [_ArgH(cppscope.handle), _ArgS(name)] +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: 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 @@ -492,8 +492,8 @@ def _unwrap_object(self, space, w_obj): from pypy.module._cppyy.interp_cppyy import W_CPPInstance if isinstance(w_obj, W_CPPInstance): - from pypy.module._cppyy.interp_cppyy import INSTANCE_FLAGS_IS_R_VALUE - if w_obj.flags & INSTANCE_FLAGS_IS_R_VALUE: + from pypy.module._cppyy.interp_cppyy import INSTANCE_FLAGS_IS_RVALUE + if w_obj.flags & INSTANCE_FLAGS_IS_RVALUE: # reject moves as all are explicit raise ValueError("lvalue expected") if capi.c_is_subtype(space, w_obj.clsdecl, self.clsdecl): @@ -522,11 +522,18 @@ class InstanceMoveConverter(InstanceRefConverter): def _unwrap_object(self, space, w_obj): # moving is same as by-ref, but have to check that move is allowed - from pypy.module._cppyy.interp_cppyy import W_CPPInstance, INSTANCE_FLAGS_IS_R_VALUE - if isinstance(w_obj, W_CPPInstance): - if w_obj.flags & INSTANCE_FLAGS_IS_R_VALUE: - w_obj.flags &= ~INSTANCE_FLAGS_IS_R_VALUE - return InstanceRefConverter._unwrap_object(self, space, w_obj) + from pypy.module._cppyy.interp_cppyy import W_CPPInstance, INSTANCE_FLAGS_IS_RVALUE + obj = space.interp_w(W_CPPInstance, w_obj) + if obj: + if obj.flags & INSTANCE_FLAGS_IS_RVALUE: + obj.flags &= ~INSTANCE_FLAGS_IS_RVALUE + try: + return InstanceRefConverter._unwrap_object(self, space, w_obj) + except Exception: + # TODO: if the method fails on some other converter, then the next + # overload can not be an rvalue anymore + obj.flags |= INSTANCE_FLAGS_IS_RVALUE + raise raise oefmt(space.w_ValueError, "object is not an rvalue") 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 @@ -135,6 +135,8 @@ RPY_EXTERN char* cppyy_method_name(cppyy_method_t); RPY_EXTERN + char* cppyy_method_full_name(cppyy_method_t); + RPY_EXTERN char* cppyy_method_mangled_name(cppyy_method_t); RPY_EXTERN char* cppyy_method_result_type(cppyy_method_t); @@ -158,7 +160,7 @@ RPY_EXTERN int cppyy_method_is_template(cppyy_scope_t scope, cppyy_index_t idx); RPY_EXTERN - cppyy_method_t cppyy_get_method_template(cppyy_scope_t scope, const char* name); + cppyy_method_t cppyy_get_method_template(cppyy_scope_t scope, const char* name, const char* proto); RPY_EXTERN cppyy_index_t cppyy_get_global_operator( 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 @@ -18,7 +18,7 @@ INSTANCE_FLAGS_PYTHON_OWNS = 0x0001 INSTANCE_FLAGS_IS_REF = 0x0002 -INSTANCE_FLAGS_IS_R_VALUE = 0x0004 +INSTANCE_FLAGS_IS_RVALUE = 0x0004 OVERLOAD_FLAGS_USE_FFI = 0x0001 @@ -679,6 +679,46 @@ self.overloads = {} self.master = self + def construct_template_args(self, w_args): + space = self.space + tmpl_args = '' + for i in range(space.len_w(w_args)): + w_obj = space.getitem(w_args, space.newint(i)) + if space.isinstance_w(w_obj, space.w_text): + s = space.text_w(w_obj) # string describing type + elif space.isinstance_w(w_obj, space.w_type): + try: + # cppyy bound types + name = space.getattr(w_obj, space.newtext('__cppname__')) + except OperationError: + # generic python types + name = space.getattr(w_obj, space.newtext('__name__')) + s = space.text_w(name) + else: + # builtin types etc. + s = space.text_w(space.str(w_obj)) + # map python types -> C++ types + if s == 'str': s = 'std::string' + if i != 0: tmpl_args += ', ' + tmpl_args += s + return tmpl_args + + def find_method_template(self, name, proto = ''): + # find/instantiate new callable function + space = self.space + cppmeth = capi.c_get_method_template(space, self.scope, name, proto) + if not cppmeth: + raise oefmt(self.space.w_AttributeError, + "scope '%s' has no function %s", self.scope.name, name) + + funcs = [] + ftype = self.scope._make_cppfunction(name, cppmeth, funcs) + if ftype & FUNCTION_IS_STATIC: + cppol = W_CPPStaticOverload(space, self.scope, funcs[:], self.flags) + else: + cppol = W_CPPOverload(space, self.scope, funcs[:], self.flags) + return cppol + @unwrap_spec(args_w='args_w') def descr_get(self, w_cppinstance, args_w): if self.space.is_w(w_cppinstance, self.space.w_None): @@ -695,13 +735,21 @@ for cppol in self.master.overloads.values(): try: cppol.descr_get(self.w_this, []).call(args_w) - except Exception as e: + except Exception: pass # completely ignore for now; have to see whether errors become confusing - # if all failed, then try to deduce type - types_w = [self.space.type(obj_w) for obj_w in args_w] - method = self.getitem(types_w) - return method.call(args_w) + # 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) + method = self.find_method_template(self.name, proto) + + # only cache result if the name retains the full template + if len(method.functions) == 1: + fullname = capi.c_method_full_name(self.space, method.functions[0].cppmethod) + if 0 <= fullname.rfind('>'): + self.master.overloads[fullname] = method + + return method.descr_get(self.w_this, []).call(args_w) @unwrap_spec(args_w='args_w') def getitem(self, args_w): @@ -712,45 +760,17 @@ else: w_args = space.newtuple(args_w) - tmpl_args = '' - for i in range(space.len_w(w_args)): - w_obj = space.getitem(w_args, space.newint(i)) - if space.isinstance_w(w_obj, space.w_text): - s = space.text_w(w_obj) # string describing type - elif space.isinstance_w(w_obj, space.w_type): - try: - # cppyy bound types - name = space.getattr(w_obj, space.newtext('__cppname__')) - except OperationError: - # generic python types - name = space.getattr(w_obj, space.newtext('__name__')) - s = space.text_w(name) - else: - # builtin types etc. - s = space.text_w(space.str(w_obj)) - if i != 0: tmpl_args += ', ' - tmpl_args += s + tmpl_args = self.construct_template_args(w_args) fullname = self.name+'<'+tmpl_args+'>' + try: + method = self.master.overloads[fullname] + except KeyError: + method = self.find_method_template(fullname) - # find/instantiate new callable function - try: - return self.master.overloads[fullname].descr_get(self.w_this, []) - except KeyError: - pass + # cache result (name is always full templated name) + self.master.overloads[fullname] = method - cppmeth = capi.c_get_method_template(space, self.scope, fullname) - if not cppmeth: - raise oefmt(self.space.w_AttributeError, - "scope '%s' has no function %s", self.scope.name, fullname) - - funcs = [] - ftype = self.scope._make_cppfunction(fullname, cppmeth, funcs) - if ftype & FUNCTION_IS_STATIC: - cppol = W_CPPStaticOverload(space, self.scope, funcs[:], self.flags) - else: - cppol = W_CPPOverload(space, self.scope, funcs[:], self.flags) - self.master.overloads[fullname] = cppol - return cppol.descr_get(self.w_this, []) + return method.descr_get(self.w_this, []) def __repr__(self): return "W_CPPTemplateOverload(%s)" % [f.prototype() for f in self.functions] @@ -1502,7 +1522,7 @@ """Casts the given instance into an C++-style rvalue.""" obj = space.interp_w(W_CPPInstance, w_obj) if obj: - obj.flags |= INSTANCE_FLAGS_IS_R_VALUE + obj.flags |= INSTANCE_FLAGS_IS_RVALUE return w_obj 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 @@ -8,9 +8,9 @@ # the interp-level does not support metaclasses, they are created at app-level. # These are the metaclass base classes: class CPPScope(type): - def __getattr__(self, name): + def __getattr__(self, name, type_only=False): try: - return get_scoped_pycppitem(self, name) # will cache on self + return get_scoped_pycppitem(self, name, type_only) # will cache on self except Exception as e: raise AttributeError("%s object has no attribute '%s' (details: %s)" % (self, name, str(e))) @@ -52,7 +52,11 @@ fullname = ''.join( [self._name, '<', ','.join(map(self._arg_to_str, args))]) fullname += '>' - return getattr(self._scope, fullname) + try: + return getattr(self._scope, fullname, True) + except AttributeError: + pass + raise TypeError("%s does not exist" % fullname) def __getitem__(self, *args): if args and type(args[0]) == tuple: @@ -214,7 +218,7 @@ return CPPTemplate(template_name, scope) -def get_scoped_pycppitem(scope, name): +def get_scoped_pycppitem(scope, name, type_only=False): import _cppyy # resolve typedefs/aliases: these may cross namespaces, in which case @@ -237,6 +241,9 @@ else: pycppitem = make_cppclass(scope, name, cppitem) + if type_only: + return pycppitem + # templates if not cppitem: cppitem = _cppyy._is_template(final_scoped_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 @@ -941,6 +941,10 @@ return cppstring_to_cstring(((Cppyy_PseudoMethodInfo*)method)->m_name); } +char* cppyy_method_full_name(cppyy_method_t method) { + return cppstring_to_cstring(((Cppyy_PseudoMethodInfo*)method)->m_name); +} + char* cppyy_method_result_type(cppyy_method_t method) { return cppstring_to_cstring(((Cppyy_PseudoMethodInfo*)method)->m_returntype); } diff --git a/pypy/module/_cppyy/test/advancedcpp.cxx b/pypy/module/_cppyy/test/advancedcpp.cxx --- a/pypy/module/_cppyy/test/advancedcpp.cxx +++ b/pypy/module/_cppyy/test/advancedcpp.cxx @@ -77,6 +77,10 @@ double my_global_array[500]; static double sd = 1234.; double* my_global_ptr = &sd; +some_int_holder my_global_int_holders[5] = { + some_int_holder(13), some_int_holder(42), some_int_holder(88), + some_int_holder(-1), some_int_holder(17) }; + // for life-line and identity testing int some_class_with_data::some_data::s_num_data = 0; diff --git a/pypy/module/_cppyy/test/advancedcpp.h b/pypy/module/_cppyy/test/advancedcpp.h --- a/pypy/module/_cppyy/test/advancedcpp.h +++ b/pypy/module/_cppyy/test/advancedcpp.h @@ -266,8 +266,8 @@ class some_comparable { }; -bool operator==(const some_comparable& c1, const some_comparable& c2 ); -bool operator!=( const some_comparable& c1, const some_comparable& c2 ); +bool operator==(const some_comparable& c1, const some_comparable& c2); +bool operator!=(const some_comparable& c1, const some_comparable& c2); //=========================================================================== @@ -276,6 +276,17 @@ extern double* my_global_ptr; static const char my_global_string[] = "aap " " noot " " mies"; +class some_int_holder { +public: + some_int_holder(int val) : m_val(val) {} + +public: + int m_val; + char gap[7]; +}; +extern some_int_holder my_global_int_holders[5]; + + //=========================================================================== class some_class_with_data { // for life-line and identity testing public: diff --git a/pypy/module/_cppyy/test/advancedcpp.xml b/pypy/module/_cppyy/test/advancedcpp.xml --- a/pypy/module/_cppyy/test/advancedcpp.xml +++ b/pypy/module/_cppyy/test/advancedcpp.xml @@ -40,6 +40,7 @@ <variable name="my_global_double" /> <variable name="my_global_array" /> <variable name="my_global_ptr" /> + <variable name="my_global_int_holders" /> <class name="ref_tester" /> <class name="std::vector<ref_tester>" /> diff --git a/pypy/module/_cppyy/test/test_pythonization.py b/pypy/module/_cppyy/test/test_pythonization.py --- a/pypy/module/_cppyy/test/test_pythonization.py +++ b/pypy/module/_cppyy/test/test_pythonization.py @@ -75,6 +75,9 @@ import _cppyy as cppyy + # TODO: disabled for now until decided on proper naming/iface + return + cppyy.gbl.pyzables.GimeDerived._creates = True result = cppyy.gbl.pyzables.GimeDerived() diff --git a/pypy/module/_cppyy/test/test_templates.py b/pypy/module/_cppyy/test/test_templates.py --- a/pypy/module/_cppyy/test/test_templates.py +++ b/pypy/module/_cppyy/test/test_templates.py @@ -124,7 +124,7 @@ import _cppyy Obj1 = _cppyy.gbl.AttrTesting.Obj1 Obj2 = _cppyy.gbl.AttrTesting.Obj2 - select_template_arg = _cppyy.gbl.AttrTesting.has_var1 + select_template_arg = _cppyy.gbl.AttrTesting.select_template_arg #assert select_template_arg[0, Obj1, Obj2].argument == Obj1 assert select_template_arg[1, Obj1, Obj2].argument == Obj2 diff --git a/pypy/module/_cppyy/test/test_zjit.py b/pypy/module/_cppyy/test/test_zjit.py --- a/pypy/module/_cppyy/test/test_zjit.py +++ b/pypy/module/_cppyy/test/test_zjit.py @@ -61,6 +61,10 @@ typename = "str" def __init__(self, val): self.val = val +class FakeTuple(FakeBase): + typename = "tuple" + def __init__(self, val): + self.val = val class FakeType(FakeBase): typename = "type" def __init__(self, name): @@ -172,6 +176,13 @@ def newtext(self, obj): return FakeString(obj) + @specialize.argtype(1) + def newtuple(self, obj): + return FakeTuple(obj) + + def getitem(self, coll, i): + return coll.val[i.val] + def float_w(self, w_obj, allow_conversion=True): assert isinstance(w_obj, FakeFloat) return w_obj.val _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit