[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