[pypy-commit] pypy cppyy-packaging: basic std::move implementation

wlav pypy.commits at gmail.com
Wed Oct 11 19:49:04 EDT 2017


Author: Wim Lavrijsen <WLavrijsen at lbl.gov>
Branch: cppyy-packaging
Changeset: r92725:1ab7076e5b66
Date: 2017-10-11 15:39 -0700
http://bitbucket.org/pypy/pypy/changeset/1ab7076e5b66/

Log:	basic std::move implementation

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
@@ -17,6 +17,7 @@
         'addressof'              : 'interp_cppyy.addressof',
         '_bind_object'           : 'interp_cppyy._bind_object',
         'bind_object'            : 'interp_cppyy.bind_object',
+        'move'                   : 'interp_cppyy.move',
     }
 
     appleveldefs = {
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
@@ -4,7 +4,7 @@
 
 from rpython.rtyper.lltypesystem import rffi, lltype
 from rpython.rlib.rarithmetic import r_singlefloat, r_longfloat
-from rpython.rlib import rfloat
+from rpython.rlib import rfloat, rawrefcount
 
 from pypy.module._rawffi.interp_rawffi import letter2tp
 from pypy.module._rawffi.array import W_Array, W_ArrayInstance
@@ -495,6 +495,10 @@
     def _unwrap_object(self, space, w_obj):
         from pypy.module._cppyy.interp_cppyy import W_CPPClass
         if isinstance(w_obj, W_CPPClass):
+            from pypy.module._cppyy.interp_cppyy import INSTANCE_FLAGS_IS_R_VALUE
+            if w_obj.flags & INSTANCE_FLAGS_IS_R_VALUE:
+                # reject moves as all are explicit
+                raise ValueError("lvalue expected")
             if capi.c_is_subtype(space, w_obj.clsdecl, self.clsdecl):
                 rawobject = w_obj.get_rawobject()
                 offset = capi.c_base_offset(space, w_obj.clsdecl, self.clsdecl, rawobject, 1)
@@ -518,6 +522,17 @@
         x = rffi.cast(rffi.VOIDPP, address)
         x[0] = rffi.cast(rffi.VOIDP, self._unwrap_object(space, w_obj))
 
+class InstanceMoveConverter(InstanceRefConverter):
+    def _unwrap_object(self, space, w_obj):
+        # moving is same as by-ref, but have to check that move is allowed
+        from pypy.module._cppyy.interp_cppyy import W_CPPClass, INSTANCE_FLAGS_IS_R_VALUE
+        if isinstance(w_obj, W_CPPClass):
+            if w_obj.flags & INSTANCE_FLAGS_IS_R_VALUE:
+                w_obj.flags &= ~INSTANCE_FLAGS_IS_R_VALUE
+                return InstanceRefConverter._unwrap_object(self, space, w_obj)
+        raise oefmt(space.w_ValueError, "object is not an rvalue")
+
+
 class InstanceConverter(InstanceRefConverter):
 
     def convert_argument_libffi(self, space, w_obj, address, call_local):
@@ -719,6 +734,8 @@
             return InstancePtrConverter(space, clsdecl)
         elif compound == "&":
             return InstanceRefConverter(space, clsdecl)
+        elif compound == "&&":
+            return InstanceMoveConverter(space, clsdecl)
         elif compound == "**":
             return InstancePtrPtrConverter(space, clsdecl)
         elif compound == "":
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
@@ -15,6 +15,10 @@
 from pypy.module._cppyy import converter, executor, ffitypes, helper
 
 
+INSTANCE_FLAGS_PYTHON_OWNS = 0x0001
+INSTANCE_FLAGS_IS_REF      = 0x0002
+INSTANCE_FLAGS_IS_R_VALUE  = 0x0004
+
 class FastCallNotPossible(Exception):
     pass
 
@@ -1001,9 +1005,9 @@
 
 
 class W_CPPClass(W_Root):
-    _attrs_ = ['space', 'clsdecl', '_rawobject', 'isref', 'python_owns',
+    _attrs_ = ['space', 'clsdecl', '_rawobject', 'flags',
                'finalizer_registered']
-    _immutable_fields_ = ['clsdecl', 'isref']
+    _immutable_fields_ = ['clsdecl']
 
     finalizer_registered = False
 
@@ -1014,35 +1018,42 @@
         assert not isref or rawobject
         self._rawobject = rawobject
         assert not isref or not python_owns
-        self.isref = isref
-        self.python_owns = python_owns
-        self._opt_register_finalizer()
+        self.flags = 0
+        if isref:
+            self.flags |= INSTANCE_FLAGS_IS_REF
+        if python_owns:
+            self.flags |= INSTANCE_FLAGS_PYTHON_OWNS
+            self._opt_register_finalizer()
 
     def _opt_register_finalizer(self):
-        if self.python_owns and not self.finalizer_registered \
-               and not hasattr(self.space, "fake"):
+        if not self.finalizer_registered and not hasattr(self.space, "fake"):
+            assert self.flags & INSTANCE_FLAGS_PYTHON_OWNS
             self.register_finalizer(self.space)
             self.finalizer_registered = True
 
     def _nullcheck(self):
-        if not self._rawobject or (self.isref and not self.get_rawobject()):
+        if not self._rawobject or \
+               ((self.flags & INSTANCE_FLAGS_IS_REF) and not self.get_rawobject()):
             raise oefmt(self.space.w_ReferenceError,
                         "trying to access a NULL pointer")
 
     # allow user to determine ownership rules on a per object level
     def fget_python_owns(self, space):
-        return space.newbool(self.python_owns)
+        return space.newbool(self.flags & INSTANCE_FLAGS_PYTHON_OWNS)
 
     @unwrap_spec(value=bool)
     def fset_python_owns(self, space, value):
-        self.python_owns = space.is_true(value)
-        self._opt_register_finalizer()
+        if space.is_true(value):
+            self.flags |= INSTANCE_FLAGS_PYTHON_OWNS
+            self._opt_register_finalizer()
+        else:
+            self.flags &= ~INSTANCE_FLAGS_PYTHON_OWNS
 
     def get_cppthis(self, calling_scope):
         return self.clsdecl.get_cppthis(self, calling_scope)
 
     def get_rawobject(self):
-        if not self.isref:
+        if not (self.flags & INSTANCE_FLAGS_IS_REF):
             return self._rawobject
         else:
             ptrptr = rffi.cast(rffi.VOIDPP, self._rawobject)
@@ -1104,7 +1115,8 @@
         return self.space.not_(self.instance__eq__(w_other))
 
     def instance__nonzero__(self):
-        if not self._rawobject or (self.isref and not self.get_rawobject()):
+        if not self._rawobject or \
+               ((self.flags & INSTANCE_FLAGS_IS_REF) and not self.get_rawobject()):
             return self.space.w_False
         return self.space.w_True
 
@@ -1130,13 +1142,13 @@
                                (self.clsdecl.name, rffi.cast(rffi.ULONG, self.get_rawobject())))
 
     def destruct(self):
-        if self._rawobject and not self.isref:
+        if self._rawobject and not (self.flags & INSTANCE_FLAGS_IS_REF):
             memory_regulator.unregister(self)
             capi.c_destruct(self.space, self.clsdecl, self._rawobject)
             self._rawobject = capi.C_NULL_OBJECT
 
     def _finalize_(self):
-        if self.python_owns:
+        if self.flags & INSTANCE_FLAGS_PYTHON_OWNS:
             self.destruct()
 
 W_CPPClass.typedef = TypeDef(
@@ -1272,3 +1284,9 @@
                         "no such class: %s", space.text_w(w_pycppclass))
     return _bind_object(space, w_obj, w_clsdecl, owns, cast)
 
+def move(space, w_obj):
+    """Casts the given instance into an C++-style rvalue."""
+    obj = space.interp_w(W_CPPClass, w_obj, can_be_None=True)
+    if obj:
+        obj.flags |= INSTANCE_FLAGS_IS_R_VALUE
+    return w_obj
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
@@ -434,6 +434,9 @@
     # pre-create std to allow direct importing
     gbl.std = make_cppnamespace(gbl, 'std', _cppyy._scope_byname('std'))
 
+    # add move cast
+    gbl.std.move = _cppyy.move
+
     # install a type for enums to refer to
     # TODO: this is correct for C++98, not for C++11 and in general there will
     # be the same issue for all typedef'd builtin types
diff --git a/pypy/module/_cppyy/test/Makefile b/pypy/module/_cppyy/test/Makefile
--- a/pypy/module/_cppyy/test/Makefile
+++ b/pypy/module/_cppyy/test/Makefile
@@ -1,5 +1,6 @@
 dicts = advancedcppDict.so \
         advancedcpp2Dict.so \
+        cpp11featuresDict.so \
         crossingDict.so \
         datatypesDict.so \
         example01Dict.so \
diff --git a/pypy/module/_cppyy/test/cpp11features.cxx b/pypy/module/_cppyy/test/cpp11features.cxx
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cppyy/test/cpp11features.cxx
@@ -0,0 +1,18 @@
+#if __cplusplus >= 201103L
+
+#include "cpp11features.h"
+
+
+// for std::shared_ptr<> testing
+int TestSharedPtr::s_counter = 0;
+
+std::shared_ptr<TestSharedPtr> create_shared_ptr_instance() {
+    return std::shared_ptr<TestSharedPtr>(new TestSharedPtr);
+}
+
+
+// for move ctors etc.
+int TestMoving1::s_move_counter = 0;
+int TestMoving2::s_move_counter = 0;
+
+#endif // c++11 and later
diff --git a/pypy/module/_cppyy/test/cpp11features.h b/pypy/module/_cppyy/test/cpp11features.h
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cppyy/test/cpp11features.h
@@ -0,0 +1,45 @@
+#if __cplusplus >= 201103L
+
+#include <memory>
+
+
+//===========================================================================
+class TestSharedPtr {        // for std::shared_ptr<> testing
+public:
+    static int s_counter;
+
+public:
+    TestSharedPtr() { ++s_counter; }
+    TestSharedPtr(const TestSharedPtr&) { ++s_counter; }
+    ~TestSharedPtr() { --s_counter; }
+};
+
+std::shared_ptr<TestSharedPtr> create_shared_ptr_instance();
+
+
+//===========================================================================
+class TestMoving1 {          // for move ctors etc.
+public:
+    static int s_move_counter;
+
+public:
+    TestMoving1() {}
+    TestMoving1(TestMoving1&&) { ++s_move_counter; }
+    TestMoving1(const TestMoving1&) {}
+    TestMoving1& operator=(TestMoving1&&) { ++s_move_counter; return *this; }
+    TestMoving1& operator=(TestMoving1&) { return *this; }
+};
+
+class TestMoving2 {          // note opposite method order from TestMoving1
+public:
+    static int s_move_counter;
+
+public:
+    TestMoving2() {}
+    TestMoving2(const TestMoving2&) {}
+    TestMoving2(TestMoving2&& other) { ++s_move_counter; }
+    TestMoving2& operator=(TestMoving2&) { return *this; }
+    TestMoving2& operator=(TestMoving2&&) { ++s_move_counter; return *this; }
+};
+
+#endif // c++11 and later
diff --git a/pypy/module/_cppyy/test/cpp11features.xml b/pypy/module/_cppyy/test/cpp11features.xml
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cppyy/test/cpp11features.xml
@@ -0,0 +1,6 @@
+<lcgdict>
+
+  <class name="TestSharedPtr" />
+  <class pattern="TestMoving*" />
+
+</lcgdict>
diff --git a/pypy/module/_cppyy/test/test_cpp11features.py b/pypy/module/_cppyy/test/test_cpp11features.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cppyy/test/test_cpp11features.py
@@ -0,0 +1,94 @@
+import py, os, sys
+from .support import setup_make
+
+
+currpath = py.path.local(__file__).dirpath()
+test_dct = str(currpath.join("cpp11featuresDict.so"))
+
+def setup_module(mod):
+    setup_make("cpp11featuresDict.so")
+
+class AppTestCPP11FEATURES:
+    spaceconfig = dict(usemodules=['_cppyy', '_rawffi', 'itertools'])
+
+    def setup_class(cls):
+        cls.w_test_dct  = cls.space.newtext(test_dct)
+        cls.w_example01 = cls.space.appexec([], """():
+            import ctypes
+            return ctypes.CDLL(%r, ctypes.RTLD_GLOBAL)""" % (test_dct, ))
+
+    def test01_shared_ptr(self):
+        """Usage and access of std::shared_ptr<>"""
+
+        import _cppyy
+        TestSharedPtr = _cppyy.gbl.TestSharedPtr
+        create_shared_ptr_instance = _cppyy.gbl.create_shared_ptr_instance
+
+      # proper memory accounting
+        assert TestSharedPtr.s_counter == 0
+
+        ptr1 = create_shared_ptr_instance()
+        assert ptr1
+        assert not not ptr1
+        assert TestSharedPtr.s_counter == 1
+
+        ptr2 = create_shared_ptr_instance()
+        assert ptr2
+        assert not not ptr2
+        assert TestSharedPtr.s_counter == 2
+
+        del ptr2
+        import gc; gc.collect()
+        assert TestSharedPtr.s_counter == 1
+
+        del ptr1
+        gc.collect()
+        assert TestSharedPtr.s_counter == 0
+
+    def test02_nullptr(self):
+        """Allow the programmer to pass NULL in certain cases"""
+
+        import _cppyy
+
+      # test existence
+        nullptr = _cppyy.nullptr
+        assert not hasattr(_cppyy.gbl, 'nullptr')
+
+      # usage is tested in datatypes.py:test15_nullptr_passing
+
+    def test03_move(self):
+        """Move construction, assignment, and methods"""
+
+        import _cppyy
+
+        def moveit(T):
+            std = _cppyy.gbl.std
+
+          # move constructor
+            i1 = T()
+            assert T.s_move_counter == 0
+
+            i2 = T(i1)  # cctor
+            assert T.s_move_counter == 0
+
+            i3 = T(std.move(T())) # Note: in CPython can check for
+                                  # ref-count == 1, so no move() needed
+            assert T.s_move_counter == 1
+
+            i4 = T(std.move(i1))
+            assert T.s_move_counter == 2
+
+          # move assignment
+            i4.__assign__(i2)
+            assert T.s_move_counter == 2
+
+            i4.__assign__(std.move(T())) # same note as above move ctor
+            assert T.s_move_counter == 3
+
+            i4.__assign__(std.move(i2))
+            assert T.s_move_counter == 4
+
+      # order of moving and normal functions are reversed in 1, 2, for
+      # overload resolution testing
+        moveit(_cppyy.gbl.TestMoving1)
+        moveit(_cppyy.gbl.TestMoving2)


More information about the pypy-commit mailing list