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