Author: Armin Rigo <ar...@tunes.org>
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
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to