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

Reply via email to