Commits

wlav  committed 0bb7cb0

write support for static (class-level) data members

  • Participants
  • Parent commits e8c0d8a
  • Branches reflex-support

Comments (0)

Files changed (5)

File pypy/module/cppyy/converter.py

 
 def get_rawobject(space, w_obj):
     from pypy.module.cppyy.interp_cppyy import W_CPPInstance
-    w_obj = space.findattr(w_obj, space.wrap("_cppinstance"))
+    w_obj = space.wrap(space.findattr(w_obj, space.wrap("_cppinstance")))
     obj = space.interp_w(W_CPPInstance, w_obj, can_be_None=True)
-    return obj.rawobject
+    if obj:
+        return obj.rawobject
+    else:
+        return lltype.nullptr(rffi.CCHARP.TO)
 
 
 class TypeConverter(object):
+    _immutable = True
     libffitype = libffi.types.NULL
 
     def __init__(self, space, array_size):
     @jit.dont_look_inside
     def _get_fieldptr(self, space, w_obj, offset):
         rawobject = get_rawobject(space, w_obj)
-        return lltype.direct_ptradd(rawobject, offset)
+        if rawobject:
+            fieldptr = lltype.direct_ptradd(rawobject, offset)
+        else:
+            fieldptr = rffi.cast(rffi.CCHARP, offset)
+        return fieldptr
 
     def _get_address(self, space, w_obj, offset):
-        if space.eq_w(w_obj, space.w_None): # meaning, static/global data
-            address = rffi.cast(rffi.CCHARP, offset)
-        else:
-            address = self._get_fieldptr(space, w_obj, offset)
+        fieldptr = self._get_fieldptr(space, w_obj, offset)
+        address = rffi.cast(rffi.CCHARP, fieldptr)
         return address
 
     def _is_abstract(self):
 
 
 class ArrayTypeConverter(TypeConverter):
+    _immutable = True
     typecode = ''
     typesize = 0         # TODO: get sizeof(type) from system
 
 
     def from_memory(self, space, w_obj, offset):
         # read access, so no copy needed
-        address = self._get_address(space, w_obj, offset)
+        address_value = self._get_address(space, w_obj, offset)
+        address = rffi.cast(rffi.UINT, address_value)
         arr = space.interp_w(W_Array, unpack_simple_shape(space, space.wrap(self.typecode)))
         return arr.fromaddress(space, address, self.size)
 
 
 
 class VoidConverter(TypeConverter):
+    _immutable = True
     libffitype = libffi.types.void
 
     def __init__(self, space, name):
            address[0] = '\x00'
 
 class CharConverter(TypeConverter):
+    _immutable = True
     libffitype = libffi.types.schar
 
     def _from_space(self, space, w_value):
         address[0] = self._from_space(space, w_value)
 
 class LongConverter(TypeConverter):
+    _immutable = True
     libffitype = libffi.types.slong
-    rffiptrtype = rffi.LONGP
 
     def _unwrap_object(self, space, w_obj):
         return space.c_int_w(w_obj)
 
     def convert_argument(self, space, w_obj):
         arg = self._unwrap_object(space, w_obj)
-        x = lltype.malloc(self.rffiptrtype.TO, 1, flavor='raw')
+        x = lltype.malloc(rffi.LONGP.TO, 1, flavor='raw')
         x[0] = arg
         return rffi.cast(rffi.VOIDP, x)
 
 
     def from_memory(self, space, w_obj, offset):
         address = self._get_address(space, w_obj, offset)
-        longptr = rffi.cast(self.rffiptrtype, address)
+        longptr = rffi.cast(rffi.LONGP, address)
         return space.wrap(longptr[0])
 
     def to_memory(self, space, w_obj, w_value, offset):
         address = self._get_address(space, w_obj, offset)
-        longptr = rffi.cast(self.rffiptrtype, address)
-        longptr[0] = self._unwrap_object(space, w_value)
+        longptr = rffi.cast(rffi.LONGP, address)
+        longptr[0] = space.c_int_w(w_value)
 
 class UnsignedLongConverter(LongConverter):
+    _immutable = True
     libffitype = libffi.types.ulong
-    rffiptrtype = rffi.ULONGP
 
     def _unwrap_object(self, space, w_obj):
         return space.c_uint_w(w_obj)
 
+    def convert_argument(self, space, w_obj):
+        arg = self._unwrap_object(space, w_obj)
+        x = lltype.malloc(rffi.ULONGP.TO, 1, flavor='raw')
+        x[0] = arg
+        return rffi.cast(rffi.VOIDP, x)
+
+    def from_memory(self, space, w_obj, offset):
+        address = self._get_address(space, w_obj, offset)
+        ulongptr = rffi.cast(rffi.ULONGP, address)
+        return space.wrap(ulongptr[0])
+
+    def to_memory(self, space, w_obj, w_value, offset):
+        address = self._get_address(space, w_obj, offset)
+        ulongptr = rffi.cast(rffi.ULONGP, address)
+        ulongptr[0] = space.c_uint_w(w_value)
+
 class ShortConverter(LongConverter):
+    _immutable = True
     libffitype = libffi.types.sshort
 
     def from_memory(self, space, w_obj, offset):
     def to_memory(self, space, w_obj, w_value, offset):
         address = self._get_address(space, w_obj, offset)
         shortptr = rffi.cast(rffi.SHORTP, address)
-        shortptr[0] = rffi.cast(rffi.SHORT, space.c_int_w(w_value))
+        shortptr[0] = rffi.cast(rffi.SHORT, self._unwrap_object(space, w_value))
 
 class FloatConverter(TypeConverter):
     def _unwrap_object(self, space, w_obj):
         floatptr[0] = r_singlefloat(space.float_w(w_value))
 
 class DoubleConverter(TypeConverter):
+    _immutable = True
     libffitype = libffi.types.double
 
     def _unwrap_object(self, space, w_obj):

File pypy/module/cppyy/executor.py

 
     def execute(self, space, func, cppthis, num_args, args):
         lresult = capi.c_call_l(func.cpptype.handle, func.method_index, cppthis, num_args, args)
-        spresult = rffi.cast(rffi.SHORTP, lresult)
+        address = rffi.cast(rffi.UINT, lresult)
         arr = space.interp_w(W_Array, unpack_simple_shape(space, space.wrap(self.typecode)))
-        return arr.fromaddress(space, spresult, sys.maxint)
+        return arr.fromaddress(space, address, sys.maxint)
 
 
 class VoidExecutor(FunctionExecutor):
 
 class BoolExecutor(FunctionExecutor):
     _immutable_ = True
+
     def execute(self, space, func, cppthis, num_args, args):
         result = capi.c_call_b(func.cpptype.handle, func.method_index, cppthis, num_args, args)
         return space.wrap(result)

File pypy/module/cppyy/interp_cppyy.py

         self.converter = converter.get_converter(self.space, cpptype)
         self.offset = offset
 
+    def is_static(self):
+        return self.space.wrap(False)
+
     def __get__(self, args_w):
         return self.converter.from_memory(self.space, args_w[0], self.offset)
 
 
 W_CPPDataMember.typedef = TypeDef(
     'CPPDataMember',
+    is_static = interp2app(W_CPPDataMember.is_static, unwrap_spec=['self']),
     __get__ = interp2app(W_CPPDataMember.__get__, unwrap_spec=['self', 'args_w']),
     __set__ = interp2app(W_CPPDataMember.__set__, unwrap_spec=['self', 'args_w']),
 )
 
 
 class W_CPPStaticDataMember(W_CPPDataMember):
+    def is_static(self):
+        return self.space.wrap(True)
+
     def __get__(self, args_w):
         return self.converter.from_memory(self.space, self.space.w_None, self.offset)
 
 
 W_CPPStaticDataMember.typedef = TypeDef(
     'CPPStaticDataMember',
+    is_static = interp2app(W_CPPStaticDataMember.is_static, unwrap_spec=['self']),
     __get__ = interp2app(W_CPPStaticDataMember.__get__, unwrap_spec=['self', 'args_w']),
     __set__ = interp2app(W_CPPStaticDataMember.__set__, unwrap_spec=['self', 'args_w']),
 )

File pypy/module/cppyy/pythonify.py

         pass
 
     # if failed, create
-    # TODO: handle base classes
+
+    # TODO: handle base classes through lookup
     cpptype = cppyy._type_byname(name)
     d = {"_cppyyclass" : cpptype}
+
+    # insert (static) methods in the class dictionary
     for f in cpptype.get_method_names():
         cppol = cpptype.get_overload(f)
         if cppol.is_static():
         else:
             d[f] = make_method(f, cppol.get_returntype())
 
+    # create a meta class to allow properties (for static data write access)
+    metacpp = type(CppyyClass)(name+'_meta', (type,), {})
+
+    # add all data members to the dictionary of the class to be created, and
+    # static ones also to the meta class (needed for property setters)
     for dm in cpptype.get_data_member_names():
-         d[dm] = cpptype.get_data_member(dm)
+        cppdm = cpptype.get_data_member(dm)
 
-    pycpptype = CppyyClass(name, (CppyyObject,), d)
+        d[dm] = cppdm
+        if cppdm.is_static():
+            setattr(metacpp, dm, cppdm)
 
+    # create the python-side C++ class representation
+    pycpptype = metacpp(name, (CppyyObject,), d)
     return pycpptype
 
-#    raise TypeError("no such C++ class %s" % name)
+    # TODO: better error reporting
+    # raise TypeError("no such C++ class %s" % name)
 
 
 class _gbl(object):
             return cppclass
         except TypeError, e:
             import traceback
+            traceback.print_exc()
             raise AttributeError("'gbl' object has no attribute '%s'" % attr)
 
 

File pypy/module/cppyy/test/test_datatypes.py

         assert c.s_int                  == -202
         assert c.s_uint                 ==  202
         assert cppyy_test_data.s_uint   ==  202
-        assert cppyy_test_data.s_long   == -303
-        assert c.s_long                 == -303
-        assert c.s_ulong                ==  303
-        assert cppyy_test_data.s_ulong  ==  303
+        assert cppyy_test_data.s_long   == -303L
+        assert c.s_long                 == -303L
+        assert c.s_ulong                ==  303L
+        assert cppyy_test_data.s_ulong  ==  303L
 
         # floating point types
         assert round(cppyy_test_data.s_float  + 404., 5) == 0
     def test4ClassDataWriteAccess(self):
         """Test write access to class public data and verify values"""
 
-        pass
+        import cppyy, sys
+        cppyy_test_data = cppyy.gbl.cppyy_test_data
+
+        c = cppyy_test_data()
+        assert isinstance(c, cppyy_test_data)
+
+        # char types
+        cppyy_test_data.s_char          = 'a'
+        assert c.s_char                == 'a'
+        c.s_char                        = 'b'
+        assert cppyy_test_data.s_char  == 'b'
+        cppyy_test_data.s_uchar         = 'c'
+        assert c.s_uchar               == 'c'
+        c.s_uchar                       = 'd'
+        assert cppyy_test_data.s_uchar == 'd'
+        raises(TypeError, setattr, cppyy_test_data, 's_uchar', -1)
+        raises(TypeError, setattr, c,               's_uchar', -1)
+
+        # integer types
+        c.s_short                        = -102
+        assert cppyy_test_data.s_short  == -102
+        cppyy_test_data.s_short          = -203
+        assert c.s_short                == -203
+        c.s_ushort                       =  127
+        assert cppyy_test_data.s_ushort ==  127
+        cppyy_test_data.s_ushort         =  227
+        assert c.s_ushort               ==  227
+        cppyy_test_data.s_int            = -234
+        assert c.s_int                  == -234
+        c.s_int                          = -321
+        assert cppyy_test_data.s_int    == -321
+        cppyy_test_data.s_uint           = 1234
+        assert c.s_uint                 == 1234
+        c.s_uint                         = 4321
+        assert cppyy_test_data.s_uint   == 4321
+        raises(ValueError, setattr, c,               's_uint', -1)
+        raises(ValueError, setattr, cppyy_test_data, 's_uint', -1)
+        cppyy_test_data.s_long           = -87L
+        assert c.s_long                 == -87L
+        c.s_long                         = 876L
+        assert cppyy_test_data.s_long   == 876L
+        cppyy_test_data.s_ulong          = 876L
+        assert c.s_ulong                == 876L
+        c.s_ulong                        = 678L
+        assert cppyy_test_data.s_ulong  == 678L
+        raises(ValueError, setattr, cppyy_test_data, 's_ulong', -1)
+        raises(ValueError, setattr, c,               's_ulong', -1)
+
+        # floating point types
+        cppyy_test_data.s_float                    = -3.1415
+        assert round(c.s_float, 5 )               == -3.1415
+        c.s_float                                  =  3.1415
+        assert round(cppyy_test_data.s_float, 5 ) ==  3.1415
+        import math
+        c.s_double                                 = -math.pi
+        assert cppyy_test_data.s_double           == -math.pi
+        cppyy_test_data.s_double                   =  math.pi
+        assert c.s_double                         ==  math.pi
+
+        c.destruct()
 
     def test5RangeAccess(self):
         """Test the ranges of integer types"""
         c = cppyy_test_data()
         assert isinstance(c, cppyy_test_data)
 
+        # TODO: should these be TypeErrors, or should char/bool raise
+        #       ValueErrors? In any case, consistency is needed ...
         raises(ValueError, setattr, c, 'm_uint',  -1)
         raises(ValueError, setattr, c, 'm_ulong', -1)