Technically, f2py
can also wrap a C code for us, but there are more efficient ways to perform this task. For instance, if we need to interface a very large library of C functions, the preferred method for doing this is Simplified Wrapper and Interface Generator (SWIG) (http://www.swig.org/). To wrap C++ code, depending on the features required and the method of interacting with Python, we have several methods such as SWIG or f2py
again, but also PyCXX, Boost.Python, Cython, or the SciPy module: weave
. When C compilers are not available (and thus linking extensive libraries is not possible in the usual way), we use ctypes
. Whenever we will use NumPy/SciPy code, and want fast solutions to our wrapping/binding, the two most common ways to interact with C/C++ are usually through the Python/C API and weave
packages.
All the methods briefly enumerated here would require an entire monograph to describe, at length, the methodology of binding the nuisances of the wrapping, depending on systems and requirements, and the caveats of their implementations. The method we would like to cover in more detail in this chapter is the weave
package, more concretely by means of the inline
routine. This command receives a string (raw or otherwise) containing a sequence of commands, and runs it in Python by calling your C/C++ compiler. The syntax is as follows:
inline(code, arg_names, local_dict=None, global_dict=None, force = 0, compiler='', verbose = 0, support_code = None, customize=None, type_factories = None, auto_downcast=1, **kw)
Let's go over the different parameters:
code
parameter is the string that holds the code to be run. Note that this code must not specify any kind of return
statement. Instead, it should assign some result that can be returned to Python.arg_names
parameter is a list of strings containing the Python variable names that are to be sent to the C/C++ code.local_dict
parameter is optional, and must be a Python dictionary containing the values used as local scope for the C/C++ code.global_dict
parameter is also optional, and must be another Python dictionary containing the values that should be used as the global scope for the C/C++ code.force
parameter is used only for debugging purposes. It is also optional, and can take only two values—0 (by default) or 1. If its value is set to 1, the C/C++ code is compiled every time inline
is called.compiler
option. It must be a string containing the name of the C/C++ compiler.Let's take an example of the inline
routine in which we use the following method to employ cout
for text displaying purposes:
>>> import scipy.weave >>> name = 'Francisco' >>> pin = 1234 >>> code = 'std::cout << name << "---PIN: " ' >>> code+= '<<std::hex << pin <<std::endl;' >>> arg_names = ['name','pin'] >>> scipy.weave.inline(code, arg_names)
The output is shown as follows:
Francisco---PIN: 4d2
That was a very simple example, in which no external header declarations were needed. If we wish to do so, those go into the support_code
option. For instance, if we wish to include math functions from R in our C/C++ code and pass it with inline
, we need to perform the following steps:
% ./configure --enable-R-static-lib --enable-static --with-readline=no
standalone
folder at src/nmath
and finish the installation of the libraries. At the end, we should have a file named libRmath.so
, which needs to be pointed to from the libpath
string back into our Python session:% cd src/nmath/standalone % make
inline
call with the proper options. For instance, if we wish to call the R routine pbinom
, we proceed as follows:>>> import scipy.weave >>> support_code= 'extern "C" double pbinom(double x, double n, double p, int lower_tail, int log_p);' >>> libpath='/opt/Rlib' #IS THE LOCATION OF LIBRARY libRmath.so >>> library_dirs=[libpath] >>> libraries=['Rmath'] >>> runtime_library_dirs=[libpath] >>> code='return_val=pbinom(100,20000,100./20000.,0,1);' >>> res=scipy.weave.inline(code, support_code=support_code, library_dirs=library_dirs, libraries=libraries, runtime_library_dirs=runtime_library_dirs) >>> print(res)
The output is shown as:
-0.747734910363
header
option, rather than support_code
or code
:>>> headers = ['<math.h>']
We have a word of advice. Care must be taken while converting the different variable types from their original C/C++ format to something that Python understands. This requires modifying the original C/C++ code in certain cases. But by default, we do not have to worry about the following C/C++ types, as SciPy automatically turns them into the indicated Python formats, as shown in the following table:
Python |
|
|
|
|
|
|
|
C/C++ |
|
|
|
|
|
|
|
File types FILE*
are sent to Python files. Python callables and instances are both obtained from py::object
. NumPy ndarrays are constructed from PyArrayObject*
. For any other Python type to be used, the corresponding C/C++ types must be carefully turned into combinations of the previous.
And that should be all. To go beyond trivial uses of the inline function, we usually create extension modules and catalog the functions within for future use.
3.139.80.209