Author: Armin Rigo <ar...@tunes.org> Branch: Changeset: r941:57fb7d3ee6f4 Date: 2012-09-18 16:54 +0200 http://bitbucket.org/cffi/cffi/changeset/57fb7d3ee6f4/
Log: Remove a special case from _cffi_backend, and replace it with a general solution: ffi.addressof(), only for structs or unions. diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -1866,18 +1866,8 @@ } ((char **)data)[1] = NULL; } - if (convert_from_object(data, argtype, obj) < 0) { - if (CData_Check(obj) && (argtype->ct_flags & CT_IS_PTR_TO_OWNED) && - argtype->ct_itemdescr == ((CDataObject *)obj)->c_type) { - /* special case to make the life of verifier.py easier: - if the formal argument type is 'struct foo *' but - we pass a 'struct foo', then get a pointer to it */ - PyErr_Clear(); - ((char **)data)[0] = ((CDataObject *)obj)->c_data; - continue; - } + if (convert_from_object(data, argtype, obj) < 0) goto error; - } } resultdata = buffer + cif_descr->exchange_offset_arg[0]; @@ -4030,6 +4020,30 @@ return PyInt_FromSsize_t(cf->cf_offset); } +static PyObject *b_addressof(PyObject *self, PyObject *args) +{ + CTypeDescrObject *ct; + CDataObject *cd; + + if (!PyArg_ParseTuple(args, "O!O!:addressof", + &CTypeDescr_Type, &ct, + &CData_Type, &cd)) + return NULL; + + if ((cd->c_type->ct_flags & (CT_STRUCT|CT_UNION)) == 0) { + PyErr_SetString(PyExc_TypeError, + "expected a 'cdata struct-or-union' object"); + return NULL; + } + if ((ct->ct_flags & CT_POINTER) == 0 || + (ct->ct_itemdescr != cd->c_type)) { + PyErr_SetString(PyExc_TypeError, + "arg 1 must be the type of pointers to arg 2"); + return NULL; + } + return new_simple_cdata(cd->c_data, ct); +} + static PyObject *b_getcname(PyObject *self, PyObject *args) { CTypeDescrObject *ct; @@ -4414,6 +4428,7 @@ {"sizeof", b_sizeof, METH_O}, {"typeof", b_typeof, METH_O}, {"offsetof", b_offsetof, METH_VARARGS}, + {"addressof", b_addressof, METH_VARARGS}, {"getcname", b_getcname, METH_VARARGS}, {"string", b_string, METH_VARARGS}, {"buffer", b_buffer, METH_VARARGS}, diff --git a/c/test_c.py b/c/test_c.py --- a/c/test_c.py +++ b/c/test_c.py @@ -892,11 +892,8 @@ BFunc20 = new_function_type((BStructPtr,), BShort, False) f = cast(BFunc20, _testfunc(20)) x = newp(BStructPtr, {'a1': b'A', 'a2': -4042}) - # test the exception that allows us to pass a 'struct foo' where the - # function really expects a 'struct foo *'. - res = f(x[0]) - assert res == -4042 + ord(b'A') - assert res == f(x) + # can't pass a 'struct foo' + py.test.raises(TypeError, f, x[0]) def test_call_function_21(): BInt = new_primitive_type("int") @@ -2119,3 +2116,22 @@ BDouble = new_primitive_type("double") assert int(cast(BBool, cast(BDouble, 0.1))) == 1 assert int(cast(BBool, cast(BDouble, 0.0))) == 0 + +def test_addressof(): + BChar = new_primitive_type("char") + BStruct = new_struct_type("foo") + BStructPtr = new_pointer_type(BStruct) + complete_struct_or_union(BStruct, [('a1', BChar, -1), + ('a2', BChar, -1), + ('a3', BChar, -1)]) + p = newp(BStructPtr) + assert repr(p) == "<cdata 'struct foo *' owning 3 bytes>" + s = p[0] + assert repr(s) == "<cdata 'struct foo' owning 3 bytes>" + a = addressof(BStructPtr, s) + assert repr(a).startswith("<cdata 'struct foo *' 0x") + py.test.raises(TypeError, addressof, BStruct, s) + py.test.raises(TypeError, addressof, new_pointer_type(BChar), s) + py.test.raises(TypeError, addressof, BStructPtr, a) + py.test.raises(TypeError, addressof, new_pointer_type(BStructPtr), a) + py.test.raises(TypeError, addressof, BStructPtr, cast(BChar, '?')) diff --git a/cffi/api.py b/cffi/api.py --- a/cffi/api.py +++ b/cffi/api.py @@ -51,6 +51,7 @@ self._new_types = types.ModuleType('new_types').__dict__ self._function_caches = [] self._cdefsources = [] + self._pointer_type_cache = {} if hasattr(backend, 'set_ffi'): backend.set_ffi(self) # @@ -286,6 +287,17 @@ errno = property(_get_errno, _set_errno, None, "the value of 'errno' from/to the C calls") + def addressof(self, cdata): + """Return the address of a <cdata 'struct-or-union'>.""" + ctype = self._backend.typeof(cdata) + try: + ctypeptr = self._pointer_type_cache[ctype] + except KeyError: + ctypeptr = self._pointer_type_cache[ctype] = ( + self._typeof(self.getctype(ctype, '*'))) + return self._backend.addressof(ctypeptr, cdata) + + def _make_ffi_library(ffi, libname): name = libname if name is None: diff --git a/cffi/backend_ctypes.py b/cffi/backend_ctypes.py --- a/cffi/backend_ctypes.py +++ b/cffi/backend_ctypes.py @@ -1044,6 +1044,12 @@ def getcname(self, BType, replace_with): return BType._get_c_name(replace_with) + def addressof(self, BTypePtr, cdata): + if not isinstance(cdata, CTypesBaseStructOrUnion): + raise TypeError("expected a <cdata 'struct-or-union'>") + ptr = ctypes.pointer(type(cdata)._to_ctypes(cdata)) + return BTypePtr._from_ctypes(ptr) + class CTypesLibrary(object): diff --git a/testing/backend_tests.py b/testing/backend_tests.py --- a/testing/backend_tests.py +++ b/testing/backend_tests.py @@ -1447,3 +1447,12 @@ }; """) q = ffi.new("struct foo_s *") + + def test_addressof(self): + ffi = FFI(backend=self.Backend()) + ffi.cdef("struct foo_s { int x, y; };") + p = ffi.new("struct foo_s *") + a = ffi.addressof(p[0]) + assert repr(a).startswith("<cdata 'struct foo_s *' 0x") + py.test.raises(TypeError, ffi.addressof, p) + py.test.raises((AttributeError, TypeError), ffi.addressof, 5) diff --git a/testing/test_verify.py b/testing/test_verify.py --- a/testing/test_verify.py +++ b/testing/test_verify.py @@ -1150,3 +1150,32 @@ type = '%s %s' % (sign, basetype) assert int(ffi.cast("_Bool", ffi.cast(type, 42))) == 1 assert int(ffi.cast("_Bool", ffi.cast(type, 0))) == 0 + +def test_addressof(): + ffi = FFI() + ffi.cdef(""" + struct point_s { int x, y; }; + struct foo_s { int z; struct point_s point; }; + struct point_s sum_coord(struct point_s *); + """) + lib = ffi.verify(""" + struct point_s { int x, y; }; + struct foo_s { int z; struct point_s point; }; + struct point_s sum_coord(struct point_s *point) { + struct point_s r; + r.x = point->x + point->y; + r.y = point->x - point->y; + return r; + } + """) + p = ffi.new("struct foo_s *") + p.point.x = 16 + p.point.y = 9 + py.test.raises(TypeError, lib.sum_coord, p.point) + res = lib.sum_coord(ffi.addressof(p.point)) + assert res.x == 25 + assert res.y == 7 + res2 = lib.sum_coord(ffi.addressof(res)) + assert res2.x == 32 + assert res2.y == 18 + py.test.raises(TypeError, lib.sum_coord, res2) _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit