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