[pypy-commit] pypy reflex-support: clean and refactoring of methods and class creation
wlav
noreply at buildbot.pypy.org
Thu May 1 09:26:11 CEST 2014
Author: Wim Lavrijsen <WLavrijsen at lbl.gov>
Branch: reflex-support
Changeset: r71118:af753187637f
Date: 2014-04-29 11:41 -0700
http://bitbucket.org/pypy/pypy/changeset/af753187637f/
Log: clean and refactoring of methods and class creation
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
@@ -16,7 +16,7 @@
'_register_class' : 'interp_cppyy.register_class',
'_is_static' : 'interp_cppyy.is_static',
'_get_nullptr' : 'interp_cppyy.get_nullptr',
- 'CPPInstance' : 'interp_cppyy.W_CPPInstance',
+ 'CPPInstanceBase' : 'interp_cppyy.W_CPPInstance',
'addressof' : 'interp_cppyy.addressof',
'bind_object' : 'interp_cppyy.bind_object',
}
@@ -25,7 +25,7 @@
'_init_pythonify' : 'pythonify._init_pythonify',
'load_reflection_info' : 'pythonify.load_reflection_info',
'add_pythonization' : 'pythonify.add_pythonization',
- 'Template' : 'pythonify.CppyyTemplateType',
+ 'Template' : 'pythonify.CPPTemplate',
}
def __init__(self, space, *args):
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
@@ -155,8 +155,7 @@
the memory_regulator."""
_attrs_ = ['space', 'scope', 'index', 'cppmethod', 'arg_defs', 'args_required',
- 'args_expected', 'converters', 'executor', '_funcaddr', 'cif_descr',
- 'uses_local']
+ 'converters', 'executor', '_funcaddr', 'cif_descr', 'uses_local']
_immutable_ = True
def __init__(self, space, containing_scope, method_index, arg_defs, args_required):
@@ -166,7 +165,6 @@
self.cppmethod = capi.c_get_method(self.space, self.scope, method_index)
self.arg_defs = arg_defs
self.args_required = args_required
- self.args_expected = len(arg_defs)
# Setup of the method dispatch's innards is done lazily, i.e. only when
# the method is actually used.
@@ -183,6 +181,12 @@
loc_idx = lltype.direct_ptradd(rffi.cast(rffi.CCHARP, call_local), idx*stride)
return rffi.cast(rffi.VOIDP, loc_idx)
+ def call_w(self, w_cppinstance, args_w):
+ cppinstance = self.space.interp_w(W_CPPInstance, w_cppinstance, can_be_None=False)
+ cppinstance._nullcheck()
+ cppthis = cppinstance.get_cppthis(self.scope)
+ return self.call(cppthis, args_w)
+
@jit.unroll_safe
def call(self, cppthis, args_w):
assert lltype.typeOf(cppthis) == capi.C_OBJECT
@@ -277,7 +281,7 @@
funcaddr = methgetter(rffi.cast(capi.C_OBJECT, cppthis))
self._funcaddr = rffi.cast(rffi.VOIDP, funcaddr)
- nargs = self.args_expected + 1 # +1: cppthis
+ nargs = len(self.arg_defs) + 1 # +1: cppthis
# memory block for CIF description (note: not tracked as the life
# time of methods is normally the duration of the application)
@@ -335,7 +339,7 @@
# extra
cif_descr.abi = clibffi.FFI_DEFAULT_ABI
- cif_descr.nargs = self.args_expected + 1 # +1: cppthis
+ cif_descr.nargs = len(self.arg_defs) + 1 # +1: cppthis
res = jit_libffi.jit_ffi_prep_cif(cif_descr)
if res != clibffi.FFI_OK:
@@ -405,21 +409,21 @@
class CPPFunction(CPPMethod):
- """Global (namespaced) function dispatcher. For now, the base class has
- all the needed functionality, by allowing the C++ this pointer to be null
- in the call. An optimization is expected there, however."""
+ """Global (namespaced) function dispatcher."""
_immutable_ = True
+ def call_w(self, w_cppinstance, args_w):
+ return CPPMethod.call(self, capi.C_NULL_OBJECT, args_w)
+
def __repr__(self):
return "CPPFunction: %s" % self.signature()
class CPPTemplatedCall(CPPMethod):
- """Method dispatcher that first needs to resolve the template instance.
- Note that the derivation is from object: the CPPMethod is a data member."""
+ """Method dispatcher that first resolves the template instance."""
- _attrs_ = ['space', 'templ_args', 'method']
+ _attrs_ = ['space', 'templ_args']
_immutable_ = True
def __init__(self, space, templ_args, containing_scope, method_index, arg_defs, args_required):
@@ -456,22 +460,17 @@
_immutable_ = True
- def call(self, cppthis, args_w):
+ def call_w(self, w_cppinstance, args_w):
# TODO: these casts are very, very un-pretty; need to find a way of
# re-using CPPMethod's features w/o these roundabouts
vscope = rffi.cast(capi.C_OBJECT, self.scope.handle)
- cppinstance = None
- try:
- cppinstance = self.space.interp_w(W_CPPInstance, args_w[0], can_be_None=False)
- use_args_w = args_w[1:]
- except (OperationError, TypeError), e:
- use_args_w = args_w
- w_result = CPPMethod.call(self, vscope, use_args_w)
+ cppinstance = self.space.interp_w(W_CPPInstance, w_cppinstance, can_be_None=True)
+ w_result = CPPMethod.call(self, vscope, args_w)
newthis = rffi.cast(capi.C_OBJECT, self.space.int_w(w_result))
- if cppinstance:
+ if cppinstance is not None:
cppinstance._rawobject = newthis
memory_regulator.register(cppinstance)
- return args_w[0]
+ return w_cppinstance
return wrap_cppobject(self.space, newthis, self.scope,
do_cast=False, python_owns=True, fresh=True)
@@ -508,6 +507,7 @@
def __init__(self, space, containing_scope, functions):
self.space = space
self.scope = containing_scope
+ assert len(functions)
from rpython.rlib import debug
self.functions = debug.make_sure_not_resized(functions)
@@ -520,14 +520,6 @@
@jit.unroll_safe
@unwrap_spec(args_w='args_w')
def call(self, w_cppinstance, args_w):
- cppinstance = self.space.interp_w(W_CPPInstance, w_cppinstance, can_be_None=True)
- if cppinstance is not None:
- cppinstance._nullcheck()
- cppthis = cppinstance.get_cppthis(self.scope)
- else:
- cppthis = capi.C_NULL_OBJECT
- assert lltype.typeOf(cppthis) == capi.C_OBJECT
-
# The following code tries out each of the functions in order. If
# argument conversion fails (or simply if the number of arguments do
# not match), that will lead to an exception, The JIT will snip out
@@ -542,7 +534,7 @@
for i in range(len(self.functions)):
cppyyfunc = self.functions[i]
try:
- return cppyyfunc.call(cppthis, args_w)
+ return cppyyfunc.call_w(w_cppinstance, args_w)
except Exception:
pass
@@ -553,7 +545,7 @@
for i in range(len(self.functions)):
cppyyfunc = self.functions[i]
try:
- return cppyyfunc.call(cppthis, args_w)
+ return cppyyfunc.call_w(w_cppinstance, args_w)
except OperationError, e:
# special case if there's just one function, to prevent clogging the error message
if len(self.functions) == 1:
@@ -906,7 +898,7 @@
def construct(self):
if self.default_constructor is not None:
- return self.default_constructor.call(capi.C_NULL_OBJECT, [])
+ return self.default_constructor.call(capi.C_NULL_OBJECT, None, [])
raise self.missing_attribute_error("default_constructor")
def find_overload(self, name):
@@ -1046,6 +1038,16 @@
raise
return None
+ def instance__init__(self, args_w):
+ try:
+ constructor_overload = self.cppclass.get_overload(self.cppclass.name)
+ constructor_overload.call(self, args_w)
+ except OperationError, e:
+ if not e.match(self.space, self.space.w_AttributeError):
+ raise
+ raise OperationError(self.space.w_TypeError,
+ self.space.wrap("cannot instantiate abstract class '%s'" % self.cppclass.name))
+
def instance__eq__(self, w_other):
# special case: if other is None, compare pointer-style
if self.space.is_w(w_other, self.space.w_None):
@@ -1128,6 +1130,7 @@
'CPPInstance',
cppclass = interp_attrproperty('cppclass', cls=W_CPPInstance),
_python_owns = GetSetProperty(W_CPPInstance.fget_python_owns, W_CPPInstance.fset_python_owns),
+ __init__ = interp2app(W_CPPInstance.instance__init__),
__eq__ = interp2app(W_CPPInstance.instance__eq__),
__ne__ = interp2app(W_CPPInstance.instance__ne__),
__nonzero__ = interp2app(W_CPPInstance.instance__nonzero__),
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
@@ -7,7 +7,7 @@
# with info from multiple dictionaries and do not need to bother with meta
# classes for inheritance. Both are python classes, though, and refactoring
# may be in order at some point.
-class CppyyScopeMeta(type):
+class CPPScope(type):
def __getattr__(self, name):
try:
return get_pycppitem(self, name) # will cache on self
@@ -15,16 +15,16 @@
raise AttributeError("%s object has no attribute '%s' (details: %s)" %
(self, name, str(e)))
-class CppyyNamespaceMeta(CppyyScopeMeta):
+class CPPNamespace(CPPScope):
def __dir__(cls):
return cls._cpp_proxy.__dir__()
-class CppyyClassMeta(CppyyScopeMeta):
+class CPPClass(CPPScope):
pass
-# class CppyyClass defined in _init_pythonify()
+# class CPPInstance defined in _init_pythonify()
-class CppyyTemplateType(object):
+class CPPTemplate(object):
def __init__(self, name, scope=None):
self._name = name
if scope is None:
@@ -91,7 +91,7 @@
# build up a representation of a C++ namespace (namespaces are classes)
# create a meta class to allow properties (for static data write access)
- metans = type(CppyyNamespaceMeta)(namespace_name+'_meta', (CppyyNamespaceMeta,), {})
+ metans = type(CPPNamespace)(namespace_name+'_meta', (CPPNamespace,), {})
if cppns:
d = {"_cpp_proxy" : cppns}
@@ -137,21 +137,14 @@
break
return tuple(bases)
-def make_new(class_name, cppclass):
- try:
- constructor_overload = cppclass.get_overload(cppclass.type_name)
- except AttributeError:
- msg = "cannot instantiate abstract class '%s'" % class_name
- def __new__(cls, *args):
- raise TypeError(msg)
- else:
- def __new__(cls, *args):
- # create a place-holder only as there may be a derived class defined
- import cppyy
- instance = cppyy.bind_object(0, class_name, True)
- if not instance.__class__ is cls:
- instance.__class__ = cls # happens for derived class
- return instance
+def make_new(class_name):
+ def __new__(cls, *args):
+ # create a place-holder only as there may be a derived class defined
+ import cppyy
+ instance = cppyy.bind_object(0, class_name, True)
+ if not instance.__class__ is cls:
+ instance.__class__ = cls # happens for derived class
+ return instance
return __new__
def make_pycppclass(scope, class_name, final_class_name, cppclass):
@@ -159,7 +152,7 @@
# get a list of base classes for class creation
bases = [get_pycppclass(base) for base in cppclass.get_base_names()]
if not bases:
- bases = [CppyyClass,]
+ bases = [CPPInstance,]
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
@@ -170,7 +163,7 @@
# create a meta class to allow properties (for static data write access)
metabases = [type(base) for base in bases]
- metacpp = type(CppyyClassMeta)(class_name+'_meta', _drop_cycles(metabases), {})
+ metacpp = type(CPPClass)(class_name+'_meta', _drop_cycles(metabases), {})
# create the python-side C++ class representation
def dispatch(self, name, signature):
@@ -178,7 +171,7 @@
return types.MethodType(make_method(name, cppol), self, type(self))
d = {"_cpp_proxy" : cppclass,
"__dispatch__" : dispatch,
- "__new__" : make_new(class_name, cppclass),
+ "__new__" : make_new(class_name),
}
pycppclass = metacpp(class_name, _drop_cycles(bases), d)
@@ -214,7 +207,7 @@
return pycppclass
def make_cpptemplatetype(scope, template_name):
- return CppyyTemplateType(template_name, scope)
+ return CPPTemplate(template_name, scope)
def get_pycppitem(scope, name):
@@ -426,15 +419,12 @@
# at pypy-c startup, rather than on the "import cppyy" statement
import cppyy
- # top-level classes
- global CppyyClass
- class CppyyClass(cppyy.CPPInstance):
- __metaclass__ = CppyyClassMeta
-
- def __init__(self, *args, **kwds):
- # self is only a placeholder; now create the actual C++ object
- args = (self,) + args
- self._cpp_proxy.get_overload(self._cpp_proxy.type_name).call(None, *args)
+ # root of all proxy classes: CPPInstance in pythonify exists to combine the
+ # CPPClass meta class with the interp-level CPPInstanceBase
+ global CPPInstance
+ class CPPInstance(cppyy.CPPInstanceBase):
+ __metaclass__ = CPPClass
+ pass
# class generator callback
cppyy._set_class_generator(clgen_callback)
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
@@ -112,4 +112,9 @@
enum E2 { kTwice=12 };
};
+class O {
+public:
+ virtual int abstract() = 0;
+};
+
} // namespace fragile
diff --git a/pypy/module/cppyy/test/fragile_LinkDef.h b/pypy/module/cppyy/test/fragile_LinkDef.h
--- a/pypy/module/cppyy/test/fragile_LinkDef.h
+++ b/pypy/module/cppyy/test/fragile_LinkDef.h
@@ -23,6 +23,7 @@
#pragma link C++ class fragile::L;
#pragma link C++ class fragile::M;
#pragma link C++ class fragile::N;
+#pragma link C++ class fragile::O;
#pragma link C++ class fragile::nested1::A;
#pragma link C++ class fragile::nested1::nested2::A;
#pragma link C++ class fragile::nested1::nested2::nested3::A;
diff --git a/pypy/module/cppyy/test/test_fragile.py b/pypy/module/cppyy/test/test_fragile.py
--- a/pypy/module/cppyy/test/test_fragile.py
+++ b/pypy/module/cppyy/test/test_fragile.py
@@ -202,6 +202,12 @@
f = fragile.fglobal
assert f.__doc__ == "void fragile::fglobal(int, double, char)"
+ try:
+ o = fragile.O() # raises TypeError
+ assert 0
+ except TypeError, e:
+ assert "cannot instantiate abstract class 'O'" in str(e)
+
def test11_dir(self):
"""Test __dir__ method"""
diff --git a/pypy/module/cppyy/test/test_pythonify.py b/pypy/module/cppyy/test/test_pythonify.py
--- a/pypy/module/cppyy/test/test_pythonify.py
+++ b/pypy/module/cppyy/test/test_pythonify.py
@@ -338,8 +338,13 @@
import cppyy
example01 = cppyy.gbl.example01
+ assert example01.getCount() == 0
+
o = example01()
assert type(o) == example01
+ assert example01.getCount() == 1
+ o.destruct()
+ assert example01.getCount() == 0
class MyClass1(example01):
def myfunc(self):
@@ -348,7 +353,10 @@
o = MyClass1()
assert type(o) == MyClass1
assert isinstance(o, example01)
+ assert example01.getCount() == 1
assert o.myfunc() == 1
+ o.destruct()
+ assert example01.getCount() == 0
class MyClass2(example01):
def __init__(self, what):
@@ -357,7 +365,11 @@
o = MyClass2('hi')
assert type(o) == MyClass2
+ assert example01.getCount() == 1
assert o.what == 'hi'
+ o.destruct()
+
+ assert example01.getCount() == 0
class AppTestPYTHONIFY_UI:
More information about the pypy-commit
mailing list