Author: Wim Lavrijsen <[email protected]>
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
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit