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

Reply via email to