Author: Wim Lavrijsen <wlavrij...@lbl.gov> Branch: Changeset: r94771:20e8b110028c Date: 2018-06-16 01:16 -0700 http://bitbucket.org/pypy/pypy/changeset/20e8b110028c/
Log: more templates and consistency with CPython/cppyy 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 @@ -706,7 +706,7 @@ "no overload found matching %s", self.signature) -class SmartPointerConverter(TypeConverter): +class SmartPtrConverter(TypeConverter): _immutable_fields = ['typecode', 'smartdecl', 'rawdecl', 'deref'] typecode = 'V' @@ -753,7 +753,7 @@ return interp_cppyy.wrap_cppinstance(space, address, self.rawdecl, smartdecl=self.smartdecl, deref=self.deref, do_cast=False) -class SmartPointerPtrConverter(SmartPointerConverter): +class SmartPtrPtrConverter(SmartPtrConverter): typecode = 'o' def from_memory(self, space, w_obj, w_pycppclass, offset): @@ -763,7 +763,7 @@ self._is_abstract(space) -class SmartPointerRefConverter(SmartPointerPtrConverter): +class SmartPtrRefConverter(SmartPtrPtrConverter): typecode = 'V' @@ -804,6 +804,12 @@ compound = helper.compound(name) clean_name = capi.c_resolve_name(space, helper.clean_type(name)) try: + return _converters[clean_name+compound](space, default) + except KeyError: + pass + + # arrays + 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 @@ -823,11 +829,11 @@ check_smart = capi.c_smartptr_info(space, clean_name) if check_smart[0]: if compound == '': - return SmartPointerConverter(space, clsdecl, check_smart[1], check_smart[2]) + return SmartPtrConverter(space, clsdecl, check_smart[1], check_smart[2]) elif compound == '*': - return SmartPointerPtrConverter(space, clsdecl, check_smart[1], check_smart[2]) + return SmartPtrPtrConverter(space, clsdecl, check_smart[1], check_smart[2]) elif compound == '&': - return SmartPointerRefConverter(space, clsdecl, check_smart[1], check_smart[2]) + 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 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 @@ -26,7 +26,7 @@ NULL = lltype.nullptr(jit_libffi.FFI_TYPE_P.TO) -class FunctionExecutor(object): +class Executor(object): def __init__(self, space, extra): pass @@ -43,7 +43,7 @@ raise FastCallNotPossible -class PtrTypeExecutor(FunctionExecutor): +class PtrTypeExecutor(Executor): _immutable_fields_ = ['typecode'] typecode = 'P' @@ -63,7 +63,7 @@ return W_ArrayInstance(space, shape, sys.maxint/shape.size, ptrval) -class VoidExecutor(FunctionExecutor): +class VoidExecutor(Executor): def cffi_type(self, space): state = space.fromcache(ffitypes.State) return state.c_void @@ -96,7 +96,7 @@ _mixin_ = True def __init__(self, space, extra): - FunctionExecutor.__init__(self, space, extra) + Executor.__init__(self, space, extra) self.do_assign = False self.item = rffi.cast(self.c_type, 0) @@ -124,7 +124,7 @@ rffi.cast(self.c_ptrtype, rffi.cast(rffi.VOIDPP, result)[0])) -class CStringExecutor(FunctionExecutor): +class CStringExecutor(Executor): def execute(self, space, cppmethod, cppthis, num_args, args): lresult = capi.c_call_l(space, cppmethod, cppthis, num_args, args) ccpresult = rffi.cast(rffi.CCHARP, lresult) @@ -134,7 +134,7 @@ return space.newbytes(result) -class ConstructorExecutor(FunctionExecutor): +class ConstructorExecutor(Executor): def execute(self, space, cppmethod, cpptype, num_args, args): from pypy.module._cppyy import interp_cppyy newthis = capi.c_constructor(space, cppmethod, cpptype, num_args, args) @@ -142,12 +142,12 @@ return space.newlong(rffi.cast(rffi.LONG, newthis)) # really want ptrdiff_t here -class InstanceExecutor(FunctionExecutor): +class InstanceExecutor(Executor): # For return of a C++ instance by pointer: MyClass* func() _immutable_fields_ = ['clsdecl'] def __init__(self, space, clsdecl): - FunctionExecutor.__init__(self, space, clsdecl) + Executor.__init__(self, space, clsdecl) self.clsdecl = clsdecl def _wrap_result(self, space, obj): @@ -338,7 +338,7 @@ return _executors['void*'](space, None) # allow at least passing of the pointer # currently used until proper lazy instantiation available in interp_cppyy - return FunctionExecutor(space, None) + return Executor(space, None) _executors["void"] = VoidExecutor @@ -374,10 +374,10 @@ ) for c_type, stub, names in type_info: - class BasicExecutor(ffitypes.typeid(c_type), NumericExecutorMixin, FunctionExecutor): + class BasicExecutor(ffitypes.typeid(c_type), NumericExecutorMixin, Executor): _immutable_ = True c_stubcall = staticmethod(stub) - class BasicRefExecutor(ffitypes.typeid(c_type), NumericRefExecutorMixin, FunctionExecutor): + class BasicRefExecutor(ffitypes.typeid(c_type), NumericRefExecutorMixin, Executor): def cffi_type(self, space): state = space.fromcache(ffitypes.State) return state.c_voidp 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 @@ -672,24 +672,33 @@ _mixin_ = True - def construct_template_args(self, w_args): + def construct_template_args(self, w_tpArgs, args_w = None): 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): + for i in range(space.len_w(w_tpArgs)): + w_tp = space.getitem(w_tpArgs, space.newint(i)) + if space.isinstance_w(w_tp, space.w_text): + s = space.text_w(w_tp) # string describing type + elif space.isinstance_w(w_tp, space.w_type): try: # cppyy bound types - name = space.getattr(w_obj, space.newtext('__cppname__')) + s = space.text_w(space.getattr(w_tp, space.newtext('__cppname__'))) + if args_w: + # try to specialize the type match for the given object + cppinstance = self.space.interp_w(W_CPPInstance, args_w[i]) + if cppinstance.flags & INSTANCE_FLAGS_IS_RVALUE: + sugar = "&&" + elif cppinstance.flags & INSTANCE_FLAGS_IS_REF: + sugar = "*" + else: + sugar = "&" + s += sugar except OperationError: # generic python types - name = space.getattr(w_obj, space.newtext('__name__')) - s = space.text_w(name) + s = space.text_w(space.getattr(w_tp, space.newtext('__name__'))) else: # builtin types etc. - s = space.text_w(space.str(w_obj)) + s = space.text_w(space.str(w_tp)) # map python types -> C++ types if s == 'str': s = 'std::string' if i != 0: tmpl_args += ', ' @@ -712,23 +721,31 @@ cppol = W_CPPOverload(space, self.scope, funcs[:], self.flags) return cppol - def instantiation_from_args(self, name, args_w): + def instantiate_and_call(self, name, args_w): # try to match with run-time instantiations for cppol in self.master.overloads.values(): try: - cppol.descr_get(self.w_this, []).call(args_w) + return cppol.descr_get(self.w_this, []).call(args_w) except Exception: pass # completely ignore for now; have to see whether errors become confusing # 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) + 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 - if len(method.functions) == 1: - fullname = capi.c_method_full_name(self.space, method.functions[0].cppmethod) - if 0 <= fullname.rfind('>'): + fullname = capi.c_method_full_name(self.space, method.functions[0].cppmethod) + if 0 <= fullname.rfind('>'): + try: + existing = self.master.overloads[fullname] + allf = existing.functions + method.functions + if isinstance(existing, W_CPPStaticOverload): + cppol = W_CPPStaticOverload(self.space, self.scope, allf, self.flags) + else: + cppol = W_CPPOverload(self.space, self.scope, allf, self.flags) + self.master.overloads[fullname] = cppol + except KeyError: self.master.overloads[fullname] = method return method.descr_get(self.w_this, []).call(args_w) @@ -747,9 +764,12 @@ 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 + # 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 return method.descr_get(self.w_this, []) @@ -774,6 +794,7 @@ return self # unbound, so no new instance needed cppol = W_CPPTemplateOverload(self.space, self.name, self.scope, self.functions, self.flags) cppol.w_this = w_cppinstance + cppol.master = self.master return cppol # bound @unwrap_spec(args_w='args_w') @@ -787,7 +808,7 @@ except Exception: pass - return self.instantiation_from_args(self.name, args_w) + return self.instantiate_and_call(self.name, args_w) @unwrap_spec(args_w='args_w') def getitem(self, args_w): @@ -842,7 +863,7 @@ pass # try new instantiation - return self.instantiation_from_args(self.name, args_w) + return self.instantiate_and_call(self.name, args_w) @unwrap_spec(args_w='args_w') def getitem(self, args_w): 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 @@ -408,16 +408,22 @@ # map push_back -> __iadd__ (generally true for STL) if 'push_back' in pyclass.__dict__ and not '__iadd__' in pyclass.__dict__: - def __iadd__(self, ll): - [self.push_back(x) for x in ll] - return self - pyclass.__iadd__ = __iadd__ + if 'reserve' in pyclass.__dict__: + def iadd(self, ll): + self.reserve(len(ll)) + for x in ll: self.push_back(x) + return self + else: + def iadd(self, ll): + for x in ll: self.push_back(x) + return self + pyclass.__iadd__ = iadd # 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) - if not 'vector' in name[:11] and \ - ('begin' in pyclass.__dict__ and 'end' in pyclass.__dict__): +# 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): @@ -430,6 +436,20 @@ pyclass.__iter__ = __iter__ # else: rely on numbered iteration + # add python collection based initializer + if 0 <= name.find('vector') <= 5: + pyclass.__real_init__ = pyclass.__init__ + def vector_init(self, *args): + if len(args) == 1 and isinstance(args[0], (tuple, list)): + ll = args[0] + self.__real_init__() + self.reserve(len(ll)) + for item in ll: + self.push_back(item) + return + return self.__real_init__(*args) + pyclass.__init__ = vector_init + # combine __getitem__ and __len__ to make a pythonized __getitem__ if '__getitem__' in pyclass.__dict__ and '__len__' in pyclass.__dict__: pyclass._getitem__unchecked = pyclass.__getitem__ @@ -470,7 +490,13 @@ for p in pythonizors: p(pyclass, name) +cppyyIsInitialized = False def _post_import_startup(): + # run only once (function is explicitly called in testing) + global cppyyIsInitialized + if cppyyIsInitialized: + return + # _cppyy should not be loaded at the module level, as that will trigger a # call to space.getbuiltinmodule(), which will cause _cppyy to be loaded # at pypy-c startup, rather than on the "import _cppyy" statement @@ -511,6 +537,9 @@ # install nullptr as a unique reference _cppyy.nullptr = _cppyy._get_nullptr() + # done + cppyyIsInitialized = True + # user-defined pythonizations interface _pythonizations = {'' : list()} diff --git a/pypy/module/_cppyy/test/templates.h b/pypy/module/_cppyy/test/templates.h --- a/pypy/module/_cppyy/test/templates.h +++ b/pypy/module/_cppyy/test/templates.h @@ -107,7 +107,7 @@ } inline std::string tuplify(std::ostringstream& out) { - out.seekp(-2, out.cur); out << ')'; + out << "NULL)"; return out.str(); } 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 @@ -13,7 +13,7 @@ def setup_class(cls): cls.w_test_dct = cls.space.newtext(test_dct) - cls.w_datatypes = cls.space.appexec([], """(): + cls.w_templates = cls.space.appexec([], """(): import ctypes, _cppyy _cppyy._post_import_startup() return ctypes.CDLL(%r, ctypes.RTLD_GLOBAL)""" % (test_dct, )) @@ -84,10 +84,11 @@ import _cppyy - s = _cppyy.gbl.std.ostringstream() - #s << '(' - #_cppyy.gbl.SomeNS.tuplify(s, 1, 4., "aap") - #assert s.str() == '(1, 4, aap) + s = _cppyy.gbl.std.ostringstream('(', _cppyy.gbl.std.ios_base.ate) + # Fails; selects void* overload (?!) + #s << "(" + _cppyy.gbl.SomeNS.tuplify(s, 1, 4., "aap") + assert s.str() == "(1, 4, aap, NULL)" _cppyy.gbl.gInterpreter.Declare(""" template<typename... myTypes> @@ -133,7 +134,7 @@ Obj2 = _cppyy.gbl.AttrTesting.Obj2 select_template_arg = _cppyy.gbl.AttrTesting.select_template_arg - #assert select_template_arg[0, Obj1, Obj2].argument == Obj1 + # assert select_template_arg[0, Obj1, Obj2].argument == Obj1 assert select_template_arg[1, Obj1, Obj2].argument == Obj2 raises(TypeError, select_template_arg.__getitem__, 2, Obj1, Obj2) @@ -169,7 +170,7 @@ # TODO: the ref_value property is inaccessible (offset == -1) - # assert cppyy.gbl.BaseClassWithStatic["size_t"].ref_value == 42 + # assert _cppyy.gbl.BaseClassWithStatic["size_t"].ref_value == 42 b1 = _cppyy.gbl.DerivedClassUsingStatic["size_t"]( 0) b2 = _cppyy.gbl.DerivedClassUsingStatic["size_t"](100) @@ -179,3 +180,62 @@ # assert b2.ref_value == 42 assert b2.m_value == 42 + + +class AppTestBOOSTANY: + spaceconfig = dict(usemodules=['_cppyy', '_rawffi', 'itertools']) + + def setup_class(cls): + cls.w_test_dct = cls.space.newtext(test_dct) + cls.w_templates = cls.space.appexec([], """(): + import ctypes, _cppyy + _cppyy._post_import_startup()""") + + def test01_any_class(self): + """Usage of boost::any""" + + import _cppyy + + if not _cppyy.gbl.gInterpreter.Declare('#include "boost/any.hpp"'): + import warnings + warnings.warn('skipping boost/any testing') + return + + assert _cppyy.gbl.boost + assert _cppyy.gbl.boost.any + + std, boost = _cppyy.gbl.std, _cppyy.gbl.boost + + assert std.list[boost.any] + + val = boost.any() + # test both by-ref and by rvalue + v = std.vector[int]() + val.__assign__(v) + val.__assign__(std.move(std.vector[int](range(100)))) + + _cppyy.gbl.gInterpreter.ProcessLine( + "namespace _cppyy_internal { auto* stdvectid = &typeid(std::vector<int>); }") + + assert val.type() == _cppyy.gbl._cppyy_internal.stdvectid + + extract = boost.any_cast[std.vector[int]](val) + assert type(extract) is std.vector[int] + assert len(extract) == 100 + extract += range(100) + assert len(extract) == 200 + + val.__assign__(std.move(extract)) # move forced + + # TODO: we hit boost::any_cast<int>(boost::any* operand) instead + # of the reference version which raises + boost.any_cast.__useffi__ = False + try: + # raises(Exception, boost.any_cast[int], val) + assert not boost.any_cast[int](val) + except Exception: + # getting here is good, too ... + pass + + extract = boost.any_cast[std.vector[int]](val) + assert len(extract) == 200 _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit