Commits

Armin Rigo committed b3ab421 Merge

hg merge default

Comments (0)

Files changed (23)

 .*.swp
 testing/__pycache__
 demo/__pycache__
+__pycache__
+_cffi_backend.so
 doc/build
 build
 dist
 recursive-include cffi *.py
 recursive-include c *.c *.h *.asm
 recursive-include testing *.py
-recursive-include doc *.py *.rst Makefile *.bat
+recursive-include doc *.py *.rst Makefile *.bat LICENSE

c/_cffi_backend.c

 #define CT_PRIMITIVE_FITS_LONG   2048
 #define CT_IS_OPAQUE             4096
 #define CT_IS_ENUM               8192
+#define CT_IS_PTR_TO_OWNED      16384
+#define CT_CUSTOM_FIELD_POS     32768
 #define CT_PRIMITIVE_ANY  (CT_PRIMITIVE_SIGNED |        \
                            CT_PRIMITIVE_UNSIGNED |      \
                            CT_PRIMITIVE_CHAR |          \
 } CDataObject_own_length;
 
 typedef struct {
+    CDataObject_own_base head;
+    PyObject *structobj;
+} CDataObject_own_structptr;
+
+typedef struct {
     ffi_cif cif;
     /* the following information is used when doing the call:
        - a buffer of size 'exchange_size' is malloced
         data[0] = res;
         return 0;
     }
-    if (ct->ct_flags & CT_STRUCT) {
+    if (ct->ct_flags & (CT_STRUCT|CT_UNION)) {
 
         if (CData_Check(init)) {
             if (((CDataObject *)init)->c_type == ct && ct->ct_size >= 0) {
                 return 0;
             }
         }
+        if (ct->ct_flags & CT_UNION) {
+            Py_ssize_t n = PyObject_Size(init);
+            if (n < 0)
+                return -1;
+            if (n > 1) {
+                PyErr_Format(PyExc_ValueError,
+                             "initializer for '%s': %zd items given, but "
+                             "only one supported (use a dict if needed)",
+                             ct->ct_name, n);
+                return -1;
+            }
+        }
         if (PyList_Check(init) || PyTuple_Check(init)) {
             PyObject **items = PySequence_Fast_ITEMS(init);
             Py_ssize_t i, n = PySequence_Fast_GET_SIZE(init);
         expected = "list or tuple or dict or struct-cdata";
         goto cannot_convert;
     }
-    if (ct->ct_flags & CT_UNION) {
-
-        if (CData_Check(init)) {
-            if (((CDataObject *)init)->c_type == ct && ct->ct_size >= 0) {
-                memcpy(data, ((CDataObject *)init)->c_data, ct->ct_size);
-                return 0;
-            }
-        }
-        CFieldObject *cf = (CFieldObject *)ct->ct_extra;   /* first field */
-        if (cf == NULL) {
-            PyErr_SetString(PyExc_ValueError, "empty union");
-            return -1;
-        }
-        return convert_field_from_object(data, cf, init);
-    }
     PyErr_Format(PyExc_SystemError,
                  "convert_from_object: '%s'", ct->ct_name);
     return -1;
     if (cdb->weakreflist != NULL)
         PyObject_ClearWeakRefs((PyObject *) cdb);
 
-    if (cdb->head.c_type->ct_flags & CT_FUNCTIONPTR) {
+    if (cdb->head.c_type->ct_flags & CT_IS_PTR_TO_OWNED) {
+        Py_DECREF(((CDataObject_own_structptr *)cdb)->structobj);
+    }
+    else if (cdb->head.c_type->ct_flags & CT_FUNCTIONPTR) {
         /* a callback */
         ffi_closure *closure = (ffi_closure *)cdb->head.c_data;
         PyObject *args = (PyObject *)(closure->user_data);
 }
 
 static PyObject *
+cdataowning_subscript(CDataObject *cd, PyObject *key)
+{
+    char *c = _cdata_get_indexed_ptr(cd, key);
+    /* use 'mp_subscript' instead of 'sq_item' because we don't want
+       negative indexes to be corrected automatically */
+    if (c == NULL && PyErr_Occurred())
+        return NULL;
+
+    if (cd->c_type->ct_flags & CT_IS_PTR_TO_OWNED) {
+        PyObject *res = ((CDataObject_own_structptr *)cd)->structobj;
+        Py_INCREF(res);
+        return res;
+    }
+    else {
+        return convert_to_object(c, cd->c_type->ct_itemdescr);
+    }
+}
+
+static PyObject *
 cdata_subscript(CDataObject *cd, PyObject *key)
 {
     char *c = _cdata_get_indexed_ptr(cd, key);
-    CTypeDescrObject *ctitem = cd->c_type->ct_itemdescr;
     /* use 'mp_subscript' instead of 'sq_item' because we don't want
        negative indexes to be corrected automatically */
-    if (c == NULL)
+    if (c == NULL && PyErr_Occurred())
         return NULL;
-    return convert_to_object(c, ctitem);
+    return convert_to_object(c, cd->c_type->ct_itemdescr);
 }
 
 static int
     CTypeDescrObject *ctitem = cd->c_type->ct_itemdescr;
     /* use 'mp_ass_subscript' instead of 'sq_ass_item' because we don't want
        negative indexes to be corrected automatically */
-    if (c == NULL)
+    if (c == NULL && PyErr_Occurred())
         return -1;
     return convert_from_object(c, ctitem, v);
 }
     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 */
 
         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);
     }
     (objobjargproc)cdata_ass_sub, /*mp_ass_subscript*/
 };
 
+static PyMappingMethods CDataOwn_as_mapping = {
+    (lenfunc)cdata_length, /*mp_length*/
+    (binaryfunc)cdataowning_subscript, /*mp_subscript*/
+    (objobjargproc)cdata_ass_sub, /*mp_ass_subscript*/
+};
+
 static PyTypeObject CData_Type = {
     PyVarObject_HEAD_INIT(NULL, 0)
     "_cffi_backend.CData",
     (reprfunc)cdataowning_repr,                 /* tp_repr */
     0,                                          /* tp_as_number */
     0,                                          /* tp_as_sequence */
-    0,                                          /* tp_as_mapping */
+    &CDataOwn_as_mapping,                       /* tp_as_mapping */
     0,                                          /* tp_hash */
     0,                                          /* tp_call */
     0,                                          /* tp_str */
 
 /************************************************************/
 
+static CDataObject_own_base *allocate_owning_object(Py_ssize_t size,
+                                                    CTypeDescrObject *ct)
+{
+    CDataObject_own_base *cdb;
+    cdb = (CDataObject_own_base *)PyObject_Malloc(size);
+    if (PyObject_Init((PyObject *)cdb, &CDataOwning_Type) == NULL)
+        return NULL;
+
+    Py_INCREF(ct);
+    cdb->head.c_type = ct;
+    cdb->weakreflist = NULL;
+    return cdb;
+}
+
+static PyObject *
+convert_struct_to_owning_object(char *data, CTypeDescrObject *ct)
+{
+    CDataObject_own_base *cdb;
+    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;
+    }
+    cdb = allocate_owning_object(dataoffset + datasize, ct);
+    if (cdb == NULL)
+        return NULL;
+    cdb->head.c_data = ((char *)cdb) + dataoffset;
+
+    memcpy(cdb->head.c_data, data, datasize);
+    return (PyObject *)cdb;
+}
+
 static PyObject *b_newp(PyObject *self, PyObject *args)
 {
     CTypeDescrObject *ct, *ctitem;
+    CDataObject *cd;
     CDataObject_own_base *cdb;
     PyObject *init = Py_None;
     Py_ssize_t dataoffset, datasize, explicitlength;
         return NULL;
     }
 
-    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;
-    if (explicitlength >= 0)
-        ((CDataObject_own_length*)cdb)->length = explicitlength;
-
-    memset(cdb->head.c_data, 0, datasize);
-    if (init != Py_None) {
-        if (convert_from_object(cdb->head.c_data,
-              (ct->ct_flags & CT_POINTER) ? ct->ct_itemdescr : ct, init) < 0) {
+    if (ct->ct_flags & CT_IS_PTR_TO_OWNED) {
+        /* common case of ptr-to-struct (or ptr-to-union): for this case
+           we build two objects instead of one, with the memory-owning
+           one being really the struct (or union) and the returned one
+           having a strong reference to it */
+        CDataObject_own_base *cdp;
+
+        cdb = allocate_owning_object(dataoffset + datasize, ct->ct_itemdescr);
+        if (cdb == NULL)
+            return NULL;
+
+        cdp = allocate_owning_object(sizeof(CDataObject_own_structptr), ct);
+        if (cdp == NULL) {
             Py_DECREF(cdb);
             return NULL;
         }
+        /* store the only reference to cdb into cdp */
+        ((CDataObject_own_structptr *)cdp)->structobj = (PyObject *)cdb;
+        assert(explicitlength < 0);
+
+        cdb->head.c_data = cdp->head.c_data = ((char *)cdb) + dataoffset;
+        cd = &cdp->head;
     }
-    return (PyObject *)cdb;
+    else {
+        cdb = allocate_owning_object(dataoffset + datasize, ct);
+        if (cdb == NULL)
+            return NULL;
+
+        cdb->head.c_data = ((char *)cdb) + dataoffset;
+        if (explicitlength >= 0)
+            ((CDataObject_own_length*)cdb)->length = explicitlength;
+        cd = &cdb->head;
+    }
+
+    memset(cd->c_data, 0, datasize);
+    if (init != Py_None) {
+        if (convert_from_object(cd->c_data,
+              (ct->ct_flags & CT_POINTER) ? ct->ct_itemdescr : ct, init) < 0) {
+            Py_DECREF(cd);
+            return NULL;
+        }
+    }
+    return (PyObject *)cd;
 }
 
 static CDataObject *_new_casted_primitive(CTypeDescrObject *ct)
  bad_ffi_type:
     PyErr_Format(PyExc_NotImplementedError,
                  "primitive type '%s' with a non-standard size %d",
-                 name, ptypes->size);
+                 name, (int)ptypes->size);
     return NULL;
 }
 
 
     td->ct_size = sizeof(void *);
     td->ct_flags = CT_POINTER;
+    if (ctitem->ct_flags & (CT_STRUCT|CT_UNION))
+        td->ct_flags |= CT_IS_PTR_TO_OWNED;
     return (PyObject *)td;
 }
 
         if (alignment < falign)
             alignment = falign;
 
-        if (foffset < 0) {
-            /* align this field to its own 'falign' by inserting padding */
-            offset = (offset + falign - 1) & ~(falign-1);
+        /* align this field to its own 'falign' by inserting padding */
+        offset = (offset + falign - 1) & ~(falign-1);
+
+        if (foffset >= 0) {
+            /* a forced field position: ignore the offset just computed,
+               except to know if we must set CT_CUSTOM_FIELD_POS */
+            if (offset != foffset)
+                ct->ct_flags |= CT_CUSTOM_FIELD_POS;
+            offset = foffset;
         }
-        else
-            offset = foffset;
 
         if (fbitsize < 0 || (fbitsize == 8 * ftype->ct_size &&
                              !(ftype->ct_flags & CT_PRIMITIVE_CHAR))) {
     }
 }
 
-static ffi_type *fb_fill_type(struct funcbuilder_s *fb, CTypeDescrObject *ct)
+static ffi_type *fb_fill_type(struct funcbuilder_s *fb, CTypeDescrObject *ct,
+                              int is_result_type)
 {
     if (ct->ct_flags & CT_PRIMITIVE_ANY) {
         return (ffi_type *)ct->ct_extra;
         Py_ssize_t i, n;
         CFieldObject *cf;
 
+        /* We can't pass a struct that was completed by verify().
+           Issue: assume verify() is given "struct { long b; ...; }".
+           Then it will complete it in the same way whether it is actually
+           "struct { long a, b; }" or "struct { double a; long b; }".
+           But on 64-bit UNIX, these two structs are passed by value
+           differently: e.g. on x86-64, "b" ends up in register "rsi" in
+           the first case and "rdi" in the second case.
+        */
+        if (ct->ct_flags & CT_CUSTOM_FIELD_POS) {
+            PyErr_SetString(PyExc_TypeError,
+                "cannot pass as an argument a struct that was completed "
+                "with verify() (see _cffi_backend.c for details of why)");
+            return NULL;
+        }
+
+#ifdef USE_C_LIBFFI_MSVC
+        /* MSVC returns small structures in registers.  Pretend int32 or
+           int64 return type.  This is needed as a workaround for what
+           is really a bug of libffi_msvc seen as an independent library
+           (ctypes has a similar workaround). */
+        if (is_result_type) {
+            if (ct->ct_size <= 4)
+                return &ffi_type_sint32;
+            if (ct->ct_size <= 8)
+                return &ffi_type_sint64;
+        }
+#endif
+
         n = PyDict_Size(ct->ct_stuff);
         elements = fb_alloc(fb, (n + 1) * sizeof(ffi_type*));
         cf = (CFieldObject *)ct->ct_extra;
                     "cannot pass as argument a struct with bit fields");
                 return NULL;
             }
-            ffifield = fb_fill_type(fb, cf->cf_type);
+            ffifield = fb_fill_type(fb, cf->cf_type, 0);
             if (elements != NULL)
                 elements[i] = ffifield;
             cf = cf->cf_next;
     fb->nargs = nargs;
 
     /* ffi buffer: next comes the result type */
-    fb->rtype = fb_fill_type(fb, fresult);
+    fb->rtype = fb_fill_type(fb, fresult, 1);
     if (PyErr_Occurred())
         return -1;
     if (cif_descr != NULL) {
-        if (fb->rtype->type == FFI_TYPE_STRUCT) {
-            PyErr_SetString(PyExc_NotImplementedError,
-                            "functions returning structs are not supported");
-            return -1;
-        }
         /* exchange data size */
         /* first, enough room for an array of 'nargs' pointers */
         exchange_offset = nargs * sizeof(void*);
         farg = (CTypeDescrObject *)PyTuple_GET_ITEM(fargs, i);
 
         /* ffi buffer: fill in the ffi for the i'th argument */
-        if (farg == NULL)    /* stands for a NULL pointer in the varargs */
-            atype = &ffi_type_pointer;
-        else {
-            atype = fb_fill_type(fb, farg);
-            if (PyErr_Occurred())
-                return -1;
-        }
+        assert(farg != NULL);
+        atype = fb_fill_type(fb, farg, 0);
+        if (PyErr_Occurred())
+            return -1;
+
         if (fb->atypes != NULL) {
             fb->atypes[i] = atype;
             /* exchange data size */
                           &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");
+                        "function returning a union");
         return NULL;
     }
     if ((fresult->ct_size < 0 && !(fresult->ct_flags & CT_VOID)) ||
     if (py_res == NULL)
         goto error;
 
-    if (SIGNATURE(0)->ct_size > 0)
+    if (SIGNATURE(0)->ct_size > 0) {
         if (convert_from_object(result, SIGNATURE(0), py_res) < 0)
             goto error;
+    }
+    else if (py_res != Py_None) {
+        PyErr_SetString(PyExc_TypeError, "callback with the return type 'void'"
+                                         " must return None");
+        goto error;
+    }
  done:
     Py_XDECREF(py_args);
     Py_XDECREF(py_res);
 }
 static void _testfunc5(void)
 {
+    errno = errno + 15;
 }
 static int *_testfunc6(int *x)
 {
     y = *x - 1000;
     return &y;
 }
-struct _testfunc7_s { char a1; short a2; };
+struct _testfunc7_s { unsigned char a1; short a2; };
 static short _testfunc7(struct _testfunc7_s inlined)
 {
     return inlined.a1 + inlined.a2;
     return total;
 }
 
+static struct _testfunc7_s _testfunc10(int n)
+{
+    struct _testfunc7_s result;
+    result.a1 = n;
+    result.a2 = n * n;
+    return result;
+}
+
+struct _testfunc11_s { int a1, a2; };
+static struct _testfunc11_s _testfunc11(int n)
+{
+    struct _testfunc11_s result;
+    result.a1 = n;
+    result.a2 = n * n;
+    return result;
+}
+
+struct _testfunc12_s { double a1; };
+static struct _testfunc12_s _testfunc12(int n)
+{
+    struct _testfunc12_s result;
+    result.a1 = n;
+    return result;
+}
+
+struct _testfunc13_s { int a1, a2, a3; };
+static struct _testfunc13_s _testfunc13(int n)
+{
+    struct _testfunc13_s result;
+    result.a1 = n;
+    result.a2 = n * n;
+    result.a3 = n * n * n;
+    return result;
+}
+
+struct _testfunc14_s { float a1; };
+static struct _testfunc14_s _testfunc14(int n)
+{
+    struct _testfunc14_s result;
+    result.a1 = (float)n;
+    return result;
+}
+
+struct _testfunc15_s { float a1; int a2; };
+static struct _testfunc15_s _testfunc15(int n)
+{
+    struct _testfunc15_s result;
+    result.a1 = (float)n;
+    result.a2 = n * n;
+    return result;
+}
+
+struct _testfunc16_s { float a1, a2; };
+static struct _testfunc16_s _testfunc16(int n)
+{
+    struct _testfunc16_s result;
+    result.a1 = (float)n;
+    result.a2 = -(float)n;
+    return result;
+}
+
+struct _testfunc17_s { int a1; float a2; };
+static struct _testfunc17_s _testfunc17(int n)
+{
+    struct _testfunc17_s result;
+    result.a1 = n;
+    result.a2 = (float)n * (float)n;
+    return result;
+}
+
 static PyObject *b__testfunc(PyObject *self, PyObject *args)
 {
     /* for testing only */
     case 7: f = &_testfunc7; break;
     case 8: f = stderr; break;
     case 9: f = &_testfunc9; break;
+    case 10: f = &_testfunc10; break;
+    case 11: f = &_testfunc11; break;
+    case 12: f = &_testfunc12; break;
+    case 13: f = &_testfunc13; break;
+    case 14: f = &_testfunc14; break;
+    case 15: f = &_testfunc15; break;
+    case 16: f = &_testfunc16; break;
+    case 17: f = &_testfunc17; break;
     default:
         PyErr_SetNone(PyExc_ValueError);
         return NULL;
     save_errno,
     _cffi_from_c_char,
     convert_to_object,
+    convert_from_object,
+    convert_struct_to_owning_object,
 };
 
 /************************************************************/
     if (v == NULL || PyModule_AddObject(m, "_C_API", v) < 0)
         return;
 
+    v = PyString_FromString("0.2");
+    if (v == NULL || PyModule_AddObject(m, "__version__", v) < 0)
+        return;
+
     init_errno();
 }
     BUInt = new_primitive_type("unsigned int")
     BUnion = new_union_type("bar")
     complete_struct_or_union(BUnion, [('a1', BInt, -1), ('a2', BUInt, -1)])
-    p = newp(new_pointer_type(BUnion), -42)
+    p = newp(new_pointer_type(BUnion), [-42])
+    bigval = -42 + (1 << (8*size_of_int()))
     assert p.a1 == -42
-    assert p.a2 == -42 + (1 << (8*size_of_int()))
+    assert p.a2 == bigval
+    p = newp(new_pointer_type(BUnion), {'a2': bigval})
+    assert p.a1 == -42
+    assert p.a2 == bigval
+    py.test.raises(OverflowError, newp, new_pointer_type(BUnion),
+                   {'a1': bigval})
+    p = newp(new_pointer_type(BUnion), [])
+    assert p.a1 == p.a2 == 0
 
 def test_struct_pointer():
     BInt = new_primitive_type("int")
     py.test.raises(TypeError, f, 1, 42)
     py.test.raises(TypeError, f, 2, None)
 
+def test_cannot_call_with_a_autocompleted_struct():
+    BSChar = new_primitive_type("signed char")
+    BDouble = new_primitive_type("double")
+    BStruct = new_struct_type("foo")
+    BStructPtr = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('c', BDouble, -1, 8),
+                                       ('a', BSChar, -1, 2),
+                                       ('b', BSChar, -1, 0)])
+    e = py.test.raises(TypeError, new_function_type, (BStruct,), BDouble)
+    msg ='cannot pass as an argument a struct that was completed with verify()'
+    assert msg in str(e.value)
+
 def test_new_charp():
     BChar = new_primitive_type("char")
     BCharP = new_pointer_type(BChar)
         return callback(BFunc, cb, 42)    # 'cb' and 'BFunc' go out of scope
     f = make_callback()
     assert f(-142) == -141
+    assert repr(f).startswith(
+        "<cdata 'int(*)(int)' calling <function cb at 0x")
 
 def test_callback_return_type():
     for rettype in ["signed char", "short", "int", "long", "long long",
         assert f(max) == 42
 
 def test_a_lot_of_callbacks():
+    BIGNUM = 10000
+    if 'PY_DOT_PY' in globals(): BIGNUM = 100   # tests on py.py
+    #
     BInt = new_primitive_type("int")
+    BFunc = new_function_type((BInt,), BInt, False)
     def make_callback(m):
         def cb(n):
             return n + m
-        BFunc = new_function_type((BInt,), BInt, False)
         return callback(BFunc, cb, 42)    # 'cb' and 'BFunc' go out of scope
     #
-    flist = [make_callback(i) for i in range(10000)]
+    flist = [make_callback(i) for i in range(BIGNUM)]
     for i, f in enumerate(flist):
         assert f(-142) == -142 + i
 
+def test_callback_returning_struct():
+    BSChar = new_primitive_type("signed char")
+    BInt = new_primitive_type("int")
+    BDouble = new_primitive_type("double")
+    BStruct = new_struct_type("foo")
+    BStructPtr = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('a', BSChar, -1),
+                                       ('b', BDouble, -1)])
+    def cb(n):
+        return newp(BStructPtr, [-n, 1E-42])[0]
+    BFunc = new_function_type((BInt,), BStruct)
+    f = callback(BFunc, cb)
+    s = f(10)
+    assert typeof(s) is BStruct
+    assert repr(s) in ["<cdata 'struct foo' owning 12 bytes>",
+                       "<cdata 'struct foo' owning 16 bytes>"]
+    assert s.a == -10
+    assert s.b == 1E-42
+
 def test_enum_type():
     BEnum = new_enum_type("foo", (), ())
     assert repr(BEnum) == "<ctype 'enum foo'>"
     #
     BUnion = new_union_type("bar")
     complete_struct_or_union(BUnion, [('a1', BInt, 1)])
-    p = newp(new_pointer_type(BUnion), -1)
+    p = newp(new_pointer_type(BUnion), [-1])
     assert p.a1 == -1
 
 def test_weakref():
     BUnion = new_union_type("foo_u")
     BUnionPtr = new_pointer_type(BUnion)
     complete_struct_or_union(BUnion, [('a1', BInt, -1)])
-    u1 = newp(BUnionPtr, 42)
+    u1 = newp(BUnionPtr, [42])
     u2 = newp(BUnionPtr, u1[0])
     assert u2.a1 == 42
     #
     p.a1 = ['x', 'y']
     assert str(p.a1) == 'xyo'
 
-def test_no_struct_return_in_func():
+def test_invalid_function_result_types():
     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"))
+    BUnion = new_union_type("foo_u")
+    complete_struct_or_union(BUnion, [])
+    py.test.raises(NotImplementedError, new_function_type, (), BUnion)
     py.test.raises(TypeError, new_function_type, (), BArray)
 
+def test_struct_return_in_func():
+    BChar = new_primitive_type("char")
+    BShort = new_primitive_type("short")
+    BFloat = new_primitive_type("float")
+    BDouble = new_primitive_type("double")
+    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
+    #
+    BStruct11 = new_struct_type("test11")
+    complete_struct_or_union(BStruct11, [('a1', BInt, -1),
+                                         ('a2', BInt, -1)])
+    BFunc11 = new_function_type((BInt,), BStruct11)
+    f = cast(BFunc11, _testfunc(11))
+    s = f(40)
+    assert repr(s) == "<cdata 'struct test11' owning 8 bytes>"
+    assert s.a1 == 40
+    assert s.a2 == 40 * 40
+    #
+    BStruct12 = new_struct_type("test12")
+    complete_struct_or_union(BStruct12, [('a1', BDouble, -1),
+                                         ])
+    BFunc12 = new_function_type((BInt,), BStruct12)
+    f = cast(BFunc12, _testfunc(12))
+    s = f(40)
+    assert repr(s) == "<cdata 'struct test12' owning 8 bytes>"
+    assert s.a1 == 40.0
+    #
+    BStruct13 = new_struct_type("test13")
+    complete_struct_or_union(BStruct13, [('a1', BInt, -1),
+                                         ('a2', BInt, -1),
+                                         ('a3', BInt, -1)])
+    BFunc13 = new_function_type((BInt,), BStruct13)
+    f = cast(BFunc13, _testfunc(13))
+    s = f(40)
+    assert repr(s) == "<cdata 'struct test13' owning 12 bytes>"
+    assert s.a1 == 40
+    assert s.a2 == 40 * 40
+    assert s.a3 == 40 * 40 * 40
+    #
+    BStruct14 = new_struct_type("test14")
+    complete_struct_or_union(BStruct14, [('a1', BFloat, -1),
+                                         ])
+    BFunc14 = new_function_type((BInt,), BStruct14)
+    f = cast(BFunc14, _testfunc(14))
+    s = f(40)
+    assert repr(s) == "<cdata 'struct test14' owning 4 bytes>"
+    assert s.a1 == 40.0
+    #
+    BStruct15 = new_struct_type("test15")
+    complete_struct_or_union(BStruct15, [('a1', BFloat, -1),
+                                         ('a2', BInt, -1)])
+    BFunc15 = new_function_type((BInt,), BStruct15)
+    f = cast(BFunc15, _testfunc(15))
+    s = f(40)
+    assert repr(s) == "<cdata 'struct test15' owning 8 bytes>"
+    assert s.a1 == 40.0
+    assert s.a2 == 40 * 40
+    #
+    BStruct16 = new_struct_type("test16")
+    complete_struct_or_union(BStruct16, [('a1', BFloat, -1),
+                                         ('a2', BFloat, -1)])
+    BFunc16 = new_function_type((BInt,), BStruct16)
+    f = cast(BFunc16, _testfunc(16))
+    s = f(40)
+    assert repr(s) == "<cdata 'struct test16' owning 8 bytes>"
+    assert s.a1 == 40.0
+    assert s.a2 == -40.0
+    #
+    BStruct17 = new_struct_type("test17")
+    complete_struct_or_union(BStruct17, [('a1', BInt, -1),
+                                         ('a2', BFloat, -1)])
+    BFunc17 = new_function_type((BInt,), BStruct17)
+    f = cast(BFunc17, _testfunc(17))
+    s = f(40)
+    assert repr(s) == "<cdata 'struct test17' owning 8 bytes>"
+    assert s.a1 == 40
+    assert s.a2 == 40.0 * 40.0
+
 def test_cast_with_functionptr():
     BFunc = new_function_type((), new_void_type())
     BFunc2 = new_function_type((), new_primitive_type("short"))
     BFunc = new_function_type((BWCharP,), BInt, False)
     f = callback(BFunc, cb, -42)
     assert f(u'a\u1234b') == 3
+
+def test_keepalive_struct():
+    # exception to the no-keepalive rule: p=newp(BStructPtr) returns a
+    # pointer owning the memory, and p[0] returns a pointer to the
+    # struct that *also* owns the memory
+    BStruct = new_struct_type("foo")
+    BStructPtr = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('a1', new_primitive_type("int"), -1),
+                                       ('a2', new_primitive_type("int"), -1),
+                                       ('a3', new_primitive_type("int"), -1)])
+    p = newp(BStructPtr)
+    assert repr(p) == "<cdata 'struct foo *' owning 12 bytes>"
+    q = p[0]
+    assert repr(q) == "<cdata 'struct foo' owning 12 bytes>"
+    q.a1 = 123456
+    assert p.a1 == 123456
+    del p
+    import gc; gc.collect()
+    assert q.a1 == 123456
+    assert repr(q) == "<cdata 'struct foo' owning 12 bytes>"
+    assert q.a1 == 123456
+
+def test_nokeepalive_struct():
+    BStruct = new_struct_type("foo")
+    BStructPtr = new_pointer_type(BStruct)
+    BStructPtrPtr = new_pointer_type(BStructPtr)
+    complete_struct_or_union(BStruct, [('a1', new_primitive_type("int"), -1)])
+    p = newp(BStructPtr)
+    pp = newp(BStructPtrPtr)
+    pp[0] = p
+    s = pp[0][0]
+    assert repr(s).startswith("<cdata 'struct foo' 0x")
+
+def test_owning_repr():
+    BInt = new_primitive_type("int")
+    BArray = new_array_type(new_pointer_type(BInt), None)   # int[]
+    p = newp(BArray, 7)
+    assert repr(p) == "<cdata 'int[]' owning 28 bytes>"
+    assert sizeof(p) == 28
+
+def test_cannot_dereference_void():
+    BVoidP = new_pointer_type(new_void_type())
+    p = cast(BVoidP, 123456)
+    py.test.raises(TypeError, "p[0]")
+    p = cast(BVoidP, 0)
+    if 'PY_DOT_PY' in globals(): py.test.skip("NULL crashes early on py.py")
+    py.test.raises(TypeError, "p[0]")
+
+def test_iter():
+    BInt = new_primitive_type("int")
+    BIntP = new_pointer_type(BInt)
+    BArray = new_array_type(BIntP, None)   # int[]
+    p = newp(BArray, 7)
+    assert list(p) == list(iter(p)) == [0] * 7
+    #
+    py.test.raises(TypeError, iter, cast(BInt, 5))
+    py.test.raises(TypeError, iter, cast(BIntP, 123456))
+
+def test_cmp():
+    BInt = new_primitive_type("int")
+    BIntP = new_pointer_type(BInt)
+    BVoidP = new_pointer_type(new_void_type())
+    p = newp(BIntP, 123)
+    q = cast(BInt, 124)
+    py.test.raises(TypeError, "p < q")
+    py.test.raises(TypeError, "p <= q")
+    assert (p == q) is False
+    assert (p != q) is True
+    py.test.raises(TypeError, "p > q")
+    py.test.raises(TypeError, "p >= q")
+    r = cast(BVoidP, p)
+    assert (p <  r) is False
+    assert (p <= r) is True
+    assert (p == r) is True
+    assert (p != r) is False
+    assert (p >  r) is False
+    assert (p >= r) is True
+    s = newp(BIntP, 125)
+    assert (p == s) is False
+    assert (p != s) is True
+    assert (p < s) is (p <= s) is (s > p) is (s >= p)
+    assert (p > s) is (p >= s) is (s < p) is (s <= p)
+    assert (p < s) ^ (p > s)
+
+def test_buffer():
+    BShort = new_primitive_type("short")
+    s = newp(new_pointer_type(BShort), 100)
+    assert sizeof(s) == size_of_ptr()
+    assert sizeof(BShort) == 2
+    assert len(str(buffer(s))) == 2
+    #
+    BChar = new_primitive_type("char")
+    BCharArray = new_array_type(new_pointer_type(BChar), None)
+    c = newp(BCharArray, "hi there")
+    buf = buffer(c)
+    assert str(buf) == "hi there\x00"
+    assert len(buf) == len("hi there\x00")
+    assert buf[0] == 'h'
+    assert buf[2] == ' '
+    assert list(buf) == ['h', 'i', ' ', 't', 'h', 'e', 'r', 'e', '\x00']
+    buf[2] = '-'
+    assert c[2] == '-'
+    assert str(buf) == "hi-there\x00"
+    buf[:2] = 'HI'
+    assert str(c) == 'HI-there'
+    assert buf[:4:2] == 'H-'
+    if '__pypy__' not in sys.builtin_module_names:
+        # XXX pypy doesn't support the following assignment so far
+        buf[:4:2] = 'XY'
+        assert str(c) == 'XIYthere'
+
+def test_getcname():
+    BUChar = new_primitive_type("unsigned char")
+    BArray = new_array_type(new_pointer_type(BUChar), 123)
+    assert getcname(BArray, "<-->") == "unsigned char<-->[123]"
+
+def test_errno():
+    BVoid = new_void_type()
+    BFunc5 = new_function_type((), BVoid)
+    f = cast(BFunc5, _testfunc(5))
+    set_errno(50)
+    f()
+    assert get_errno() == 65
+    f(); f()
+    assert get_errno() == 95
+    #
+    def cb():
+        e = get_errno()
+        set_errno(e - 6)
+    f = callback(BFunc5, cb)
+    f()
+    assert get_errno() == 89
+    f(); f()
+    assert get_errno() == 77
 
 from .api import FFI, CDefError, FFIError
 from .ffiplatform import VerificationError, VerificationMissing
+
+__version__ = "0.2"
+__version_info__ = (0, 2)
         self._cached_btypes = {}
         self._parsed_types = new.module('parsed_types').__dict__
         self._new_types = new.module('new_types').__dict__
+        self._function_caches = []
         if hasattr(backend, 'set_ffi'):
             backend.set_ffi(self)
         #
         #
         self.NULL = self.cast("void *", 0)
 
-    def cdef(self, csource):
+    def cdef(self, csource, override=False):
         """Parse the given C source.  This registers all declared functions,
         types, and global variables.  The functions and global variables can
         then be accessed via either 'ffi.dlopen()' or 'ffi.verify()'.
         The types can be used in 'ffi.new()' and other functions.
         """
-        self._parser.parse(csource)
+        self._parser.parse(csource, override=override)
+        if override:
+            for cache in self._function_caches:
+                cache.clear()
 
     def dlopen(self, name):
         """Load and return a dynamic library identified by 'name'.
         library we only look for the actual (untyped) symbols.
         """
         assert isinstance(name, str) or name is None
-        return _make_ffi_library(self, name)
+        lib, function_cache = _make_ffi_library(self, name)
+        self._function_caches.append(function_cache)
+        return lib
 
     def typeof(self, cdecl, consider_function_as_funcptr=False):
         """Parse the C type given as a string and return the
         try:
             BType = self._cached_btypes[type]
         except KeyError:
-            args = type.prepare_backend_type(self)
-            if args is None:
-                args = ()
-            BType = type.finish_backend_type(self, *args)
-            self._cached_btypes[type] = BType
+            BType = type.finish_backend_type(self)
+            BType2 = self._cached_btypes.setdefault(type, BType)
+            assert BType2 is BType
         return BType
 
     def verify(self, source='', **kwargs):
     #
     if libname is not None:
         FFILibrary.__name__ = 'FFILibrary_%s' % libname
-    return FFILibrary()
+    return FFILibrary(), function_cache

cffi/backend_ctypes.py

                 return None
             @staticmethod
             def _to_ctypes(novalue):
+                if novalue is not None:
+                    raise TypeError("None expected, got %s object" %
+                                    (type(novalue).__name__,))
                 return None
         CTypesVoid._fix_class()
         return CTypesVoid
             return result
         CTypesStructOrUnion._create_ctype_obj = _create_ctype_obj
         #
-        if CTypesStructOrUnion._kind == 'struct':
-            def initialize(blob, init):
-                if not isinstance(init, dict):
-                    init = tuple(init)
-                    if len(init) > len(fnames):
-                        raise ValueError("too many values for %s initializer" %
-                                         CTypesStructOrUnion._get_c_name())
-                    init = dict(zip(fnames, init))
-                addr = ctypes.addressof(blob)
-                for fname, value in init.items():
-                    BField, bitsize = name2fieldtype[fname]
-                    assert bitsize < 0, \
-                           "not implemented: initializer with bit fields"
-                    offset = CTypesStructOrUnion._offsetof(fname)
-                    PTR = ctypes.POINTER(BField._ctype)
-                    p = ctypes.cast(addr + offset, PTR)
-                    BField._initialize(p.contents, value)
-            name2fieldtype = dict(zip(fnames, zip(btypes, bitfields)))
-        #
-        if CTypesStructOrUnion._kind == 'union':
-            def initialize(blob, init):
-                addr = ctypes.addressof(blob)
-                #fname = fnames[0]
-                BField = btypes[0]
+        def initialize(blob, init):
+            if is_union:
+                if len(init) > 1:
+                    raise ValueError("union initializer: %d items given, but "
+                                    "only one supported (use a dict if needed)"
+                                     % (len(init),))
+            if not isinstance(init, dict):
+                if isinstance(init, str):
+                    raise TypeError("union initializer: got a str")
+                init = tuple(init)
+                if len(init) > len(fnames):
+                    raise ValueError("too many values for %s initializer" %
+                                     CTypesStructOrUnion._get_c_name())
+                init = dict(zip(fnames, init))
+            addr = ctypes.addressof(blob)
+            for fname, value in init.items():
+                BField, bitsize = name2fieldtype[fname]
+                assert bitsize < 0, \
+                       "not implemented: initializer with bit fields"
+                offset = CTypesStructOrUnion._offsetof(fname)
                 PTR = ctypes.POINTER(BField._ctype)
-                BField._initialize(ctypes.cast(addr, PTR).contents, init)
+                p = ctypes.cast(addr + offset, PTR)
+                BField._initialize(p.contents, value)
+        is_union = CTypesStructOrUnion._kind == 'union'
+        name2fieldtype = dict(zip(fnames, zip(btypes, bitfields)))
         #
         for fname, BField, bitsize in fields:
             if hasattr(CTypesStructOrUnion, fname):
                                 # .value: http://bugs.python.org/issue1574593
                         else:
                             res2 = None
-                    print repr(res2)
+                    #print repr(res2)
                     return res2
                 if issubclass(BResult, CTypesGenericPtr):
                     # The only pointers callbacks can return are void*s:
         self._declarations = {}
         self._anonymous_counter = 0
         self._structnode2type = weakref.WeakKeyDictionary()
+        self._override = False
 
     def _parse(self, csource):
         # XXX: for more efficiency we would need to poke into the
         ast = _get_parser().parse(csource)
         return ast, macros
 
-    def parse(self, csource):
+    def parse(self, csource, override=False):
+        prev_override = self._override
+        try:
+            self._override = override
+            self._internal_parse(csource)
+        finally:
+            self._override = prev_override
+
+    def _internal_parse(self, csource):
         ast, macros = self._parse(csource)
         # add the macros
         for key, value in macros.items():
         if name in self._declarations:
             if self._declarations[name] is obj:
                 return
-            raise api.FFIError("multiple declarations of %s" % (name,))
+            if not self._override:
+                raise api.FFIError(
+                    "multiple declarations of %s (for interactive usage, "
+                    "try cdef(xx, override=True))" % (name,))
         assert name != '__dotdotdot__'
         self._declarations[name] = obj
 
     def _get_type_pointer(self, type, const=False):
         if isinstance(type, model.RawFunctionType):
-            return model.FunctionPtrType(type.args, type.result, type.ellipsis)
+            return type.as_function_pointer()
         if const:
             return model.ConstPointerType(type)
         return model.PointerType(type)

cffi/ffiplatform.py

         dist.run_command('build_ext')
     except (distutils.errors.CompileError,
             distutils.errors.LinkError), e:
-        raise VerificationError(str(e))
+        raise VerificationError('%s: %s' % (e.__class__.__name__, e))
     #
     cmd_obj = dist.get_command_obj('build_ext')
     [soname] = cmd_obj.get_outputs()
+import weakref
 
 class BaseType(object):
 
     def __hash__(self):
         return hash((self.__class__, tuple(self._get_items())))
 
-    def prepare_backend_type(self, ffi):
-        pass
-
-    def finish_backend_type(self, ffi, *args):
-        try:
-            return ffi._cached_btypes[self]
-        except KeyError:
-            return self.new_backend_type(ffi, *args)
-
 
 class VoidType(BaseType):
     _attrs_ = ()
     def _get_c_name(self, replace_with):
         return 'void' + replace_with
 
-    def new_backend_type(self, ffi):
-        return ffi._backend.new_void_type()
+    def finish_backend_type(self, ffi):
+        return global_cache(ffi, 'new_void_type')
 
 void_type = VoidType()
 
     def is_float_type(self):
         return self.name in ('double', 'float')
 
-    def new_backend_type(self, ffi):
-        return ffi._backend.new_primitive_type(self.name)
+    def finish_backend_type(self, ffi):
+        return global_cache(ffi, 'new_primitive_type', self.name)
 
 
-class RawFunctionType(BaseType):
-    # Corresponds to a C type like 'int(int)', which is the C type of
-    # a function, but not a pointer-to-function.  The backend has no
-    # notion of such a type; it's used temporarily by parsing.
+class BaseFunctionType(BaseType):
     _attrs_ = ('args', 'result', 'ellipsis')
 
     def __init__(self, args, result, ellipsis):
         replace_with = '(%s)(%s)' % (replace_with, ', '.join(reprargs))
         return self.result._get_c_name(replace_with)
 
-    def prepare_backend_type(self, ffi):
+
+class RawFunctionType(BaseFunctionType):
+    # Corresponds to a C type like 'int(int)', which is the C type of
+    # a function, but not a pointer-to-function.  The backend has no
+    # notion of such a type; it's used temporarily by parsing.
+
+    def finish_backend_type(self, ffi):
         from . import api
         raise api.CDefError("cannot render the type %r: it is a function "
                             "type, not a pointer-to-function type" % (self,))
 
+    def as_function_pointer(self):
+        return FunctionPtrType(self.args, self.result, self.ellipsis)
 
-class FunctionPtrType(RawFunctionType):
+
+class FunctionPtrType(BaseFunctionType):
 
     def _get_c_name(self, replace_with):
-        return RawFunctionType._get_c_name(self, '*'+replace_with)
+        return BaseFunctionType._get_c_name(self, '*'+replace_with)
 
-    def prepare_backend_type(self, ffi):
-        args = [ffi._get_cached_btype(self.result)]
+    def finish_backend_type(self, ffi):
+        result = ffi._get_cached_btype(self.result)
+        args = []
         for tp in self.args:
+            if isinstance(tp, RawFunctionType):
+                tp = tp.as_function_pointer()
             args.append(ffi._get_cached_btype(tp))
-        return args
-
-    def new_backend_type(self, ffi, result, *args):
-        return ffi._backend.new_function_type(args, result, self.ellipsis)
+        return global_cache(ffi, 'new_function_type',
+                            tuple(args), result, self.ellipsis)
 
 
 class PointerType(BaseType):
     def _get_c_name(self, replace_with):
         return self.totype._get_c_name('* ' + replace_with)
 
-    def prepare_backend_type(self, ffi):
-        return (ffi._get_cached_btype(self.totype),)
-
-    def new_backend_type(self, ffi, BItem):
-        return ffi._backend.new_pointer_type(BItem)
+    def finish_backend_type(self, ffi):
+        BItem = ffi._get_cached_btype(self.totype)
+        return global_cache(ffi, 'new_pointer_type', BItem)
 
 
 class ConstPointerType(PointerType):
     def _get_c_name(self, replace_with):
         return self.totype._get_c_name(' const * ' + replace_with)
 
-    def prepare_backend_type(self, ffi):
-        return (ffi._get_cached_btype(PointerType(self.totype)),)
-
-    def new_backend_type(self, ffi, BPtr):
+    def finish_backend_type(self, ffi):
+        BPtr = ffi._get_cached_btype(PointerType(self.totype))
         return BPtr
 
 
             brackets = '[%d]' % self.length
         return self.item._get_c_name(replace_with + brackets)
 
-    def prepare_backend_type(self, ffi):
-        return (ffi._get_cached_btype(PointerType(self.item)),)
-
-    def new_backend_type(self, ffi, BPtrItem):
-        return ffi._backend.new_array_type(BPtrItem, self.length)
+    def finish_backend_type(self, ffi):
+        BPtrItem = ffi._get_cached_btype(PointerType(self.item))
+        return global_cache(ffi, 'new_array_type', BPtrItem, self.length)
 
 
 class StructOrUnion(BaseType):
         name = self.forcename or '%s %s' % (self.kind, self.name)
         return name + replace_with
 
-    def prepare_backend_type(self, ffi):
-        BType = self.get_btype(ffi)
+    def finish_backend_type(self, ffi):
+        BType = self.new_btype(ffi)
         ffi._cached_btypes[self] = BType
-        args = [BType]
-        if self.fldtypes is not None:
-            for tp in self.fldtypes:
-                args.append(ffi._get_cached_btype(tp))
-        return args
-
-    def finish_backend_type(self, ffi, BType, *fldtypes):
-        if self.fldnames is None:
-            return BType   # not completing it: it's an opaque struct
+        if self.fldtypes is None:
+            return BType    # not completing it: it's an opaque struct
+        #
+        fldtypes = tuple(ffi._get_cached_btype(tp) for tp in self.fldtypes)
         #
         if self.fixedlayout is None:
             lst = zip(self.fldnames, fldtypes, self.fldbitsize)
             from . import ffiplatform
             raise ffiplatform.VerificationMissing(self._get_c_name(''))
 
-    def get_btype(self, ffi):
+    def new_btype(self, ffi):
         self.check_not_partial()
         return ffi._backend.new_struct_type(self.name)
 
 class UnionType(StructOrUnion):
     kind = 'union'
 
-    def get_btype(self, ffi):
+    def new_btype(self, ffi):
         return ffi._backend.new_union_type(self.name)
 
 
             from . import ffiplatform
             raise ffiplatform.VerificationMissing(self._get_c_name(''))
 
-    def new_backend_type(self, ffi):
+    def finish_backend_type(self, ffi):
         self.check_not_partial()
         return ffi._backend.new_enum_type(self.name, self.enumerators,
                                           self.enumvalues)
     tp = StructType('$%s' % name, None, None, None)
     tp.forcename = name
     return tp
+
+def global_cache(ffi, funcname, *args):
+    try:
+        return ffi._backend.__typecache[args]
+    except KeyError:
+        pass
+    except AttributeError:
+        # initialize the __typecache attribute, either at the module level
+        # if ffi._backend is a module, or at the class level if ffi._backend
+        # is some instance.
+        ModuleType = type(weakref)
+        if isinstance(ffi._backend, ModuleType):
+            ffi._backend.__typecache = weakref.WeakValueDictionary()
+        else:
+            type(ffi._backend).__typecache = weakref.WeakValueDictionary()
+    res = getattr(ffi._backend, funcname)(*args)
+    ffi._backend.__typecache[args] = res
+    return res
 
     # ----------
 
-    def convert_to_c(self, tp, fromvar, tovar, errcode, is_funcarg=False):
+    def convert_funcarg_to_c(self, tp, fromvar, tovar, errcode):
         extraarg = ''
         if isinstance(tp, model.PrimitiveType):
             converter = '_cffi_to_c_%s' % (tp.name.replace(' ', '_'),)
             errvalue = '-1'
         #
         elif isinstance(tp, model.PointerType):
-            if (is_funcarg and
-                    isinstance(tp.totype, model.PrimitiveType) and
+            if (isinstance(tp.totype, model.PrimitiveType) and
                     tp.totype.name == 'char'):
                 converter = '_cffi_to_c_char_p'
             else:
                 extraarg = ', _cffi_type(%d)' % self.gettypenum(tp)
             errvalue = 'NULL'
         #
+        elif isinstance(tp, model.StructType):
+            # a struct (not a struct pointer) as a function argument
+            self.prnt('  if (_cffi_to_c((char*)&%s, _cffi_type(%d), %s) < 0)'
+                      % (tovar, self.gettypenum(tp), fromvar))
+            self.prnt('    %s;' % errcode)
+            return
+        #
         else:
             raise NotImplementedError(tp)
         #
         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)
 
         prnt()
         #
         for i, type in enumerate(tp.args):
-            self.convert_to_c(type, 'arg%d' % i, 'x%d' % i, 'return NULL',
-                              is_funcarg=True)
+            self.convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i,
+                                      'return NULL')
             prnt()
         #
         prnt('  _cffi_restore_errno();')
     ((PyObject *(*)(char))_cffi_exports[15])
 #define _cffi_from_c_deref                                               \
     ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[16])
-#define _CFFI_NUM_EXPORTS 17
+#define _cffi_to_c                                                       \
+    ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[17])
+#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
 # A Linux-only demo
 #
+import sys
 from cffi import FFI
 
+if not sys.platform.startswith('linux'):
+    raise Exception("Linux-only demo")
+
 
 ffi = FFI()
 ffi.cdef("""
 # A Linux-only demo, using verify() instead of hard-coding the exact layouts
 #
+import sys
 from cffi import FFI
 
+if not sys.platform.startswith('linux'):
+    raise Exception("Linux-only demo")
+
 
 ffi = FFI()
 ffi.cdef("""
     pass
 
 def main():
-    display = XOpenDisplay(None)
+    display = XOpenDisplay(ffi.NULL)
     if display == ffi.NULL:
         raise XError("cannot open display")
     w = XCreateSimpleWindow(display, DefaultRootWindow(display),

doc/source/index.rst

 * https://bitbucket.org/cffi/cffi/downloads
 
 * ``python setup.py install`` or ``python setup_base.py install``
+  (should work out of the box on Ubuntu or Windows; see below for
+  `MacOS 10.6`_)
 
 * or you can directly import and use ``cffi``, but if you don't
-  compile the ``_ffi_backend`` extension module, it will fall back
+  compile the ``_cffi_backend`` extension module, it will fall back
   to using internally ``ctypes`` (slower and does not support
   ``verify()``).
 
 .. _`testing/test_verify.py`: https://bitbucket.org/cffi/cffi/src/default/testing/test_verify.py
 
 
+Platform-specific instructions
+------------------------------
+
+``libffi`` is notoriously messy to install and use --- to the point that
+CPython includes its own copy to avoid relying on external packages.
+CFFI did the same for Windows, but (so far) not for other platforms.
+Ubuntu Linux seems to work out of the box.  Here are some
+(user-supplied) instructions for other platforms.
+
+
+MacOS 10.6
+++++++++++
+
+(Thanks Juraj Sukop for this)
+
+For building libffi you can use the default install path, but then, in
+``setup.py`` you need to change::
+
+    include_dirs = []
+
+to::
+
+    include_dirs = ['/usr/local/lib/libffi-3.0.11/include']
+
+Then running ``python setup.py build`` complains about "fatal error: error writing to -: Broken pipe", which can be fixed by running::
+
+    ARCHFLAGS="-arch i386 -arch x86_64" python setup.py build
+
+as described here_.
+
+.. _here: http://superuser.com/questions/259278/python-2-6-1-pycrypto-2-3-pypi-package-broken-pipe-during-build
+
+
+=======================================================
+
 Examples
 =======================================================
 
     ... """)                                  
     >>> C = ffi.dlopen(None)                     # loads the entire C namespace
     >>> arg = ffi.new("char[]", "world")         # equivalent to C code: char arg[] = "world";
-    >>> C.printf("hi there, %s!\n", arg);        # call printf
+    >>> C.printf("hi there, %s!\n", arg)         # call printf
     hi there, world!
 
 
 ``struct passwd``, but so far require a C compiler at runtime.  (We plan
 to improve with caching and a way to distribute the compiled code.)
 
+You will find a number of larger examples using ``verify()`` in the
+`demo`_ directory.
+
 Struct/Array Example
 --------------------
 
 If specified, this argument gives an "initializer", like you can use
 with C code to initialize global variables.
 
-The actual function calls should be obvious.  It's like C.  Did you even
-notice that we accidentally left a trailing semicolon?  That's fine
-because it is valid Python syntax as well ``:-)``
+The actual function calls should be obvious.  It's like C.
 
 
 =======================================================
   size_t, ssize_t
 
 As we will see on `the verification step`_ below, the declarations can
-also contain "``...``" at various places; there are placeholders that will
+also contain "``...``" at various places; these are placeholders that will
 be completed by a call to ``verify()``.
 
 
 -----------------
 
 ``ffi.dlopen(libpath)``: this function opens a shared library and
-returns a module-like library object.  You can use the library object to
-call the functions previously declared by ``ffi.cdef()``, and to read or
-write global variables.  Note that you can use a single ``cdef()`` to
-declare functions from multiple libraries, as long as you load each of
-them with ``dlopen()`` and access the functions from the correct one.
+returns a module-like library object.  You need to use *either*
+``ffi.dlopen()`` *or* ``ffi.verify()``, documented below_.
+
+You can use the library object to call the functions previously declared
+by ``ffi.cdef()``, and to read or write global variables.  Note that you
+can use a single ``cdef()`` to declare functions from multiple
+libraries, as long as you load each of them with ``dlopen()`` and access
+the functions from the correct one.
 
 The ``libpath`` is the file name of the shared library, which can
 contain a full path or not (in which case it is searched in standard
 cannot call functions from a library without linking it in your program,
 as ``dlopen()`` does dynamically in C.
 
+.. _below:
+
 
 The verification step
 ---------------------
 ``ffi.verify(source, **kwargs)``: verifies that the current ffi signatures
 compile on this machine, and return a dynamic library object.  The
 dynamic library can be used to call functions and access global
-variables declared by a previous ``ffi.cdef()``.  The library is compiled
-by the C compiler: it gives you C-level API compatibility (including
-calling macros, as long as you declared them as functions in
-``ffi.cdef()``).  This differs from ``ffi.dlopen()``, which requires
-ABI-level compatibility and must be called several times to open several
-shared libraries.
+variables declared by a previous ``ffi.cdef()``.  You don't need to use
+``ffi.dlopen()`` in this case.
+
+The returned library is a custom one, compiled just-in-time by the C
+compiler: it gives you C-level API compatibility (including calling
+macros, as long as you declared them as functions in ``ffi.cdef()``).
+This differs from ``ffi.dlopen()``, which requires ABI-level
+compatibility and must be called several times to open several shared
+libraries.
 
 On top of CPython, the new library is actually a CPython C extension
 module.  This solution constrains you to have a C compiler (future work
    if you pass a ``int *`` argument to a function expecting a ``long *``.
 
 Moreover, you can use "``...``" in the following places in the ``cdef()``
-for leaving details unspecified (filled in by the C compiler):
+for leaving details unspecified, which are then completed by the C
+compiler during ``verify()``:
 
 *  structure declarations: any ``struct`` that ends with "``...;``" is
-   partial.  It will be completed by the compiler.  (But note that you
-   can only access fields that you declared.)  Any ``struct``
-   declaration without "``...;``" is assumed to be exact, and this is
+   partial: it may be missing fields and/or have them declared out of order.
+   This declaration will be corrected by the compiler.  (But note that you
+   can only access fields that you declared, not others.)  Any ``struct``
+   declaration which doesn't use "``...``" is assumed to be exact, but this is
    checked: you get a ``VerificationError`` if it is not.
 
 *  unknown types: the syntax "``typedef ... foo_t;``" declares the type
-   ``foo_t`` as opaque.
+   ``foo_t`` as opaque.  Useful mainly for when the API takes and returns
+   ``foo_t *`` without you needing to looking inside the ``foo_t``.
 
 *  array lengths: when used as structure fields, arrays can have an
    unspecified length, as in "``int n[];``".  The length is completed
    to write the ``const`` together with the variable name, as in
    ``static char *const FOO;``).
 
+Currently, finding automatically the size of an integer type is not
+supported.  You need to declare them with ``typedef int myint;`` or
+``typedef long myint;`` or ``typedef long long myint;`` or their
+unsigned equivalent.  Depending on the usage, the C compiler might give
+warnings if you misdeclare ``myint`` as the wrong type even if it is
+equivalent on this platform (e.g. using ``long`` instead of ``long
+long`` or vice-versa on 64-bit Linux).
+
 
 Working with pointers, structures and arrays
 --------------------------------------------
 fit nicely in the model, and it does not seem to be needed here).
 
 Any operation that would in C return a pointer or array or struct type
-gives you a new cdata object.  Unlike the "original" one, these new
+gives you a fresh cdata object.  Unlike the "original" one, these fresh
 cdata objects don't have ownership: they are merely references to
 existing memory.
 
+.. versionchanged:: 0.2
+   As an exception the above rule, dereferencing a pointer that owns a
+   *struct* or *union* object returns a cdata struct or union object
+   that "co-owns" the same memory.  Thus in this case there are two
+   objects that can keep the memory alive.  This is done for cases where
+   you really want to have a struct object but don't have any convenient
+   place to keep alive the original pointer object (returned by
+   ``ffi.new()``).
+
 Example::
 
     ffi.cdef("void somefunction(int *);")
     foo_t v = { 1, 2 };            // C syntax
     v = ffi.new("foo_t", [1, 2])   # CFFI equivalent
 
-    foo_t v = { .y=1, .x=2 };               // C syntax
+    foo_t v = { .y=1, .x=2 };               // C99 syntax
     v = ffi.new("foo_t", {'y': 1, 'x': 2})  # CFFI equivalent
 
 Like C, arrays of chars can also be initialized from a string, in
     array = ffi.new("int[1000]")      # CFFI 1st equivalent
     array = ffi.new("int[]", 1000)    # CFFI 2nd equivalent
 
-This is useful if the length is not actually a constant, to avoid doing
-things like ``ffi.new("int[%d]"%x)``.  Indeed, this is not recommended:
+This is useful if the length is not actually a constant, to avoid things
+like ``ffi.new("int[%d]" % x)``.  Indeed, this is not recommended:
 ``ffi`` normally caches the string ``"int[]"`` to not need to re-parse
 it all the time.
 
 
+Function calls
+--------------
+
+When calling C functions, passing arguments follows mostly the same
+rules as assigning to structure fields, and the return value follows the
+same rules as reading a structure field.  For example::
+
+    ffi.cdef("""
+        int foo(short a, int b);
+    """)
+    lib = ffi.verify("#include <foo.h>")
+
+    n = lib.foo(2, 3)     # returns a normal integer
+    lib.foo(40000, 3)     # raises OverflowError
+
+As an extension, you can pass to ``char *`` arguments a normal Python
+string (but don't pass a normal Python string to functions that take a
+``char *`` argument and may mutate it!)::
+
+    ffi.cdef("""
+        size_t strlen(const char *);
+    """)
+    C = ffi.dlopen(None)
+
+    assert C.strlen("hello") == 5
+
+CFFI supports passing and returning structs to functions and callbacks.
+Example (sketch)::
+
+    >>> ffi.cdef("""
+    ...     struct foo_s { int a, b; };
+    ...     struct foo_s function_returning_a_struct(void);
+    ... """)
+    >>> lib = ffi.verify("#include <somewhere.h>")
+    >>> lib.function_returning_a_struct()
+    <cdata 'struct foo_s' owning 8 bytes>
+
+There are a few (obscure) limitations to the argument types and
+return type.  You cannot pass directly as argument a union, nor a struct
+which uses bitfields (note that passing a *pointer* to anything is
+fine).  If you pass a struct, the struct type cannot have been declared
+with "``...;``" and completed with ``verify()``; you need to declare it
+completely in ``cdef()``.
+
+.. versionadded:: 0.2
+   Aside from these limitations, functions and callbacks can now return
+   structs.
+
+
 Variadic function calls
 -----------------------
 
     C.printf("hello, %d\n", ffi.cast("int", 42))
     C.printf("hello, %ld\n", ffi.cast("long", 42))
     C.printf("hello, %f\n", ffi.cast("double", 42))
+    C.printf("hello, %s\n", ffi.new("char[]", "world"))
 
 
 Callbacks
 representation of the given C type.  If non-empty, the "extra" string is
 appended (or inserted at the right place in more complicated cases); it
 can be the name of a variable to declare, or an extra part of the type
-like ``"*"`` or ``"[5]"``, so that for example
+like ``"*"`` or ``"[5]"``.  For example
 ``ffi.getcname(ffi.typeof(x), "*")`` returns the string representation
 of the C type "pointer to the same type than x".
 
 Comments and bugs
 =================
 
-Please report to the `issue tracker`_ any bugs.  Feel free to discuss
-matters in the `mailing list`_.  As a general rule, when there is a
-design issue to resolve, we pick the solution that is the "most C-like".
-We hope that this module has got everything you need to access C code
-and nothing more.
+The best way to contact us is on the IRC ``#pypy`` channel of
+``irc.freenode.net``.  Feel free to discuss matters either there or in
+the `mailing list`_.  Please report to the `issue tracker`_ any bugs.
+
+As a general rule, when there is a design issue to resolve, we pick the
+solution that is the "most C-like".  We hope that this module has got
+everything you need to access C code and nothing more.
 
 --- the authors, Armin Rigo and Maciej Fijalkowski
 
 sources = ['c/_cffi_backend.c']
 libraries = ['ffi']
 include_dirs = []
+define_macros = []
 
 
 if sys.platform == 'win32':
         _filenames.remove('win32.c')
     sources.extend(os.path.join(COMPILE_LIBFFI, filename)
                    for filename in _filenames)
+    define_macros.append(('USE_C_LIBFFI_MSVC', '1'))
 else:
     try:
         p = subprocess.Popen(['pkg-config', '--cflags-only-I', 'libffi'],
                 Extension(name='_cffi_backend',
                           include_dirs=include_dirs,
                           sources=sources,
-                          libraries=libraries),
+                          libraries=libraries,
+                          define_macros=define_macros),
             ],
         ),
     },
 import sys, os
 
 
-from setup import include_dirs, sources, libraries
+from setup import include_dirs, sources, libraries, define_macros
 
 
 if __name__ == '__main__':
                                  include_dirs=include_dirs,
                                  sources=sources,
                                  libraries=libraries,
+                                 define_macros=define_macros,
                                  )])

testing/backend_tests.py

         assert u.a != 0
         py.test.raises(OverflowError, "u.b = 32768")
         #
-        u = ffi.new("union foo", -2)
+        u = ffi.new("union foo", [-2])
         assert u.a == -2
         py.test.raises((AttributeError, TypeError), "del u.a")
         assert repr(u) == "<cdata 'union foo *' owning %d bytes>" % SIZE_OF_INT
         u = ffi.new("union baz *")   # this works
         assert u[0] == ffi.NULL
 
+    def test_union_initializer(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("union foo { char a; int b; };")
+        py.test.raises(TypeError, ffi.new, "union foo", 'A')
+        py.test.raises(TypeError, ffi.new, "union foo", 5)
+        py.test.raises(ValueError, ffi.new, "union foo", ['A', 5])
+        u = ffi.new("union foo", ['A'])
+        assert u.a == 'A'
+        py.test.raises(TypeError, ffi.new, "union foo", [5])
+        u = ffi.new("union foo", {'b': 12345})
+        assert u.b == 12345
+        u = ffi.new("union foo", [])
+        assert u.a == '\x00'
+        assert u.b == 0
+
     def test_sizeof_type(self):
         ffi = FFI(backend=self.Backend())
         ffi.cdef("""
         f = ffi.callback("int(*)(int)", cb)
         a = ffi.new("int(*[5])(int)", [f, f])
         assert a[1](42) == 43
+
+    def test_callback_as_function_argument(self):
+        # In C, function arguments can be declared with a function type,
+        # which is automatically replaced with the ptr-to-function type.
+        ffi = FFI(backend=self.Backend())
+        def cb(a, b):
+            return chr(ord(a) + ord(b))
+        f = ffi.callback("char cb(char, char)", cb)
+        assert f('A', chr(1)) == 'B'
+        def g(callback):
+            return callback('A', chr(1))
+        g = ffi.callback("char g(char cb(char, char))", g)
+        assert g(f) == 'B'
+
+    def test_vararg_callback(self):
+        py.test.skip("callback with '...'")
+        ffi = FFI(backend=self.Backend())
+        def cb(i, va_list):
+            j = ffi.va_arg(va_list, "int")
+            k = ffi.va_arg(va_list, "long long")
+            return i * 2 + j * 3 + k * 5
+        f = ffi.callback("long long cb(long i, ...)", cb)
+        res = f(10, ffi.cast("int", 100), ffi.cast("long long", 1000))
+        assert res == 20 + 300 + 5000
+
+    def test_unique_types(self):
+        ffi1 = FFI(backend=self.Backend())
+        ffi2 = FFI(backend=self.Backend())
+        assert ffi1.typeof("char") is ffi2.typeof("char ")
+        assert ffi1.typeof("long") is ffi2.typeof("signed long int")
+        assert ffi1.typeof("double *") is ffi2.typeof("double*")
+        assert ffi1.typeof("int ***") is ffi2.typeof(" int * * *")
+        assert ffi1.typeof("int[]") is ffi2.typeof("signed int[]")
+        assert ffi1.typeof("signed int*[17]") is ffi2.typeof("int *[17]")
+        assert ffi1.typeof("void") is ffi2.typeof("void")
+        assert ffi1.typeof("int(*)(int,int)") is ffi2.typeof("int(*)(int,int)")
+        #
+        # these depend on user-defined data, so should not be shared
+        assert ffi1.typeof("struct foo") is not ffi2.typeof("struct foo")
+        assert ffi1.typeof("union foo *") is not ffi2.typeof("union foo*")
+        assert ffi1.typeof("enum foo") is not ffi2.typeof("enum foo")
+        # sanity check: twice 'ffi1'
+        assert ffi1.typeof("struct foo*") is ffi1.typeof("struct foo *")

testing/test_cdata.py

         return "fake library"
 
     def new_primitive_type(self, name):
-        return FakePrimitiveType(name)
+        return FakeType("primitive " + name)
 
     def new_void_type(self):
-        return "void!"
+        return FakeType("void")
     def new_pointer_type(self, x):
-        return 'ptr-to-%r!' % (x,)
+        return FakeType('ptr-to-%r' % (x,))
     def cast(self, x, y):
         return 'casted!'
 
-class FakePrimitiveType(object):
 
+class FakeType(object):
     def __init__(self, cdecl):
         self.cdecl = cdecl
 
 def test_typeof():
     ffi = FFI(backend=FakeBackend())
     clong = ffi.typeof("signed long int")
-    assert isinstance(clong, FakePrimitiveType)
-    assert clong.cdecl == 'long'
+    assert isinstance(clong, FakeType)
+    assert clong.cdecl == 'primitive long'

testing/test_function.py

 import py
 from cffi import FFI
-import math, os, sys
+import math, os, sys, StringIO
 from cffi.backend_ctypes import CTypesBackend
 
 
         res = fd.getvalue()
         assert res == 'world\n'
 
+    def test_callback_returning_void(self):
+        ffi = FFI(backend=self.Backend())
+        for returnvalue in [None, 42]:
+            def cb():
+                return returnvalue
+            fptr = ffi.callback("void(*)(void)", cb)
+            old_stderr = sys.stderr
+            try:
+                sys.stderr = StringIO.StringIO()
+                returned = fptr()
+                printed = sys.stderr.getvalue()
+            finally:
+                sys.stderr = old_stderr
+            assert returned is None
+            if returnvalue is None:
+                assert printed == ''
+            else:
+                assert "None" in printed
+
     def test_passing_array(self):
         ffi = FFI(backend=self.Backend())
         ffi.cdef("""

testing/test_parsing.py

 import py, sys
-from cffi import FFI, CDefError, VerificationError
+from cffi import FFI, FFIError, CDefError, VerificationError
 
 class FakeBackend(object):
 
         return FakeLibrary()
 
     def new_function_type(self, args, result, has_varargs):
-        return '<func (%s), %s, %s>' % (', '.join(args), result, has_varargs)
+        args = [arg.cdecl for arg in args]
+        result = result.cdecl
+        return FakeType(
+            '<func (%s), %s, %s>' % (', '.join(args), result, has_varargs))
 
     def new_primitive_type(self, name):
         assert name == name.lower()
-        return '<%s>' % name
+        return FakeType('<%s>' % name)
 
     def new_pointer_type(self, itemtype):
-        return '<pointer to %s>' % (itemtype,)
+        return FakeType('<pointer to %s>' % (itemtype,))
 
     def new_struct_type(self, name):
         return FakeStruct(name)
         s.fields = fields
     
     def new_array_type(self, ptrtype, length):
-        return '<array %s x %s>' % (ptrtype, length)
+        return FakeType('<array %s x %s>' % (ptrtype, length))
 
     def new_void_type(self):
-        return "<void>"
+        return FakeType("<void>")
     def cast(self, x, y):
         return 'casted!'
 
+class FakeType(object):
+    def __init__(self, cdecl):
+        self.cdecl = cdecl
+    def __str__(self):
+        return self.cdecl
+
 class FakeStruct(object):
     def __init__(self, name):
         self.name = name
     def __str__(self):
-        return ', '.join([y + x for x, y, z in self.fields])
+        return ', '.join([str(y) + str(x) for x, y, z in self.fields])
 
 class FakeLibrary(object):
     
 class FakeFunction(object):
 
     def __init__(self, BType, name):
-        self.BType = BType
+        self.BType = str(BType)
         self.name = name
 
 
         UInt foo(void);
         """)
     C = ffi.dlopen(None)
-    assert ffi.typeof("UIntReally") == '<unsigned int>'
+    assert str(ffi.typeof("UIntReally")) == '<unsigned int>'
     assert C.foo.BType == '<func (), <unsigned int>, False>'
 
 def test_typedef_more_complex():
         """)
     C = ffi.dlopen(None)
     assert str(ffi.typeof("foo_t")) == '<int>a, <int>b'
-    assert ffi.typeof("foo_p") == '<pointer to <int>a, <int>b>'
+    assert str(ffi.typeof("foo_p")) == '<pointer to <int>a, <int>b>'
     assert C.foo.BType == ('<func (<pointer to <pointer to '
                            '<int>a, <int>b>>), <int>, False>')
 
         """)
     type = ffi._parser.parse_type("array_t", force_pointer=True)
     BType = ffi._get_cached_btype(type)
-    assert BType == '<array <pointer to <int>> x 5>'
+    assert str(BType) == '<array <pointer to <int>> x 5>'
 
 def test_typedef_array_convert_array_to_pointer():
     ffi = FFI(backend=FakeBackend())
         """)
     type = ffi._parser.parse_type("fn_t")
     BType = ffi._get_cached_btype(type)
-    assert BType == '<func (<pointer to <int>>), <int>, False>'
+    assert str(BType) == '<func (<pointer to <int>>), <int>, False>'
 
 def test_remove_comments():
     ffi = FFI(backend=FakeBackend())
     assert repr(type_bar) == "<struct $1>"
     py.test.raises(VerificationError, type_bar.get_c_name)
     assert type_foo.get_c_name() == "foo_t"
+
+def test_override():
+    ffi = FFI(backend=FakeBackend())
+    C = ffi.dlopen(None)
+    ffi.cdef("int foo(void);")
+    py.test.raises(FFIError, ffi.cdef, "long foo(void);")
+    assert C.foo.BType == '<func (), <int>, False>'
+    ffi.cdef("long foo(void);", override=True)
+    assert C.foo.BType == '<func (), <long>, False>'

testing/test_verify.py

     lib = ffi.verify("#include <string.h>")
     assert lib.strlen("hi there!") == 9
 
+def test_strlen_array_of_char():
+    ffi = FFI()
+    ffi.cdef("int strlen(char[]);")
+    lib = ffi.verify("#include <string.h>")
+    assert lib.strlen("hello") == 5
+
 
 all_integer_types = ['short', 'int', 'long', 'long long',
                      'signed char', 'unsigned char',
     """)
     s = ffi.new("struct foo_s", ['B', 1])
     assert lib.foo(50, s[0]) == ord('A')
+
+def test_autofilled_struct_as_argument():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { long a; double b; ...; };\n"
+             "int foo(struct foo_s);")
+    lib = ffi.verify("""
+        struct foo_s {
+            double b;
+            long a;
+        };
+        int foo(struct foo_s s) {
+            return s.a - (int)s.b;
+        }
+    """)
+    s = ffi.new("struct foo_s", [100, 1])
+    assert lib.foo(s[0]) == 99
+
+def test_autofilled_struct_as_argument_dynamic():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { long a; ...; };\n"
+             "int (*foo)(struct foo_s);")
+    e = py.test.raises(TypeError, ffi.verify, """
+        struct foo_s {
+            double b;
+            long a;
+        };
+        int foo1(struct foo_s s) {
+            return s.a - (int)s.b;
+        }
+        int (*foo)(struct foo_s s) = &foo1;
+    """)
+    msg ='cannot pass as an argument a struct that was completed with verify()'
+    assert msg in str(e.value)
+
+def test_func_returns_struct():
+    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)
+    assert repr(s) == "<cdata 'struct foo_s' owning 8 bytes>"
+    assert s.aa == 36
+    assert s.bb == 49
+
+def test_func_as_funcptr():
+    ffi = FFI()
+    ffi.cdef("int *(*const fooptr)(void);")
+    lib = ffi.verify("""
+        int *foo(void) {
+            return (int*)"foobar";
+        }
+        int *(*fooptr)(void) = foo;
+    """)