Author: Wim Lavrijsen <wlavrij...@lbl.gov> Branch: Changeset: r92872:0b8528722439 Date: 2017-10-28 15:55 -0700 http://bitbucket.org/pypy/pypy/changeset/0b8528722439/
Log: merge cppyy-packaging with improved consistency for cppyy CPython <-> PyPy diff too long, truncating to 2000 out of 3147 lines diff --git a/pypy/module/_cppyy/__init__.py b/pypy/module/_cppyy/__init__.py --- a/pypy/module/_cppyy/__init__.py +++ b/pypy/module/_cppyy/__init__.py @@ -7,7 +7,7 @@ interpleveldefs = { '_resolve_name' : 'interp_cppyy.resolve_name', '_scope_byname' : 'interp_cppyy.scope_byname', - '_template_byname' : 'interp_cppyy.template_byname', + '_is_template' : 'interp_cppyy.is_template', '_std_string_name' : 'interp_cppyy.std_string_name', '_set_class_generator' : 'interp_cppyy.set_class_generator', '_set_function_generator': 'interp_cppyy.set_function_generator', @@ -15,7 +15,9 @@ '_get_nullptr' : 'interp_cppyy.get_nullptr', 'CPPClassBase' : 'interp_cppyy.W_CPPClass', 'addressof' : 'interp_cppyy.addressof', + '_bind_object' : 'interp_cppyy._bind_object', 'bind_object' : 'interp_cppyy.bind_object', + 'move' : 'interp_cppyy.move', } appleveldefs = { 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 @@ -217,7 +217,8 @@ '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_ccharp), + 'method_signature' : ([c_scope, c_index, c_int], c_ccharp), + 'method_prototype' : ([c_scope, c_index, c_int], c_ccharp), 'method_is_template' : ([c_scope, c_index], c_int), 'method_num_template_args' : ([c_scope, c_index], c_int), @@ -498,9 +499,12 @@ def c_method_arg_default(space, cppscope, index, arg_index): args = [_ArgH(cppscope.handle), _ArgL(index), _ArgL(arg_index)] return charp2str_free(space, call_capi(space, 'method_arg_default', args)) -def c_method_signature(space, cppscope, index): - args = [_ArgH(cppscope.handle), _ArgL(index)] +def c_method_signature(space, cppscope, index, show_formalargs=True): + args = [_ArgH(cppscope.handle), _ArgL(index), _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)] + return charp2str_free(space, call_capi(space, 'method_prototype', args)) def c_method_is_template(space, cppscope, index): args = [_ArgH(cppscope.handle), _ArgL(index)] 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 @@ -4,7 +4,7 @@ from rpython.rtyper.lltypesystem import rffi, lltype from rpython.rlib.rarithmetic import r_singlefloat, r_longfloat -from rpython.rlib import rfloat +from rpython.rlib import rfloat, rawrefcount from pypy.module._rawffi.interp_rawffi import letter2tp from pypy.module._rawffi.array import W_Array, W_ArrayInstance @@ -21,9 +21,9 @@ # match for the qualified type. -def get_rawobject(space, w_obj): +def get_rawobject(space, w_obj, can_be_None=True): from pypy.module._cppyy.interp_cppyy import W_CPPClass - cppinstance = space.interp_w(W_CPPClass, w_obj, can_be_None=True) + cppinstance = space.interp_w(W_CPPClass, w_obj, can_be_None=can_be_None) if cppinstance: rawobject = cppinstance.get_rawobject() assert lltype.typeOf(rawobject) == capi.C_OBJECT @@ -48,17 +48,16 @@ return capi.C_NULL_OBJECT def is_nullpointer_specialcase(space, w_obj): - # 0, None, and nullptr may serve as "NULL", check for any of them + # 0 and nullptr may serve as "NULL" # integer 0 try: return space.int_w(w_obj) == 0 except Exception: pass - # None or nullptr + # C++-style nullptr from pypy.module._cppyy import interp_cppyy - return space.is_true(space.is_(w_obj, space.w_None)) or \ - space.is_true(space.is_(w_obj, interp_cppyy.get_nullptr(space))) + return space.is_true(space.is_(w_obj, interp_cppyy.get_nullptr(space))) def get_rawbuffer(space, w_obj): # raw buffer @@ -74,7 +73,7 @@ return rffi.cast(rffi.VOIDP, space.uint_w(arr.getbuffer(space))) except Exception: pass - # pre-defined NULL + # pre-defined nullptr if is_nullpointer_specialcase(space, w_obj): return rffi.cast(rffi.VOIDP, 0) raise TypeError("not an addressable buffer") @@ -392,6 +391,7 @@ _immutable_fields_ = ['typecode'] typecode = 'g' + class CStringConverter(TypeConverter): def convert_argument(self, space, w_obj, address, call_local): x = rffi.cast(rffi.LONGP, address) @@ -408,18 +408,27 @@ def free_argument(self, space, arg, call_local): lltype.free(rffi.cast(rffi.CCHARPP, arg)[0], flavor='raw') +class CStringConverterWithSize(CStringConverter): + _immutable_fields_ = ['size'] + + def __init__(self, space, extra): + self.size = extra + + def from_memory(self, space, w_obj, w_pycppclass, offset): + address = self._get_raw_address(space, w_obj, offset) + charpptr = rffi.cast(rffi.CCHARP, address) + strsize = self.size + if charpptr[self.size-1] == '\0': + strsize = self.size-1 # rffi will add \0 back + return space.newbytes(rffi.charpsize2str(charpptr, strsize)) + class VoidPtrConverter(TypeConverter): def _unwrap_object(self, space, w_obj): try: obj = get_rawbuffer(space, w_obj) except TypeError: - try: - # TODO: accept a 'capsule' rather than naked int - # (do accept int(0), though) - obj = rffi.cast(rffi.VOIDP, space.uint_w(w_obj)) - except Exception: - obj = rffi.cast(rffi.VOIDP, get_rawobject(space, w_obj)) + obj = rffi.cast(rffi.VOIDP, get_rawobject(space, w_obj, False)) return obj def cffi_type(self, space): @@ -463,12 +472,12 @@ def convert_argument(self, space, w_obj, address, call_local): x = rffi.cast(rffi.VOIDPP, address) ba = rffi.cast(rffi.CCHARP, address) - r = rffi.cast(rffi.VOIDPP, call_local) try: - r[0] = get_rawbuffer(space, w_obj) + x[0] = get_rawbuffer(space, w_obj) except TypeError: + r = rffi.cast(rffi.VOIDPP, call_local) r[0] = rffi.cast(rffi.VOIDP, get_rawobject(space, w_obj)) - x[0] = rffi.cast(rffi.VOIDP, call_local) + x[0] = rffi.cast(rffi.VOIDP, call_local) ba[capi.c_function_arg_typeoffset(space)] = self.typecode def finalize_call(self, space, w_obj, call_local): @@ -495,9 +504,13 @@ def _unwrap_object(self, space, w_obj): from pypy.module._cppyy.interp_cppyy import W_CPPClass if isinstance(w_obj, W_CPPClass): - if capi.c_is_subtype(space, w_obj.cppclass, self.clsdecl): + from pypy.module._cppyy.interp_cppyy import INSTANCE_FLAGS_IS_R_VALUE + if w_obj.flags & INSTANCE_FLAGS_IS_R_VALUE: + # reject moves as all are explicit + raise ValueError("lvalue expected") + if capi.c_is_subtype(space, w_obj.clsdecl, self.clsdecl): rawobject = w_obj.get_rawobject() - offset = capi.c_base_offset(space, w_obj.cppclass, self.clsdecl, rawobject, 1) + offset = capi.c_base_offset(space, w_obj.clsdecl, self.clsdecl, rawobject, 1) obj_address = capi.direct_ptradd(rawobject, offset) return rffi.cast(capi.C_OBJECT, obj_address) raise oefmt(space.w_TypeError, @@ -518,6 +531,17 @@ x = rffi.cast(rffi.VOIDPP, address) x[0] = rffi.cast(rffi.VOIDP, self._unwrap_object(space, w_obj)) +class InstanceMoveConverter(InstanceRefConverter): + def _unwrap_object(self, space, w_obj): + # moving is same as by-ref, but have to check that move is allowed + from pypy.module._cppyy.interp_cppyy import W_CPPClass, INSTANCE_FLAGS_IS_R_VALUE + if isinstance(w_obj, W_CPPClass): + if w_obj.flags & INSTANCE_FLAGS_IS_R_VALUE: + w_obj.flags &= ~INSTANCE_FLAGS_IS_R_VALUE + return InstanceRefConverter._unwrap_object(self, space, w_obj) + raise oefmt(space.w_ValueError, "object is not an rvalue") + + class InstanceConverter(InstanceRefConverter): def convert_argument_libffi(self, space, w_obj, address, call_local): @@ -527,7 +551,7 @@ def from_memory(self, space, w_obj, w_pycppclass, offset): address = rffi.cast(capi.C_OBJECT, self._get_raw_address(space, w_obj, offset)) from pypy.module._cppyy import interp_cppyy - return interp_cppyy.wrap_cppobject(space, address, self.clsdecl, do_cast=False) + return interp_cppyy.wrap_cppinstance(space, address, self.clsdecl, do_cast=False) def to_memory(self, space, w_obj, w_value, offset): self._is_abstract(space) @@ -548,7 +572,7 @@ def from_memory(self, space, w_obj, w_pycppclass, offset): address = rffi.cast(capi.C_OBJECT, self._get_raw_address(space, w_obj, offset)) from pypy.module._cppyy import interp_cppyy - return interp_cppyy.wrap_cppobject(space, address, self.clsdecl, do_cast=False) + return interp_cppyy.wrap_cppinstance(space, address, self.clsdecl, do_cast=False) def to_memory(self, space, w_obj, w_value, offset): address = rffi.cast(rffi.VOIDPP, self._get_raw_address(space, w_obj, offset)) @@ -582,8 +606,8 @@ def from_memory(self, space, w_obj, w_pycppclass, offset): address = rffi.cast(capi.C_OBJECT, self._get_raw_address(space, w_obj, offset)) from pypy.module._cppyy import interp_cppyy - return interp_cppyy.wrap_cppobject(space, address, self.clsdecl, - do_cast=False, is_ref=True) + return interp_cppyy.wrap_cppinstance( + space, address, self.clsdecl, do_cast=False, is_ref=True) class StdStringConverter(InstanceConverter): @@ -606,7 +630,7 @@ assign = self.clsdecl.get_overload("__assign__") from pypy.module._cppyy import interp_cppyy assign.call( - interp_cppyy.wrap_cppobject(space, address, self.clsdecl, do_cast=False), [w_value]) + interp_cppyy.wrap_cppinstance(space, address, self.clsdecl, do_cast=False), [w_value]) except Exception: InstanceConverter.to_memory(self, space, w_obj, w_value, offset) @@ -672,7 +696,7 @@ _converters = {} # builtin and custom types _a_converters = {} # array and ptr versions of above -def get_converter(space, name, default): +def get_converter(space, _name, default): # The matching of the name to a converter should follow: # 1) full, exact match # 1a) const-removed match @@ -680,9 +704,9 @@ # 3) accept ref as pointer (for the stubs, const& can be # by value, but that does not work for the ffi path) # 4) generalized cases (covers basically all user classes) - # 5) void converter, which fails on use + # 5) void* or void converter (which fails on use) - name = capi.c_resolve_name(space, name) + name = capi.c_resolve_name(space, _name) # 1) full, exact match try: @@ -701,7 +725,7 @@ clean_name = capi.c_resolve_name(space, helper.clean_type(name)) try: # array_index may be negative to indicate no size or no size found - array_size = helper.array_size(name) + array_size = helper.array_size(_name) # uses original arg return _a_converters[clean_name+compound](space, array_size) except KeyError: pass @@ -719,6 +743,8 @@ return InstancePtrConverter(space, clsdecl) elif compound == "&": return InstanceRefConverter(space, clsdecl) + elif compound == "&&": + return InstanceMoveConverter(space, clsdecl) elif compound == "**": return InstancePtrPtrConverter(space, clsdecl) elif compound == "": @@ -726,11 +752,13 @@ elif capi.c_is_enum(space, clean_name): return _converters['unsigned'](space, default) - # 5) void converter, which fails on use - # + # 5) void* or void converter (which fails on use) + if 0 <= compound.find('*'): + return VoidPtrConverter(space, default) # "user knows best" + # return a void converter here, so that the class can be build even - # when some types are unknown; this overload will simply fail on use - return VoidConverter(space, name) + # when some types are unknown + return VoidConverter(space, name) # fails on use _converters["bool"] = BoolConverter @@ -847,6 +875,10 @@ for name in names: _a_converters[name+'[]'] = ArrayConverter _a_converters[name+'*'] = PtrConverter + + # special case, const char* w/ size and w/o '\0' + _a_converters["const char[]"] = CStringConverterWithSize + _build_array_converters() # add another set of aliased names diff --git a/pypy/module/_cppyy/executor.py b/pypy/module/_cppyy/executor.py --- a/pypy/module/_cppyy/executor.py +++ b/pypy/module/_cppyy/executor.py @@ -159,7 +159,7 @@ from pypy.module._cppyy import interp_cppyy long_result = capi.c_call_l(space, cppmethod, cppthis, num_args, args) ptr_result = rffi.cast(capi.C_OBJECT, long_result) - pyres = interp_cppyy.wrap_cppobject(space, ptr_result, self.cppclass) + pyres = interp_cppyy.wrap_cppinstance(space, ptr_result, self.cppclass) return pyres def execute_libffi(self, space, cif_descr, funcaddr, buffer): @@ -167,7 +167,7 @@ result = rffi.ptradd(buffer, cif_descr.exchange_result) from pypy.module._cppyy import interp_cppyy ptr_result = rffi.cast(capi.C_OBJECT, rffi.cast(rffi.VOIDPP, result)[0]) - return interp_cppyy.wrap_cppobject(space, ptr_result, self.cppclass) + return interp_cppyy.wrap_cppinstance(space, ptr_result, self.cppclass) class InstancePtrPtrExecutor(InstancePtrExecutor): @@ -176,7 +176,7 @@ voidp_result = capi.c_call_r(space, cppmethod, cppthis, num_args, args) ref_address = rffi.cast(rffi.VOIDPP, voidp_result) ptr_result = rffi.cast(capi.C_OBJECT, ref_address[0]) - return interp_cppyy.wrap_cppobject(space, ptr_result, self.cppclass) + return interp_cppyy.wrap_cppinstance(space, ptr_result, self.cppclass) def execute_libffi(self, space, cif_descr, funcaddr, buffer): from pypy.module._cppyy.interp_cppyy import FastCallNotPossible @@ -188,8 +188,8 @@ from pypy.module._cppyy import interp_cppyy long_result = capi.c_call_o(space, cppmethod, cppthis, num_args, args, self.cppclass) ptr_result = rffi.cast(capi.C_OBJECT, long_result) - return interp_cppyy.wrap_cppobject(space, ptr_result, self.cppclass, - do_cast=False, python_owns=True, fresh=True) + return interp_cppyy.wrap_cppinstance(space, ptr_result, self.cppclass, + do_cast=False, python_owns=True, fresh=True) def execute_libffi(self, space, cif_descr, funcaddr, buffer): from pypy.module._cppyy.interp_cppyy import FastCallNotPossible 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 @@ -19,14 +19,15 @@ RPY_EXTERN int cppyy_num_scopes(cppyy_scope_t parent); RPY_EXTERN - char* cppyy_scope_name(cppyy_scope_t parent, int iscope); - + char* cppyy_scope_name(cppyy_scope_t parent, cppyy_index_t iscope); RPY_EXTERN char* cppyy_resolve_name(const char* cppitem_name); RPY_EXTERN cppyy_scope_t cppyy_get_scope(const char* scope_name); RPY_EXTERN cppyy_type_t cppyy_actual_class(cppyy_type_t klass, cppyy_object_t obj); + RPY_EXTERN + size_t cppyy_size_of(cppyy_type_t klass); /* memory management ------------------------------------------------------ */ RPY_EXTERN @@ -120,6 +121,8 @@ RPY_EXTERN char* cppyy_method_name(cppyy_scope_t scope, cppyy_index_t idx); RPY_EXTERN + char* cppyy_method_mangled_name(cppyy_scope_t scope, cppyy_index_t idx); + RPY_EXTERN char* cppyy_method_result_type(cppyy_scope_t scope, cppyy_index_t idx); RPY_EXTERN int cppyy_method_num_args(cppyy_scope_t scope, cppyy_index_t idx); @@ -130,7 +133,9 @@ RPY_EXTERN char* cppyy_method_arg_default(cppyy_scope_t scope, cppyy_index_t idx, int arg_index); RPY_EXTERN - char* cppyy_method_signature(cppyy_scope_t scope, cppyy_index_t idx); + char* cppyy_method_signature(cppyy_scope_t scope, cppyy_index_t idx, int show_formalargs); + RPY_EXTERN + char* cppyy_method_prototype(cppyy_scope_t scope, cppyy_index_t idx, int show_formalargs); RPY_EXTERN int cppyy_method_is_template(cppyy_scope_t scope, cppyy_index_t idx); @@ -147,8 +152,12 @@ /* method properties ------------------------------------------------------ */ RPY_EXTERN + int cppyy_is_publicmethod(cppyy_type_t type, cppyy_index_t idx); + RPY_EXTERN int cppyy_is_constructor(cppyy_type_t type, cppyy_index_t idx); RPY_EXTERN + int cppyy_is_destructor(cppyy_type_t type, cppyy_index_t idx); + RPY_EXTERN int cppyy_is_staticmethod(cppyy_type_t type, cppyy_index_t idx); /* data member reflection information ------------------------------------- */ 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 @@ -2,7 +2,7 @@ from pypy.interpreter.error import OperationError, oefmt from pypy.interpreter.gateway import interp2app, unwrap_spec -from pypy.interpreter.typedef import TypeDef, GetSetProperty, interp_attrproperty, interp_attrproperty_w +from pypy.interpreter.typedef import TypeDef, GetSetProperty, interp_attrproperty from pypy.interpreter.baseobjspace import W_Root from rpython.rtyper.lltypesystem import rffi, lltype, llmemory @@ -15,6 +15,10 @@ from pypy.module._cppyy import converter, executor, ffitypes, helper +INSTANCE_FLAGS_PYTHON_OWNS = 0x0001 +INSTANCE_FLAGS_IS_REF = 0x0002 +INSTANCE_FLAGS_IS_R_VALUE = 0x0004 + class FastCallNotPossible(Exception): pass @@ -33,16 +37,21 @@ class State(object): def __init__(self, space): + # final scoped name -> opaque handle self.cppscope_cache = { - "void" : W_CPPClassDecl(space, "void", capi.C_NULL_TYPE) } + 'void' : W_CPPClassDecl(space, capi.C_NULL_TYPE, 'void') } + # opaque handle -> app-level python class + self.cppclass_registry = {} + # app-level class generator callback + self.w_clgen_callback = None + # app-level function generator callback (currently not used) + self.w_fngen_callback = None + # C++11's nullptr self.w_nullptr = None - self.cpptemplate_cache = {} - self.cppclass_registry = {} - self.w_clgen_callback = None - self.w_fngen_callback = None def get_nullptr(space): - if hasattr(space, "fake"): + # construct a unique address that compares to NULL, serves as nullptr + if hasattr(space, 'fake'): raise NotImplementedError state = space.fromcache(State) if state.w_nullptr is None: @@ -58,52 +67,48 @@ state.w_nullptr = nullarr return state.w_nullptr -@unwrap_spec(name='text') -def resolve_name(space, name): - return space.newtext(capi.c_resolve_name(space, name)) +@unwrap_spec(scoped_name='text') +def resolve_name(space, scoped_name): + return space.newtext(capi.c_resolve_name(space, scoped_name)) -@unwrap_spec(name='text') -def scope_byname(space, name): - true_name = capi.c_resolve_name(space, name) +# memoized lookup of handles by final, scoped, name of classes/namespaces +@unwrap_spec(final_scoped_name='text') +def scope_byname(space, final_scoped_name): state = space.fromcache(State) try: - return state.cppscope_cache[true_name] + return state.cppscope_cache[final_scoped_name] except KeyError: pass - opaque_handle = capi.c_get_scope_opaque(space, true_name) + opaque_handle = capi.c_get_scope_opaque(space, final_scoped_name) assert lltype.typeOf(opaque_handle) == capi.C_SCOPE if opaque_handle: - final_name = capi.c_final_name(space, opaque_handle) - if capi.c_is_namespace(space, opaque_handle): - cppscope = W_CPPNamespaceDecl(space, final_name, opaque_handle) - elif capi.c_has_complex_hierarchy(space, opaque_handle): - cppscope = W_CPPComplexClassDecl(space, final_name, opaque_handle) + isns = capi.c_is_namespace(space, opaque_handle) + if isns: + cppscope = W_CPPNamespaceDecl(space, opaque_handle, final_scoped_name) else: - cppscope = W_CPPClassDecl(space, final_name, opaque_handle) - state.cppscope_cache[name] = cppscope + if capi.c_has_complex_hierarchy(space, opaque_handle): + cppscope = W_CPPComplexClassDecl(space, opaque_handle, final_scoped_name) + else: + cppscope = W_CPPClassDecl(space, opaque_handle, final_scoped_name) - cppscope._build_methods() - cppscope._find_datamembers() + # store in the cache to prevent recursion + state.cppscope_cache[final_scoped_name] = cppscope + + if not isns: + # build methods/data; TODO: also defer this for classes (a functional __dir__ + # and instrospection for help() is enough and allows more lazy loading) + cppscope._build_methods() + cppscope._find_datamembers() + return cppscope return None -@unwrap_spec(name='text') -def template_byname(space, name): - state = space.fromcache(State) - try: - return state.cpptemplate_cache[name] - except KeyError: - pass - - if capi.c_is_template(space, name): - cpptemplate = W_CPPTemplateType(space, name) - state.cpptemplate_cache[name] = cpptemplate - return cpptemplate - - return None +@unwrap_spec(final_scoped_name='text') +def is_template(space, final_scoped_name): + return space.newbool(capi.c_is_template(space, final_scoped_name)) def std_string_name(space): return space.newtext(capi.std_string_name) @@ -189,8 +194,13 @@ # check number of given arguments against required (== total - defaults) args_expected = len(self.arg_defs) args_given = len(args_w) - if args_expected < args_given or args_given < self.args_required: - raise oefmt(self.space.w_TypeError, "wrong number of arguments") + + if args_given < self.args_required: + raise oefmt(self.space.w_TypeError, + "takes at least %d arguments (%d given)", self.args_required, args_given) + elif args_expected < args_given: + raise oefmt(self.space.w_TypeError, + "takes at most %d arguments (%d given)", args_expected, args_given) # initial setup of converters, executors, and libffi (if available) if self.converters is None: @@ -376,8 +386,11 @@ conv.free_argument(self.space, rffi.cast(capi.C_OBJECT, arg_i), loc_i) capi.c_deallocate_function_args(self.space, args) - def signature(self): - return capi.c_method_signature(self.space, self.scope, self.index) + def signature(self, show_formalargs=True): + return capi.c_method_signature(self.space, self.scope, self.index, show_formalargs) + + def prototype(self, show_formalargs=True): + return capi.c_method_prototype(self.space, self.scope, self.index, show_formalargs) def priority(self): total_arg_priority = 0 @@ -391,7 +404,7 @@ lltype.free(self.cif_descr, flavor='raw') def __repr__(self): - return "CPPMethod: %s" % self.signature() + return "CPPMethod: %s" % self.prototype() def _freeze_(self): assert 0, "you should never have a pre-built instance of this!" @@ -407,7 +420,7 @@ return capi.C_NULL_OBJECT def __repr__(self): - return "CPPFunction: %s" % self.signature() + return "CPPFunction: %s" % self.prototype() class CPPTemplatedCall(CPPMethod): @@ -440,7 +453,7 @@ return CPPMethod.call(self, cppthis, args_w) def __repr__(self): - return "CPPTemplatedCall: %s" % self.signature() + return "CPPTemplatedCall: %s" % self.prototype() class CPPConstructor(CPPMethod): @@ -462,7 +475,7 @@ return CPPMethod.call(self, cppthis, args_w) def __repr__(self): - return "CPPConstructor: %s" % self.signature() + return "CPPConstructor: %s" % self.prototype() class CPPSetItem(CPPMethod): @@ -549,12 +562,12 @@ w_exc_type = e.w_type elif all_same_type and not e.match(self.space, w_exc_type): all_same_type = False - errmsg += '\n '+cppyyfunc.signature()+' =>\n' + errmsg += '\n '+cppyyfunc.prototype()+' =>\n' errmsg += ' '+e.errorstr(self.space) except Exception as e: # can not special case this for non-overloaded functions as we anyway need an # OperationError error down from here - errmsg += '\n '+cppyyfunc.signature()+' =>\n' + errmsg += '\n '+cppyyfunc.prototype()+' =>\n' errmsg += ' Exception: '+str(e) if all_same_type and w_exc_type is not None: @@ -562,20 +575,20 @@ else: raise OperationError(self.space.w_TypeError, self.space.newtext(errmsg)) - def signature(self): - sig = self.functions[0].signature() + def prototype(self): + sig = self.functions[0].prototype() for i in range(1, len(self.functions)): - sig += '\n'+self.functions[i].signature() + sig += '\n'+self.functions[i].prototype() return self.space.newtext(sig) def __repr__(self): - return "W_CPPOverload(%s)" % [f.signature() for f in self.functions] + return "W_CPPOverload(%s)" % [f.prototype() for f in self.functions] W_CPPOverload.typedef = TypeDef( 'CPPOverload', is_static = interp2app(W_CPPOverload.is_static), call = interp2app(W_CPPOverload.call), - signature = interp2app(W_CPPOverload.signature), + prototype = interp2app(W_CPPOverload.prototype), ) @@ -591,24 +604,40 @@ @jit.unroll_safe @unwrap_spec(args_w='args_w') def call(self, w_cppinstance, 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) newthis = rffi.cast(capi.C_OBJECT, self.space.uint_w(w_result)) cppinstance = self.space.interp_w(W_CPPClass, w_cppinstance, can_be_None=True) if cppinstance is not None: cppinstance._rawobject = newthis memory_regulator.register(cppinstance) - return w_cppinstance - return wrap_cppobject(self.space, newthis, self.functions[0].scope, - do_cast=False, python_owns=True, fresh=True) def __repr__(self): - return "W_CPPConstructorOverload(%s)" % [f.signature() for f in self.functions] + return "W_CPPConstructorOverload(%s)" % [f.prototype() for f in self.functions] W_CPPConstructorOverload.typedef = TypeDef( 'CPPConstructorOverload', is_static = interp2app(W_CPPConstructorOverload.is_static), call = interp2app(W_CPPConstructorOverload.call), - signature = interp2app(W_CPPOverload.signature), + prototype = interp2app(W_CPPConstructorOverload.prototype), +) + + +class W_CPPTemplateOverload(W_CPPOverload): + @unwrap_spec(args_w='args_w') + def __getitem__(self, args_w): + pass + + def __repr__(self): + return "W_CPPTemplateOverload(%s)" % [f.prototype() for f in self.functions] + +W_CPPTemplateOverload.typedef = TypeDef( + 'CPPTemplateOverload', + __getitem__ = interp2app(W_CPPTemplateOverload.call), ) @@ -622,6 +651,9 @@ def __call__(self, args_w): return self.method.bound_call(self.cppthis, args_w) + def __repr__(self): + return "W_CPPBoundMethod(%s)" % [f.prototype() for f in self.functions] + W_CPPBoundMethod.typedef = TypeDef( 'CPPBoundMethod', __call__ = interp2app(W_CPPBoundMethod.__call__), @@ -643,8 +675,8 @@ def _get_offset(self, cppinstance): if cppinstance: - assert lltype.typeOf(cppinstance.cppclass.handle) == lltype.typeOf(self.scope.handle) - offset = self.offset + cppinstance.cppclass.get_base_offset(cppinstance, self.scope) + assert lltype.typeOf(cppinstance.clsdecl.handle) == lltype.typeOf(self.scope.handle) + offset = self.offset + cppinstance.clsdecl.get_base_offset(cppinstance, self.scope) else: offset = self.offset return offset @@ -652,7 +684,7 @@ def get(self, w_cppinstance, w_pycppclass): cppinstance = self.space.interp_w(W_CPPClass, w_cppinstance, can_be_None=True) if not cppinstance: - raise oefmt(self.space.w_ReferenceError, + raise oefmt(self.space.w_AttributeError, "attribute access requires an instance") offset = self._get_offset(cppinstance) return self.converter.from_memory(self.space, w_cppinstance, w_pycppclass, offset) @@ -660,7 +692,7 @@ def set(self, w_cppinstance, w_value): cppinstance = self.space.interp_w(W_CPPClass, w_cppinstance, can_be_None=True) if not cppinstance: - raise oefmt(self.space.w_ReferenceError, + raise oefmt(self.space.w_AttributeError, "attribute access requires an instance") offset = self._get_offset(cppinstance) self.converter.to_memory(self.space, w_cppinstance, w_value, offset) @@ -705,12 +737,12 @@ return space.w_False class W_CPPScopeDecl(W_Root): - _attrs_ = ['space', 'name', 'handle', 'methods', 'datamembers'] + _attrs_ = ['space', 'handle', 'name', 'methods', 'datamembers'] _immutable_fields_ = ['handle', 'name'] - def __init__(self, space, name, opaque_handle): + def __init__(self, space, opaque_handle, final_scoped_name): self.space = space - self.name = name + self.name = final_scoped_name assert lltype.typeOf(opaque_handle) == capi.C_SCOPE self.handle = opaque_handle self.methods = {} @@ -753,7 +785,7 @@ overload = self.get_overload(name) sig = '(%s)' % signature for f in overload.functions: - if 0 < f.signature().find(sig): + if f.signature(False) == sig: return W_CPPOverload(self.space, self, [f]) raise oefmt(self.space.w_LookupError, "no overload matches signature") @@ -769,6 +801,9 @@ # classes for inheritance. Both are python classes, though, and refactoring # may be in order at some point. class W_CPPNamespaceDecl(W_CPPScopeDecl): + _attrs_ = ['space', 'handle', 'name', 'methods', 'datamembers'] + _immutable_fields_ = ['handle', 'name'] + def _make_cppfunction(self, pyname, index): num_args = capi.c_method_num_args(self.space, self, index) args_required = capi.c_method_req_args(self.space, self, index) @@ -779,9 +814,6 @@ arg_defs.append((arg_type, arg_dflt)) return CPPFunction(self.space, self, index, arg_defs, args_required) - def _build_methods(self): - pass # force lazy lookups in namespaces - def _make_datamember(self, dm_name, dm_idx): type_name = capi.c_datamember_type(self.space, self, dm_idx) offset = capi.c_datamember_offset(self.space, self, dm_idx) @@ -791,9 +823,6 @@ self.datamembers[dm_name] = datamember return datamember - def _find_datamembers(self): - pass # force lazy lookups in namespaces - def find_overload(self, meth_name): indices = capi.c_method_indices_from_name(self.space, self, meth_name) if not indices: @@ -855,18 +884,21 @@ class W_CPPClassDecl(W_CPPScopeDecl): - _attrs_ = ['space', 'name', 'handle', 'methods', 'datamembers'] - _immutable_fields_ = ['handle', 'constructor', 'methods[*]', 'datamembers[*]'] + _attrs_ = ['space', 'handle', 'name', 'methods', 'datamembers'] + _immutable_fields_ = ['handle', 'name', 'methods[*]', 'datamembers[*]'] def _build_methods(self): assert len(self.methods) == 0 methods_temp = {} for i in range(capi.c_num_methods(self.space, self)): idx = capi.c_method_index_at(self.space, self, i) - 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)) + if capi.c_is_constructor(self.space, self, idx): + 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)) cppmethod = self._make_cppfunction(pyname, idx) methods_temp.setdefault(pyname, []).append(cppmethod) # the following covers the case where the only kind of operator[](idx) @@ -883,7 +915,7 @@ # create the overload methods from the method sets for pyname, methods in methods_temp.iteritems(): CPPMethodSort(methods).sort() - if pyname == self.name: + if pyname == '__init__': overload = W_CPPConstructorOverload(self.space, self, methods[:]) else: overload = W_CPPOverload(self.space, self, methods[:]) @@ -934,11 +966,11 @@ raise self.missing_attribute_error(name) def get_base_offset(self, cppinstance, calling_scope): - assert self == cppinstance.cppclass + assert self == cppinstance.clsdecl return 0 def get_cppthis(self, cppinstance, calling_scope): - assert self == cppinstance.cppclass + assert self == cppinstance.clsdecl return cppinstance.get_rawobject() def is_namespace(self): @@ -973,13 +1005,13 @@ class W_CPPComplexClassDecl(W_CPPClassDecl): def get_base_offset(self, cppinstance, calling_scope): - assert self == cppinstance.cppclass + assert self == cppinstance.clsdecl offset = capi.c_base_offset(self.space, self, calling_scope, cppinstance.get_rawobject(), 1) return offset def get_cppthis(self, cppinstance, calling_scope): - assert self == cppinstance.cppclass + assert self == cppinstance.clsdecl offset = self.get_base_offset(cppinstance, calling_scope) return capi.direct_ptradd(cppinstance.get_rawobject(), offset) @@ -997,70 +1029,56 @@ W_CPPComplexClassDecl.typedef.acceptable_as_base_class = False -class W_CPPTemplateType(W_Root): - _attrs_ = ['space', 'name'] - _immutable_fields = ['name'] - - def __init__(self, space, name): - self.space = space - self.name = name - - @unwrap_spec(args_w='args_w') - def __call__(self, args_w): - # TODO: this is broken but unused (see pythonify.py) - fullname = "".join([self.name, '<', self.space.text_w(args_w[0]), '>']) - return scope_byname(self.space, fullname) - -W_CPPTemplateType.typedef = TypeDef( - 'CPPTemplateType', - __call__ = interp2app(W_CPPTemplateType.__call__), -) -W_CPPTemplateType.typedef.acceptable_as_base_class = False - - class W_CPPClass(W_Root): - _attrs_ = ['space', 'cppclass', '_rawobject', 'isref', 'python_owns', + _attrs_ = ['space', 'clsdecl', '_rawobject', 'flags', 'finalizer_registered'] - _immutable_fields_ = ["cppclass", "isref"] + _immutable_fields_ = ['clsdecl'] finalizer_registered = False - def __init__(self, space, cppclass, rawobject, isref, python_owns): + def __init__(self, space, decl, rawobject, isref, python_owns): self.space = space - self.cppclass = cppclass + self.clsdecl = decl assert lltype.typeOf(rawobject) == capi.C_OBJECT assert not isref or rawobject self._rawobject = rawobject assert not isref or not python_owns - self.isref = isref - self.python_owns = python_owns - self._opt_register_finalizer() + self.flags = 0 + if isref: + self.flags |= INSTANCE_FLAGS_IS_REF + if python_owns: + self.flags |= INSTANCE_FLAGS_PYTHON_OWNS + self._opt_register_finalizer() def _opt_register_finalizer(self): - if self.python_owns and not self.finalizer_registered \ - and not hasattr(self.space, "fake"): + if not self.finalizer_registered and not hasattr(self.space, "fake"): + assert self.flags & INSTANCE_FLAGS_PYTHON_OWNS self.register_finalizer(self.space) self.finalizer_registered = True def _nullcheck(self): - if not self._rawobject or (self.isref and not self.get_rawobject()): + if not self._rawobject or \ + ((self.flags & INSTANCE_FLAGS_IS_REF) and not self.get_rawobject()): raise oefmt(self.space.w_ReferenceError, "trying to access a NULL pointer") # allow user to determine ownership rules on a per object level def fget_python_owns(self, space): - return space.newbool(self.python_owns) + return space.newbool(bool(self.flags & INSTANCE_FLAGS_PYTHON_OWNS)) @unwrap_spec(value=bool) def fset_python_owns(self, space, value): - self.python_owns = space.is_true(value) - self._opt_register_finalizer() + if space.is_true(value): + self.flags |= INSTANCE_FLAGS_PYTHON_OWNS + self._opt_register_finalizer() + else: + self.flags &= ~INSTANCE_FLAGS_PYTHON_OWNS def get_cppthis(self, calling_scope): - return self.cppclass.get_cppthis(self, calling_scope) + return self.clsdecl.get_cppthis(self, calling_scope) def get_rawobject(self): - if not self.isref: + if not (self.flags & INSTANCE_FLAGS_IS_REF): return self._rawobject else: ptrptr = rffi.cast(rffi.VOIDPP, self._rawobject) @@ -1078,12 +1096,9 @@ return None def instance__init__(self, args_w): - if capi.c_is_abstract(self.space, self.cppclass.handle): - raise oefmt(self.space.w_TypeError, - "cannot instantiate abstract class '%s'", - self.cppclass.name) - constructor_overload = self.cppclass.get_overload(self.cppclass.name) - constructor_overload.call(self, args_w) + raise oefmt(self.space.w_TypeError, + "cannot instantiate abstract class '%s'", + self.clsdecl.name) def instance__eq__(self, w_other): # special case: if other is None, compare pointer-style @@ -1099,7 +1114,7 @@ for name in ["", "__gnu_cxx", "__1"]: nss = scope_byname(self.space, name) meth_idx = capi.c_get_global_operator( - self.space, nss, self.cppclass, other.cppclass, "operator==") + self.space, nss, self.clsdecl, other.clsdecl, "operator==") if meth_idx != -1: f = nss._make_cppfunction("operator==", meth_idx) ol = W_CPPOverload(self.space, nss, [f]) @@ -1118,14 +1133,15 @@ # fallback 2: direct pointer comparison (the class comparison is needed since # the first data member in a struct and the struct have the same address) other = self.space.interp_w(W_CPPClass, w_other, can_be_None=False) # TODO: factor out - iseq = (self._rawobject == other._rawobject) and (self.cppclass == other.cppclass) + iseq = (self._rawobject == other._rawobject) and (self.clsdecl == other.clsdecl) return self.space.newbool(iseq) def instance__ne__(self, w_other): return self.space.not_(self.instance__eq__(w_other)) def instance__nonzero__(self): - if not self._rawobject or (self.isref and not self.get_rawobject()): + if not self._rawobject or \ + ((self.flags & INSTANCE_FLAGS_IS_REF) and not self.get_rawobject()): return self.space.w_False return self.space.w_True @@ -1134,36 +1150,35 @@ if w_as_builtin is not None: return self.space.len(w_as_builtin) raise oefmt(self.space.w_TypeError, - "'%s' has no length", self.cppclass.name) + "'%s' has no length", self.clsdecl.name) def instance__cmp__(self, w_other): w_as_builtin = self._get_as_builtin() if w_as_builtin is not None: return self.space.cmp(w_as_builtin, w_other) raise oefmt(self.space.w_AttributeError, - "'%s' has no attribute __cmp__", self.cppclass.name) + "'%s' has no attribute __cmp__", self.clsdecl.name) def instance__repr__(self): w_as_builtin = self._get_as_builtin() if w_as_builtin is not None: return self.space.repr(w_as_builtin) return self.space.newtext("<%s object at 0x%x>" % - (self.cppclass.name, rffi.cast(rffi.ULONG, self.get_rawobject()))) + (self.clsdecl.name, rffi.cast(rffi.ULONG, self.get_rawobject()))) def destruct(self): - if self._rawobject and not self.isref: + if self._rawobject and not (self.flags & INSTANCE_FLAGS_IS_REF): memory_regulator.unregister(self) - capi.c_destruct(self.space, self.cppclass, self._rawobject) + capi.c_destruct(self.space, self.clsdecl, self._rawobject) self._rawobject = capi.C_NULL_OBJECT def _finalize_(self): - if self.python_owns: + if self.flags & INSTANCE_FLAGS_PYTHON_OWNS: self.destruct() W_CPPClass.typedef = TypeDef( 'CPPClass', - cppclass = interp_attrproperty_w('cppclass', cls=W_CPPClass), - _python_owns = GetSetProperty(W_CPPClass.fget_python_owns, W_CPPClass.fset_python_owns), + __python_owns__ = GetSetProperty(W_CPPClass.fget_python_owns, W_CPPClass.fset_python_owns), __init__ = interp2app(W_CPPClass.instance__init__), __eq__ = interp2app(W_CPPClass.instance__eq__), __ne__ = interp2app(W_CPPClass.instance__ne__), @@ -1220,21 +1235,21 @@ state = space.fromcache(State) return space.call_function(state.w_fngen_callback, w_callable, space.newint(npar)) -def wrap_cppobject(space, rawobject, cppclass, - do_cast=True, python_owns=False, is_ref=False, fresh=False): +def wrap_cppinstance(space, rawobject, clsdecl, + do_cast=True, python_owns=False, is_ref=False, fresh=False): rawobject = rffi.cast(capi.C_OBJECT, rawobject) # cast to actual if requested and possible w_pycppclass = None if do_cast and rawobject: - actual = capi.c_actual_class(space, cppclass, rawobject) - if actual != cppclass.handle: + actual = capi.c_actual_class(space, clsdecl, rawobject) + if actual != clsdecl.handle: try: w_pycppclass = get_pythonized_cppclass(space, actual) - offset = capi.c_base_offset1(space, actual, cppclass, rawobject, -1) + offset = capi.c_base_offset1(space, actual, clsdecl, rawobject, -1) rawobject = capi.direct_ptradd(rawobject, offset) - w_cppclass = space.findattr(w_pycppclass, space.newtext("__cppdecl__")) - cppclass = space.interp_w(W_CPPClassDecl, w_cppclass, can_be_None=False) + w_cppdecl = space.findattr(w_pycppclass, space.newtext("__cppdecl__")) + clsdecl = space.interp_w(W_CPPClassDecl, w_cppdecl, can_be_None=False) except Exception: # failed to locate/build the derived class, so stick to the base (note # that only get_pythonized_cppclass is expected to raise, so none of @@ -1242,18 +1257,18 @@ pass if w_pycppclass is None: - w_pycppclass = get_pythonized_cppclass(space, cppclass.handle) + w_pycppclass = get_pythonized_cppclass(space, clsdecl.handle) # try to recycle existing object if this one is not newly created if not fresh and rawobject: obj = memory_regulator.retrieve(rawobject) - if obj is not None and obj.cppclass is cppclass: + if obj is not None and obj.clsdecl is clsdecl: return obj # fresh creation w_cppinstance = space.allocate_instance(W_CPPClass, w_pycppclass) cppinstance = space.interp_w(W_CPPClass, w_cppinstance, can_be_None=False) - cppinstance.__init__(space, cppclass, rawobject, is_ref, python_owns) + cppinstance.__init__(space, clsdecl, rawobject, is_ref, python_owns) memory_regulator.register(cppinstance) return w_cppinstance @@ -1264,7 +1279,7 @@ except TypeError: pass # attempt to get address of C++ instance - return rffi.cast(rffi.INTPTR_T, converter.get_rawobject(space, w_obj)) + return rffi.cast(rffi.INTPTR_T, converter.get_rawobject(space, w_obj, False)) @unwrap_spec(w_obj=W_Root) def addressof(space, w_obj): @@ -1273,19 +1288,30 @@ return space.newlong(address) @unwrap_spec(owns=bool, cast=bool) -def bind_object(space, w_obj, w_pycppclass, owns=False, cast=False): - """Takes an address and a bound C++ class proxy, returns a bound instance.""" +def _bind_object(space, w_obj, w_clsdecl, owns=False, cast=False): try: # attempt address from array or C++ instance rawobject = rffi.cast(capi.C_OBJECT, _addressof(space, w_obj)) except Exception: # accept integer value as address rawobject = rffi.cast(capi.C_OBJECT, space.uint_w(w_obj)) - w_cppclass = space.findattr(w_pycppclass, space.newtext("__cppdecl__")) - if not w_cppclass: - w_cppclass = scope_byname(space, space.text_w(w_pycppclass)) - if not w_cppclass: + decl = space.interp_w(W_CPPClassDecl, w_clsdecl, can_be_None=False) + return wrap_cppinstance(space, rawobject, decl, python_owns=owns, do_cast=cast) + +@unwrap_spec(owns=bool, cast=bool) +def bind_object(space, w_obj, w_pycppclass, owns=False, cast=False): + """Takes an address and a bound C++ class proxy, returns a bound instance.""" + w_clsdecl = space.findattr(w_pycppclass, space.newtext("__cppdecl__")) + if not w_clsdecl: + w_clsdecl = scope_byname(space, space.text_w(w_pycppclass)) + if not w_clsdecl: raise oefmt(space.w_TypeError, "no such class: %s", space.text_w(w_pycppclass)) - cppclass = space.interp_w(W_CPPClassDecl, w_cppclass, can_be_None=False) - return wrap_cppobject(space, rawobject, cppclass, do_cast=cast, python_owns=owns) + return _bind_object(space, w_obj, w_clsdecl, owns, cast) + +def move(space, w_obj): + """Casts the given instance into an C++-style rvalue.""" + obj = space.interp_w(W_CPPClass, w_obj, can_be_None=True) + if obj: + obj.flags |= INSTANCE_FLAGS_IS_R_VALUE + return w_obj diff --git a/pypy/module/_cppyy/pythonify.py b/pypy/module/_cppyy/pythonify.py --- a/pypy/module/_cppyy/pythonify.py +++ b/pypy/module/_cppyy/pythonify.py @@ -10,7 +10,7 @@ class CPPMetaScope(type): def __getattr__(self, name): try: - return get_pycppitem(self, name) # will cache on self + return get_scoped_pycppitem(self, name) # will cache on self except Exception as e: raise AttributeError("%s object has no attribute '%s' (details: %s)" % (self, name, str(e))) @@ -36,11 +36,14 @@ self._scope = scope def _arg_to_str(self, arg): - if arg == str: - import _cppyy - arg = _cppyy._std_string_name() - elif type(arg) != str: - arg = arg.__name__ + try: + arg = arg.__cppname__ + except AttributeError: + if arg == str: + import _cppyy + arg = _cppyy._std_string_name() + elif type(arg) != str: + arg = arg.__name__ return arg def __call__(self, *args): @@ -58,8 +61,36 @@ return self.__call__(*args) -def clgen_callback(name): - return get_pycppclass(name) +def scope_splitter(name): + is_open_template, scope = 0, "" + for c in name: + if c == ':' and not is_open_template: + if scope: + yield scope + scope = "" + continue + elif c == '<': + is_open_template += 1 + elif c == '>': + is_open_template -= 1 + scope += c + yield scope + +def get_pycppitem(final_scoped_name): + # walk scopes recursively down from global namespace ("::") to get the + # actual (i.e. not typedef'ed) class, triggering all necessary creation + scope = gbl + for name in scope_splitter(final_scoped_name): + scope = getattr(scope, name) + return scope +get_pycppclass = get_pycppitem # currently no distinction, but might + # in future for performance + + +# callbacks (originating from interp_cppyy.py) to allow interp-level to +# initiate creation of app-level classes and function +def clgen_callback(final_scoped_name): + return get_pycppclass(final_scoped_name) def fngen_callback(func, npar): # todo, some kind of arg transform spec if npar == 0: @@ -75,20 +106,19 @@ return wrapper +# construction of namespaces and classes, and their helpers +def make_module_name(scope): + if scope: + 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.signature() + function.__doc__ = cppol.prototype() return staticmethod(function) -def make_method(meth_name, cppol): - def method(self, *args): - return cppol.call(self, *args) - method.__name__ = meth_name - method.__doc__ = cppol.signature() - return method - def make_cppnamespace(scope, name, decl): # build up a representation of a C++ namespace (namespaces are classes) @@ -98,20 +128,19 @@ ns_meta = type(name+'_meta', (CPPMetaNamespace,), {}) # create the python-side C++ namespace representation, cache in scope if given - d = {"__cppdecl__" : decl, "__cppname__" : decl.__cppname__ } + d = {"__cppdecl__" : decl, + "__module__" : make_module_name(scope), + "__cppname__" : decl.__cppname__ } pyns = ns_meta(name, (CPPNamespace,), d) if scope: setattr(scope, name, pyns) # install as modules to allow importing from (note naming: cppyy) - modname = 'cppyy.gbl' - if scope: - modname = 'cppyy.gbl.'+pyns.__cppname__.replace('::', '.') - sys.modules[modname] = pyns + sys.modules[make_module_name(pyns)] = pyns return pyns def _drop_cycles(bases): - # TODO: figure this out, as it seems to be a PyPy bug?! + # TODO: figure out why this is necessary? for b1 in bases: for b2 in bases: if not (b1 is b2) and issubclass(b2, b1): @@ -119,27 +148,37 @@ break return tuple(bases) -def make_new(class_name): + +def make_new(decl): def __new__(cls, *args): # create a place-holder only as there may be a derived class defined + # TODO: get rid of the import and add user-land bind_object that uses + # _bind_object (see interp_cppyy.py) import _cppyy - instance = _cppyy.bind_object(0, class_name, True) + instance = _cppyy._bind_object(0, decl, True) if not instance.__class__ is cls: instance.__class__ = cls # happens for derived class return instance return __new__ -def make_cppclass(scope, class_name, final_class_name, decl): +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): # get a list of base classes for class creation bases = [get_pycppclass(base) for base in decl.get_base_names()] if not bases: bases = [CPPClass,] else: - # it's technically possible that the required class now has been built - # if one of the base classes uses it in e.g. a function interface + # it's possible that the required class now has been built if one of + # the base classes uses it in e.g. a function interface try: - return scope.__dict__[final_class_name] + return scope.__dict__[cl_name] except KeyError: pass @@ -147,39 +186,41 @@ d_meta = {} # prepare dictionary for python-side C++ class representation - def dispatch(self, name, signature): - cppol = decl.dispatch(name, signature) - return types.MethodType(make_method(name, cppol), self, type(self)) + def dispatch(self, m_name, signature): + cppol = decl.__dispatch__(m_name, signature) + return types.MethodType(make_method(m_name, cppol), self, type(self)) d_class = {"__cppdecl__" : decl, + "__new__" : make_new(decl), + "__module__" : make_module_name(scope), "__cppname__" : decl.__cppname__, - "__new__" : make_new(class_name), + "__dispatch__" : dispatch, } # insert (static) methods into the class dictionary - for name in decl.get_method_names(): - cppol = decl.get_overload(name) + for m_name in decl.get_method_names(): + cppol = decl.get_overload(m_name) if cppol.is_static(): - d_class[name] = make_static_function(name, cppol) + d_class[m_name] = make_static_function(m_name, cppol) else: - d_class[name] = make_method(name, cppol) + d_class[m_name] = make_method(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) - for name in decl.get_datamember_names(): - cppdm = decl.get_datamember(name) - d_class[name] = cppdm + for d_name in decl.get_datamember_names(): + cppdm = decl.get_datamember(d_name) + d_class[d_name] = cppdm if cppdm.is_static(): - d_meta[name] = cppdm + d_meta[d_name] = cppdm # create a metaclass to allow properties (for static data write access) metabases = [type(base) for base in bases] - metacpp = type(CPPMetaScope)(class_name+'_meta', _drop_cycles(metabases), d_meta) + metacpp = type(CPPMetaScope)(cl_name+'_meta', _drop_cycles(metabases), d_meta) # create the python-side C++ class - pycls = metacpp(class_name, _drop_cycles(bases), d_class) + pycls = metacpp(cl_name, _drop_cycles(bases), d_class) # store the class on its outer scope - setattr(scope, final_class_name, pycls) + setattr(scope, cl_name, pycls) # the call to register will add back-end specific pythonizations and thus # needs to run first, so that the generic pythonizations can use them @@ -192,32 +233,32 @@ return CPPTemplate(template_name, scope) -def get_pycppitem(scope, name): +def get_scoped_pycppitem(scope, name): import _cppyy - # resolve typedefs/aliases - full_name = (scope == gbl) and name or (scope.__name__+'::'+name) - true_name = _cppyy._resolve_name(full_name) - if true_name != full_name: - return get_pycppclass(true_name) + # resolve typedefs/aliases: these may cross namespaces, in which case + # the lookup must trigger the creation of all necessary scopes + scoped_name = (scope == gbl) and name or (scope.__cppname__+'::'+name) + final_scoped_name = _cppyy._resolve_name(scoped_name) + if final_scoped_name != scoped_name: + pycppitem = get_pycppitem(final_scoped_name) + # also store on the requested scope (effectively a typedef or pointer copy) + setattr(scope, name, pycppitem) + return pycppitem pycppitem = None - # classes - cppitem = _cppyy._scope_byname(true_name) + # scopes (classes and namespaces) + cppitem = _cppyy._scope_byname(final_scoped_name) if cppitem: - name = true_name - if scope != gbl: - name = true_name[len(scope.__cppname__)+2:] if cppitem.is_namespace(): pycppitem = make_cppnamespace(scope, name, cppitem) - setattr(scope, name, pycppitem) else: - pycppitem = make_cppclass(scope, name, true_name, cppitem) + pycppitem = make_cppclass(scope, name, cppitem) # templates if not cppitem: - cppitem = _cppyy._template_byname(true_name) + cppitem = _cppyy._is_template(final_scoped_name) if cppitem: pycppitem = make_cpptemplatetype(scope, name) setattr(scope, name, pycppitem) @@ -249,29 +290,6 @@ raise AttributeError("'%s' has no attribute '%s'" % (str(scope), name)) -def scope_splitter(name): - is_open_template, scope = 0, "" - for c in name: - if c == ':' and not is_open_template: - if scope: - yield scope - scope = "" - continue - elif c == '<': - is_open_template += 1 - elif c == '>': - is_open_template -= 1 - scope += c - yield scope - -def get_pycppclass(name): - # break up the name, to walk the scopes and get the class recursively - scope = gbl - for part in scope_splitter(name): - scope = getattr(scope, part) - return scope - - # pythonization by decoration (move to their own file?) def python_style_getitem(self, idx): # python-style indexing: check for size and allow indexing from the back @@ -346,8 +364,8 @@ # also the fallback on the indexed __getitem__, but that is slower) if not 'vector' in pyclass.__name__[:11] and \ ('begin' in pyclass.__dict__ and 'end' in pyclass.__dict__): - if _cppyy._scope_byname(pyclass.__name__+'::iterator') or \ - _cppyy._scope_byname(pyclass.__name__+'::const_iterator'): + if _cppyy._scope_byname(pyclass.__cppname__+'::iterator') or \ + _cppyy._scope_byname(pyclass.__cppname__+'::const_iterator'): def __iter__(self): i = self.begin() while i != self.end(): @@ -416,17 +434,21 @@ # pre-create std to allow direct importing gbl.std = make_cppnamespace(gbl, 'std', _cppyy._scope_byname('std')) + # add move cast + gbl.std.move = _cppyy.move + # install a type for enums to refer to # TODO: this is correct for C++98, not for C++11 and in general there will # be the same issue for all typedef'd builtin types setattr(gbl, 'internal_enum_type_t', int) - # install nullptr as a unique reference - setattr(gbl, 'nullptr', _cppyy._get_nullptr()) - # install for user access _cppyy.gbl = gbl + # install nullptr as a unique reference + _cppyy.nullptr = _cppyy._get_nullptr() + + # user-defined pythonizations interface _pythonizations = {} def add_pythonization(class_name, callback): 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 @@ -955,7 +955,13 @@ return cppstring_to_cstring(""); } -char* cppyy_method_signature(cppyy_scope_t /* handle */, cppyy_index_t /* method_index */) { +char* cppyy_method_signature( + cppyy_scope_t /* handle */, cppyy_index_t /* method_index */, int /* show_formalargs */) { + return cppstring_to_cstring(""); +} + +char* cppyy_method_prototype( + cppyy_scope_t /* handle */, cppyy_index_t /* method_index */, int /* show_formalargs */) { return cppstring_to_cstring(""); } diff --git a/pypy/module/_cppyy/test/Makefile b/pypy/module/_cppyy/test/Makefile --- a/pypy/module/_cppyy/test/Makefile +++ b/pypy/module/_cppyy/test/Makefile @@ -1,12 +1,14 @@ dicts = advancedcppDict.so \ advancedcpp2Dict.so \ + cpp11featuresDict.so \ crossingDict.so \ datatypesDict.so \ example01Dict.so \ fragileDict.so \ operatorsDict.so \ overloadsDict.so \ - stltypesDict.so + stltypesDict.so \ + templatesDict.so all : $(dicts) diff --git a/pypy/module/_cppyy/test/advancedcpp.cxx b/pypy/module/_cppyy/test/advancedcpp.cxx --- a/pypy/module/_cppyy/test/advancedcpp.cxx +++ b/pypy/module/_cppyy/test/advancedcpp.cxx @@ -106,17 +106,6 @@ } -// more template testing -long my_templated_method_class::get_size() { return -1; } - -long my_templated_method_class::get_char_size() { return (long)sizeof(char); } -long my_templated_method_class::get_int_size() { return (long)sizeof(int); } -long my_templated_method_class::get_long_size() { return (long)sizeof(long); } -long my_templated_method_class::get_float_size() { return (long)sizeof(float); } -long my_templated_method_class::get_double_size() { return (long)sizeof(double); } -long my_templated_method_class::get_self_size() { return (long)sizeof(my_templated_method_class); } - - // overload order testing int overload_one_way::gime() const { return 1; } std::string overload_one_way::gime() { return "aap"; } diff --git a/pypy/module/_cppyy/test/advancedcpp.h b/pypy/module/_cppyy/test/advancedcpp.h --- a/pypy/module/_cppyy/test/advancedcpp.h +++ b/pypy/module/_cppyy/test/advancedcpp.h @@ -246,8 +246,6 @@ int m_i; }; -template class std::vector<ref_tester>; - //=========================================================================== class some_convertible { // for math conversions testing @@ -275,6 +273,7 @@ extern double my_global_double; // a couple of globals for access testing extern double my_global_array[500]; extern double* my_global_ptr; +static const char my_global_string[] = "aap " " noot " " mies"; //=========================================================================== class some_class_with_data { // for life-line and identity testing @@ -387,37 +386,6 @@ template char my_templated_function<char>(char); template double my_templated_function<double>(double); -class my_templated_method_class { -public: - long get_size(); // to get around bug in genreflex - template<class B> long get_size(); - - long get_char_size(); - long get_int_size(); - long get_long_size(); - long get_float_size(); - long get_double_size(); - - long get_self_size(); - -private: - double m_data[3]; -}; - -template<class B> -inline long my_templated_method_class::get_size() { - return sizeof(B); -} - -template long my_templated_method_class::get_size<char>(); -template long my_templated_method_class::get_size<int>(); -template long my_templated_method_class::get_size<long>(); -template long my_templated_method_class::get_size<float>(); -template long my_templated_method_class::get_size<double>(); - -typedef my_templated_method_class my_typedef_t; -template long my_templated_method_class::get_size<my_typedef_t>(); - //=========================================================================== class overload_one_way { // overload order testing diff --git a/pypy/module/_cppyy/test/advancedcpp.xml b/pypy/module/_cppyy/test/advancedcpp.xml --- a/pypy/module/_cppyy/test/advancedcpp.xml +++ b/pypy/module/_cppyy/test/advancedcpp.xml @@ -53,8 +53,6 @@ <class name="std::vector<float>" /> <class pattern="my_templated_class<*>" /> <function pattern="my_templated_function<*>" /> - <class name="my_templated_method_class" /> - <class name="my_typedef_t" /> <class name="overload_one_way" /> <class name="overload_the_other_way" /> diff --git a/pypy/module/_cppyy/test/cpp11features.cxx b/pypy/module/_cppyy/test/cpp11features.cxx new file mode 100644 --- /dev/null +++ b/pypy/module/_cppyy/test/cpp11features.cxx @@ -0,0 +1,18 @@ +#if __cplusplus >= 201103L + +#include "cpp11features.h" + + +// for std::shared_ptr<> testing +int TestSharedPtr::s_counter = 0; + +std::shared_ptr<TestSharedPtr> create_shared_ptr_instance() { + return std::shared_ptr<TestSharedPtr>(new TestSharedPtr); +} + + +// for move ctors etc. +int TestMoving1::s_move_counter = 0; +int TestMoving2::s_move_counter = 0; + +#endif // c++11 and later diff --git a/pypy/module/_cppyy/test/cpp11features.h b/pypy/module/_cppyy/test/cpp11features.h new file mode 100644 --- /dev/null +++ b/pypy/module/_cppyy/test/cpp11features.h @@ -0,0 +1,45 @@ +#if __cplusplus >= 201103L + +#include <memory> + + +//=========================================================================== +class TestSharedPtr { // for std::shared_ptr<> testing +public: + static int s_counter; + +public: + TestSharedPtr() { ++s_counter; } + TestSharedPtr(const TestSharedPtr&) { ++s_counter; } + ~TestSharedPtr() { --s_counter; } +}; + +std::shared_ptr<TestSharedPtr> create_shared_ptr_instance(); + + +//=========================================================================== +class TestMoving1 { // for move ctors etc. +public: + static int s_move_counter; + +public: + TestMoving1() {} + TestMoving1(TestMoving1&&) { ++s_move_counter; } + TestMoving1(const TestMoving1&) {} + TestMoving1& operator=(TestMoving1&&) { ++s_move_counter; return *this; } + TestMoving1& operator=(TestMoving1&) { return *this; } +}; + +class TestMoving2 { // note opposite method order from TestMoving1 +public: + static int s_move_counter; + +public: + TestMoving2() {} + TestMoving2(const TestMoving2&) {} + TestMoving2(TestMoving2&& other) { ++s_move_counter; } + TestMoving2& operator=(TestMoving2&) { return *this; } + TestMoving2& operator=(TestMoving2&&) { ++s_move_counter; return *this; } +}; + +#endif // c++11 and later diff --git a/pypy/module/_cppyy/test/cpp11features.xml b/pypy/module/_cppyy/test/cpp11features.xml new file mode 100644 --- /dev/null +++ b/pypy/module/_cppyy/test/cpp11features.xml @@ -0,0 +1,6 @@ +<lcgdict> + + <class name="TestSharedPtr" /> + <class pattern="TestMoving*" /> + +</lcgdict> diff --git a/pypy/module/_cppyy/test/fragile.h b/pypy/module/_cppyy/test/fragile.h --- a/pypy/module/_cppyy/test/fragile.h +++ b/pypy/module/_cppyy/test/fragile.h @@ -30,9 +30,11 @@ void overload(int, no_such_class* p = 0) {} }; +static const int dummy_location = 0xdead; + class E { public: - E() : m_pp_no_such(0), m_pp_a(0) {} + E() : m_pp_no_such((no_such_class**)&dummy_location), m_pp_a(0) {} virtual int check() { return (int)'E'; } void overload(no_such_class**) {} diff --git a/pypy/module/_cppyy/test/templates.cxx b/pypy/module/_cppyy/test/templates.cxx new file mode 100644 --- /dev/null +++ b/pypy/module/_cppyy/test/templates.cxx @@ -0,0 +1,12 @@ +#include "templates.h" + + +// template methods +long MyTemplatedMethodClass::get_size() { return -1; } + +long MyTemplatedMethodClass::get_char_size() { return (long)sizeof(char); } +long MyTemplatedMethodClass::get_int_size() { return (long)sizeof(int); } +long MyTemplatedMethodClass::get_long_size() { return (long)42; /* "lying" */ } +long MyTemplatedMethodClass::get_float_size() { return (long)sizeof(float); } +long MyTemplatedMethodClass::get_double_size() { return (long)sizeof(double); } +long MyTemplatedMethodClass::get_self_size() { return (long)sizeof(MyTemplatedMethodClass); } diff --git a/pypy/module/_cppyy/test/templates.h b/pypy/module/_cppyy/test/templates.h new file mode 100644 --- /dev/null +++ b/pypy/module/_cppyy/test/templates.h @@ -0,0 +1,35 @@ +//=========================================================================== +class MyTemplatedMethodClass { // template methods +public: + long get_size(); // to get around bug in genreflex + template<class B> long get_size(); + + long get_char_size(); + long get_int_size(); + long get_long_size(); + long get_float_size(); + long get_double_size(); + + long get_self_size(); + +private: + double m_data[3]; +}; + +template<class B> +inline long MyTemplatedMethodClass::get_size() { + return sizeof(B); +} + +// +typedef MyTemplatedMethodClass MyTMCTypedef_t; + +// explicit instantiation +template long MyTemplatedMethodClass::get_size<char>(); +template long MyTemplatedMethodClass::get_size<int>(); + +// "lying" specialization +template<> +inline long MyTemplatedMethodClass::get_size<long>() { + return 42; +} diff --git a/pypy/module/_cppyy/test/templates.xml b/pypy/module/_cppyy/test/templates.xml new file mode 100644 --- /dev/null +++ b/pypy/module/_cppyy/test/templates.xml @@ -0,0 +1,6 @@ +<lcgdict> + + <class name="MyTemplatedMethodClass" /> + <class name="MyTMCTypedef_t" /> + +</lcgdict> 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 @@ -28,9 +28,9 @@ def test01_default_arguments(self): """Test usage of default arguments""" - import _cppyy + import _cppyy as cppyy def test_defaulter(n, t): - defaulter = getattr(_cppyy.gbl, '%s_defaulter' % n) + defaulter = getattr(cppyy.gbl, '%s_defaulter' % n) d = defaulter() assert d.m_a == t(11) @@ -55,23 +55,23 @@ assert d.m_b == t(4) assert d.m_c == t(5) d.__destruct__() - test_defaulter('short', int) + test_defaulter('short', int) test_defaulter('ushort', int) - test_defaulter('int', int) - test_defaulter('uint', int) - test_defaulter('long', long) - test_defaulter('ulong', long) - test_defaulter('llong', long) + test_defaulter('int', int) + test_defaulter('uint', int) + test_defaulter('long', long) + test_defaulter('ulong', long) + test_defaulter('llong', long) test_defaulter('ullong', long) - test_defaulter('float', float) + test_defaulter('float', float) test_defaulter('double', float) def test02_simple_inheritance(self): """Test binding of a basic inheritance structure""" - import _cppyy - base_class = _cppyy.gbl.base_class - derived_class = _cppyy.gbl.derived_class + import _cppyy as cppyy + base_class = cppyy.gbl.base_class + derived_class = cppyy.gbl.derived_class assert issubclass(derived_class, base_class) assert not issubclass(base_class, derived_class) @@ -123,8 +123,8 @@ def test03_namespaces(self): """Test access to namespaces and inner classes""" - import _cppyy - gbl = _cppyy.gbl + import _cppyy as cppyy + gbl = cppyy.gbl assert gbl.a_ns is gbl.a_ns assert gbl.a_ns.d_ns is gbl.a_ns.d_ns @@ -150,8 +150,8 @@ def test03a_namespace_lookup_on_update(self): """Test whether namespaces can be shared across dictionaries.""" - import _cppyy, ctypes - gbl = _cppyy.gbl + import _cppyy as cppyy, ctypes + gbl = cppyy.gbl lib2 = ctypes.CDLL("./advancedcpp2Dict.so", ctypes.RTLD_GLOBAL) @@ -179,8 +179,8 @@ def test04_template_types(self): """Test bindings of templated types""" - import _cppyy - gbl = _cppyy.gbl + import _cppyy as cppyy + gbl = cppyy.gbl assert gbl.T1 is gbl.T1 assert gbl.T2 is gbl.T2 @@ -245,8 +245,8 @@ def test05_abstract_classes(self): """Test non-instatiatability of abstract classes""" - import _cppyy - gbl = _cppyy.gbl + import _cppyy as cppyy + gbl = cppyy.gbl raises(TypeError, gbl.a_class) raises(TypeError, gbl.some_abstract_class) @@ -260,12 +260,12 @@ def test06_datamembers(self): """Test data member access when using virtual inheritence""" - import _cppyy - a_class = _cppyy.gbl.a_class - b_class = _cppyy.gbl.b_class - c_class_1 = _cppyy.gbl.c_class_1 - c_class_2 = _cppyy.gbl.c_class_2 - d_class = _cppyy.gbl.d_class + import _cppyy as cppyy + a_class = cppyy.gbl.a_class + b_class = cppyy.gbl.b_class + c_class_1 = cppyy.gbl.c_class_1 + c_class_2 = cppyy.gbl.c_class_2 + d_class = cppyy.gbl.d_class assert issubclass(b_class, a_class) assert issubclass(c_class_1, a_class) @@ -354,8 +354,8 @@ def test07_pass_by_reference(self): """Test reference passing when using virtual inheritance""" - import _cppyy - gbl = _cppyy.gbl + import _cppyy as cppyy + gbl = cppyy.gbl b_class = gbl.b_class c_class = gbl.c_class_2 d_class = gbl.d_class @@ -387,71 +387,75 @@ def test08_void_pointer_passing(self): """Test passing of variants of void pointer arguments""" - import _cppyy - pointer_pass = _cppyy.gbl.pointer_pass - some_concrete_class = _cppyy.gbl.some_concrete_class + import _cppyy as cppyy + pointer_pass = cppyy.gbl.pointer_pass + some_concrete_class = cppyy.gbl.some_concrete_class pp = pointer_pass() o = some_concrete_class() - assert _cppyy.addressof(o) == pp.gime_address_ptr(o) - assert _cppyy.addressof(o) == pp.gime_address_ptr_ptr(o) - assert _cppyy.addressof(o) == pp.gime_address_ptr_ref(o) + assert cppyy.addressof(o) == pp.gime_address_ptr(o) + assert cppyy.addressof(o) == pp.gime_address_ptr_ptr(o) + assert cppyy.addressof(o) == pp.gime_address_ptr_ref(o) import array - addressofo = array.array('l', [_cppyy.addressof(o)]) - assert addressofo.buffer_info()[0] == pp.gime_address_ptr_ptr(addressofo) + addressofo = array.array('l', [cppyy.addressof(o)]) + assert addressofo[0] == pp.gime_address_ptr_ptr(addressofo) assert 0 == pp.gime_address_ptr(0) - assert 0 == pp.gime_address_ptr(None) + raises(TypeError, pp.gime_address_ptr, None) - ptr = _cppyy.bind_object(0, some_concrete_class) - assert _cppyy.addressof(ptr) == 0 + ptr = cppyy.bind_object(0, some_concrete_class) + assert cppyy.addressof(ptr) == 0 pp.set_address_ptr_ref(ptr) - assert _cppyy.addressof(ptr) == 0x1234 + assert cppyy.addressof(ptr) == 0x1234 pp.set_address_ptr_ptr(ptr) - assert _cppyy.addressof(ptr) == 0x4321 + assert cppyy.addressof(ptr) == 0x4321 + + assert cppyy.addressof(cppyy.nullptr) == 0 + raises(TypeError, cppyy.addressof, None) + assert cppyy.addressof(0) == 0 def test09_opaque_pointer_passing(self): """Test passing around of opaque pointers""" - import _cppyy - some_concrete_class = _cppyy.gbl.some_concrete_class + import _cppyy as cppyy + some_concrete_class = cppyy.gbl.some_concrete_class o = some_concrete_class() # TODO: figure out the PyPy equivalent of CObject (may have to do this # through the C-API from C++) - #cobj = _cppyy.as_cobject(o) - addr = _cppyy.addressof(o) + #cobj = cppyy.as_cobject(o) + addr = cppyy.addressof(o) - #assert o == _cppyy.bind_object(cobj, some_concrete_class) - #assert o == _cppyy.bind_object(cobj, type(o)) - #assert o == _cppyy.bind_object(cobj, o.__class__) - #assert o == _cppyy.bind_object(cobj, "some_concrete_class") - assert _cppyy.addressof(o) == _cppyy.addressof(_cppyy.bind_object(addr, some_concrete_class)) - assert o == _cppyy.bind_object(addr, some_concrete_class) - assert o == _cppyy.bind_object(addr, type(o)) - assert o == _cppyy.bind_object(addr, o.__class__) - assert o == _cppyy.bind_object(addr, "some_concrete_class") - raises(TypeError, _cppyy.bind_object, addr, "does_not_exist") - raises(TypeError, _cppyy.bind_object, addr, 1) + #assert o == cppyy.bind_object(cobj, some_concrete_class) + #assert o == cppyy.bind_object(cobj, type(o)) + #assert o == cppyy.bind_object(cobj, o.__class__) + #assert o == cppyy.bind_object(cobj, "some_concrete_class") + assert cppyy.addressof(o) == cppyy.addressof(cppyy.bind_object(addr, some_concrete_class)) + assert o == cppyy.bind_object(addr, some_concrete_class) + assert o == cppyy.bind_object(addr, type(o)) + assert o == cppyy.bind_object(addr, o.__class__) + assert o == cppyy.bind_object(addr, "some_concrete_class") + raises(TypeError, cppyy.bind_object, addr, "does_not_exist") + raises(TypeError, cppyy.bind_object, addr, 1) def test10_object_identity(self): """Test object identity""" - import _cppyy - some_concrete_class = _cppyy.gbl.some_concrete_class - some_class_with_data = _cppyy.gbl.some_class_with_data + import _cppyy as cppyy + some_concrete_class = cppyy.gbl.some_concrete_class + some_class_with_data = cppyy.gbl.some_class_with_data o = some_concrete_class() - addr = _cppyy.addressof(o) + addr = cppyy.addressof(o) - o2 = _cppyy.bind_object(addr, some_concrete_class) + o2 = cppyy.bind_object(addr, some_concrete_class) assert o is o2 - o3 = _cppyy.bind_object(addr, some_class_with_data) + o3 = cppyy.bind_object(addr, some_class_with_data) assert not o is o3 d1 = some_class_with_data() @@ -472,13 +476,13 @@ def test11_multi_methods(self): """Test calling of methods from multiple inheritance""" - import _cppyy - multi = _cppyy.gbl.multi + import _cppyy as cppyy + multi = cppyy.gbl.multi - assert _cppyy.gbl.multi1 is multi.__bases__[0] - assert _cppyy.gbl.multi2 is multi.__bases__[1] _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit