[pypy-commit] cffi default: Support more generally struct-returning functions

arigo noreply at buildbot.pypy.org
Thu Jun 28 22:13:44 CEST 2012


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r557:f0cdef5768b5
Date: 2012-06-28 22:13 +0200
http://bitbucket.org/cffi/cffi/changeset/f0cdef5768b5/

Log:	Support more generally struct-returning functions (but not callbacks
	yet).

diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -22,6 +22,11 @@
 # define USE__THREAD
 #endif
 
+/* Define CAN_RETURN_STRUCTS if the C compiler supports returning structs */
+#if !defined(MS_WIN32)
+# define CAN_RETURN_STRUCTS
+#endif
+
 /************************************************************/
 
 /* base type flag: exactly one of the following: */
@@ -1420,6 +1425,9 @@
     return PyObject_GenericSetAttr((PyObject *)cd, attr, value);
 }
 
+static PyObject *
+convert_struct_to_owning_object(char *data, CTypeDescrObject *ct); /*forward*/
+
 static cif_description_t *
 fb_prepare_cif(PyObject *fargs, CTypeDescrObject *fresult);    /* forward */
 
@@ -1542,6 +1550,9 @@
         res = Py_None;
         Py_INCREF(res);
     }
+    else if (fresult->ct_flags & CT_STRUCT) {
+        res = convert_struct_to_owning_object(resultdata, fresult);
+    }
     else {
         res = convert_to_object(resultdata, fresult);
     }
@@ -1765,6 +1776,26 @@
     return &cdb->head;
 }
 
+static PyObject *
+convert_struct_to_owning_object(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 PyObject *b_newp(PyObject *self, PyObject *args)
 {
     CTypeDescrObject *ct, *ctitem;
@@ -2754,11 +2785,14 @@
     if (PyErr_Occurred())
         return -1;
     if (cif_descr != NULL) {
+#ifndef CAN_RETURN_STRUCTS
         if (fb->rtype->type == FFI_TYPE_STRUCT) {
             PyErr_SetString(PyExc_NotImplementedError,
-                            "functions returning structs are not supported");
+                            "functions returning structs are not supported "
+                            "on this platform");
             return -1;
         }
+#endif
         /* exchange data size */
         /* first, enough room for an array of 'nargs' pointers */
         exchange_offset = nargs * sizeof(void*);
@@ -2956,9 +2990,9 @@
                           &ellipsis))
         return NULL;
 
-    if (fresult->ct_flags & (CT_STRUCT|CT_UNION)) {
+    if (fresult->ct_flags & CT_UNION) {
         PyErr_SetString(PyExc_NotImplementedError,
-                        "functions returning a struct or a union");
+                        "functions returning a union");
         return NULL;
     }
     if ((fresult->ct_size < 0 && !(fresult->ct_flags & CT_VOID)) ||
@@ -3431,6 +3465,16 @@
     return total;
 }
 
+#ifdef CAN_RETURN_STRUCTS
+static struct _testfunc7_s _testfunc10(int n)
+{
+    struct _testfunc7_s result;
+    result.a1 = n;
+    result.a2 = n * n;
+    return result;
+}
+#endif
+
 static PyObject *b__testfunc(PyObject *self, PyObject *args)
 {
     /* for testing only */
@@ -3449,6 +3493,9 @@
     case 7: f = &_testfunc7; break;
     case 8: f = stderr; break;
     case 9: f = &_testfunc9; break;
+#ifdef CAN_RETURN_STRUCTS
+    case 10: f = &_testfunc10; break;
+#endif
     default:
         PyErr_SetNone(PyExc_ValueError);
         return NULL;
@@ -3577,25 +3624,6 @@
     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,
@@ -3620,7 +3648,7 @@
     _cffi_from_c_char,
     convert_to_object,
     convert_from_object,
-    _cffi_from_c_struct,
+    convert_struct_to_owning_object,
 };
 
 /************************************************************/
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -1140,18 +1140,31 @@
     p.a1 = ['x', 'y']
     assert str(p.a1) == 'xyo'
 
-def test_no_struct_return_in_func():
+def test_struct_return_in_func():
     BFunc = new_function_type((), new_void_type())
     BArray = new_array_type(new_pointer_type(BFunc), 5)        # works
     new_function_type((), BFunc)    # works
     new_function_type((), new_primitive_type("int"))
     new_function_type((), new_pointer_type(BFunc))
-    py.test.raises(NotImplementedError, new_function_type, (),
-                   new_struct_type("foo_s"))
-    py.test.raises(NotImplementedError, new_function_type, (),
-                   new_union_type("foo_u"))
+    py.test.raises(NotImplementedError, new_function_type,
+                   (), new_union_type("foo_u"))
     py.test.raises(TypeError, new_function_type, (), BArray)
 
+    if sys.platform == 'win32':
+        py.test.skip("function returning struct")
+    BChar = new_primitive_type("char")
+    BShort = new_primitive_type("short")
+    BInt = new_primitive_type("int")
+    BStruct = new_struct_type("foo_s")
+    complete_struct_or_union(BStruct, [('a1', BChar, -1),
+                                       ('a2', BShort, -1)])
+    BFunc10 = new_function_type((BInt,), BStruct)
+    f = cast(BFunc10, _testfunc(10))
+    s = f(40)
+    assert repr(s) == "<cdata 'struct foo_s' owning 4 bytes>"
+    assert s.a1 == chr(40)
+    assert s.a2 == 40 * 40
+
 def test_cast_with_functionptr():
     BFunc = new_function_type((), new_void_type())
     BFunc2 = new_function_type((), new_primitive_type("short"))


More information about the pypy-commit mailing list