Interaction with C/C++

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:

  • The 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.
  • The arg_names parameter is a list of strings containing the Python variable names that are to be sent to the C/C++ code.
  • The local_dict parameter is optional, and must be a Python dictionary containing the values used as local scope for the C/C++ code.
  • The 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.
  • The 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.
  • We may specify the compiler that takes over the C/C++ code with the 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:

  1. Configure the C functions as a shared library. In the folder, holding the R release in a terminal session, issue the following command:
    % ./configure --enable-R-static-lib --enable-static --with-readline=no
    
  2. Change to the 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
    
  3. Back in our Python session, we prepare the 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 
    

    Note

    Note how the function declaration is passed in support_code, not in code. Also, note that this option needs to start with extern "C" whenever we are not using C++.

  4. If extra headers need to be passed, we do so with the 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

int

float

complex

string

list

dict

tuple

C/C++

int

double

std::complex

py::string

py::list

py:dict

py::tuple

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.139.80.209