Author: Wim Lavrijsen <wlavrij...@lbl.gov>
Branch: cppyy-packaging
Changeset: r94732:a1135702ca77
Date: 2018-05-18 10:28 -0700
http://bitbucket.org/pypy/pypy/changeset/a1135702ca77/

Log:    first stab at transparent smart pointer support and improved
        templated methods

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
@@ -207,6 +207,8 @@
             'num_bases'                : ([c_type],                   c_int),
             'base_name'                : ([c_type, c_int],            
c_ccharp),
             'is_subtype'               : ([c_type, c_type],           c_int),
+            'smartptr_info'            : ([c_ccharp, c_voidp, c_voidp],        
 c_int),
+            'add_smartptr_type'        : ([c_ccharp],                 c_void),
 
             'base_offset'              : ([c_type, c_type, c_object, c_int],   
 c_ptrdiff_t),
 
@@ -479,6 +481,21 @@
     if derived == base:
         return bool(1)
     return space.bool_w(call_capi(space, 'is_subtype', [_ArgH(derived.handle), 
_ArgH(base.handle)]))
+def c_smartptr_info(space, name):
+    out_raw   = lltype.malloc(rffi.ULONGP.TO, 1, flavor='raw', zero=True)
+    out_deref = lltype.malloc(rffi.ULONGP.TO, 1, flavor='raw', zero=True)
+    try:
+        args = [_ArgS(name),
+           _ArgP(rffi.cast(rffi.VOIDP, out_raw)), _ArgP(rffi.cast(rffi.VOIDP, 
out_deref))]
+        result = space.bool_w(call_capi(space, 'smartptr_info', args))
+        raw   = rffi.cast(C_TYPE, out_raw[0])
+        deref = rffi.cast(C_METHOD, out_deref[0])
+    finally:
+        lltype.free(out_deref, flavor='raw')
+        lltype.free(out_raw, flavor='raw')
+    return (result, raw, deref)
+def c_add_smartptr_type(space, name):
+    return space.bool_w(call_capi(space, 'add_smartptr_type', [_ArgS(name)]))
 
 def _c_base_offset(space, derived_h, base_h, address, direction):
     args = [_ArgH(derived_h), _ArgH(base_h), _ArgH(address), _ArgL(direction)]
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
@@ -700,6 +700,24 @@
                     "no overload found matching %s", self.signature)
 
 
+class SmartPtrCppObjectConverter(TypeConverter):
+    _immutable_fields = ['smart', 'raw', 'deref']
+
+    def __init__(self, space, smartdecl, raw, deref):
+        from pypy.module._cppyy.interp_cppyy import W_CPPClassDecl, 
get_pythonized_cppclass
+        self.smartdecl = smartdecl
+        w_raw   = get_pythonized_cppclass(space, raw)
+        self.rawdecl   = space.interp_w(W_CPPClassDecl,
+            space.findattr(w_raw, space.newtext("__cppdecl__")))
+        self.deref     = deref
+
+    def from_memory(self, space, w_obj, w_pycppclass, offset):
+        address = rffi.cast(capi.C_OBJECT, self._get_raw_address(space, w_obj, 
offset))
+        from pypy.module._cppyy import interp_cppyy
+        return interp_cppyy.wrap_cppinstance(space, address,
+            self.rawdecl, smartdecl=self.smartdecl, deref=self.deref, 
do_cast=False)
+
+
 class MacroConverter(TypeConverter):
     def from_memory(self, space, w_obj, w_pycppclass, offset):
         # TODO: get the actual type info from somewhere ...
@@ -715,26 +733,25 @@
     #   1) full, exact match
     #       1a) const-removed match
     #   2) match of decorated, unqualified type
-    #   3) accept ref as pointer (for the stubs, const& can be
-    #       by value, but that does not work for the ffi path)
-    #   4) generalized cases (covers basically all user classes)
-    #   5) void* or void converter (which fails on use)
+    #   3) generalized cases (covers basically all user classes)
+    #       3a) smart pointers
+    #   4) void* or void converter (which fails on use)
 
     name = capi.c_resolve_name(space, _name)
 
-    #   1) full, exact match
+    # full, exact match
     try:
         return _converters[name](space, default)
     except KeyError:
         pass
 
-    #   1a) const-removed match
+    # const-removed match
     try:
         return _converters[helper.remove_const(name)](space, default)
     except KeyError:
         pass
 
-    #   2) match of decorated, unqualified type
+    # match of decorated, unqualified type
     compound = helper.compound(name)
     clean_name = capi.c_resolve_name(space, helper.clean_type(name))
     try:
@@ -744,15 +761,19 @@
     except KeyError:
         pass
 
-    #   3) TODO: accept ref as pointer
-
-    #   4) generalized cases (covers basically all user classes)
+    # generalized cases (covers basically all user classes)
     from pypy.module._cppyy import interp_cppyy
     scope_decl = interp_cppyy.scope_byname(space, clean_name)
     if scope_decl:
-        # type check for the benefit of the annotator
         from pypy.module._cppyy.interp_cppyy import W_CPPClassDecl
         clsdecl = space.interp_w(W_CPPClassDecl, scope_decl, can_be_None=False)
+
+        # check smart pointer type
+        check_smart = capi.c_smartptr_info(space, clean_name)
+        if check_smart[0]:
+            return SmartPtrCppObjectConverter(space, clsdecl, check_smart[1], 
check_smart[2])
+
+        # type check for the benefit of the annotator
         if compound == "*":
             return InstancePtrConverter(space, clsdecl)
         elif compound == "&":
@@ -772,7 +793,7 @@
         if pos > 0:
             return FunctionPointerConverter(space, name[pos+2:])
 
-    #   5) void* or void converter (which fails on use)
+    # void* or void converter (which fails on use)
     if 0 <= compound.find('*'):
         return VoidPtrConverter(space, default)  # "user knows best"
 
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
@@ -22,6 +22,13 @@
 
 OVERLOAD_FLAGS_USE_FFI     = 0x0001
 
+FUNCTION_IS_GLOBAL         = 0x0001
+FUNCTION_IS_STATIC         = 0x0001
+FUNCTION_IS_METHOD         = 0x0002
+FUNCTION_IS_CONSTRUCTOR    = 0x0004
+FUNCTION_IS_TEMPLATE       = 0x0008
+FUNCTION_IS_SETITEM        = 0x0010
+
 
 class FastCallNotPossible(Exception):
     pass
@@ -101,9 +108,9 @@
         state.cppscope_cache[final_scoped_name] = cppscope
 
         if not isns:
-            # build methods/data; TODO: also defer this for classes (a 
functional __dir__
+            # build overloads/data; TODO: also defer this for classes (a 
functional __dir__
             # and instrospection for help() is enough and allows more lazy 
loading)
-            cppscope._build_methods()
+            cppscope._build_overloads()
             cppscope._find_datamembers()
 
         return cppscope
@@ -157,7 +164,7 @@
 #  CPPConstructor:    specialization for allocating a new object
 #  CPPFunction:       specialization for free and static functions
 #  CPPSetItem:        specialization for Python's __setitem__
-#  CPPTemplatedCall:  trampoline to instantiate and bind templated functions
+#  CPPTemplateMethod: trampoline to instantiate and bind templated functions
 #  W_CPPOverload, W_CPPConstructorOverload, W_CPPTemplateOverload:
 #     user-facing, app-level, collection of overloads, with specializations
 #     for constructors and templates
@@ -456,7 +463,7 @@
         return "CPPFunction: %s" % self.prototype()
 
 
-class CPPTemplatedCall(CPPMethod):
+class CPPTemplateMethod(CPPMethod):
     """Method dispatcher that first resolves the template instance."""
 
     _attrs_ = ['space', 'templ_args']
@@ -465,7 +472,7 @@
     def __init__(self, space, templ_args, declaring_scope, method_index, 
arg_defs, args_required):
         self.space = space
         self.templ_args = templ_args
-        # TODO: might have to specialize for CPPTemplatedCall on 
CPPMethod/CPPFunction here
+        # TODO: might have to specialize for CPPTemplateMethod on 
CPPMethod/CPPFunction here
         CPPMethod.__init__(self, space, declaring_scope, method_index, 
arg_defs, args_required)
 
     def call(self, cppthis, args_w, useffi):
@@ -486,7 +493,7 @@
         return CPPMethod.call(self, cppthis, args_w, useffi)
 
     def __repr__(self):
-        return "CPPTemplatedCall: %s" % self.prototype()
+        return "CPPTemplateMethod: %s" % self.prototype()
 
 
 class CPPConstructor(CPPMethod):
@@ -632,8 +639,8 @@
 
 W_CPPOverload.typedef = TypeDef(
     'CPPOverload',
+    call = interp2app(W_CPPOverload.call),
     is_static = interp2app(W_CPPOverload.is_static),
-    call = interp2app(W_CPPOverload.call),
     __useffi__ = GetSetProperty(W_CPPOverload.fget_useffi, 
W_CPPOverload.fset_useffi),
     prototype = interp2app(W_CPPOverload.prototype),
 )
@@ -668,8 +675,8 @@
 
 W_CPPConstructorOverload.typedef = TypeDef(
     'CPPConstructorOverload',
+    call = interp2app(W_CPPConstructorOverload.call),
     is_static = interp2app(W_CPPConstructorOverload.is_static),
-    call = interp2app(W_CPPConstructorOverload.call),
     prototype = interp2app(W_CPPConstructorOverload.prototype),
 )
 
@@ -685,6 +692,10 @@
 W_CPPTemplateOverload.typedef = TypeDef(
     'CPPTemplateOverload',
     __getitem__ = interp2app(W_CPPTemplateOverload.call),
+    call = interp2app(W_CPPTemplateOverload.call),
+    is_static = interp2app(W_CPPTemplateOverload.is_static),
+    __useffi__ = GetSetProperty(W_CPPTemplateOverload.fget_useffi, 
W_CPPTemplateOverload.fset_useffi),
+    prototype = interp2app(W_CPPTemplateOverload.prototype),
 )
 
 
@@ -818,7 +829,7 @@
 
 
 class W_CPPScopeDecl(W_Root):
-    _attrs_ = ['space', 'handle', 'flags', 'name', 'methods', 'datamembers']
+    _attrs_ = ['space', 'handle', 'flags', 'name', 'overloads', 'datamembers']
     _immutable_fields_ = ['handle', 'name']
 
     def __init__(self, space, opaque_handle, final_scoped_name):
@@ -827,27 +838,27 @@
         self.handle = opaque_handle
         self.flags = 0
         self.name = final_scoped_name
-        self.methods = {}
-        # Do not call "self._build_methods()" here, so that a distinction can
+        self.overloads = {}
+        # Do not call "self._build_overloadss()" here, so that a distinction 
can
         #  be made between testing for existence (i.e. existence in the cache
         #  of classes) and actual use. Point being that a class can use itself,
         #  e.g. as a return type or an argument to one of its methods.
 
         self.datamembers = {}
-        # Idem as for self.methods: a type could hold itself by pointer.
+        # Idem as for self.overloads: a type could hold itself by pointer.
 
     def get_method_names(self):
-        return self.space.newlist([self.space.newtext(name) for name in 
self.methods])
+        return self.space.newlist([self.space.newtext(name) for name in 
self.overloads])
 
     @unwrap_spec(name='text')
     def get_overload(self, name):
         try:
-            return self.methods[name]
+            return self.overloads[name]
         except KeyError:
             pass
-        new_method = self.find_overload(name)
-        self.methods[name] = new_method
-        return new_method
+        new_ol = self.find_overload(name)
+        self.overloads[name] = new_ol
+        return new_ol
 
     def get_datamember_names(self):
         return self.space.newlist([self.space.newtext(name) for name in 
self.datamembers])
@@ -883,10 +894,10 @@
 # classes for inheritance. Both are python classes, though, and refactoring
 # may be in order at some point.
 class W_CPPNamespaceDecl(W_CPPScopeDecl):
-    _attrs_ = ['space', 'handle', 'name', 'methods', 'datamembers']
+    _attrs_ = ['space', 'handle', 'name', 'overloads', 'datamembers']
     _immutable_fields_ = ['handle', 'name']
 
-    def _make_cppfunction(self, pyname, index):
+    def _make_cppfunction(self, pyname, index, funcs):
         num_args = capi.c_method_num_args(self.space, self, index)
         args_required = capi.c_method_req_args(self.space, self, index)
         arg_defs = []
@@ -894,7 +905,8 @@
             arg_type = capi.c_method_arg_type(self.space, self, index, i)
             arg_dflt = capi.c_method_arg_default(self.space, self, index, i)
             arg_defs.append((arg_type, arg_dflt))
-        return CPPFunction(self.space, self, index, arg_defs, args_required)
+        funcs.append(CPPFunction(self.space, self, index, arg_defs, 
args_required))
+        return FUNCTION_IS_GLOBAL
 
     def _make_datamember(self, dm_name, dm_idx):
         type_name = capi.c_datamember_type(self.space, self, dm_idx)
@@ -912,12 +924,12 @@
         indices = capi.c_method_indices_from_name(self.space, self, meth_name)
         if not indices:
             raise self.missing_attribute_error(meth_name)
-        cppfunctions = []
+        cppfunctions, ftype = [], 0
         for meth_idx in indices:
-            f = self._make_cppfunction(meth_name, meth_idx)
-            cppfunctions.append(f)
-        overload = W_CPPOverload(self.space, self, cppfunctions)
-        return overload
+            ftype |= self._make_cppfunction(meth_name, meth_idx, cppfunctions)
+        if ftype & FUNCTION_IS_TEMPLATE:
+            return W_CPPTemplateOverload(self.sace, self, cppfunctions)
+        return W_CPPOverload(self.space, self, cppfunctions)
 
     def find_datamember(self, dm_name):
         dm_idx = capi.c_datamember_index(self.space, self, dm_name)
@@ -956,12 +968,12 @@
 
 
 class W_CPPClassDecl(W_CPPScopeDecl):
-    _attrs_ = ['space', 'handle', 'name', 'methods', 'datamembers']
-    _immutable_fields_ = ['handle', 'name', 'methods[*]', 'datamembers[*]']
+    _attrs_ = ['space', 'handle', 'name', 'overloads', 'datamembers']
+    _immutable_fields_ = ['handle', 'name', 'overloads[*]', 'datamembers[*]']
 
-    def _build_methods(self):
-        assert len(self.methods) == 0
-        methods_temp = {}
+    def _build_overloads(self):
+        assert len(self.overloads) == 0
+        overloads_temp = {}
         for idx in range(capi.c_num_methods(self.space, self)):
             if capi.c_is_constructor(self.space, self, idx):
                 pyname = '__init__'
@@ -970,29 +982,36 @@
                     capi.c_method_name(self.space, self, idx),
                     capi.c_method_num_args(self.space, self, idx),
                     capi.c_method_result_type(self.space, self, idx))
-            cppmethod = self._make_cppfunction(pyname, idx)
-            methods_temp.setdefault(pyname, []).append(cppmethod)
+            try:
+                detail = overloads_temp[pyname]
+            except KeyError:
+                detail = [[], 0]; overloads_temp[pyname] = detail
+            detail[1] |= self._make_cppfunction(pyname, idx, detail[0])
         # the following covers the case where the only kind of operator[](idx)
         # returns are the ones that produce non-const references; these can be
         # used for __getitem__ just as much as for __setitem__, though
-        if not "__getitem__" in methods_temp:
+        if not "__getitem__" in overloads_temp:
             try:
-                for m in methods_temp["__setitem__"]:
-                    cppmethod = self._make_cppfunction("__getitem__", m.index)
-                    methods_temp.setdefault("__getitem__", 
[]).append(cppmethod)
+                sid = overloads_temp["__setitem__"]
+                gid = [[], 0]; overloads_temp["__getitem__"] = gid
+                for m in sid[0]:
+                    gid[1] |= self._make_cppfunction("__getitem__", m.index, 
gid[0])
             except KeyError:
                 pass          # just means there's no __setitem__ either
 
-        # create the overload methods from the method sets
-        for pyname, methods in methods_temp.iteritems():
+        # create the overloads from the method sets
+        for pyname, detail in overloads_temp.iteritems():
+            methods = detail[0]
             CPPMethodSort(methods).sort()
             if pyname == '__init__':
-                overload = W_CPPConstructorOverload(self.space, self, 
methods[:])
+                overload = W_CPPConstructorOverload(self.space, self, methods)
+            elif detail[1] & FUNCTION_IS_TEMPLATE:
+                overload = W_CPPTemplateOverload(self.space, self, methods)
             else:
-                overload = W_CPPOverload(self.space, self, methods[:])
-            self.methods[pyname] = overload
+                overload = W_CPPOverload(self.space, self, methods)
+            self.overloads[pyname] = overload
 
-    def _make_cppfunction(self, pyname, index):
+    def _make_cppfunction(self, pyname, index, funcs):
         num_args = capi.c_method_num_args(self.space, self, index)
         args_required = capi.c_method_req_args(self.space, self, index)
         arg_defs = []
@@ -1000,18 +1019,25 @@
             arg_type = capi.c_method_arg_type(self.space, self, index, i)
             arg_dflt = capi.c_method_arg_default(self.space, self, index, i)
             arg_defs.append((arg_type, arg_dflt))
+        ftype = 0
         if capi.c_is_constructor(self.space, self, index):
             cppfunction = CPPConstructor(self.space, self, index, arg_defs, 
args_required)
+            ftype = FUNCTION_IS_CONSTRUCTOR
         elif capi.c_method_is_template(self.space, self, index):
             templ_args = capi.c_template_args(self.space, self, index)
-            cppfunction = CPPTemplatedCall(self.space, templ_args, self, 
index, arg_defs, args_required)
+            cppfunction = CPPTemplateMethod(self.space, templ_args, self, 
index, arg_defs, args_required)
+            ftype = FUNCTION_IS_TEMPLATE
         elif capi.c_is_staticmethod(self.space, self, index):
             cppfunction = CPPFunction(self.space, self, index, arg_defs, 
args_required)
+            ftype = FUNCTION_IS_STATIC
         elif pyname == "__setitem__":
             cppfunction = CPPSetItem(self.space, self, index, arg_defs, 
args_required)
+            ftype = FUNCTION_IS_SETITEM
         else:
             cppfunction = CPPMethod(self.space, self, index, arg_defs, 
args_required)
-        return cppfunction
+            ftype = FUNCTION_IS_METHOD
+        funcs.append(cppfunction)
+        return ftype
 
     def _find_datamembers(self):
         num_datamembers = capi.c_num_datamembers(self.space, self)
@@ -1106,13 +1132,14 @@
 
 
 class W_CPPInstance(W_Root):
-    _attrs_ = ['space', 'clsdecl', '_rawobject', 'flags',
+    _attrs_ = ['space', 'clsdecl', '_rawobject', 'smartdecl', 'deref', 'flags',
                'finalizer_registered']
-    _immutable_fields_ = ['clsdecl']
+    _immutable_fields_ = ['clsdecl', 'smartdecl', 'deref']
 
     finalizer_registered = False
 
-    def __init__(self, space, decl, rawobject, isref, python_owns):
+    def __init__(self, space, decl, rawobject, isref, python_owns,
+                 smartdecl=None, deref=rffi.cast(capi.C_METHOD, 0)):
         self.space = space
         self.clsdecl = decl
         assert lltype.typeOf(rawobject) == capi.C_OBJECT
@@ -1120,11 +1147,13 @@
         self._rawobject = rawobject
         assert not isref or not python_owns
         self.flags = 0
-        if isref:
+        if isref or (smartdecl and deref):
             self.flags |= INSTANCE_FLAGS_IS_REF
         if python_owns:
             self.flags |= INSTANCE_FLAGS_PYTHON_OWNS
             self._opt_register_finalizer()
+        self.smartdecl = smartdecl
+        self.deref     = deref
 
     def _opt_register_finalizer(self):
         if not self.finalizer_registered and not hasattr(self.space, "fake"):
@@ -1156,6 +1185,11 @@
     def get_rawobject(self):
         if not (self.flags & INSTANCE_FLAGS_IS_REF):
             return self._rawobject
+        elif self.smartdecl and self.deref:
+            args = capi.c_allocate_function_args(self.space, 0)
+            rawptr = capi.c_call_l(self.space, self.deref, self._rawobject, 0, 
args)
+            capi.c_deallocate_function_args(self.space, args)
+            return rffi.cast(capi.C_OBJECT, rawptr)
         else:
             ptrptr = rffi.cast(rffi.VOIDPP, self._rawobject)
             return rffi.cast(capi.C_OBJECT, ptrptr[0])
@@ -1192,8 +1226,9 @@
                 meth_idx = capi.c_get_global_operator(
                     self.space, nss, self.clsdecl, other.clsdecl, "operator==")
                 if meth_idx != -1:
-                    f = nss._make_cppfunction("operator==", meth_idx)
-                    ol = W_CPPOverload(self.space, nss, [f])
+                    funcs = []
+                    nss._make_cppfunction("operator==", meth_idx, funcs)
+                    ol = W_CPPOverload(self.space, nss, funcs)
                     # TODO: cache this operator (not done yet, as the above 
does not
                     # select all overloads)
                     return ol.call(self, [self, w_other])
@@ -1244,6 +1279,10 @@
         return self.space.newtext("<%s object at 0x%x>" %
                                (self.clsdecl.name, rffi.cast(rffi.ULONG, 
self.get_rawobject())))
 
+    def smartptr(self):
+        if self._rawobject and self.smartdecl:
+            return wrap_cppinstance(self.space, self._rawobject, 
self.smartdecl, do_cast=False)
+
     def destruct(self):
         if self._rawobject and not (self.flags & INSTANCE_FLAGS_IS_REF):
             memory_regulator.unregister(self)
@@ -1264,6 +1303,7 @@
     __len__ = interp2app(W_CPPInstance.instance__len__),
     __cmp__ = interp2app(W_CPPInstance.instance__cmp__),
     __repr__ = interp2app(W_CPPInstance.instance__repr__),
+    __smartptr__ = interp2app(W_CPPInstance.smartptr),
     __destruct__ = interp2app(W_CPPInstance.destruct),
 )
 W_CPPInstance.typedef.acceptable_as_base_class = True
@@ -1314,6 +1354,7 @@
     return space.call_function(state.w_fngen_callback, w_callable, 
space.newint(npar))
 
 def wrap_cppinstance(space, rawobject, clsdecl,
+                     smartdecl=None, deref=rffi.cast(capi.C_METHOD, 0),
                      do_cast=True, python_owns=False, is_ref=False, 
fresh=False):
     rawobject = rffi.cast(capi.C_OBJECT, rawobject)
 
@@ -1346,7 +1387,7 @@
     # fresh creation
     w_cppinstance = space.allocate_instance(W_CPPInstance, w_pycppclass)
     cppinstance = space.interp_w(W_CPPInstance, w_cppinstance)
-    cppinstance.__init__(space, clsdecl, rawobject, is_ref, python_owns)
+    cppinstance.__init__(space, clsdecl, rawobject, is_ref, python_owns, 
smartdecl, deref)
     memory_regulator.register(cppinstance)
     return w_cppinstance
 
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
@@ -36,23 +36,22 @@
             self._scope = scope
 
     def _arg_to_str(self, arg):
-        try:
-            arg = arg.__cppname__
-        except AttributeError:
-            if arg == str:
-                import _cppyy
-                arg = _cppyy._std_string_name()
-            elif type(arg) != str:
-                arg = arg.__name__
-        return arg
+      # arguments are strings representing types, types, or builtins
+        if type(arg) == str:
+            return arg                       # string describing type
+        elif hasattr(arg, '__cppname__'):
+            return arg.__cppname__           # C++ bound type
+        elif arg == str:
+            import _cppyy
+            return _cppyy._std_string_name() # special case pystr -> C++ string
+        elif isinstance(arg, type):          # builtin types
+            return arg.__name__
+        return str(arg)                      # builtin values
 
     def __call__(self, *args):
         fullname = ''.join(
             [self._name, '<', ','.join(map(self._arg_to_str, args))])
-        if fullname[-1] == '>':
-            fullname += ' >'
-        else:
-            fullname += '>'
+        fullname += '>'
         return getattr(self._scope, fullname)
 
     def __getitem__(self, *args):
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
@@ -1,3 +1,7 @@
+#include <string>
+#include <sstream>
+
+
 //===========================================================================
 class MyTemplatedMethodClass {         // template methods
 public:
@@ -21,7 +25,7 @@
     return sizeof(B);
 }
 
-//
+// 
 typedef MyTemplatedMethodClass MyTMCTypedef_t;
 
 // explicit instantiation
@@ -33,3 +37,109 @@
 inline long MyTemplatedMethodClass::get_size<long>() {
     return 42;
 }
+
+
+//===========================================================================
+// global templated functions
+template<typename T>
+long global_get_size() {
+    return sizeof(T);
+}
+
+template <typename T>
+int global_some_foo(T) {
+    return 42;
+}
+
+template <typename T>
+int global_some_bar(T) {
+    return 13;
+}
+
+
+//===========================================================================
+// variadic functions
+inline bool isSomeInt(int) { return true; }
+inline bool isSomeInt(double) { return false; }
+template <typename ...Args>
+inline bool isSomeInt(Args...) { return false; }
+
+namespace AttrTesting {
+
+struct Obj1 { int var1; };
+struct Obj2 { int var2; };
+
+template <typename T>
+constexpr auto has_var1(T t) -> decltype(t.var1, true) { return true; }
+
+template <typename ...Args>
+constexpr bool has_var1(Args...) { return false; }
+
+template <typename T>
+constexpr bool call_has_var1(T&& t) { return 
AttrTesting::has_var1(std::forward<T>(t)); }
+
+template <int N, typename... T>
+struct select_template_arg {};
+
+template <typename T0, typename... T>
+struct select_template_arg<0, T0, T...> {
+    typedef T0 type;
+};
+
+template <int N, typename T0, typename... T>
+struct select_template_arg<N, T0, T...> {
+    typedef typename select_template_arg<N-1, T...>::type argument;
+};
+
+} // AttrTesting
+
+
+namespace SomeNS {
+
+template <typename T>
+int some_foo(T) {
+    return 42;
+}
+
+template <int T>
+int some_bar() {
+    return T;
+}
+
+inline std::string tuplify(std::ostringstream& out) {
+    out.seekp(-2, out.cur); out << ')';
+    return out.str();
+}
+
+template<typename T, typename... Args>
+std::string tuplify(std::ostringstream& out, T value, Args... args)
+{
+    out << value << ", ";
+    return tuplify(out, args...);
+}
+
+} // namespace SomeNS
+
+
+//===========================================================================
+// using of static data
+// TODO: this should live here instead of in test_templates.test08
+/*
+template <typename T> struct BaseClassWithStatic {
+    static T const ref_value;
+};
+
+template <typename T>
+T const BaseClassWithStatic<T>::ref_value = 42;
+
+template <typename T>
+struct DerivedClassUsingStatic : public BaseClassWithStatic<T> {
+    using BaseClassWithStatic<T>::ref_value;
+
+    explicit DerivedClassUsingStatic(T x) : BaseClassWithStatic<T>() {
+        m_value = x > ref_value ? ref_value : x;
+    }
+
+    T m_value;
+};
+*/
diff --git a/pypy/module/_cppyy/test/templates.xml 
b/pypy/module/_cppyy/test/templates.xml
--- a/pypy/module/_cppyy/test/templates.xml
+++ b/pypy/module/_cppyy/test/templates.xml
@@ -3,4 +3,17 @@
   <class name="MyTemplatedMethodClass" />
   <class name="MyTMCTypedef_t" />
 
+  <function name="global_get_size" />
+  <function name="global_some_foo" />
+  <function name="global_some_bar" />
+
+  <function name="isSomeInt" />
+
+  <namespace name="AttrTesting" />
+  <function pattern="AttrTesting::*" />
+
+  <function name="SomeNS::some_foo" />
+  <function name="SomeNS::some_bar" />
+  <function name="SomeNS::tuplify" />
+
 </lcgdict>
diff --git a/pypy/module/_cppyy/test/test_cppyy.py 
b/pypy/module/_cppyy/test/test_cppyy.py
--- a/pypy/module/_cppyy/test/test_cppyy.py
+++ b/pypy/module/_cppyy/test/test_cppyy.py
@@ -18,7 +18,7 @@
         w_cppyyclass = interp_cppyy.scope_byname(space, "example01")
         w_cppyyclass2 = interp_cppyy.scope_byname(space, "example01")
         assert space.is_w(w_cppyyclass, w_cppyyclass2)
-        adddouble = w_cppyyclass.methods["staticAddToDouble"]
+        adddouble = w_cppyyclass.overloads["staticAddToDouble"]
         func, = adddouble.functions
         assert func.executor is None
         func._setup(None)     # creates executor
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
@@ -25,8 +25,6 @@
 
         m = _cppyy.gbl.MyTemplatedMethodClass()
 
-        return
-
       # pre-instantiated
         assert m.get_size['char']()   == m.get_char_size()
         assert m.get_size[int]()      == m.get_int_size()
@@ -41,3 +39,136 @@
 
       # auto through typedef
         assert m.get_size['MyTMCTypedef_t']() == m.get_self_size()
+
+    def test02_non_type_template_args(self):
+        """Use of non-types as template arguments"""
+
+        import _cppyy
+
+        _cppyy.gbl.gInterpreter.Declare("template<int i> int nt_templ_args() { 
return i; };")
+
+        assert _cppyy.gbl.nt_templ_args[1]()   == 1
+        assert _cppyy.gbl.nt_templ_args[256]() == 256
+
+    def test03_templated_function(self):
+        """Templated global and static functions lookup and calls"""
+
+        import _cppyy
+
+        # TODO: the following only works if something else has already
+        # loaded the headers associated with this template
+        ggs = _cppyy.gbl.global_get_size
+        assert ggs['char']() == 1
+
+        gsf = _cppyy.gbl.global_some_foo
+
+        assert gsf[int](3) == 42
+        assert gsf(3)      == 42
+        assert gsf(3.)     == 42
+
+        gsb = _cppyy.gbl.global_some_bar
+
+        assert gsb(3)            == 13
+        assert gsb['double'](3.) == 13
+
+        # TODO: the following only works in a namespace
+        nsgsb = _cppyy.gbl.SomeNS.some_bar
+
+        assert nsgsb[3]
+        assert nsgsb[3]() == 3
+
+        # TODO: add some static template method
+
+    def test04_variadic_function(self):
+        """Call a variadic function"""
+
+        import _cppyy
+
+        s = _cppyy.gbl.std.ostringstream()
+        #s << '('
+        #_cppyy.gbl.SomeNS.tuplify(s, 1, 4., "aap")
+        #assert s.str() == '(1, 4, aap)
+
+    def test05_variadic_overload(self):
+        """Call an overloaded variadic function"""
+
+        import _cppyy
+
+        assert _cppyy.gbl.isSomeInt(3.)        == False
+        assert _cppyy.gbl.isSomeInt(1)         == True
+        assert _cppyy.gbl.isSomeInt()          == False
+        assert _cppyy.gbl.isSomeInt(1, 2, 3)   == False
+
+    def test06_variadic_sfinae(self):
+        """Attribute testing through SFINAE"""
+
+        import _cppyy
+        Obj1             = _cppyy.gbl.AttrTesting.Obj1
+        Obj2             = _cppyy.gbl.AttrTesting.Obj2
+        has_var1         = _cppyy.gbl.AttrTesting.has_var1
+        call_has_var1    = _cppyy.gbl.AttrTesting.call_has_var1
+
+        move = _cppyy.gbl.std.move
+
+        assert has_var1(Obj1()) == hasattr(Obj1(), 'var1')
+        assert has_var1(Obj2()) == hasattr(Obj2(), 'var1')
+        assert has_var1(3)      == hasattr(3,      'var1')
+        assert has_var1("aap")  == hasattr("aap",  'var1')
+
+        assert call_has_var1(move(Obj1())) == True
+        assert call_has_var1(move(Obj2())) == False
+
+    def test07_type_deduction(self):
+        """Traits/type deduction"""
+
+        import _cppyy
+        Obj1                  = _cppyy.gbl.AttrTesting.Obj1
+        Obj2                  = _cppyy.gbl.AttrTesting.Obj2
+        select_template_arg   = _cppyy.gbl.AttrTesting.has_var1
+
+       #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)
+
+        # TODO, this doesn't work for builtin types as the 'argument'
+        # typedef will not resolve to a class
+        #assert select_template_arg[1, int, float].argument == float
+
+    def test08_using_of_static_data(self):
+        """Derived class using static data of base"""
+
+        import _cppyy
+
+      # TODO: the following should live in templates.h, but currently fails
+      # in TClass::GetListOfMethods()
+        _cppyy.gbl.gInterpreter.Declare("""
+        template <typename T> struct BaseClassWithStatic {
+            static T const ref_value;
+        };
+
+        template <typename T>
+        T const BaseClassWithStatic<T>::ref_value = 42;
+
+        template <typename T>
+        struct DerivedClassUsingStatic : public BaseClassWithStatic<T> {
+            using BaseClassWithStatic<T>::ref_value;
+
+            explicit DerivedClassUsingStatic(T x) : BaseClassWithStatic<T>() {
+                m_value = x > ref_value ? ref_value : x;
+            }
+
+            T m_value;
+        };""")
+
+
+      # TODO: the ref_value property is inaccessible (offset == -1)
+      # assert cppyy.gbl.BaseClassWithStatic["size_t"].ref_value == 42
+
+        b1 = _cppyy.gbl.DerivedClassUsingStatic["size_t"](  0)
+        b2 = _cppyy.gbl.DerivedClassUsingStatic["size_t"](100)
+
+      # assert b1.ref_value == 42
+        assert b1.m_value   ==  0
+
+      # assert b2.ref_value == 42
+        assert b2.m_value   == 42
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to