diff options
| author | Armin Rigo <arigo@tunes.org> | 2016-08-22 17:40:35 +0200 |
|---|---|---|
| committer | Armin Rigo <arigo@tunes.org> | 2016-08-22 17:40:35 +0200 |
| commit | ec18b7f424ff64bb15154bc69c7103b2b36ff072 (patch) | |
| tree | 38e60aae6447568c0c8b7a0cccdb66fd8c1b2b4f | |
| parent | 2401c14d0a24079b305cc3ecf36f84a1ea663d7c (diff) | |
| parent | 4a14e501899044786bbe9c906f8bb3147d529c33 (diff) | |
| download | cffi-ec18b7f424ff64bb15154bc69c7103b2b36ff072.tar.gz | |
merge heads
| -rw-r--r-- | c/_cffi_backend.c | 7 | ||||
| -rw-r--r-- | cffi/recompiler.py | 4 | ||||
| -rw-r--r-- | cffi/vengine_cpy.py | 2 | ||||
| -rw-r--r-- | doc/source/cdef.rst | 12 | ||||
| -rw-r--r-- | doc/source/ref.rst | 2 | ||||
| -rw-r--r-- | doc/source/using.rst | 51 | ||||
| -rw-r--r-- | testing/cffi1/test_recompiler.py | 18 |
7 files changed, 56 insertions, 40 deletions
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c index b358f8b..945a8dc 100644 --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -3042,13 +3042,14 @@ static CDataObject *allocate_owning_object(Py_ssize_t size, static PyObject * convert_struct_to_owning_object(char *data, CTypeDescrObject *ct) { + /* also accepts unions, for the API mode */ 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) { + if (datasize < 0) { PyErr_SetString(PyExc_TypeError, - "return type is not a struct or is opaque"); + "return type is an opaque structure or union"); return NULL; } cd = allocate_owning_object(dataoffset + datasize, ct); @@ -4623,6 +4624,8 @@ static ffi_type *fb_fill_type(struct funcbuilder_s *fb, CTypeDescrObject *ct, ct = ct->ct_itemdescr; } ffifield = fb_fill_type(fb, ct, 0); + if (PyErr_Occurred()) + return NULL; if (elements != NULL) { for (j=0; j<flat; j++) elements[nflat++] = ffifield; diff --git a/cffi/recompiler.py b/cffi/recompiler.py index eb7c891..8e66d1c 100644 --- a/cffi/recompiler.py +++ b/cffi/recompiler.py @@ -515,7 +515,7 @@ class Recompiler: tovar, errcode) return # - elif isinstance(tp, (model.StructOrUnion, model.EnumType)): + elif isinstance(tp, model.StructOrUnionOrEnum): # 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)) @@ -572,7 +572,7 @@ class Recompiler: elif isinstance(tp, model.ArrayType): return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( var, self._gettypenum(model.PointerType(tp.item))) - elif isinstance(tp, model.StructType): + elif isinstance(tp, model.StructOrUnion): if tp.fldnames is None: raise TypeError("'%s' is used as %s, but is opaque" % ( tp._get_c_name(), context)) diff --git a/cffi/vengine_cpy.py b/cffi/vengine_cpy.py index 6bb0986..bef0f52 100644 --- a/cffi/vengine_cpy.py +++ b/cffi/vengine_cpy.py @@ -308,7 +308,7 @@ class VCPythonEngine(object): elif isinstance(tp, model.ArrayType): return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( var, self._gettypenum(model.PointerType(tp.item))) - elif isinstance(tp, model.StructType): + elif isinstance(tp, model.StructOrUnion): if tp.fldnames is None: raise TypeError("'%s' is used as %s, but is opaque" % ( tp._get_c_name(), context)) diff --git a/doc/source/cdef.rst b/doc/source/cdef.rst index 6f9b8a4..1e90aa8 100644 --- a/doc/source/cdef.rst +++ b/doc/source/cdef.rst @@ -269,9 +269,7 @@ specialized versions, and declares the correct (unicode or not) types. Usually, the right thing to do is to call this method with True. Be aware (particularly on Python 2) that, afterwards, you need to pass unicode -strings as arguments instead of byte strings. (Before cffi version 0.9, -``TCHAR`` and friends where hard-coded as unicode, but ``UNICODE`` was, -inconsistently, not defined by default.) +strings as arguments instead of byte strings. .. _loading-libraries: @@ -336,7 +334,7 @@ ffibuilder.set_source(): preparing out-of-line modules **ffibuilder.set_source(module_name, c_header_source, [\*\*keywords...])**: prepare the ffi for producing out-of-line an external module called -``module_name``. *New in version 1.0.* +``module_name``. ``ffibuilder.set_source()`` by itself does not write any file, but merely records its arguments for later. It can therefore be called before or @@ -425,7 +423,7 @@ in the details. These places are: declaration which doesn't use "``...``" is assumed to be exact, but this is checked: you get an error if it is not correct. -* *New in version 1.1:* integer types: the syntax "``typedef +* integer types: the syntax "``typedef int... foo_t;``" declares the type ``foo_t`` as an integer type whose exact size and signedness is not specified. The compiler will figure it out. (Note that this requires ``set_source()``; it does @@ -462,8 +460,8 @@ in the details. These places are: length is completed by the C compiler. This is slightly different from "``int n[];``", because the latter means that the length is not known even to the C compiler, and thus - no attempt is made to complete it. *New in version 1.1:* support - for multidimensional arrays: "``int n[...][...];``". + no attempt is made to complete it. This supports + multidimensional arrays: "``int n[...][...];``". *New in version 1.2:* "``int m[][...];``", i.e. ``...`` can be used in the innermost dimensions without being also used in the outermost diff --git a/doc/source/ref.rst b/doc/source/ref.rst index 25822e9..18c22f8 100644 --- a/doc/source/ref.rst +++ b/doc/source/ref.rst @@ -292,7 +292,7 @@ and in C, where ``&array[index]`` is just ``array + index``. 3. ``ffi.addressof(<library>, "name")`` returns the address of the named function or global variable from the given library object. -*New in version 1.1:* for functions, it returns a regular cdata +For functions, it returns a regular cdata object containing a pointer to the function. Note that the case 1. cannot be used to take the address of a diff --git a/doc/source/using.rst b/doc/source/using.rst index 9d14075..b480d42 100644 --- a/doc/source/using.rst +++ b/doc/source/using.rst @@ -366,8 +366,8 @@ See `Reference: conversions`__ for a similar way to pass ``struct foo_s __ ref.html#conversions -CFFI supports passing and returning structs to functions and callbacks. -Example: +CFFI supports passing and returning structs and unions to functions and +callbacks. Example: .. code-block:: python @@ -377,36 +377,33 @@ Example: myfoo = lib.function_returning_a_struct() # `myfoo`: <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 (but a *pointer* -to a union is fine), nor a struct which uses bitfields (but a -*pointer* to such a struct is fine). If you pass a struct (not a -*pointer* to a struct), the struct type cannot have been declared with -"``...;``" in the ``cdef()``; you need to declare it completely in -``cdef()``. You can work around these limitations by writing a C -function with a simpler signature in the C header code passed to -``ffibuilder.set_source()``, and have this C function call the real one. - -Aside from these limitations, functions and callbacks can receive and -return structs. - -For performance, API-level functions are not returned as ``<cdata>`` -objects, but as a different type (on CPython, ``<built-in -function>``). This means you cannot e.g. pass them to some other C +For performance, non-variadic API-level functions that you get by +writing ``lib.some_function`` are not ``<cdata>`` +objects, but an object of a different type (on CPython, ``<built-in +function>``). This means you cannot pass them directly to some other C function expecting a function pointer argument. Only ``ffi.typeof()`` works on them. To get a cdata containing a regular function pointer, -use ``ffi.addressof(lib, "name")`` (new in version 1.1). +use ``ffi.addressof(lib, "name")``. -Before version 1.1 (or with the deprecated ``ffi.verify()``), if you -really need a cdata pointer to the function, use the following -workaround: +There are a few (obscure) limitations to the supported argument and +return types. These limitations come from libffi and apply only to +calling ``<cdata>`` function pointers; in other words, they don't +apply to non-variadic ``cdef()``-declared functions if you are using +the API mode. The limitations are that you cannot pass directly as +argument or return type: -.. code-block:: python - - ffi.cdef(""" int (*foo)(int a, int b); """) +* a union (but a *pointer* to a union is fine); + +* a struct which uses bitfields (but a *pointer* to such a struct is + fine); + +* a struct that was declared with "``...``" in the ``cdef()``. -i.e. declare them as pointer-to-function in the cdef (even if they are -regular functions in the C code). +In API mode, you can work around these limitations: for example, if you +need to call such a function pointer from Python, you can instead write +a custom C function that accepts the function pointer and the real +arguments and that does the call from C. Then declare that custom C +function in the ``cdef()`` and use it from Python. Variadic function calls diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py index 59e1e3e..248c619 100644 --- a/testing/cffi1/test_recompiler.py +++ b/testing/cffi1/test_recompiler.py @@ -1962,3 +1962,21 @@ def test_function_returns_opaque(): ffi, "test_function_returns_opaque", "?") assert str(e.value) == ("function foo: 'struct a' is used as result type," " but is opaque") + +def test_function_returns_union(): + ffi = FFI() + ffi.cdef("union u1 { int a, b; }; union u1 f1(int);") + lib = verify(ffi, "test_function_returns_union", """ + union u1 { int a, b; }; + static union u1 f1(int x) { union u1 u; u.b = x; return u; } + """) + assert lib.f1(51).a == 51 + +def test_function_returns_partial_struct(): + ffi = FFI() + ffi.cdef("struct a { int a; ...; }; struct a f1(int);") + lib = verify(ffi, "test_function_returns_partial_struct", """ + struct a { int b, a, c; }; + static struct a f1(int x) { struct a s = {0}; s.a = x; return s; } + """) + assert lib.f1(52).a == 52 |
