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

Reply via email to