Author: Armin Rigo <[email protected]>
Branch:
Changeset: r3138:549cf1a22e97
Date: 2018-08-12 20:10 +0200
http://bitbucket.org/cffi/cffi/changeset/549cf1a22e97/
Log: Refactor again overview.rst, including all cases in the order that
seems to make the most sense to me
diff --git a/doc/source/overview.rst b/doc/source/overview.rst
--- a/doc/source/overview.rst
+++ b/doc/source/overview.rst
@@ -7,7 +7,7 @@
This document starts, in the first section, with a simple working
example of using CFFI to call a C function from Python. CFFI is
-flexible and covers several use cases presented in the second
+flexible and covers several other use cases presented in the second
section. Then, the next section shows how to export Python functions
to a Python interpreter embedded in a C or C++ application. The last
two sections delve deeper in the CFFI library.
@@ -20,96 +20,65 @@
.. _real-example:
-Example: calling a C function from Python
------------------------------------------
+Main mode of usage
+------------------
-This example is about the use case when the library sources are
-available, the next section shows how use a compiled, installed
-library.
+The main way to use CFFI is as an interface to some already-compiled
+library which is provided by other means. Imagine that you have a
+system-installed library called ``piapprox.dll`` (Windows) or
+``libpiapprox.so`` (Linux and others) or ``libpiapprox.dylib`` (OS X),
+containing a function ``float pi_approx(int n);`` that computes some
+approximation of pi given a number of iterations. You want to call
+this function from Python.
-1. Make sure the sources of the library defining the useful C function
- is available. For this example, create the file ``pi.c`` and ``pi.h``:
+Create the file ``piapprox_build.py``:
- .. code-block:: C
-
- /* filename: pi.c*/
- # include <stdlib.h>
- # include <math.h>
-
- /* Returns a very crude approximation of Pi
- given a int: a number of iteration */
- float pi_approx(int n){
-
- double i,x,y,sum=0;
-
- for(i=0;i<n;i++){
-
- x=rand();
- y=rand();
-
- if (sqrt(x*x+y*y) < sqrt((double)RAND_MAX*RAND_MAX))
- sum++; }
-
- return 4*(float)sum/(float)n; }
-
- .. code-block:: C
-
- /* filename: pi.h*/
- float pi_approx(int n);
-
-2. Create a script named ``pi_extension_build.py``, building
- the C extension:
-
- .. code-block:: python
+.. code-block:: python
from cffi import FFI
ffibuilder = FFI()
-
- # cdef() expects a string listing the C types, functions and
- # globals needed from Python. The string is in the C syntax,
- # which saves us from learning a Python descriptive dialect.
- ffibuilder.cdef("float pi_approx(int n);")
-
- ffibuilder.set_source(
- "_pi", # name of the output C extension
- '# include "pi.h"',
- sources=['pi.c'],
- libraries=['m'])
-
+
+ # cdef() expects a string listing the C types, functions and
+ # globals needed from Python. The string follows the C syntax.
+ ffibuilder.cdef("""
+ float pi_approx(int n);
+ """)
+
+ # This describes the extension module "_pi_cffi" to produce.
+ ffibuilder.set_source("_pi_cffi",
+ """
+ #include "pi.h" // the C header of the library
+ """,
+ libraries=['piapprox']) # library name, for the linker
+
if __name__ == "__main__":
ffibuilder.compile(verbose=True)
-
-3. Build the extension:
-
- .. code-block:: shell
+Execute this script. If everything is OK, it should produce
+``_pi_cffi.c``, and then invoke the compiler on it. The produced
+``_pi_cffi.c`` contains a copy of the string given in ``set_source()``,
+in this example the ``#include "pi.h"``; then it contains some glue code
+for all the functions declared in the ``cdef()`` above.
- python pi_extension_build.py
+At runtime, you use the extension module like this:
- Observe, in the working directory, the generated output files:
- ``_pi.c``, ``_pi.o`` and the C extension ``_pi.so``.
+.. code-block:: python
+ from _pi_cffi import ffi, lib
+ print(lib.pi_approx(5000))
-4. Call the C function from Python:
+That's all! In the rest of this page, we describe some more advanced
+examples and other CFFI modes. In particular, there is a complete
+example `if you don't have an already-installed C library to call`_.
- .. code-block:: python
-
- from _pi.lib import pi_approx
-
- approx = pi_approx(10)
- assert str(pi_approximation).startswith("3.")
-
- approx = pi_approx(10000)
- assert str(approx).startswith("3.1")
-
-For more information, see the ``cdef()`` and ``set_source()`` methods
-of the ``FFI`` class covered in `Preparing and Distributing modules`__.
+For more information about the ``cdef()`` and ``set_source()`` methods
+of the ``FFI`` class, see `Preparing and Distributing modules`__.
.. __: cdef.html
-
-A common alternative for the step 3. and running the build script is
-to write a ``setup.py`` Setuptools distribution:
+When your example works, a common alternative to running the build
+script manually is to have it run as part of a ``setup.py``. Here is
+an example using the Setuptools distribution:
.. code-block:: python
@@ -118,13 +87,10 @@
setup(
...
setup_requires=["cffi>=1.0.0"],
- cffi_modules=["pi_extension_build:ffibuilder"],
+ cffi_modules=["piapprox_build:ffibuilder"], # "filename:global"
install_requires=["cffi>=1.0.0"],
)
-``cffi_modules`` is a list of ``<extension builder script>:<FFI
-instance>`` describing the modules to build.
-
Other CFFI modes
----------------
@@ -133,8 +99,8 @@
each with "in-line" or "out-of-line" preparation (or compilation).
The **ABI mode** accesses libraries at the binary level, whereas the
-faster **API mode** accesses them with a C compiler. This is described in
-detail below__.
+faster **API mode** accesses them with a C compiler. We explain the
+difference in more details below__.
.. __: `abi-versus-api`_
@@ -176,9 +142,7 @@
cdef().*
If using a C compiler to install your module is an option, it is highly
-recommended to use the API mode described in the next paragraph. (It is
-also faster.)
-
+recommended to use the API mode instead. (It is also faster.)
Struct/Array Example (minimal, in-line)
@@ -217,7 +181,7 @@
*This example does not call any C compiler.*
This example also admits an out-of-line equivalent. It is similar to
-the first example `Example: calling a C function from Python`_ above,
+the first example `Main mode of usage`_ above,
but passing ``None`` as the second argument to
``ffibuilder.set_source()``. Then in the main program you write
``from _simple_example import ffi`` and then the same content as the
@@ -225,6 +189,173 @@
ffi.new("pixel_t[]", 800*600)``.
+API Mode, calling the C standard library
+++++++++++++++++++++++++++++++++++++++++
+
+.. code-block:: python
+
+ # file "example_build.py"
+
+ # Note: we instantiate the same 'cffi.FFI' class as in the previous
+ # example, but call the result 'ffibuilder' now instead of 'ffi';
+ # this is to avoid confusion with the other 'ffi' object you get below
+
+ from cffi import FFI
+ ffibuilder = FFI()
+
+ ffibuilder.set_source("_example",
+ r""" // passed to the real C compiler,
+ // contains implementation of things declared in cdef()
+ #include <sys/types.h>
+ #include <pwd.h>
+
+ // We can also define custom wrappers or other functions
+ // here (this is an example only):
+ static struct passwd *get_pw_for_root(void) {
+ return getpwuid(0);
+ }
+ """,
+ libraries=[]) # or a list of libraries to link with
+ # (more arguments like setup.py's Extension class:
+ # include_dirs=[..], extra_objects=[..], and so on)
+
+ ffibuilder.cdef("""
+ // declarations that are shared between Python and C
+ struct passwd {
+ char *pw_name;
+ ...; // literally dot-dot-dot
+ };
+ struct passwd *getpwuid(int uid); // defined in <pwd.h>
+ struct passwd *get_pw_for_root(void); // defined in set_source()
+ """)
+
+ if __name__ == "__main__":
+ ffibuilder.compile(verbose=True)
+
+You need to run the ``example_build.py`` script once to generate
+"source code" into the file ``_example.c`` and compile this to a
+regular C extension module. (CFFI selects either Python or C for the
+module to generate based on whether the second argument to
+``set_source()`` is ``None`` or not.)
+
+*You need a C compiler for this single step. It produces a file called
+e.g. _example.so or _example.pyd. If needed, it can be distributed in
+precompiled form like any other extension module.*
+
+Then, in your main program, you use:
+
+.. code-block:: python
+
+ from _example import ffi, lib
+
+ p = lib.getpwuid(0)
+ assert ffi.string(p.pw_name) == b'root'
+ p = lib.get_pw_for_root()
+ assert ffi.string(p.pw_name) == b'root'
+
+Note that this works independently of the exact C layout of ``struct
+passwd`` (it is "API level", as opposed to "ABI level"). It requires
+a C compiler in order to run ``example_build.py``, but it is much more
+portable than trying to get the details of the fields of ``struct
+passwd`` exactly right. Similarly, in the ``cdef()`` we declared
+``getpwuid()`` as taking an ``int`` argument; on some platforms this
+might be slightly incorrect---but it does not matter.
+
+Note also that at runtime, the API mode is faster than the ABI mode.
+
+To integrate it inside a ``setup.py`` distribution with Setuptools:
+
+.. code-block:: python
+
+ from setuptools import setup
+
+ setup(
+ ...
+ setup_requires=["cffi>=1.0.0"],
+ cffi_modules=["example_build.py:ffibuilder"],
+ install_requires=["cffi>=1.0.0"],
+ )
+
+
+.. _`if you don't have an already-installed C library to call`:
+
+API Mode, calling C sources instead of a compiled library
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+If you want to call some library that is not precompiled, but for which
+you have C sources, then the easiest solution is to make a single
+extension module that is compiled from both the C sources of this
+library, and the additional CFFI wrappers. For example, say you start
+with the files ``pi.c`` and ``pi.h``:
+
+ .. code-block:: C
+
+ /* filename: pi.c*/
+ # include <stdlib.h>
+ # include <math.h>
+
+ /* Returns a very crude approximation of Pi
+ given a int: a number of iteration */
+ float pi_approx(int n){
+
+ double i,x,y,sum=0;
+
+ for(i=0;i<n;i++){
+
+ x=rand();
+ y=rand();
+
+ if (sqrt(x*x+y*y) < sqrt((double)RAND_MAX*RAND_MAX))
+ sum++; }
+
+ return 4*(float)sum/(float)n; }
+
+ .. code-block:: C
+
+ /* filename: pi.h*/
+ float pi_approx(int n);
+
+Create a script named ``pi_extension_build.py``, building
+the C extension:
+
+ .. code-block:: python
+
+ from cffi import FFI
+ ffibuilder = FFI()
+
+ ffibuilder.cdef("float pi_approx(int n);")
+
+ ffibuilder.set_source("_pi", # name of the output C extension
+ """
+ #include "pi.h"',
+ """,
+ sources=['pi.c'], # includes pi.c as additional sources
+ libraries=['m']) # on Unix, link with the math library
+
+ if __name__ == "__main__":
+ ffibuilder.compile(verbose=True)
+
+Build the extension:
+
+ .. code-block:: shell
+
+ python pi_extension_build.py
+
+Observe, in the working directory, the generated output files:
+``_pi.c``, ``_pi.o`` and the compiled C extension (called ``_pi.so`` on
+Linux for example). It can be called from Python:
+
+ .. code-block:: python
+
+ from _pi.lib import pi_approx
+
+ approx = pi_approx(10)
+ assert str(pi_approximation).startswith("3.")
+
+ approx = pi_approx(10000)
+ assert str(approx).startswith("3.1")
+
+
.. _performance:
Purely for performance (API level, out-of-line)
@@ -347,6 +478,16 @@
)
+In-line, API level
+++++++++++++++++++
+
+The "API level + in-line" mode combination exists but is long
+deprecated. It used to be done with ``lib = ffi.verify("C header")``.
+The out-of-line variant with ``set_source("modname", "C header")`` is
+preferred. It avoids a number of problems when the project grows in
+size.
+
+
.. _embedding:
Embedding
@@ -495,8 +636,3 @@
should include or not.
.. __: real-example_
-
-Note that the "API level + in-line" mode combination exists but is long
-deprecated. It used to be done with ``lib = ffi.verify("C header")``.
-The out-of-line variant with ``set_source("modname", "C header")`` is
-preferred.
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit