Author: Wim Lavrijsen <wlavrij...@lbl.gov> Branch: cppyy-packaging Changeset: r94769:cb6f0a7dbc3a Date: 2018-06-15 23:24 -0700 http://bitbucket.org/pypy/pypy/changeset/cb6f0a7dbc3a/
Log: improved handling of templated methods 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 @@ -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 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 @@ -721,11 +721,11 @@ 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 @@ -735,9 +735,17 @@ 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) @@ -756,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, []) @@ -783,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') @@ -796,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): @@ -851,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 @@ -422,8 +422,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) - 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): @@ -436,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__ @@ -476,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 @@ -517,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