Author: Wim Lavrijsen <wlavrij...@lbl.gov>
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
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to