Author: Wim Lavrijsen <wlavrij...@lbl.gov>
Branch: cppyy-packaging
Changeset: r94737:264a0794b659
Date: 2018-06-07 08:40 -0700
http://bitbucket.org/pypy/pypy/changeset/264a0794b659/

Log:    reduce layers in method dispatch for simplicity, performance, and
        support of templated methods (this requires backend 1.1.0)

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
@@ -88,7 +88,7 @@
                     assert obj._voidp != rffi.cast(rffi.VOIDP, 0)
                     data = rffi.cast(rffi.VOIDPP, data)
                     data[0] = obj._voidp
-                else:    # only other use is sring
+                else:    # only other use is string
                     assert obj.tc == 's'
                     n = len(obj._string)
                     assert raw_string == rffi.cast(rffi.CCHARP, 0)
@@ -183,8 +183,7 @@
             'constructor'  : ([c_method, c_object, c_int, c_voidp],   
c_object),
             'call_o'       : ([c_method, c_object, c_int, c_voidp, c_type],    
 c_object),
 
-            'function_address_from_index'  : ([c_scope, c_index],     
c_voidp), # TODO: verify
-            'function_address_from_method' : ([c_method],             
c_voidp), # id.
+            'function_address' : ([c_method],                         
c_voidp), # TODO: verify
 
             # handling of function argument buffer
             'allocate_function_args'   : ([c_int],                    c_voidp),
@@ -216,30 +215,30 @@
             'num_methods'              : ([c_scope],                  c_int),
             'method_indices_from_name' : ([c_scope, c_ccharp],        
c_index_array),
 
-            'method_name'              : ([c_scope, c_index],         
c_ccharp),
-            'method_mangled_name'      : ([c_scope, c_index],         
c_ccharp),
-            'method_result_type'       : ([c_scope, c_index],         
c_ccharp),
-            'method_num_args'          : ([c_scope, c_index],         c_int),
-            'method_req_args'          : ([c_scope, c_index],         c_int),
-            'method_arg_type'          : ([c_scope, c_index, c_int],  
c_ccharp),
-            'method_arg_default'       : ([c_scope, c_index, c_int],  
c_ccharp),
-            'method_signature'         : ([c_scope, c_index, c_int],  
c_ccharp),
-            'method_prototype'         : ([c_scope, c_index, c_int],  
c_ccharp),
+            'get_method'               : ([c_scope, c_index],         
c_method),
+
+            'method_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),
+            'method_req_args'          : ([c_method],                 c_int),
+            'method_arg_type'          : ([c_method, c_int],          
c_ccharp),
+            'method_arg_default'       : ([c_method, c_int],          
c_ccharp),
+            'method_signature'         : ([c_method, c_int],          
c_ccharp),
+            'method_prototype'         : ([c_scope, c_method, c_int], 
c_ccharp),
             'is_const_method'          : ([c_method],                 c_int),
 
             'exists_method_template'   : ([c_scope, c_ccharp],        c_int),
             'method_is_template'       : ([c_scope, c_index],         c_int),
-            'method_num_template_args' : ([c_scope, c_index],         c_int),
-            'method_template_arg_name' : ([c_scope, c_index, c_index],         
 c_ccharp),
+            'get_method_template'      : ([c_scope, c_ccharp, c_ccharp],       
 c_method),
 
-            'get_method'               : ([c_scope, c_index],         
c_method),
             'get_global_operator'      : ([c_scope, c_scope, c_scope, 
c_ccharp],   c_index),
 
             # method properties
-            'is_public_method'         : ([c_type, c_index],          c_int),
-            'is_constructor'           : ([c_type, c_index],          c_int),
-            'is_destructor'            : ([c_type, c_index],          c_int),
-            'is_staticmethod'          : ([c_type, c_index],          c_int),
+            'is_public_method'         : ([c_method],                 c_int),
+            'is_constructor'           : ([c_method],                 c_int),
+            'is_destructor'            : ([c_method],                 c_int),
+            'is_staticmethod'          : ([c_method],                 c_int),
 
             # data member reflection information
             'num_datamembers'          : ([c_scope],                  c_int),
@@ -417,13 +416,9 @@
     args = [_ArgH(cppmethod), _ArgH(cppobject), _ArgL(nargs), _ArgP(cargs), 
_ArgH(cppclass.handle)]
     return _cdata_to_cobject(space, call_capi(space, 'call_o', args))
 
-def c_function_address_from_index(space, cppscope, index):
-    args = [_ArgH(cppscope.handle), _ArgL(index)]
+def c_function_address(space, cppmethod):
     return rffi.cast(C_FUNC_PTR,
-        _cdata_to_ptr(space, call_capi(space, 'function_address_from_index', 
args)))
-def c_function_address_from_method(space, cppmethod):
-    return rffi.cast(C_FUNC_PTR,
-        _cdata_to_ptr(space, call_capi(space, 'function_address_from_method', 
[_ArgH(cppmethod)])))
+        _cdata_to_ptr(space, call_capi(space, 'function_address', 
[_ArgH(cppmethod)])))
 
 # handling of function argument buffer ---------------------------------------
 def c_allocate_function_args(space, size):
@@ -527,30 +522,34 @@
     c_free(space, rffi.cast(rffi.VOIDP, indices))   # c_free defined below
     return py_indices
 
-def c_method_name(space, cppscope, index):
+def c_get_method(space, cppscope, index):
     args = [_ArgH(cppscope.handle), _ArgL(index)]
-    return charp2str_free(space, call_capi(space, 'method_name', args))
-def c_method_result_type(space, cppscope, index):
-    args = [_ArgH(cppscope.handle), _ArgL(index)]
-    return charp2str_free(space, call_capi(space, 'method_result_type', args))
-def c_method_num_args(space, cppscope, index):
-    args = [_ArgH(cppscope.handle), _ArgL(index)]
-    return space.int_w(call_capi(space, 'method_num_args', args))
-def c_method_req_args(space, cppscope, index):
-    args = [_ArgH(cppscope.handle), _ArgL(index)]
-    return space.int_w(call_capi(space, 'method_req_args', args))
-def c_method_arg_type(space, cppscope, index, arg_index):
-    args = [_ArgH(cppscope.handle), _ArgL(index), _ArgL(arg_index)]
+    return rffi.cast(C_METHOD, space.uint_w(call_capi(space, 'get_method', 
args)))
+
+def c_method_name(space, cppmeth):
+    return charp2str_free(space, call_capi(space, 'method_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):
+    return charp2str_free(space, call_capi(space, 'method_result_type', 
[_ArgH(cppmeth)]))
+def c_method_num_args(space, cppmeth):
+    return space.int_w(call_capi(space, 'method_num_args', [_ArgH(cppmeth)]))
+def c_method_req_args(space, cppmeth):
+    return space.int_w(call_capi(space, 'method_req_args', [_ArgH(cppmeth)]))
+def c_method_arg_type(space, cppmeth, arg_index):
+    args = [_ArgH(cppmeth), _ArgL(arg_index)]
     return charp2str_free(space, call_capi(space, 'method_arg_type', args))
-def c_method_arg_default(space, cppscope, index, arg_index):
-    args = [_ArgH(cppscope.handle), _ArgL(index), _ArgL(arg_index)]
+def c_method_arg_default(space, cppmeth, arg_index):
+    args = [_ArgH(cppmeth), _ArgL(arg_index)]
     return charp2str_free(space, call_capi(space, 'method_arg_default', args))
-def c_method_signature(space, cppscope, index, show_formalargs=True):
-    args = [_ArgH(cppscope.handle), _ArgL(index), _ArgL(show_formalargs)]
+def c_method_signature(space, cppmeth, show_formalargs=True):
+    args = [_ArgH(cppmeth), _ArgL(show_formalargs)]
     return charp2str_free(space, call_capi(space, 'method_signature', args))
-def c_method_prototype(space, cppscope, index, show_formalargs=True):
-    args = [_ArgH(cppscope.handle), _ArgL(index), _ArgL(show_formalargs)]
+def c_method_prototype(space, cppscope, cppmeth, show_formalargs=True):
+    args = [_ArgH(cppscope.handle), _ArgH(cppmeth), _ArgL(show_formalargs)]
     return charp2str_free(space, call_capi(space, 'method_prototype', args))
+def c_is_const_method(space, cppmeth):
+    return space.bool_w(call_capi(space, 'is_const_method', [_ArgH(cppmeth)]))
 
 def c_exists_method_template(space, cppscope, name):
     args = [_ArgH(cppscope.handle), _ArgS(name)]
@@ -558,21 +557,10 @@
 def c_method_is_template(space, cppscope, index):
     args = [_ArgH(cppscope.handle), _ArgL(index)]
     return space.bool_w(call_capi(space, 'method_is_template', args))
-def _c_method_num_template_args(space, cppscope, index):
-    args = [_ArgH(cppscope.handle), _ArgL(index)]
-    return space.int_w(call_capi(space, 'method_num_template_args', args)) 
-def c_template_args(space, cppscope, index):
-    nargs = _c_method_num_template_args(space, cppscope, index)
-    arg1 = _ArgH(cppscope.handle)
-    arg2 = _ArgL(index)
-    args = [c_resolve_name(space, charp2str_free(space,
-                call_capi(space, 'method_template_arg_name', [arg1, arg2, 
_ArgL(iarg)]))
-            ) for iarg in range(nargs)]
-    return args
 
-def c_get_method(space, cppscope, index):
-    args = [_ArgH(cppscope.handle), _ArgL(index)]
-    return rffi.cast(C_METHOD, space.uint_w(call_capi(space, 'get_method', 
args)))
+def c_get_method_template(space, cppscope, name):
+    args = [_ArgH(cppscope.handle), _ArgS(name)]
+    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:
         args = [_ArgH(nss.handle), _ArgH(lc.handle), _ArgH(rc.handle), 
_ArgS(op)]
@@ -580,18 +568,14 @@
     return rffi.cast(WLAVC_INDEX, -1)
 
 # method properties ----------------------------------------------------------
-def c_is_public_method(space, cppclass, index):
-    args = [_ArgH(cppclass.handle), _ArgL(index)]
-    return space.bool_w(call_capi(space, 'is_public_method', args))
-def c_is_constructor(space, cppclass, index):
-    args = [_ArgH(cppclass.handle), _ArgL(index)]
-    return space.bool_w(call_capi(space, 'is_constructor', args))
-def c_is_destructor(space, cppclass, index):
-    args = [_ArgH(cppclass.handle), _ArgL(index)]
-    return space.bool_w(call_capi(space, 'is_destructor', args))
-def c_is_staticmethod(space, cppclass, index):
-    args = [_ArgH(cppclass.handle), _ArgL(index)]
-    return space.bool_w(call_capi(space, 'is_staticmethod', args))
+def c_is_public_method(space, cppmeth):
+    return space.bool_w(call_capi(space, 'is_public_method', [_ArgH(cppmeth)]))
+def c_is_constructor(space, cppmeth):
+    return space.bool_w(call_capi(space, 'is_constructor', [_ArgH(cppmeth)]))
+def c_is_destructor(space, cppmeth):
+    return space.bool_w(call_capi(space, 'is_destructor', [_ArgH(cppmeth)]))
+def c_is_staticmethod(space, cppmeth):
+    return space.bool_w(call_capi(space, 'is_staticmethod', [_ArgH(cppmeth)]))
 
 # data member reflection information -----------------------------------------
 def c_num_datamembers(space, cppscope):
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
@@ -69,8 +69,8 @@
     # array type
     try:
         arr = space.interp_w(W_ArrayInstance, w_obj, can_be_None=True)
-        if arr:
-            return rffi.cast(rffi.VOIDP, space.uint_w(arr.getbuffer(space)))
+        #if arr:
+            #return rffi.cast(rffi.VOIDP, space.uint_w(arr.getbuffer(space)))
     except Exception:
         pass
     # pre-defined nullptr
@@ -384,7 +384,7 @@
         arg = space.text_w(w_obj)
         x[0] = rffi.cast(rffi.LONG, rffi.str2charp(arg))
         ba = rffi.cast(rffi.CCHARP, address)
-        ba[capi.c_function_arg_typeoffset(space)] = 'o'
+        ba[capi.c_function_arg_typeoffset(space)] = 'p'
 
     def from_memory(self, space, w_obj, w_pycppclass, offset):
         address = self._get_raw_address(space, w_obj, offset)
@@ -500,7 +500,7 @@
                 obj_address = capi.direct_ptradd(rawobject, offset)
                 return rffi.cast(capi.C_OBJECT, obj_address)
         raise oefmt(space.w_TypeError,
-                    "cannot pass %T as %s", w_obj, self.clsdecl.name)
+                    "cannot pass %T instance as %s", w_obj, self.clsdecl.name)
 
     def cffi_type(self, space):
         state = space.fromcache(ffitypes.State)
@@ -615,8 +615,7 @@
             address = rffi.cast(capi.C_OBJECT, self._get_raw_address(space, 
w_obj, offset))
             assign = self.clsdecl.get_overload("__assign__")
             from pypy.module._cppyy import interp_cppyy
-            assign.call(
-                interp_cppyy.wrap_cppinstance(space, address, self.clsdecl, 
do_cast=False), [w_value])
+            assign.call_impl(address, [w_value])
         except Exception:
             InstanceConverter.to_memory(self, space, w_obj, w_value, offset)
 
@@ -687,8 +686,7 @@
             m = cppol.functions[i]
             if m.signature(False) == self.signature:
                 x = rffi.cast(rffi.VOIDPP, address)
-                x[0] = rffi.cast(rffi.VOIDP,
-                    capi.c_function_address_from_method(space, m.cppmethod))
+                x[0] = rffi.cast(rffi.VOIDP, capi.c_function_address(space, 
m.cppmethod))
                 address = rffi.cast(capi.C_OBJECT, address)
                 ba = rffi.cast(rffi.CCHARP, address)
                 ba[capi.c_function_arg_typeoffset(space)] = 'p'
@@ -731,7 +729,7 @@
                 return rffi.cast(capi.C_OBJECT, obj_address)
 
         raise oefmt(space.w_TypeError,
-                    "cannot pass %T as %s", w_obj, self.clsdecl.name)
+                    "cannot pass %T instance as %s", w_obj, self.rawdecl.name)
 
     def convert_argument(self, space, w_obj, address, call_local):
         x = rffi.cast(rffi.VOIDPP, address)
@@ -799,6 +797,8 @@
     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
+        # never be seen this way)
         return _a_converters[clean_name+compound](space, array_size)
     except KeyError:
         pass
@@ -971,6 +971,7 @@
 
     # special case, const char* w/ size and w/o '\0'
     _a_converters["const char[]"] = CStringConverterWithSize
+    _a_converters["char[]"]       = _a_converters["const char[]"]     # 
debatable
 
 _build_array_converters()
 
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
@@ -76,9 +76,7 @@
     cppyy_object_t cppyy_call_o(cppyy_method_t method, cppyy_object_t self, 
int nargs, void* args, cppyy_type_t result_type);
 
     RPY_EXTERN
-    cppyy_funcaddr_t cppyy_function_address_from_index(cppyy_scope_t scope, 
cppyy_index_t idx);
-    RPY_EXTERN
-    cppyy_funcaddr_t cppyy_function_address_from_method(cppyy_method_t method);
+    cppyy_funcaddr_t cppyy_function_address(cppyy_method_t method);
 
     /* handling of function argument buffer 
----------------------------------- */
     RPY_EXTERN
@@ -132,23 +130,26 @@
     cppyy_index_t* cppyy_method_indices_from_name(cppyy_scope_t scope, const 
char* name);
 
     RPY_EXTERN
-    char* cppyy_method_name(cppyy_scope_t scope, cppyy_index_t idx);
+    cppyy_method_t cppyy_get_method(cppyy_scope_t scope, cppyy_index_t idx);
+
     RPY_EXTERN
-    char* cppyy_method_mangled_name(cppyy_scope_t scope, cppyy_index_t idx);
+    char* cppyy_method_name(cppyy_method_t);
     RPY_EXTERN
-    char* cppyy_method_result_type(cppyy_scope_t scope, cppyy_index_t idx);
+    char* cppyy_method_mangled_name(cppyy_method_t);
     RPY_EXTERN
-    int cppyy_method_num_args(cppyy_scope_t scope, cppyy_index_t idx);
+    char* cppyy_method_result_type(cppyy_method_t);
     RPY_EXTERN
-    int cppyy_method_req_args(cppyy_scope_t scope, cppyy_index_t idx);
+    int cppyy_method_num_args(cppyy_method_t);
     RPY_EXTERN
-    char* cppyy_method_arg_type(cppyy_scope_t scope, cppyy_index_t idx, int 
arg_index);
+    int cppyy_method_req_args(cppyy_method_t);
     RPY_EXTERN
-    char* cppyy_method_arg_default(cppyy_scope_t scope, cppyy_index_t idx, int 
arg_index);
+    char* cppyy_method_arg_type(cppyy_method_t, int arg_index);
     RPY_EXTERN
-    char* cppyy_method_signature(cppyy_scope_t scope, cppyy_index_t idx, int 
show_formalargs);
+    char* cppyy_method_arg_default(cppyy_method_t, int arg_index);
     RPY_EXTERN
-    char* cppyy_method_prototype(cppyy_scope_t scope, cppyy_index_t idx, int 
show_formalargs);
+    char* cppyy_method_signature(cppyy_method_t, int show_formalargs);
+    RPY_EXTERN
+    char* cppyy_method_prototype(cppyy_scope_t scope, cppyy_method_t idx, int 
show_formalargs);
     RPY_EXTERN
     int cppyy_is_const_method(cppyy_method_t);
 
@@ -157,25 +158,21 @@
     RPY_EXTERN
     int cppyy_method_is_template(cppyy_scope_t scope, cppyy_index_t idx);
     RPY_EXTERN
-    int cppyy_method_num_template_args(cppyy_scope_t scope, cppyy_index_t idx);
-    RPY_EXTERN
-    char* cppyy_method_template_arg_name(cppyy_scope_t scope, cppyy_index_t 
idx, cppyy_index_t iarg);
+    cppyy_method_t cppyy_get_method_template(cppyy_scope_t scope, const char* 
name);
 
     RPY_EXTERN
-    cppyy_method_t cppyy_get_method(cppyy_scope_t scope, cppyy_index_t idx);
-    RPY_EXTERN
     cppyy_index_t cppyy_get_global_operator(
         cppyy_scope_t scope, cppyy_scope_t lc, cppyy_scope_t rc, const char* 
op);
 
     /* method properties 
------------------------------------------------------ */
     RPY_EXTERN
-    int cppyy_is_publicmethod(cppyy_type_t type, cppyy_index_t idx);
+    int cppyy_is_publicmethod(cppyy_method_t);
     RPY_EXTERN
-    int cppyy_is_constructor(cppyy_type_t type, cppyy_index_t idx);
+    int cppyy_is_constructor(cppyy_method_t);
     RPY_EXTERN
-    int cppyy_is_destructor(cppyy_type_t type, cppyy_index_t idx);
+    int cppyy_is_destructor(cppyy_method_t);
     RPY_EXTERN
-    int cppyy_is_staticmethod(cppyy_type_t type, cppyy_index_t idx);
+    int cppyy_is_staticmethod(cppyy_method_t);
 
     /* data member reflection information 
------------------------------------- */
     RPY_EXTERN
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
@@ -158,17 +158,17 @@
 
 
 #-----
-# Classes involved with methods and functions:
+# Classes involved with methods and functions come at two levels:
+#   - overloads: user-facing collections of overloaded functions
+#   - wrappers: internal holders of the individual C++ methods
 #
-#  CPPMethod:         base class wrapping a single function or method
-#  CPPConstructor:    specialization for allocating a new object
-#  CPPFunction:       specialization for free and static functions
+#  W_CPPOverload:             instance methods (base class)
+#  W_CPPConstructorOverload:  constructors
+#  W_CPPStaticOverload:       free and static functions
+#  W_CPPTemplateOverload:     templated methods/functions
+#
+#  CPPMethod:         a single function or method (base class)
 #  CPPSetItem:        specialization for Python's __setitem__
-#  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
-#  W_CPPBoundMethod:  instantiated template method
 #
 # All methods/functions derive from CPPMethod and are collected as overload
 # candidates in user-facing overload classes. Templated methods are a two-step
@@ -181,15 +181,15 @@
     also takes care of offset casting and recycling of known objects through
     the memory_regulator."""
 
-    _attrs_ = ['space', 'scope', 'index', 'cppmethod', 'arg_defs', 
'args_required',
+    _attrs_ = ['space', 'scope', 'cppmethod', 'arg_defs', 'args_required',
                'converters', 'executor', '_funcaddr', 'cif_descr', 
'uses_local']
-    _immutable_ = True
+    _immutable_fields_ = ['scope', 'cppmethod', 'arg_defs', 'args_required',
+                          'converters', 'executor', 'uses_local']
 
-    def __init__(self, space, declaring_scope, method_index, arg_defs, 
args_required):
+    def __init__(self, space, declaring_scope, cppmethod, arg_defs, 
args_required):
         self.space = space
         self.scope = declaring_scope
-        self.index = method_index
-        self.cppmethod = capi.c_get_method(self.space, self.scope, 
method_index)
+        self.cppmethod = cppmethod
         self.arg_defs = arg_defs
         self.args_required = args_required
 
@@ -201,12 +201,6 @@
         self._funcaddr = lltype.nullptr(capi.C_FUNC_PTR.TO)
         self.uses_local = False
 
-    @staticmethod
-    def unpack_cppthis(space, w_cppinstance, declaring_scope):
-        cppinstance = space.interp_w(W_CPPInstance, w_cppinstance)
-        cppinstance._nullcheck()
-        return cppinstance.get_cppthis(declaring_scope)
-
     def _address_from_local_buffer(self, call_local, idx):
         if not call_local:
             return call_local
@@ -349,7 +343,7 @@
         self.converters = [converter.get_converter(self.space, arg_type, 
arg_dflt)
                                for arg_type, arg_dflt in self.arg_defs]
         self.executor = executor.get_executor(
-            self.space, capi.c_method_result_type(self.space, self.scope, 
self.index))
+            self.space, capi.c_method_result_type(self.space, self.cppmethod))
 
         for conv in self.converters:
             if conv.uses_local:
@@ -359,7 +353,7 @@
         # Each CPPMethod corresponds one-to-one to a C++ equivalent and cppthis
         # has been offset to the matching class. Hence, the libffi pointer is
         # uniquely defined and needs to be setup only once.
-        funcaddr = capi.c_function_address_from_index(self.space, self.scope, 
self.index)
+        funcaddr = capi.c_function_address(self.space, self.cppmethod)
         if funcaddr and cppthis:      # TODO: methods only for now
             state = self.space.fromcache(ffitypes.State)
 
@@ -427,10 +421,10 @@
         capi.c_deallocate_function_args(self.space, args)
 
     def signature(self, show_formalargs=True):
-        return capi.c_method_signature(self.space, self.scope, self.index, 
show_formalargs)
+        return capi.c_method_signature(self.space, self.cppmethod, 
show_formalargs)
 
     def prototype(self, show_formalargs=True):
-        return capi.c_method_prototype(self.space, self.scope, self.index, 
show_formalargs)
+        return capi.c_method_prototype(self.space, self.scope, self.cppmethod, 
show_formalargs)
 
     def priority(self):
         total_arg_priority = 0
@@ -440,8 +434,11 @@
 
     @rgc.must_be_light_finalizer
     def __del__(self):
-        if self.cif_descr:
-            lltype.free(self.cif_descr, flavor='raw')
+        try:
+            if self.cif_descr:
+                lltype.free(self.cif_descr, flavor='raw')
+        except Exception:     # TODO: happens for templates, why?
+            pass
 
     def __repr__(self):
         return "CPPMethod: %s" % self.prototype()
@@ -450,80 +447,12 @@
         assert 0, "you should never have a pre-built instance of this!"
 
 
-class CPPFunction(CPPMethod):
-    """Global (namespaced) / static function dispatcher."""
-
-    _immutable_ = True
-
-    @staticmethod
-    def unpack_cppthis(space, w_cppinstance, declaring_scope):
-        return capi.C_NULL_OBJECT
-
-    def __repr__(self):
-        return "CPPFunction: %s" % self.prototype()
-
-
-class CPPTemplateMethod(CPPMethod):
-    """Method dispatcher that first resolves the template instance."""
-
-    _attrs_ = ['space', 'templ_args']
-    _immutable_ = True
-
-    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 CPPTemplateMethod on 
CPPMethod/CPPFunction here
-        CPPMethod.__init__(self, space, declaring_scope, method_index, 
arg_defs, args_required)
-
-    def call(self, cppthis, args_w, useffi):
-        assert lltype.typeOf(cppthis) == capi.C_OBJECT
-        for i in range(len(args_w)):
-            try:
-                s = self.space.text_w(args_w[i])
-            except OperationError:
-                s = self.space.text_w(self.space.getattr(args_w[i], 
self.space.newtext('__name__')))
-            s = capi.c_resolve_name(self.space, s)
-            if s != self.templ_args[i]:
-                raise oefmt(self.space.w_TypeError,
-                            "non-matching template (got %s where %s expected)",
-                            s, self.templ_args[i])
-        return W_CPPBoundMethod(cppthis, self, useffi)
-
-    def bound_call(self, cppthis, args_w, useffi):
-        return CPPMethod.call(self, cppthis, args_w, useffi)
-
-    def __repr__(self):
-        return "CPPTemplateMethod: %s" % self.prototype()
-
-
-class CPPConstructor(CPPMethod):
-    """Method dispatcher that constructs new objects. This method can not have
-    a fast path, as the allocation of the object is currently left to the
-    reflection layer only, since the C++ class may have an overloaded operator
-    new, disallowing malloc here."""
-
-    _immutable_ = True
-
-    @staticmethod
-    def unpack_cppthis(space, w_cppinstance, declaring_scope):
-        return rffi.cast(capi.C_OBJECT, declaring_scope.handle)
-
-    def call(self, cppthis, args_w, useffi):
-        # Note: this does not return a wrapped instance, just a pointer to the
-        # new instance; the overload must still wrap it before returning. Also,
-        # cppthis is declaring_scope.handle (as per unpack_cppthis(), above).
-        return CPPMethod.call(self, cppthis, args_w, useffi)
-
-    def __repr__(self):
-        return "CPPConstructor: %s" % self.prototype()
-
-
 class CPPSetItem(CPPMethod):
     """Method dispatcher specific to Python's __setitem__ mapped onto C++'s
     operator[](int). The former function takes an extra argument to assign to
     the return type of the latter."""
 
-    _immutable_ = True
+    _attrs_ = []
 
     def call(self, cppthis, args_w, useffi):
         end = len(args_w)-1
@@ -537,46 +466,44 @@
 
 
 class W_CPPOverload(W_Root):
-    """Dispatcher that is actually available at the app-level: it is a
-    collection of (possibly) overloaded methods or functions. It calls these
-    in order and deals with error handling and reporting."""
+    """App-level dispatcher: controls a collection of (potentially) overloaded 
methods
+    or functions. Calls these in order and deals with error handling and 
reporting."""
 
-    _attrs_ = ['space', 'scope', 'functions', 'flags']
+    _attrs_ = ['space', 'scope', 'functions', 'flags', 'w_this']
     _immutable_fields_ = ['scope', 'functions[*]']
 
-    def __init__(self, space, declaring_scope, functions):
-        self.space = space
-        self.scope = declaring_scope
-        assert len(functions)
+    def __init__(self, space, declaring_scope, functions, flags = 
OVERLOAD_FLAGS_USE_FFI):
+        self.space  = space
+        self.scope  = declaring_scope
         from rpython.rlib import debug
         self.functions = debug.make_sure_not_resized(functions)
-        self.flags = 0
-        self.flags |= OVERLOAD_FLAGS_USE_FFI
+        self.flags  = flags
+        self.w_this = self.space.w_None
 
-    # allow user to determine ffi use rules per overload
-    def fget_useffi(self, space):
-        return space.newbool(bool(self.flags & OVERLOAD_FLAGS_USE_FFI))
+    @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):
+            return self  # unbound, so no new instance needed
+        cppol = W_CPPOverload(self.space, self.scope, self.functions, 
self.flags)
+        cppol.w_this = w_cppinstance
+        return cppol     # bound
 
-    @unwrap_spec(value=bool)
-    def fset_useffi(self, space, value):
-        if space.is_true(value):
-            self.flags |= OVERLOAD_FLAGS_USE_FFI
+    @unwrap_spec(args_w='args_w')
+    def call(self, args_w):
+        if self.space.is_w(self.w_this, self.space.w_None) and len(args_w):
+            w_this = args_w[0]
+            args_w = args_w[1:]
         else:
-            self.flags &= ~OVERLOAD_FLAGS_USE_FFI
-
-    @jit.elidable_promote()
-    def is_static(self):
-        if isinstance(self.functions[0], CPPFunction):
-            return self.space.w_True
-        return self.space.w_False
+            w_this = self.w_this
+        cppinstance = self.space.interp_w(W_CPPInstance, w_this)
+        cppinstance._nullcheck()
+        if not capi.c_is_subtype(self.space, cppinstance.clsdecl, self.scope):
+            raise oefmt(self.space.w_TypeError,
+                "cannot pass %T instance as %s", w_this, self.scope.name)
+        return self.call_impl(cppinstance.get_cppthis(self.scope), args_w)
 
     @jit.unroll_safe
-    @unwrap_spec(args_w='args_w')
-    def call(self, w_cppinstance, args_w):
-        # instance handling is specific to the function type only, so take it 
out
-        # of the loop over function overloads
-        cppthis = self.functions[0].unpack_cppthis(
-            self.space, w_cppinstance, self.functions[0].scope)
+    def call_impl(self, cppthis, args_w):
         assert lltype.typeOf(cppthis) == capi.C_OBJECT
 
         # The following code tries out each of the functions in order. If
@@ -634,38 +561,96 @@
             sig += '\n'+self.functions[i].prototype()
         return self.space.newtext(sig)
 
+    # allow user to determine ffi use rules per overload
+    def fget_useffi(self, space):
+        return space.newbool(bool(self.flags & OVERLOAD_FLAGS_USE_FFI))
+
+    @unwrap_spec(value=bool)
+    def fset_useffi(self, space, value):
+        if space.is_true(value):
+            self.flags |= OVERLOAD_FLAGS_USE_FFI
+        else:
+            self.flags &= ~OVERLOAD_FLAGS_USE_FFI
+
+    def fget_doc(self, space):
+        return self.prototype()
+
     def __repr__(self):
         return "W_CPPOverload(%s)" % [f.prototype() for f in self.functions]
 
 W_CPPOverload.typedef = TypeDef(
     'CPPOverload',
-    call = interp2app(W_CPPOverload.call),
-    is_static = interp2app(W_CPPOverload.is_static),
+    __get__    = interp2app(W_CPPOverload.descr_get),
+    __call__   = interp2app(W_CPPOverload.call),
     __useffi__ = GetSetProperty(W_CPPOverload.fget_useffi, 
W_CPPOverload.fset_useffi),
-    prototype = interp2app(W_CPPOverload.prototype),
+    __doc__    = GetSetProperty(W_CPPOverload.fget_doc)
 )
 
 
+# overload collection of static (class and free) functions; these differ
+# from methods only in the handling of 'cppthis'
+class W_CPPStaticOverload(W_CPPOverload):
+    _attrs_ = []
+
+    @unwrap_spec(args_w='args_w')
+    def descr_get(self, w_cppinstance, args_w):
+        if isinstance(w_cppinstance, W_CPPInstance):
+            # two possibilities: this is a static function called on an
+            # instance and w_this must not be set, or a free function rebound
+            # onto a class and w_this should be set
+            cppinstance = self.space.interp_w(W_CPPInstance, w_cppinstance)
+            if cppinstance.clsdecl.handle != self.scope.handle:
+                cppol = W_CPPStaticOverload(self.space, self.scope, 
self.functions, self.flags)
+                cppol.w_this = w_cppinstance
+                return cppol       # bound
+        return self      # unbound
+
+    @unwrap_spec(args_w='args_w')
+    def call(self, args_w):
+        if not self.space.is_w(self.w_this, self.space.w_None):
+            # free function used as bound method, put self back into args_w
+            cppinstance = self.space.interp_w(W_CPPInstance, self.w_this)
+            cppinstance._nullcheck()
+            args_w = [self.w_this] + args_w
+        return self.call_impl(capi.C_NULL_OBJECT, args_w)
+
+    def __repr__(self):
+        return "W_CPPStaticOverload(%s)" % [f.prototype() for f in 
self.functions]
+
+W_CPPStaticOverload.typedef = TypeDef(
+    'CPPStaticOverload',
+    __get__    = interp2app(W_CPPStaticOverload.descr_get),
+    __call__   = interp2app(W_CPPStaticOverload.call),
+    __useffi__ = GetSetProperty(W_CPPStaticOverload.fget_useffi, 
W_CPPStaticOverload.fset_useffi),
+    __doc__    = GetSetProperty(W_CPPStaticOverload.fget_doc)
+)
+
+
 class W_CPPConstructorOverload(W_CPPOverload):
-    @jit.elidable_promote()
-    def is_static(self):
-        return self.space.w_False
+    _attrs_ = []
 
-    @jit.elidable_promote()
-    def unpack_cppthis(self, w_cppinstance):
-        return rffi.cast(capi.C_OBJECT, self.scope.handle)
+    @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):
+            return self  # unbound (TODO: probably useless)
+        cppol = W_CPPConstructorOverload(self.space, self.scope, 
self.functions, self.flags)
+        cppol.w_this = w_cppinstance
+        return cppol     # bound
 
-    @jit.unroll_safe
     @unwrap_spec(args_w='args_w')
-    def call(self, w_cppinstance, args_w):
+    def call(self, args_w):
         # TODO: factor out the following:
         if capi.c_is_abstract(self.space, self.scope.handle):
             raise oefmt(self.space.w_TypeError,
                         "cannot instantiate abstract class '%s'",
                         self.scope.name)
-        w_result = W_CPPOverload.call(self, w_cppinstance, args_w)
+        if self.space.is_w(self.w_this, self.space.w_None) and len(args_w):
+            cppinstance = self.space.interp_w(W_CPPInstance, args_w[0])
+            args_w = args_w[1:]
+        else:
+            cppinstance = self.space.interp_w(W_CPPInstance, self.w_this)
+        w_result = self.call_impl(rffi.cast(capi.C_OBJECT, self.scope.handle), 
args_w)
         newthis = rffi.cast(capi.C_OBJECT, self.space.uint_w(w_result))
-        cppinstance = self.space.interp_w(W_CPPInstance, w_cppinstance, 
can_be_None=True)
         if cppinstance is not None:
             cppinstance._rawobject = newthis
             memory_regulator.register(cppinstance)
@@ -675,47 +660,86 @@
 
 W_CPPConstructorOverload.typedef = TypeDef(
     'CPPConstructorOverload',
-    call = interp2app(W_CPPConstructorOverload.call),
-    is_static = interp2app(W_CPPConstructorOverload.is_static),
-    prototype = interp2app(W_CPPConstructorOverload.prototype),
+    __get__    = interp2app(W_CPPConstructorOverload.descr_get),
+    __call__   = interp2app(W_CPPConstructorOverload.call),
+    __doc__    = GetSetProperty(W_CPPConstructorOverload.fget_doc)
 )
 
 
 class W_CPPTemplateOverload(W_CPPOverload):
+    """App-level dispatcher to allow both lookup/instantiation of templated 
methods and
+    dispatch among overloads between templated and non-templated overloads."""
+
+    _attrs_ = ['name', 'overloads', 'master']
+    _immutable_fields_ = ['name']
+
+    def __init__(self, space, name, declaring_scope, functions, flags = 
OVERLOAD_FLAGS_USE_FFI):
+         W_CPPOverload.__init__(self, space, declaring_scope, functions, flags)
+         self.name = name
+         self.overloads = {}
+         self.master = None
+
     @unwrap_spec(args_w='args_w')
-    def __getitem__(self, args_w):
-        pass
+    def descr_get(self, w_cppinstance, args_w):
+        if self.space.is_w(w_cppinstance, self.space.w_None):
+            return self  # unbound
+        cppol = W_CPPTemplateOverload(self.space, self.name, self.scope, 
self.functions, self.flags)
+        cppol.w_this = w_cppinstance
+        cppol.master = self
+        return cppol     # bound
+
+    @unwrap_spec(args_w='args_w')
+    def getitem(self, args_w):
+        space = self.space
+        tmpl_args = ''
+        for i in range(len(args_w)):
+            w_obj = args_w[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
+        fullname = self.name+'<'+tmpl_args+'>'
+
+        # find/instantiate new callable function
+        master = self.master
+        if not master:
+            master = self
+        try:
+            return master.overloads[fullname].descr_get(self.w_this, [])
+        except KeyError:
+            pass
+
+        cppmeth = capi.c_get_method_template(space, self.scope, 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)
+        master.overloads[fullname] = cppol
+        return cppol.descr_get(self.w_this, [])
 
     def __repr__(self):
         return "W_CPPTemplateOverload(%s)" % [f.prototype() for f in 
self.functions]
 
 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),
-)
-
-
-class W_CPPBoundMethod(W_Root):
-    _attrs_ = ['cppthis', 'method', 'useffi']
-
-    def __init__(self, cppthis, method, useffi):
-        self.cppthis = cppthis
-        self.method = method
-        self.useffi = useffi
-
-    def __call__(self, args_w):
-        return self.method.bound_call(self.cppthis, args_w, self.useffi)
-
-    def __repr__(self):
-        return "W_CPPBoundMethod(%s)" % self.method.prototype()
-
-W_CPPBoundMethod.typedef = TypeDef(
-    'CPPBoundMethod',
-    __call__ = interp2app(W_CPPBoundMethod.__call__),
+    __get__     = interp2app(W_CPPTemplateOverload.descr_get),
+    __getitem__ = interp2app(W_CPPTemplateOverload.getitem),
+    __call__    = interp2app(W_CPPTemplateOverload.call),
+    __useffi__  = GetSetProperty(W_CPPTemplateOverload.fget_useffi, 
W_CPPTemplateOverload.fset_useffi),
+    __doc__     = GetSetProperty(W_CPPTemplateOverload.fget_doc)
 )
 
 
@@ -826,7 +850,16 @@
         return space.w_False
 
 #-----
-
+# Classes for data members:
+#
+# W_CPPScopeDecl         : scope base class
+# W_CPPNamespaceDecl     : namespace scope
+# W_CPPClassDecl         : class scope
+#
+# Namespaces and classes mainly differ in lookups of methods. Whereas classes
+# can grown templated methods, namespaces are wide open to any additions. Such
+# lookups are triggered from get_scoped_pycppitem (in pythonify.py). Further
+# specialization is done on the type of data/methods that each can have.
 
 class W_CPPScopeDecl(W_Root):
     _attrs_ = ['space', 'handle', 'flags', 'name', 'overloads', 'datamembers']
@@ -897,15 +930,15 @@
     _attrs_ = ['space', 'handle', 'name', 'overloads', 'datamembers']
     _immutable_fields_ = ['handle', 'name']
 
-    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)
+    def _make_cppfunction(self, pyname, cppmeth, funcs):
+        num_args = capi.c_method_num_args(self.space, cppmeth)
+        args_required = capi.c_method_req_args(self.space, cppmeth)
         arg_defs = []
         for i in range(num_args):
-            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_type = capi.c_method_arg_type(self.space, cppmeth, i)
+            arg_dflt = capi.c_method_arg_default(self.space, cppmeth, i)
             arg_defs.append((arg_type, arg_dflt))
-        funcs.append(CPPFunction(self.space, self, index, arg_defs, 
args_required))
+        funcs.append(CPPMethod(self.space, self, cppmeth, arg_defs, 
args_required))
         return FUNCTION_IS_GLOBAL
 
     def _make_datamember(self, dm_name, dm_idx):
@@ -922,14 +955,20 @@
 
     def find_overload(self, meth_name):
         indices = capi.c_method_indices_from_name(self.space, self, meth_name)
-        if not indices:
-            raise self.missing_attribute_error(meth_name)
-        cppfunctions, ftype = [], 0
-        for meth_idx in indices:
-            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)
+        if indices:
+            cppfunctions, ftype = [], 0
+            templated = False
+            for idx in indices:
+                cppmeth = capi.c_get_method(self.space, self, idx)
+                ftype |= self._make_cppfunction(meth_name, cppmeth, 
cppfunctions)
+                if capi.c_method_is_template(self.space, self, idx):
+                    templated = True
+            if templated:
+                return W_CPPTemplateOverload(self.space, meth_name, self, 
cppfunctions[:])
+            return W_CPPStaticOverload(self.space, self, cppfunctions[:])
+        elif capi.c_exists_method_template(self.space, self, meth_name):
+            return W_CPPTemplateOverload(self.space, meth_name, self, [])
+        raise self.missing_attribute_error(meth_name)
 
     def find_datamember(self, dm_name):
         dm_idx = capi.c_datamember_index(self.space, self, dm_name)
@@ -973,69 +1012,71 @@
 
     def _build_overloads(self):
         assert len(self.overloads) == 0
-        overloads_temp = {}
+        methods_tmp = {}; ftype_tmp = {}
         for idx in range(capi.c_num_methods(self.space, self)):
-            if capi.c_is_constructor(self.space, self, idx):
+            cppmeth = capi.c_get_method(self.space, self, idx)
+            if capi.c_is_constructor(self.space, cppmeth):
                 pyname = '__init__'
             else:
                 pyname = helper.map_operator_name(self.space,
-                    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))
+                    capi.c_method_name(self.space, cppmeth),
+                    capi.c_method_num_args(self.space, cppmeth),
+                    capi.c_method_result_type(self.space, cppmeth))
             try:
-                detail = overloads_temp[pyname]
+                methods = methods_tmp[pyname]
             except KeyError:
-                detail = [[], 0]; overloads_temp[pyname] = detail
-            detail[1] |= self._make_cppfunction(pyname, idx, detail[0])
+                methods_tmp[pyname] = []; ftype_tmp[pyname] = 0
+                methods = methods_tmp[pyname]
+            ftype_tmp[pyname] |= self._make_cppfunction(pyname, cppmeth, 
methods)
+            if capi.c_method_is_template(self.space, self, idx):
+                ftype_tmp[pyname] |= FUNCTION_IS_TEMPLATE
         # 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 overloads_temp:
+        if not "__getitem__" in methods_tmp:
             try:
-                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])
+                si_methods = methods_tmp["__setitem__"]
+                gi_methods = []; ftype = 0
+                for m in si_methods:
+                    ftype |= self._make_cppfunction("__getitem__", 
m.cppmethod, gi_methods)
+                methods_tmp["__getitem__"] = gi_methods; 
ftype_tmp["__getitem__"] = ftype
             except KeyError:
                 pass          # just means there's no __setitem__ either
 
         # create the overloads from the method sets
-        for pyname, detail in overloads_temp.iteritems():
-            methods = detail[0]
+        for pyname, methods in methods_tmp.iteritems():
+            ftype = ftype_tmp[pyname]
             CPPMethodSort(methods).sort()
-            if pyname == '__init__':
-                overload = W_CPPConstructorOverload(self.space, self, methods)
-            elif detail[1] & FUNCTION_IS_TEMPLATE:
-                overload = W_CPPTemplateOverload(self.space, self, methods)
+            if ftype & FUNCTION_IS_CONSTRUCTOR:
+                overload = W_CPPConstructorOverload(self.space, self, 
methods[:])
+            elif ftype & FUNCTION_IS_STATIC:
+                overload = W_CPPStaticOverload(self.space, self, methods[:])
+            elif ftype & FUNCTION_IS_TEMPLATE:
+                overload = W_CPPTemplateOverload(self.space, pyname, self, 
methods[:])
             else:
-                overload = W_CPPOverload(self.space, self, methods)
+                overload = W_CPPOverload(self.space, self, methods[:])
             self.overloads[pyname] = overload
 
-    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)
+    def _make_cppfunction(self, pyname, cppmeth, funcs):
+        num_args = capi.c_method_num_args(self.space, cppmeth)
+        args_required = capi.c_method_req_args(self.space, cppmeth)
         arg_defs = []
         for i in range(num_args):
-            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_type = capi.c_method_arg_type(self.space, cppmeth, i)
+            arg_dflt = capi.c_method_arg_default(self.space, cppmeth, 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 = 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)
+        if pyname == "__setitem__":
+            cppfunction = CPPSetItem(self.space, self, cppmeth, arg_defs, 
args_required)
             ftype = FUNCTION_IS_SETITEM
         else:
-            cppfunction = CPPMethod(self.space, self, index, arg_defs, 
args_required)
-            ftype = FUNCTION_IS_METHOD
+            cppfunction = CPPMethod(self.space, self, cppmeth, arg_defs, 
args_required)
+            if capi.c_is_constructor(self.space, cppmeth):
+                ftype = FUNCTION_IS_CONSTRUCTOR
+            elif capi.c_is_staticmethod(self.space, cppmeth):
+                ftype = FUNCTION_IS_STATIC
+            else:
+                ftype = FUNCTION_IS_METHOD
         funcs.append(cppfunction)
         return ftype
 
@@ -1061,8 +1102,8 @@
                 datamember = W_CPPDataMember(self.space, self, type_name, 
offset)
             self.datamembers[datamember_name] = datamember
 
-    def find_overload(self, name):
-        raise self.missing_attribute_error(name)
+    def find_overload(self, meth_name):
+        raise self.missing_attribute_error(meth_name)
 
     def find_datamember(self, name):
         raise self.missing_attribute_error(name)
@@ -1227,11 +1268,12 @@
                     self.space, nss, self.clsdecl, other.clsdecl, "operator==")
                 if meth_idx != -1:
                     funcs = []
-                    nss._make_cppfunction("operator==", meth_idx, funcs)
-                    ol = W_CPPOverload(self.space, nss, funcs)
+                    cppmeth = capi.c_get_method(self.space, nss, meth_idx)
+                    nss._make_cppfunction("operator==", cppmeth, funcs)
+                    ol = W_CPPStaticOverload(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])
+                    return ol.call([self, w_other])
         except OperationError as e:
             if not e.match(self.space, self.space.w_TypeError):
                 raise
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
@@ -111,14 +111,6 @@
         return scope.__module__ + '.' + scope.__name__
     return 'cppyy'
 
-def make_static_function(func_name, cppol):
-    def function(*args):
-        return cppol.call(None, *args)
-    function.__name__ = func_name
-    function.__doc__ = cppol.prototype()
-    return staticmethod(function)
-
-
 def make_cppnamespace(scope, name, decl):
     # build up a representation of a C++ namespace (namespaces are classes)
 
@@ -147,7 +139,6 @@
                 break
     return tuple(bases)
 
-
 def make_new(decl):
     def __new__(cls, *args):
         # create a place-holder only as there may be a derived class defined
@@ -160,13 +151,6 @@
         return instance
     return __new__
 
-def make_method(meth_name, cppol):
-    def method(self, *args):
-        return cppol.call(self, *args)
-    method.__name__ = meth_name
-    method.__doc__ = cppol.prototype()
-    return method
-
 def make_cppclass(scope, cl_name, decl):
     import _cppyy
 
@@ -188,7 +172,7 @@
     # prepare dictionary for python-side C++ class representation
     def dispatch(self, m_name, signature):
         cppol = decl.__dispatch__(m_name, signature)
-        return types.MethodType(make_method(m_name, cppol), self, type(self))
+        return types.MethodType(cppol, self, type(self))
     d_class = {"__cppdecl__"   : decl,
          "__new__"      : make_new(decl),
          "__module__"   : make_module_name(scope),
@@ -199,10 +183,7 @@
     # insert (static) methods into the class dictionary
     for m_name in decl.get_method_names():
         cppol = decl.get_overload(m_name)
-        if cppol.is_static():
-            d_class[m_name] = make_static_function(m_name, cppol)
-        else:
-            d_class[m_name] = make_method(m_name, cppol)
+        d_class[m_name] = cppol
 
     # add all data members to the dictionary of the class to be created, and
     # static ones also to the metaclass (needed for property setters)
@@ -267,8 +248,7 @@
     if not cppitem:
         try:
             cppitem = scope.__cppdecl__.get_overload(name)
-            pycppitem = make_static_function(name, cppitem)
-            setattr(scope.__class__, name, pycppitem)
+            setattr(scope.__class__, name, cppitem)
             pycppitem = getattr(scope, name)      # binds function as needed
         except AttributeError:
             pass
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
@@ -924,6 +924,15 @@
 
 
 /* method/function reflection information --------------------------------- */
+cppyy_method_t cppyy_get_method(cppyy_scope_t handle, cppyy_index_t 
method_index) {
+    if (s_scopes.find(handle) != s_scopes.end()) {
+        long id = s_scopes[handle].m_method_offset + (long)method_index;
+        return (cppyy_method_t)id;
+    }
+    assert(!"unknown class in cppyy_get_method");
+    return (cppyy_method_t)0;
+}
+
 int cppyy_num_methods(cppyy_scope_t handle) {
     return s_scopes[handle].m_methods.size();
 }
@@ -948,18 +957,15 @@
     return 
cppstring_to_cstring(s_scopes[handle].m_methods[method_index].m_argtypes[arg_index]);
 }
 
-char* cppyy_method_arg_default(
-        cppyy_scope_t /* handle */, cppyy_index_t /* method_index */, int /* 
arg_index */) {
+char* cppyy_method_arg_default(cppyy_method_t, int /* arg_index */) {
     return cppstring_to_cstring("");
 }
 
-char* cppyy_method_signature(
-        cppyy_scope_t /* handle */, cppyy_index_t /* method_index */, int /* 
show_formalargs */) {
+char* cppyy_method_signature(cppyy_method_t, int /* show_formalargs */) {
     return cppstring_to_cstring("");
 }
 
-char* cppyy_method_prototype(
-        cppyy_scope_t /* handle */, cppyy_index_t /* method_index */, int /* 
show_formalargs */) {
+char* cppyy_method_prototype(cppyy_scope_t, cppyy_method_t, int /* 
show_formalargs */) {
     return cppstring_to_cstring("");
 }
 
@@ -967,15 +973,6 @@
     return 0;
 }
     
-cppyy_method_t cppyy_get_method(cppyy_scope_t handle, cppyy_index_t 
method_index) {
-    if (s_scopes.find(handle) != s_scopes.end()) {
-        long id = s_scopes[handle].m_method_offset + (long)method_index;
-        return (cppyy_method_t)id;
-    }
-    assert(!"unknown class in cppyy_get_method");
-    return (cppyy_method_t)0;
-}
-
 cppyy_index_t cppyy_get_global_operator(cppyy_scope_t /* scope */,
         cppyy_scope_t /* lc */, cppyy_scope_t /* rc */, const char* /* op */) {
     return (cppyy_index_t)-1;
diff --git a/pypy/module/_cppyy/test/test_advancedcpp.py 
b/pypy/module/_cppyy/test/test_advancedcpp.py
--- a/pypy/module/_cppyy/test/test_advancedcpp.py
+++ b/pypy/module/_cppyy/test/test_advancedcpp.py
@@ -663,9 +663,8 @@
         import _cppyy as cppyy
         Thrower = cppyy.gbl.Thrower
 
-        # TODO: clean up this interface:
-        Thrower.__cppdecl__.get_overload('throw_anything').__useffi__  = False
-        Thrower.__cppdecl__.get_overload('throw_exception').__useffi__ = False
+        Thrower.throw_anything.__useffi__  = False
+        Thrower.throw_exception.__useffi__ = False
 
         t = Thrower()
 
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
@@ -37,7 +37,8 @@
             lib = ctypes.CDLL(%r, ctypes.RTLD_GLOBAL)
             def cpp_instantiate(tt, *args):
                 inst = _cppyy._bind_object(0, tt, True)
-                tt.get_overload("__init__").call(inst, *args)
+                ol = tt.get_overload("__init__").__get__(inst)
+                ol(*args)
                 return inst
             return lib, cpp_instantiate, _cppyy._scope_byname('example01'),\
                           _cppyy._scope_byname('payload')""" % (test_dct, )))
@@ -49,30 +50,30 @@
         import sys, math
         t = self.example01
 
-        res = t.get_overload("staticAddOneToInt").call(None, 1)
+        res = t.get_overload("staticAddOneToInt")(1)
         assert res == 2
-        res = t.get_overload("staticAddOneToInt").call(None, 1L)
+        res = t.get_overload("staticAddOneToInt")(1L)
         assert res == 2
-        res = t.get_overload("staticAddOneToInt").call(None, 1, 2)
+        res = t.get_overload("staticAddOneToInt")(1, 2)
         assert res == 4
-        res = t.get_overload("staticAddOneToInt").call(None, -1)
+        res = t.get_overload("staticAddOneToInt")(-1)
         assert res == 0
         maxint32 = int(2 ** 31 - 1)
-        res = t.get_overload("staticAddOneToInt").call(None, maxint32-1)
+        res = t.get_overload("staticAddOneToInt")(maxint32-1)
         assert res == maxint32
-        res = t.get_overload("staticAddOneToInt").call(None, maxint32)
+        res = t.get_overload("staticAddOneToInt")(maxint32)
         assert res == -maxint32-1
 
-        raises(TypeError, 't.get_overload("staticAddOneToInt").call(None, 1, 
[])')
-        raises(TypeError, 't.get_overload("staticAddOneToInt").call(None, 1.)')
-        raises(TypeError, 't.get_overload("staticAddOneToInt").call(None, 
maxint32+1)')
+        raises(TypeError, 't.get_overload("staticAddOneToInt")(1, [])')
+        raises(TypeError, 't.get_overload("staticAddOneToInt")(1.)')
+        raises(TypeError, 't.get_overload("staticAddOneToInt")(maxint32+1)')
 
     def test02_static_double(self):
         """Test passing of a double and returning of a double on a static 
function."""
 
         t = self.example01
 
-        res = t.get_overload("staticAddToDouble").call(None, 0.09)
+        res = t.get_overload("staticAddToDouble")(0.09)
         assert res == 0.09 + 0.01
 
     def test03_static_constcharp(self):
@@ -81,14 +82,14 @@
 
         t = self.example01
 
-        res = t.get_overload("staticAtoi").call(None, "1")
+        res = t.get_overload("staticAtoi")("1")
         assert res == 1
-        res = t.get_overload("staticStrcpy").call(None, "aap")       # TODO: 
this leaks
+        res = t.get_overload("staticStrcpy")("aap")    # TODO: this leaks
         assert res == "aap"
-        res = t.get_overload("staticStrcpy").call(None, u"aap")      # TODO: 
this leaks
+        res = t.get_overload("staticStrcpy")(u"aap")   # TODO: this leaks
         assert res == "aap"
 
-        raises(TypeError, 't.get_overload("staticStrcpy").call(None, 1.)')     
# TODO: this leaks
+        raises(TypeError, 't.get_overload("staticStrcpy")(1.)')  # TODO: this 
leaks
 
     def test04_method_int(self):
         """Test passing of a int, returning of a int, and memory cleanup, on
@@ -97,30 +98,30 @@
 
         t = self.example01
 
-        assert t.get_overload("getCount").call(None) == 0
+        assert t.get_overload("getCount")() == 0
 
         e1 = self.instantiate(t, 7)
-        assert t.get_overload("getCount").call(None) == 1
-        res = t.get_overload("addDataToInt").call(e1, 4)
+        assert t.get_overload("getCount")() == 1
+        res = t.get_overload("addDataToInt")(e1, 4)
         assert res == 11
-        res = t.get_overload("addDataToInt").call(e1, -4)
+        res = t.get_overload("addDataToInt")(e1, -4)
         assert res == 3
         e1.__destruct__()
-        assert t.get_overload("getCount").call(None) == 0
-        raises(ReferenceError, 't.get_overload("addDataToInt").call(e1, 4)')
+        assert t.get_overload("getCount")() == 0
+        raises(ReferenceError, 't.get_overload("addDataToInt")(e1, 4)')
 
         e1 = self.instantiate(t, 7)
         e2 = self.instantiate(t, 8)
-        assert t.get_overload("getCount").call(None) == 2
+        assert t.get_overload("getCount")() == 2
         e1.__destruct__()
-        assert t.get_overload("getCount").call(None) == 1
+        assert t.get_overload("getCount")() == 1
         e2.__destruct__()
-        assert t.get_overload("getCount").call(None) == 0
+        assert t.get_overload("getCount")() == 0
 
         e2.__destruct__()
-        assert t.get_overload("getCount").call(None) == 0
+        assert t.get_overload("getCount")() == 0
 
-        raises(TypeError, t.get_overload("addDataToInt").call, 41, 4)
+        raises(TypeError, t.get_overload("addDataToInt"), 41, 4)
 
     def test05_memory(self):
         """Test memory destruction and integrity."""
@@ -130,29 +131,29 @@
 
         t = self.example01
 
-        assert t.get_overload("getCount").call(None) == 0
+        assert t.get_overload("getCount")() == 0
 
         e1 = self.instantiate(t, 7)
-        assert t.get_overload("getCount").call(None) == 1
-        res = t.get_overload("addDataToInt").call(e1, 4)
+        assert t.get_overload("getCount")() == 1
+        res = t.get_overload("addDataToInt")(e1, 4)
         assert res == 11
-        res = t.get_overload("addDataToInt").call(e1, -4)
+        res = t.get_overload("addDataToInt")(e1, -4)
         assert res == 3
         e1 = None
         gc.collect()
-        assert t.get_overload("getCount").call(None) == 0
+        assert t.get_overload("getCount")() == 0
 
         e1 = self.instantiate(t, 7)
         e2 = self.instantiate(t, 8)
-        assert t.get_overload("getCount").call(None) == 2
+        assert t.get_overload("getCount")() == 2
         e1 = None
         gc.collect()
-        assert t.get_overload("getCount").call(None) == 1
+        assert t.get_overload("getCount")() == 1
         e2.__destruct__()
-        assert t.get_overload("getCount").call(None) == 0
+        assert t.get_overload("getCount")() == 0
         e2 = None
         gc.collect()
-        assert t.get_overload("getCount").call(None) == 0
+        assert t.get_overload("getCount")() == 0
 
     def test05a_memory2(self):
         """Test ownership control."""
@@ -161,18 +162,18 @@
 
         t = self.example01
 
-        assert t.get_overload("getCount").call(None) == 0
+        assert t.get_overload("getCount")() == 0
 
         e1 = self.instantiate(t, 7)
-        assert t.get_overload("getCount").call(None) == 1
+        assert t.get_overload("getCount")() == 1
         assert e1.__python_owns__ == True
         e1.__python_owns__ = False
         e1 = None
         gc.collect()
-        assert t.get_overload("getCount").call(None) == 1
+        assert t.get_overload("getCount")() == 1
 
         # forced fix-up of object count for later tests
-        t.get_overload("setCount").call(None, 0)
+        t.get_overload("setCount")(0)
 
 
     def test06_method_double(self):
@@ -183,15 +184,15 @@
         t = self.example01
 
         e = self.instantiate(t, 13)
-        res = t.get_overload("addDataToDouble").call(e, 16)
+        res = t.get_overload("addDataToDouble")(e, 16)
         assert round(res-29, 8) == 0.
         e.__destruct__()
 
         e = self.instantiate(t, -13)
-        res = t.get_overload("addDataToDouble").call(e, 16)
+        res = t.get_overload("addDataToDouble")(e, 16)
         assert round(res-3, 8) == 0.
         e.__destruct__()
-        assert t.get_overload("getCount").call(None) == 0
+        assert t.get_overload("getCount")() == 0
 
     def test07_method_constcharp(self):
         """Test passing of a C string and returning of a C string on a
@@ -201,14 +202,14 @@
         t = self.example01
 
         e = self.instantiate(t, 42)
-        res = t.get_overload("addDataToAtoi").call(e, "13")
+        res = t.get_overload("addDataToAtoi")(e, "13")
         assert res == 55
-        res = t.get_overload("addToStringValue").call(e, "12")       # TODO: 
this leaks
+        res = t.get_overload("addToStringValue")(e, "12")       # TODO: this 
leaks
         assert res == "54"
-        res = t.get_overload("addToStringValue").call(e, "-12")      # TODO: 
this leaks
+        res = t.get_overload("addToStringValue")(e, "-12")      # TODO: this 
leaks
         assert res == "30"
         e.__destruct__()
-        assert t.get_overload("getCount").call(None) == 0
+        assert t.get_overload("getCount")() == 0
 
     def test08_pass_object_by_pointer(self):
         """Test passing of an instance as an argument."""
@@ -218,17 +219,17 @@
         t2 = self.payload
 
         pl = self.instantiate(t2, 3.14)
-        assert round(t2.get_overload("getData").call(pl)-3.14, 8) == 0
-        t1.get_overload("staticSetPayload").call(None, pl, 41.)
-        assert t2.get_overload("getData").call(pl) == 41.
+        assert round(t2.get_overload("getData")(pl)-3.14, 8) == 0
+        t1.get_overload("staticSetPayload")(pl, 41.)
+        assert t2.get_overload("getData")(pl) == 41.
 
         e = self.instantiate(t1, 50)
-        t1.get_overload("setPayload").call(e, pl);
-        assert round(t2.get_overload("getData").call(pl)-50., 8) == 0
+        t1.get_overload("setPayload")(e, pl);
+        assert round(t2.get_overload("getData")(pl)-50., 8) == 0
 
         e.__destruct__()
         pl.__destruct__() 
-        assert t1.get_overload("getCount").call(None) == 0
+        assert t1.get_overload("getCount")() == 0
 
     def test09_return_object_by_pointer(self):
         """Test returning of an instance as an argument."""
@@ -238,14 +239,14 @@
         t2 = self.payload
 
         pl1 = self.instantiate(t2, 3.14)
-        assert round(t2.get_overload("getData").call(pl1)-3.14, 8) == 0
-        pl2 = t1.get_overload("staticCyclePayload").call(None, pl1, 38.)
-        assert t2.get_overload("getData").call(pl2) == 38.
+        assert round(t2.get_overload("getData")(pl1)-3.14, 8) == 0
+        pl2 = t1.get_overload("staticCyclePayload")(pl1, 38.)
+        assert t2.get_overload("getData")(pl2) == 38.
 
         e = self.instantiate(t1, 50)
-        pl2 = t1.get_overload("cyclePayload").call(e, pl1);
-        assert round(t2.get_overload("getData").call(pl2)-50., 8) == 0
+        pl2 = t1.get_overload("cyclePayload")(e, pl1);
+        assert round(t2.get_overload("getData")(pl2)-50., 8) == 0
 
         e.__destruct__()
         pl1.__destruct__() 
-        assert t1.get_overload("getCount").call(None) == 0
+        assert t1.get_overload("getCount")() == 0
diff --git a/pypy/module/_cppyy/test/test_datatypes.py 
b/pypy/module/_cppyy/test/test_datatypes.py
--- a/pypy/module/_cppyy/test/test_datatypes.py
+++ b/pypy/module/_cppyy/test/test_datatypes.py
@@ -767,7 +767,4 @@
 
         raises(TypeError, f3, f1, 2, 3)
 
-        # TODO: get straightforward access to the overload type
-        f2 = cppyy.gbl.__cppdecl__.get_overload('sum_of_double')
-
         assert 5. == f3(f2, 5., 0.)
diff --git a/pypy/module/_cppyy/test/test_pythonify.py 
b/pypy/module/_cppyy/test/test_pythonify.py
--- a/pypy/module/_cppyy/test/test_pythonify.py
+++ b/pypy/module/_cppyy/test/test_pythonify.py
@@ -318,7 +318,7 @@
 
        _cppyy.gbl.example01.fresh = _cppyy.gbl.installableAddOneToInt
 
-       e =  _cppyy.gbl.example01(0)
+       e = _cppyy.gbl.example01(0)
        assert 2 == e.fresh(1)
        assert 3 == e.fresh(2)
 
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
@@ -268,19 +268,19 @@
         def f():
             cls  = interp_cppyy.scope_byname(space, "example01")
             inst = interp_cppyy._bind_object(space, FakeInt(0), cls, True)
-            cls.get_overload("__init__").call(inst, [FakeInt(0)])
+            cls.get_overload("__init__").descr_get(inst, []).call([FakeInt(0)])
             cppmethod = cls.get_overload(method_name)
             assert isinstance(inst, interp_cppyy.W_CPPInstance)
             i = 10
             while i > 0:
                 drv.jit_merge_point(inst=inst, cppmethod=cppmethod, i=i)
-                cppmethod.call(inst, [FakeInt(i)])
+                cppmethod.descr_get(inst, []).call([FakeInt(i)])
                 i -= 1
             return 7
         f()
         space = FakeSpace()
         result = self.meta_interp(f, [], listops=True, backendopt=True, 
listcomp=True)
-        self.check_jitcell_token_count(1)   # same for fast and slow path??
+        self.check_jitcell_token_count(0)   # same for fast and slow path??
         # rely on replacement of capi calls to raise exception instead (see 
FakeSpace.__init__)
 
     @py.test.mark.dont_track_allocations("cppmethod.cif_descr kept 'leaks'")
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to