[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