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

Reply via email to