Author: Armin Rigo <[email protected]>
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
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit