Author: Wim Lavrijsen <[email protected]>
Branch: cppyy-packaging
Changeset: r94769:cb6f0a7dbc3a
Date: 2018-06-15 23:24 -0700
http://bitbucket.org/pypy/pypy/changeset/cb6f0a7dbc3a/
Log: improved handling of templated methods
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
@@ -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
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
@@ -721,11 +721,11 @@
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
@@ -735,9 +735,17 @@
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)
@@ -756,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, [])
@@ -783,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')
@@ -796,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):
@@ -851,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
@@ -422,8 +422,8 @@
# 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):
@@ -436,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__
@@ -476,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
@@ -517,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