Author: Wim Lavrijsen <[email protected]>
Branch:
Changeset: r94771:20e8b110028c
Date: 2018-06-16 01:16 -0700
http://bitbucket.org/pypy/pypy/changeset/20e8b110028c/
Log: more templates and consistency with CPython/cppyy
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
@@ -706,7 +706,7 @@
"no overload found matching %s", self.signature)
-class SmartPointerConverter(TypeConverter):
+class SmartPtrConverter(TypeConverter):
_immutable_fields = ['typecode', 'smartdecl', 'rawdecl', 'deref']
typecode = 'V'
@@ -753,7 +753,7 @@
return interp_cppyy.wrap_cppinstance(space, address,
self.rawdecl, smartdecl=self.smartdecl, deref=self.deref,
do_cast=False)
-class SmartPointerPtrConverter(SmartPointerConverter):
+class SmartPtrPtrConverter(SmartPtrConverter):
typecode = 'o'
def from_memory(self, space, w_obj, w_pycppclass, offset):
@@ -763,7 +763,7 @@
self._is_abstract(space)
-class SmartPointerRefConverter(SmartPointerPtrConverter):
+class SmartPtrRefConverter(SmartPtrPtrConverter):
typecode = 'V'
@@ -804,6 +804,12 @@
compound = helper.compound(name)
clean_name = capi.c_resolve_name(space, helper.clean_type(name))
try:
+ return _converters[clean_name+compound](space, default)
+ except KeyError:
+ pass
+
+ # arrays
+ try:
# array_index may be negative to indicate no size or no size found
array_size = helper.array_size(_name) # uses original arg
# TODO: using clean_name here drops const (e.g. const char[] will
@@ -823,11 +829,11 @@
check_smart = capi.c_smartptr_info(space, clean_name)
if check_smart[0]:
if compound == '':
- return SmartPointerConverter(space, clsdecl, check_smart[1],
check_smart[2])
+ return SmartPtrConverter(space, clsdecl, check_smart[1],
check_smart[2])
elif compound == '*':
- return SmartPointerPtrConverter(space, clsdecl,
check_smart[1], check_smart[2])
+ return SmartPtrPtrConverter(space, clsdecl, check_smart[1],
check_smart[2])
elif compound == '&':
- return SmartPointerRefConverter(space, clsdecl,
check_smart[1], check_smart[2])
+ return SmartPtrRefConverter(space, clsdecl, check_smart[1],
check_smart[2])
# fall through: can still return smart pointer in non-smart way
# type check for the benefit of the annotator
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
@@ -26,7 +26,7 @@
NULL = lltype.nullptr(jit_libffi.FFI_TYPE_P.TO)
-class FunctionExecutor(object):
+class Executor(object):
def __init__(self, space, extra):
pass
@@ -43,7 +43,7 @@
raise FastCallNotPossible
-class PtrTypeExecutor(FunctionExecutor):
+class PtrTypeExecutor(Executor):
_immutable_fields_ = ['typecode']
typecode = 'P'
@@ -63,7 +63,7 @@
return W_ArrayInstance(space, shape, sys.maxint/shape.size, ptrval)
-class VoidExecutor(FunctionExecutor):
+class VoidExecutor(Executor):
def cffi_type(self, space):
state = space.fromcache(ffitypes.State)
return state.c_void
@@ -96,7 +96,7 @@
_mixin_ = True
def __init__(self, space, extra):
- FunctionExecutor.__init__(self, space, extra)
+ Executor.__init__(self, space, extra)
self.do_assign = False
self.item = rffi.cast(self.c_type, 0)
@@ -124,7 +124,7 @@
rffi.cast(self.c_ptrtype, rffi.cast(rffi.VOIDPP, result)[0]))
-class CStringExecutor(FunctionExecutor):
+class CStringExecutor(Executor):
def execute(self, space, cppmethod, cppthis, num_args, args):
lresult = capi.c_call_l(space, cppmethod, cppthis, num_args, args)
ccpresult = rffi.cast(rffi.CCHARP, lresult)
@@ -134,7 +134,7 @@
return space.newbytes(result)
-class ConstructorExecutor(FunctionExecutor):
+class ConstructorExecutor(Executor):
def execute(self, space, cppmethod, cpptype, num_args, args):
from pypy.module._cppyy import interp_cppyy
newthis = capi.c_constructor(space, cppmethod, cpptype, num_args, args)
@@ -142,12 +142,12 @@
return space.newlong(rffi.cast(rffi.LONG, newthis)) # really want
ptrdiff_t here
-class InstanceExecutor(FunctionExecutor):
+class InstanceExecutor(Executor):
# For return of a C++ instance by pointer: MyClass* func()
_immutable_fields_ = ['clsdecl']
def __init__(self, space, clsdecl):
- FunctionExecutor.__init__(self, space, clsdecl)
+ Executor.__init__(self, space, clsdecl)
self.clsdecl = clsdecl
def _wrap_result(self, space, obj):
@@ -338,7 +338,7 @@
return _executors['void*'](space, None) # allow at least passing of
the pointer
# currently used until proper lazy instantiation available in interp_cppyy
- return FunctionExecutor(space, None)
+ return Executor(space, None)
_executors["void"] = VoidExecutor
@@ -374,10 +374,10 @@
)
for c_type, stub, names in type_info:
- class BasicExecutor(ffitypes.typeid(c_type), NumericExecutorMixin,
FunctionExecutor):
+ class BasicExecutor(ffitypes.typeid(c_type), NumericExecutorMixin,
Executor):
_immutable_ = True
c_stubcall = staticmethod(stub)
- class BasicRefExecutor(ffitypes.typeid(c_type),
NumericRefExecutorMixin, FunctionExecutor):
+ class BasicRefExecutor(ffitypes.typeid(c_type),
NumericRefExecutorMixin, Executor):
def cffi_type(self, space):
state = space.fromcache(ffitypes.State)
return state.c_voidp
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
@@ -672,24 +672,33 @@
_mixin_ = True
- def construct_template_args(self, w_args):
+ def construct_template_args(self, w_tpArgs, args_w = None):
space = self.space
tmpl_args = ''
- for i in range(space.len_w(w_args)):
- w_obj = space.getitem(w_args, space.newint(i))
- if space.isinstance_w(w_obj, space.w_text):
- s = space.text_w(w_obj) # string describing type
- elif space.isinstance_w(w_obj, space.w_type):
+ for i in range(space.len_w(w_tpArgs)):
+ w_tp = space.getitem(w_tpArgs, space.newint(i))
+ if space.isinstance_w(w_tp, space.w_text):
+ s = space.text_w(w_tp) # string describing type
+ elif space.isinstance_w(w_tp, space.w_type):
try:
# cppyy bound types
- name = space.getattr(w_obj, space.newtext('__cppname__'))
+ s = space.text_w(space.getattr(w_tp,
space.newtext('__cppname__')))
+ if args_w:
+ # try to specialize the type match for the given object
+ cppinstance = self.space.interp_w(W_CPPInstance,
args_w[i])
+ if cppinstance.flags & INSTANCE_FLAGS_IS_RVALUE:
+ sugar = "&&"
+ elif cppinstance.flags & INSTANCE_FLAGS_IS_REF:
+ sugar = "*"
+ else:
+ sugar = "&"
+ s += sugar
except OperationError:
# generic python types
- name = space.getattr(w_obj, space.newtext('__name__'))
- s = space.text_w(name)
+ s = space.text_w(space.getattr(w_tp,
space.newtext('__name__')))
else:
# builtin types etc.
- s = space.text_w(space.str(w_obj))
+ s = space.text_w(space.str(w_tp))
# map python types -> C++ types
if s == 'str': s = 'std::string'
if i != 0: tmpl_args += ', '
@@ -712,23 +721,31 @@
cppol = W_CPPOverload(space, self.scope, funcs[:], self.flags)
return cppol
- def instantiation_from_args(self, name, args_w):
+ def instantiate_and_call(self, name, args_w):
# try to match with run-time instantiations
for cppol in self.master.overloads.values():
try:
- cppol.descr_get(self.w_this, []).call(args_w)
+ return cppol.descr_get(self.w_this, []).call(args_w)
except Exception:
pass # completely ignore for now; have to see whether
errors become confusing
# if all failed, then try to deduce from argument types
w_types = self.space.newtuple([self.space.type(obj_w) for obj_w in
args_w])
- proto = self.construct_template_args(w_types)
+ proto = self.construct_template_args(w_types, args_w)
method = self.find_method_template(name, proto)
# only cache result if the name retains the full template
- if len(method.functions) == 1:
- fullname = capi.c_method_full_name(self.space,
method.functions[0].cppmethod)
- if 0 <= fullname.rfind('>'):
+ fullname = capi.c_method_full_name(self.space,
method.functions[0].cppmethod)
+ if 0 <= fullname.rfind('>'):
+ try:
+ existing = self.master.overloads[fullname]
+ allf = existing.functions + method.functions
+ if isinstance(existing, W_CPPStaticOverload):
+ cppol = W_CPPStaticOverload(self.space, self.scope, allf,
self.flags)
+ else:
+ cppol = W_CPPOverload(self.space, self.scope, allf,
self.flags)
+ self.master.overloads[fullname] = cppol
+ except KeyError:
self.master.overloads[fullname] = method
return method.descr_get(self.w_this, []).call(args_w)
@@ -747,9 +764,12 @@
method = self.master.overloads[fullname]
except KeyError:
method = self.find_method_template(fullname)
-
- # cache result (name is always full templated name)
- self.master.overloads[fullname] = method
+ # cache result (name is always full templated name)
+ self.master.overloads[fullname] = method
+ # also cache on "official" name (may include default template
arguments)
+ c_fullname = capi.c_method_full_name(self.space,
method.functions[0].cppmethod)
+ if c_fullname != fullname:
+ self.master.overloads[c_fullname] = method
return method.descr_get(self.w_this, [])
@@ -774,6 +794,7 @@
return self # unbound, so no new instance needed
cppol = W_CPPTemplateOverload(self.space, self.name, self.scope,
self.functions, self.flags)
cppol.w_this = w_cppinstance
+ cppol.master = self.master
return cppol # bound
@unwrap_spec(args_w='args_w')
@@ -787,7 +808,7 @@
except Exception:
pass
- return self.instantiation_from_args(self.name, args_w)
+ return self.instantiate_and_call(self.name, args_w)
@unwrap_spec(args_w='args_w')
def getitem(self, args_w):
@@ -842,7 +863,7 @@
pass
# try new instantiation
- return self.instantiation_from_args(self.name, args_w)
+ return self.instantiate_and_call(self.name, args_w)
@unwrap_spec(args_w='args_w')
def getitem(self, args_w):
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
@@ -408,16 +408,22 @@
# map push_back -> __iadd__ (generally true for STL)
if 'push_back' in pyclass.__dict__ and not '__iadd__' in pyclass.__dict__:
- def __iadd__(self, ll):
- [self.push_back(x) for x in ll]
- return self
- pyclass.__iadd__ = __iadd__
+ if 'reserve' in pyclass.__dict__:
+ def iadd(self, ll):
+ self.reserve(len(ll))
+ for x in ll: self.push_back(x)
+ return self
+ else:
+ def iadd(self, ll):
+ for x in ll: self.push_back(x)
+ return self
+ pyclass.__iadd__ = iadd
# map begin()/end() protocol to iter protocol on STL(-like) classes, but
# not on vector, which is pythonized in the capi (interp-level; there is
# also the fallback on the indexed __getitem__, but that is slower)
- if not 'vector' in name[:11] and \
- ('begin' in pyclass.__dict__ and 'end' in pyclass.__dict__):
+# TODO: if not (0 <= name.find('vector') <= 5):
+ if ('begin' in pyclass.__dict__ and 'end' in pyclass.__dict__):
if _cppyy._scope_byname(name+'::iterator') or \
_cppyy._scope_byname(name+'::const_iterator'):
def __iter__(self):
@@ -430,6 +436,20 @@
pyclass.__iter__ = __iter__
# else: rely on numbered iteration
+ # add python collection based initializer
+ if 0 <= name.find('vector') <= 5:
+ pyclass.__real_init__ = pyclass.__init__
+ def vector_init(self, *args):
+ if len(args) == 1 and isinstance(args[0], (tuple, list)):
+ ll = args[0]
+ self.__real_init__()
+ self.reserve(len(ll))
+ for item in ll:
+ self.push_back(item)
+ return
+ return self.__real_init__(*args)
+ pyclass.__init__ = vector_init
+
# combine __getitem__ and __len__ to make a pythonized __getitem__
if '__getitem__' in pyclass.__dict__ and '__len__' in pyclass.__dict__:
pyclass._getitem__unchecked = pyclass.__getitem__
@@ -470,7 +490,13 @@
for p in pythonizors:
p(pyclass, name)
+cppyyIsInitialized = False
def _post_import_startup():
+ # run only once (function is explicitly called in testing)
+ global cppyyIsInitialized
+ if cppyyIsInitialized:
+ return
+
# _cppyy should not be loaded at the module level, as that will trigger a
# call to space.getbuiltinmodule(), which will cause _cppyy to be loaded
# at pypy-c startup, rather than on the "import _cppyy" statement
@@ -511,6 +537,9 @@
# install nullptr as a unique reference
_cppyy.nullptr = _cppyy._get_nullptr()
+ # done
+ cppyyIsInitialized = True
+
# user-defined pythonizations interface
_pythonizations = {'' : list()}
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
@@ -107,7 +107,7 @@
}
inline std::string tuplify(std::ostringstream& out) {
- out.seekp(-2, out.cur); out << ')';
+ out << "NULL)";
return out.str();
}
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
@@ -13,7 +13,7 @@
def setup_class(cls):
cls.w_test_dct = cls.space.newtext(test_dct)
- cls.w_datatypes = cls.space.appexec([], """():
+ cls.w_templates = cls.space.appexec([], """():
import ctypes, _cppyy
_cppyy._post_import_startup()
return ctypes.CDLL(%r, ctypes.RTLD_GLOBAL)""" % (test_dct, ))
@@ -84,10 +84,11 @@
import _cppyy
- s = _cppyy.gbl.std.ostringstream()
- #s << '('
- #_cppyy.gbl.SomeNS.tuplify(s, 1, 4., "aap")
- #assert s.str() == '(1, 4, aap)
+ s = _cppyy.gbl.std.ostringstream('(', _cppyy.gbl.std.ios_base.ate)
+ # Fails; selects void* overload (?!)
+ #s << "("
+ _cppyy.gbl.SomeNS.tuplify(s, 1, 4., "aap")
+ assert s.str() == "(1, 4, aap, NULL)"
_cppyy.gbl.gInterpreter.Declare("""
template<typename... myTypes>
@@ -133,7 +134,7 @@
Obj2 = _cppyy.gbl.AttrTesting.Obj2
select_template_arg = _cppyy.gbl.AttrTesting.select_template_arg
- #assert select_template_arg[0, Obj1, Obj2].argument == Obj1
+ # 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)
@@ -169,7 +170,7 @@
# TODO: the ref_value property is inaccessible (offset == -1)
- # assert cppyy.gbl.BaseClassWithStatic["size_t"].ref_value == 42
+ # assert _cppyy.gbl.BaseClassWithStatic["size_t"].ref_value == 42
b1 = _cppyy.gbl.DerivedClassUsingStatic["size_t"]( 0)
b2 = _cppyy.gbl.DerivedClassUsingStatic["size_t"](100)
@@ -179,3 +180,62 @@
# assert b2.ref_value == 42
assert b2.m_value == 42
+
+
+class AppTestBOOSTANY:
+ spaceconfig = dict(usemodules=['_cppyy', '_rawffi', 'itertools'])
+
+ def setup_class(cls):
+ cls.w_test_dct = cls.space.newtext(test_dct)
+ cls.w_templates = cls.space.appexec([], """():
+ import ctypes, _cppyy
+ _cppyy._post_import_startup()""")
+
+ def test01_any_class(self):
+ """Usage of boost::any"""
+
+ import _cppyy
+
+ if not _cppyy.gbl.gInterpreter.Declare('#include "boost/any.hpp"'):
+ import warnings
+ warnings.warn('skipping boost/any testing')
+ return
+
+ assert _cppyy.gbl.boost
+ assert _cppyy.gbl.boost.any
+
+ std, boost = _cppyy.gbl.std, _cppyy.gbl.boost
+
+ assert std.list[boost.any]
+
+ val = boost.any()
+ # test both by-ref and by rvalue
+ v = std.vector[int]()
+ val.__assign__(v)
+ val.__assign__(std.move(std.vector[int](range(100))))
+
+ _cppyy.gbl.gInterpreter.ProcessLine(
+ "namespace _cppyy_internal { auto* stdvectid =
&typeid(std::vector<int>); }")
+
+ assert val.type() == _cppyy.gbl._cppyy_internal.stdvectid
+
+ extract = boost.any_cast[std.vector[int]](val)
+ assert type(extract) is std.vector[int]
+ assert len(extract) == 100
+ extract += range(100)
+ assert len(extract) == 200
+
+ val.__assign__(std.move(extract)) # move forced
+
+ # TODO: we hit boost::any_cast<int>(boost::any* operand) instead
+ # of the reference version which raises
+ boost.any_cast.__useffi__ = False
+ try:
+ # raises(Exception, boost.any_cast[int], val)
+ assert not boost.any_cast[int](val)
+ except Exception:
+ # getting here is good, too ...
+ pass
+
+ extract = boost.any_cast[std.vector[int]](val)
+ assert len(extract) == 200
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit