Author: Wim Lavrijsen <wlavrij...@lbl.gov>
Branch: cppyy-packaging
Changeset: r94854:80195e50eca4
Date: 2018-07-12 12:39 -0700
http://bitbucket.org/pypy/pypy/changeset/80195e50eca4/

Log:    improve object identity handling (now also includes data members)

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
@@ -566,10 +566,6 @@
         from pypy.module._cppyy import interp_cppyy
         return interp_cppyy.wrap_cppinstance(space, address, self.clsdecl, 
do_cast=False)
 
-    def to_memory(self, space, w_obj, w_value, offset):
-        address = rffi.cast(rffi.VOIDPP, self._get_raw_address(space, w_obj, 
offset))
-        address[0] = rffi.cast(rffi.VOIDP, self._unwrap_object(space, w_value))
-
 class InstancePtrPtrConverter(InstancePtrConverter):
     typecode = 'o'
 
@@ -597,6 +593,25 @@
         return interp_cppyy.wrap_cppinstance(
             space, address, self.clsdecl, do_cast=False, is_ref=True)
 
+    def to_memory(self, space, w_obj, w_value, offset):
+        # the actual data member is of object* type, but we receive a pointer 
to that
+        # data member in order to modify its value, so by convention, the 
internal type
+        # used is object**
+        address = rffi.cast(rffi.VOIDPP, self._get_raw_address(space, w_obj, 
offset))
+        from pypy.module._cppyy.interp_cppyy import W_CPPInstance
+        cppinstance = space.interp_w(W_CPPInstance, w_value, can_be_None=True)
+        if cppinstance:
+            rawobject = cppinstance.get_rawobject()
+            offset = capi.c_base_offset(space, cppinstance.clsdecl, 
self.clsdecl, rawobject, 1)
+            obj_address = capi.direct_ptradd(rawobject, offset)
+            address[0] = rffi.cast(rffi.VOIDP, obj_address);
+            # register the value for potential recycling
+            from pypy.module._cppyy.interp_cppyy import memory_regulator
+            memory_regulator.register(cppinstance)
+        else:
+            raise oefmt(space.w_TypeError,
+                "cannot pass %T instance as %s", w_value, self.clsdecl.name)
+
     def finalize_call(self, space, w_obj):
         if self.ref_buffer:
             set_rawobject(space, w_obj, self.ref_buffer[0])
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
@@ -167,6 +167,7 @@
 #
 #  W_CPPOverload:                 instance methods (base class)
 #  W_CPPConstructorOverload:      constructors
+#  W_CPPAbstractCtorOverload:     to provent instantiation of abstract classes
 #  W_CPPStaticOverload:           free and static functions
 #  W_CPPTemplateOverload:         templated methods
 #  W_CPPTemplateStaticOverload:   templated free and static functions
@@ -674,7 +675,7 @@
     __doc__    = GetSetProperty(W_CPPConstructorOverload.fget_doc)
 )
 
-class W_CPPAbstractConstructorOverload(W_CPPOverload):
+class W_CPPAbstractCtorOverload(W_CPPOverload):
     _attrs_ = []
 
     @unwrap_spec(args_w='args_w')
@@ -683,12 +684,12 @@
             "cannot instantiate abstract class '%s'", self.scope.name)
 
     def __repr__(self):
-        return "W_CPPAbstractConstructorOverload"
+        return "W_CPPAbstractCtorOverload"
 
-W_CPPAbstractConstructorOverload.typedef = TypeDef(
-    'CPPAbstractConstructorOverload',
-    __get__    = interp2app(W_CPPAbstractConstructorOverload.descr_get),
-    __call__   = interp2app(W_CPPAbstractConstructorOverload.call_args),
+W_CPPAbstractCtorOverload.typedef = TypeDef(
+    'CPPAbstractCtorOverload',
+    __get__    = interp2app(W_CPPAbstractCtorOverload.descr_get),
+    __call__   = interp2app(W_CPPAbstractCtorOverload.call_args),
 )
 
 
@@ -1086,7 +1087,7 @@
         sig = '(%s)' % signature
         for f in overload.functions:
             if f.signature(False) == sig:
-                return W_CPPOverload(self.space, self, [f])
+                return type(overload)(self.space, self, [f])
         raise oefmt(self.space.w_LookupError, "no overload matches signature")
 
     def __eq__(self, other):
@@ -1181,9 +1182,13 @@
 
 
 class W_CPPClassDecl(W_CPPScopeDecl):
-    _attrs_ = ['space', 'handle', 'name', 'overloads', 'datamembers']
+    _attrs_ = ['space', 'handle', 'name', 'overloads', 'datamembers', 
'cppobjects']
     _immutable_fields_ = ['handle', 'name', 'overloads[*]', 'datamembers[*]']
 
+    def __init__(self, space, opaque_handle, final_scoped_name):
+        W_CPPScopeDecl.__init__(self, space, opaque_handle, final_scoped_name)
+        self.cppobjects = rweakref.RWeakValueDictionary(int, W_CPPInstance)
+
     def _build_overloads(self):
         assert len(self.overloads) == 0
         methods_tmp = {}; ftype_tmp = {}
@@ -1223,7 +1228,7 @@
             CPPMethodSort(methods).sort()
             if ftype & FUNCTION_IS_CONSTRUCTOR:
                 if capi.c_is_abstract(self.space, self.handle):
-                    overload = W_CPPAbstractConstructorOverload(self.space, 
self, methods[:])
+                    overload = W_CPPAbstractCtorOverload(self.space, self, 
methods[:])
                 else:
                     overload = W_CPPConstructorOverload(self.space, self, 
methods[:])
             elif ftype & FUNCTION_IS_STATIC:
@@ -1543,31 +1548,33 @@
 
 
 class MemoryRegulator:
-    # TODO: (?) An object address is not unique if e.g. the class has a
-    # public data member of class type at the start of its definition and
-    # has no virtual functions. A _key class that hashes on address and
-    # type would be better, but my attempt failed in the rtyper, claiming
-    # a call on None ("None()") and needed a default ctor. (??)
-    # Note that for now, the associated test carries an m_padding to make
-    # a difference in the addresses.
-    def __init__(self):
-        self.objects = rweakref.RWeakValueDictionary(int, W_CPPInstance)
+    _immutable_ = True
 
-    def register(self, obj):
+    @staticmethod
+    def register(obj):
         if not obj._rawobject:
             return
-        int_address = int(rffi.cast(rffi.LONG, obj._rawobject))
-        self.objects.set(int_address, obj)
+        addr_as_int = int(rffi.cast(rffi.LONG, obj.get_rawobject()))
+        clsdecl = obj.clsdecl
+        assert isinstance(clsdecl, W_CPPClassDecl)
+        clsdecl.cppobjects.set(addr_as_int, obj)
 
-    def unregister(self, obj):
+    @staticmethod
+    def unregister(obj):
         if not obj._rawobject:
             return
-        int_address = int(rffi.cast(rffi.LONG, obj._rawobject))
-        self.objects.set(int_address, None)
+        addr_as_int = int(rffi.cast(rffi.LONG, obj.get_rawobject()))
+        clsdecl = obj.clsdecl
+        assert isinstance(clsdecl, W_CPPClassDecl)
+        clsdecl.cppobjects.set(addr_as_int, None) # actually deletes (pops)
 
-    def retrieve(self, address):
-        int_address = int(rffi.cast(rffi.LONG, address))
-        return self.objects.get(int_address)
+    @staticmethod
+    def retrieve(clsdecl, address):
+        if not address:
+            return None
+        addr_as_int = int(rffi.cast(rffi.LONG, address))
+        assert isinstance(clsdecl, W_CPPClassDecl)
+        return clsdecl.cppobjects.get(addr_as_int)
 
 memory_regulator = MemoryRegulator()
 
@@ -1613,8 +1620,11 @@
 
     # try to recycle existing object if this one is not newly created
     if not fresh and rawobject:
-        obj = memory_regulator.retrieve(rawobject)
-        if obj is not None and obj.clsdecl is clsdecl:
+        address = rawobject
+        if is_ref:
+            address = rffi.cast(capi.C_OBJECT, rffi.cast(rffi.VOIDPP, 
address)[0])
+        obj = memory_regulator.retrieve(clsdecl, address)
+        if obj is not None:
             return obj
 
     # fresh creation
diff --git a/pypy/module/_cppyy/test/test_advancedcpp.py 
b/pypy/module/_cppyy/test/test_advancedcpp.py
--- a/pypy/module/_cppyy/test/test_advancedcpp.py
+++ b/pypy/module/_cppyy/test/test_advancedcpp.py
@@ -489,6 +489,23 @@
         d2.__destruct__()
         d1.__destruct__()
 
+        RTS = cppyy.gbl.refers_to_self
+
+        r1 = RTS()
+        r2 = RTS()
+        r1.m_other = r2
+
+        r3 = r1.m_other
+        r4 = r1.m_other
+        assert r3 is r4
+
+        assert r3 == r2
+        assert r3 is r2
+
+        r3.extra = 42
+        assert r2.extra == 42
+        assert r4.extra == 42
+
     def test11_multi_methods(self):
         """Test calling of methods from multiple inheritance"""
 
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to