Author: Wim Lavrijsen <[email protected]>
Branch: cppyy-packaging
Changeset: r94449:e7331182c14c
Date: 2018-04-24 13:39 -0700
http://bitbucket.org/pypy/pypy/changeset/e7331182c14c/
Log: const correctness for data members and associated tests
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
@@ -7,6 +7,7 @@
interpleveldefs = {
'_resolve_name' : 'interp_cppyy.resolve_name',
'_scope_byname' : 'interp_cppyy.scope_byname',
+ '_is_static_data' : 'interp_cppyy.is_static_data',
'_is_template' : 'interp_cppyy.is_template',
'_std_string_name' : 'interp_cppyy.std_string_name',
'_set_class_generator' : 'interp_cppyy.set_class_generator',
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
@@ -149,6 +149,24 @@
W_CPPLibrary.typedef.acceptable_as_base_class = True
+#-----
+# Classes involved with methods and functions:
+#
+# CPPMethod: base class wrapping a single function or method
+# CPPConstructor: specialization for allocating a new object
+# CPPFunction: specialization for free and static functions
+# CPPSetItem: specialization for Python's __setitem__
+# CPPTemplatedCall: trampoline to instantiate and bind templated functions
+# W_CPPOverload, W_CPPConstructorOverload, W_CPPTemplateOverload:
+# user-facing, app-level, collection of overloads, with specializations
+# for constructors and templates
+# W_CPPBoundMethod: instantiated template method
+#
+# All methods/functions derive from CPPMethod and are collected as overload
+# candidates in user-facing overload classes. Templated methods are a two-step
+# process, where first the template is instantiated (or selected if already
+# available), which returns a callable object that is the actual bound method.
+
class CPPMethod(object):
"""Dispatcher of methods. Checks the arguments, find the corresponding FFI
function if available, makes the call, and returns the wrapped result. It
@@ -688,6 +706,18 @@
)
+#-----
+# Classes for data members:
+#
+# W_CPPDataMember: instance data members
+# W_CPPConstDataMember: specialization for const data members
+# W_CPPStaticData: class-level and global/static data
+# W_CPPConstStaticData: specialization for const global/static data
+#
+# Data is represented by an offset which is either a global pointer (static
data)
+# or an offset from the start of an instance (data members). The "const"
+# specializations raise when attempting to set their value.
+
class W_CPPDataMember(W_Root):
_attrs_ = ['space', 'scope', 'converter', 'offset']
_immutable_fields = ['scope', 'converter', 'offset']
@@ -698,9 +728,6 @@
self.converter = converter.get_converter(self.space, type_name, '')
self.offset = offset
- def is_static(self):
- return self.space.w_False
-
def _get_offset(self, cppinstance):
if cppinstance:
assert lltype.typeOf(cppinstance.clsdecl.handle) ==
lltype.typeOf(self.scope.handle)
@@ -728,16 +755,25 @@
W_CPPDataMember.typedef = TypeDef(
'CPPDataMember',
- is_static = interp2app(W_CPPDataMember.is_static),
__get__ = interp2app(W_CPPDataMember.get),
__set__ = interp2app(W_CPPDataMember.set),
)
W_CPPDataMember.typedef.acceptable_as_base_class = False
+
+class W_CPPConstDataMember(W_CPPDataMember):
+ def set(self, w_cppinstance, w_value):
+ raise oefmt(self.space.w_TypeError, "assignment to const data not
allowed")
+
+W_CPPConstDataMember.typedef = TypeDef(
+ 'CPPConstDataMember',
+ __get__ = interp2app(W_CPPDataMember.get),
+ __set__ = interp2app(W_CPPConstDataMember.set),
+)
+W_CPPConstDataMember.typedef.acceptable_as_base_class = False
+
+
class W_CPPStaticData(W_CPPDataMember):
- def is_static(self):
- return self.space.w_True
-
@jit.elidable_promote()
def _get_offset(self, cppinstance):
return self.offset
@@ -751,19 +787,34 @@
W_CPPStaticData.typedef = TypeDef(
'CPPStaticData',
- is_static = interp2app(W_CPPStaticData.is_static),
__get__ = interp2app(W_CPPStaticData.get),
__set__ = interp2app(W_CPPStaticData.set),
)
W_CPPStaticData.typedef.acceptable_as_base_class = False
-def is_static(space, w_obj):
+
+class W_CPPConstStaticData(W_CPPStaticData):
+ def set(self, w_cppinstance, w_value):
+ raise oefmt(self.space.w_TypeError, "assignment to const data not
allowed")
+
+W_CPPConstStaticData.typedef = TypeDef(
+ 'CPPConstStaticData',
+ __get__ = interp2app(W_CPPConstStaticData.get),
+ __set__ = interp2app(W_CPPConstStaticData.set),
+)
+W_CPPConstStaticData.typedef.acceptable_as_base_class = False
+
+
+def is_static_data(space, w_obj):
try:
space.interp_w(W_CPPStaticData, w_obj, can_be_None=False)
return space.w_True
except Exception:
return space.w_False
+#-----
+
+
class W_CPPScopeDecl(W_Root):
_attrs_ = ['space', 'handle', 'name', 'methods', 'datamembers']
_immutable_fields_ = ['handle', 'name']
@@ -847,7 +898,10 @@
offset = capi.c_datamember_offset(self.space, self, dm_idx)
if offset == -1:
raise self.missing_attribute_error(dm_name)
- datamember = W_CPPStaticData(self.space, self, type_name, offset)
+ if capi.c_is_const_data(self.space, self, dm_idx):
+ datamember = W_CPPConstStaticData(self.space, self, type_name,
offset)
+ else:
+ datamember = W_CPPStaticData(self.space, self, type_name, offset)
self.datamembers[dm_name] = datamember
return datamember
@@ -967,8 +1021,13 @@
if offset == -1:
continue # dictionary problem; raises AttributeError on
use
is_static = bool(capi.c_is_staticdata(self.space, self, i))
- if is_static:
+ is_const = bool(capi.c_is_const_data(self.space, self, i))
+ if is_static and is_const:
+ datamember = W_CPPConstStaticData(self.space, self, type_name,
offset)
+ elif is_static:
datamember = W_CPPStaticData(self.space, self, type_name,
offset)
+ elif is_const:
+ datamember = W_CPPConstDataMember(self.space, self, type_name,
offset)
else:
datamember = W_CPPDataMember(self.space, self, type_name,
offset)
self.datamembers[datamember_name] = datamember
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
@@ -169,6 +169,7 @@
return method
def make_cppclass(scope, cl_name, decl):
+ import _cppyy
# get a list of base classes for class creation
bases = [get_pycppclass(base) for base in decl.get_base_names()]
@@ -209,7 +210,7 @@
for d_name in decl.get_datamember_names():
cppdm = decl.get_datamember(d_name)
d_class[d_name] = cppdm
- if cppdm.is_static():
+ if _cppyy._is_static_data(cppdm):
d_meta[d_name] = cppdm
# create a metaclass to allow properties (for static data write access)
@@ -278,7 +279,7 @@
try:
cppdm = scope.__cppdecl__.get_datamember(name)
setattr(scope, name, cppdm)
- if cppdm.is_static():
+ if _cppyy._is_static_data(cppdm):
setattr(scope.__class__, name, cppdm)
pycppitem = getattr(scope, name) # gets actual property value
except AttributeError:
diff --git a/pypy/module/_cppyy/test/datatypes.cxx
b/pypy/module/_cppyy/test/datatypes.cxx
--- a/pypy/module/_cppyy/test/datatypes.cxx
+++ b/pypy/module/_cppyy/test/datatypes.cxx
@@ -6,7 +6,7 @@
//===========================================================================
-CppyyTestData::CppyyTestData() : m_owns_arrays(false)
+CppyyTestData::CppyyTestData() : m_const_int(17), m_owns_arrays(false)
{
m_bool = false;
m_char = 'a';
@@ -333,3 +333,17 @@
CppyyTestPod* get_null_pod() {
return (CppyyTestPod*)0;
}
+
+
+//= function pointer passing ================================================
+int sum_of_int(int i1, int i2) {
+ return i1+i2;
+}
+
+double sum_of_double(double d1, double d2) {
+ return d1+d2;
+}
+
+double call_double_double(double (*d)(double, double), double d1, double d2) {
+ return d(d1, d2);
+}
diff --git a/pypy/module/_cppyy/test/datatypes.h
b/pypy/module/_cppyy/test/datatypes.h
--- a/pypy/module/_cppyy/test/datatypes.h
+++ b/pypy/module/_cppyy/test/datatypes.h
@@ -1,5 +1,5 @@
// copied from RtypesCore.h ...
-#if defined(R__WIN32)
+#if defined(R__WIN32) && !defined(__CINT__)
typedef __int64 Long64_t; //Portable signed long integer 8 bytes
typedef unsigned __int64 ULong64_t; //Portable unsigned long integer 8 bytes
#else
@@ -26,8 +26,13 @@
//===========================================================================
namespace EnumSpace {
- enum E {E1 = 1, E2};
-};
+ enum E {E1 = 1, E2};
+ class EnumClass {
+ public:
+ enum {E1 = -1};
+ enum EE {E2 = -1};
+ };
+}
//===========================================================================
@@ -243,6 +248,7 @@
short m_short;
unsigned short m_ushort;
int m_int;
+ const int m_const_int; // special case: const testing
unsigned int m_uint;
long m_long;
unsigned long m_ulong;
@@ -364,3 +370,9 @@
void set_global_pod(CppyyTestPod* t);
CppyyTestPod* get_global_pod();
CppyyTestPod* get_null_pod();
+
+
+//= function pointer passing ================================================
+int sum_of_int(int i1, int i2);
+double sum_of_double(double d1, double d2);
+double call_double_double(double (*d)(double, double), double d1, double d2);
diff --git a/pypy/module/_cppyy/test/datatypes.xml
b/pypy/module/_cppyy/test/datatypes.xml
--- a/pypy/module/_cppyy/test/datatypes.xml
+++ b/pypy/module/_cppyy/test/datatypes.xml
@@ -4,6 +4,8 @@
<class name="FourVector" />
<enum name="EFruit" />
+ <enum name="EnumSpace::E" />
+ <class name="EnumSpace::EnumClass" />
<function pattern="get_*" />
<function pattern="set_*" />
@@ -14,4 +16,8 @@
<variable name="g_int" />
<variable name="g_pod" />
+ <function name="sum_of_int" />
+ <function name="sum_of_double" />
+ <function name="call_double_double" />
+
</lcgdict>
diff --git a/pypy/module/_cppyy/test/test_datatypes.py
b/pypy/module/_cppyy/test/test_datatypes.py
--- a/pypy/module/_cppyy/test/test_datatypes.py
+++ b/pypy/module/_cppyy/test/test_datatypes.py
@@ -191,6 +191,10 @@
for i in range(self.N):
assert eval('c.m_%s_array2[i]' % names[j]) == b[i]
+ # can not write to constant data
+ assert c.m_const_int == 17
+ raises(TypeError, setattr, c, 'm_const_int', 71)
+
c.__destruct__()
def test03_array_passing(self):
@@ -464,6 +468,10 @@
assert gbl.kBanana == 29
assert gbl.kCitrus == 34
+ assert gbl.EnumSpace.E
+ assert gbl.EnumSpace.EnumClass.E1 == -1 # anonymous
+ assert gbl.EnumSpace.EnumClass.E2 == -1 # named type
+
def test11_string_passing(self):
"""Test passing/returning of a const char*"""
@@ -741,3 +749,19 @@
c.s_voidp = c2
address_equality_test(c.s_voidp, c2)
+
+ def test21_function_pointers(self):
+ """Function pointer passing"""
+
+ import _cppyy as cppyy
+
+ f1 = cppyy.gbl.sum_of_int
+ f2 = cppyy.gbl.sum_of_double
+ f3 = cppyy.gbl.call_double_double
+
+ assert 5 == f1(2, 3)
+ assert 5. == f2(5., 0.)
+
+ raises(TypeError, f3, f1, 2, 3)
+
+ assert 5. == f3(f2, 5., 0.)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit