[pypy-commit] cffi default: Support the baseline case of functions returning structs.

arigo noreply at buildbot.pypy.org
Thu Jun 28 21:39:11 CEST 2012


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r556:0ed9b03dd40d
Date: 2012-06-28 21:38 +0200
http://bitbucket.org/cffi/cffi/changeset/0ed9b03dd40d/

Log:	Support the baseline case of functions returning structs.

diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -1749,10 +1749,26 @@
 
 /************************************************************/
 
+static CDataObject *allocate_owning_object(Py_ssize_t dataoffset,
+                                           Py_ssize_t datasize,
+                                           CTypeDescrObject *ct)
+{
+    CDataObject_own_base *cdb;
+    cdb = (CDataObject_own_base *)PyObject_Malloc(dataoffset + datasize);
+    if (PyObject_Init((PyObject *)cdb, &CDataOwning_Type) == NULL)
+        return NULL;
+
+    Py_INCREF(ct);
+    cdb->head.c_type = ct;
+    cdb->head.c_data = ((char *)cdb) + dataoffset;
+    cdb->weakreflist = NULL;
+    return &cdb->head;
+}
+
 static PyObject *b_newp(PyObject *self, PyObject *args)
 {
     CTypeDescrObject *ct, *ctitem;
-    CDataObject_own_base *cdb;
+    CDataObject *cd;
     PyObject *init = Py_None;
     Py_ssize_t dataoffset, datasize, explicitlength;
     if (!PyArg_ParseTuple(args, "O!|O:newp", &CTypeDescr_Type, &ct, &init))
@@ -1809,26 +1825,21 @@
         return NULL;
     }
 
-    cdb = (CDataObject_own_base *)PyObject_Malloc(dataoffset + datasize);
-    if (PyObject_Init((PyObject *)cdb, &CDataOwning_Type) == NULL)
+    cd = allocate_owning_object(dataoffset, datasize, ct);
+    if (cd == NULL)
         return NULL;
-
-    Py_INCREF(ct);
-    cdb->head.c_type = ct;
-    cdb->head.c_data = ((char *)cdb) + dataoffset;
-    cdb->weakreflist = NULL;
     if (explicitlength >= 0)
-        ((CDataObject_own_length*)cdb)->length = explicitlength;
-
-    memset(cdb->head.c_data, 0, datasize);
+        ((CDataObject_own_length*)cd)->length = explicitlength;
+
+    memset(cd->c_data, 0, datasize);
     if (init != Py_None) {
-        if (convert_from_object(cdb->head.c_data,
+        if (convert_from_object(cd->c_data,
               (ct->ct_flags & CT_POINTER) ? ct->ct_itemdescr : ct, init) < 0) {
-            Py_DECREF(cdb);
+            Py_DECREF(cd);
             return NULL;
         }
     }
-    return (PyObject *)cdb;
+    return (PyObject *)cd;
 }
 
 static CDataObject *_new_casted_primitive(CTypeDescrObject *ct)
@@ -3566,6 +3577,25 @@
     return PyString_FromStringAndSize(&x, 1);
 }
 
+static PyObject *_cffi_from_c_struct(char *data, CTypeDescrObject *ct)
+{
+    CDataObject *cd;
+    Py_ssize_t dataoffset = offsetof(CDataObject_own_nolength, alignment);
+    Py_ssize_t datasize = ct->ct_size;
+
+    if ((ct->ct_flags & (CT_STRUCT|CT_IS_OPAQUE)) != CT_STRUCT) {
+        PyErr_SetString(PyExc_TypeError,
+                        "return type is not a struct or is opaque");
+        return NULL;
+    }
+    cd = allocate_owning_object(dataoffset, datasize, ct);
+    if (cd == NULL)
+        return NULL;
+
+    memcpy(cd->c_data, data, datasize);
+    return (PyObject *)cd;
+}
+
 static void *cffi_exports[] = {
     _cffi_to_c_char_p,
     _cffi_to_c_signed_char,
@@ -3590,6 +3620,7 @@
     _cffi_from_c_char,
     convert_to_object,
     convert_from_object,
+    _cffi_from_c_struct,
 };
 
 /************************************************************/
diff --git a/cffi/verifier.py b/cffi/verifier.py
--- a/cffi/verifier.py
+++ b/cffi/verifier.py
@@ -184,6 +184,9 @@
         elif isinstance(tp, model.ArrayType):
             return '_cffi_from_c_deref((char *)%s, _cffi_type(%d))' % (
                 var, self.gettypenum(tp))
+        elif isinstance(tp, model.StructType):
+            return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % (
+                var, self.gettypenum(tp))
         else:
             raise NotImplementedError(tp)
 
@@ -614,7 +617,9 @@
     ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[16])
 #define _cffi_to_c                                                       \
     ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[17])
-#define _CFFI_NUM_EXPORTS 18
+#define _cffi_from_c_struct                                              \
+    ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[18])
+#define _CFFI_NUM_EXPORTS 19
 
 #if SIZEOF_LONG < SIZEOF_LONG_LONG
 #  define _cffi_to_c_long_long PyLong_AsLongLong
diff --git a/testing/test_verify.py b/testing/test_verify.py
--- a/testing/test_verify.py
+++ b/testing/test_verify.py
@@ -576,3 +576,27 @@
     """)
     msg = 'cannot pass as a argument a struct that was completed with verify()'
     assert msg in str(e.value)
+
+def test_func_returns_struct():
+    # only supported via verify(), when GCC is the compiler; and only for
+    # regular functions.
+    ffi = FFI()
+    ffi.cdef("""
+        struct foo_s { int aa, bb; };
+        struct foo_s foo(int a, int b);
+    """)
+    lib = ffi.verify("""
+        struct foo_s { int aa, bb; };
+        struct foo_s foo(int a, int b) {
+            struct foo_s r;
+            r.aa = a*a;
+            r.bb = b*b;
+            return r;
+        }
+    """)
+    s = lib.foo(6, 7)
+    # It's the only way to have a 'struct foo_s' that owns its memory.
+    # With ffi.new() we always get a 'struct foo_s *' that owns the memory.
+    assert repr(s) == "<cdata 'struct foo_s' owning 8 bytes>"
+    assert s.aa == 36
+    assert s.bb == 49


More information about the pypy-commit mailing list