November 26, 2014
Hot Topics:

Extending Python with C

  • April 15, 2003
  • By Thomas Gutschmidt
  • Send Email »
  • More Articles »

There are two, standard built-in ways of integrating Python with C/C++. The first is writing an extension, which involves creating a wrapper for C that Python imports, builds, and then can execute. The wrapper extension is the focus of this article. A second built-in way of integrating these languages is called embedding, and is where C and C++ are given direct access to the Python interpreter. Embedding is not part of this article. There are also third-party integration solutions, alternatives to traditional Python embedding or extending, and I mention a few of these briefly towards the end.

Python is extended for many different reasons. A developer may want to use an existing C library, or port work from an old project into a new Python development effort. Often, Python is used as a prototype, and then profiled to see where pieces should be re-written in C or C++.

To access C or C++ via an extension, you need to write a wrapper. The wrapper acts as glue between the two languages, converting function arguments from Python into C, and then returning results from C back to Python in a way that Python can understand it. Say we have a simple C function called .function.:

int function (int x)
  {
/*code that does something useful*/
  }

A Python wrapper for function looks something like this:

#include <Python.h>
PyObject *wrap_function(PyObject *self, PyObject *args)
{
    int x, result;
    if (!PyArg_ParseTuple(args, .i:function.,&x))
      return NULL;
    result = function(x);
  return Py_BuildValue(.i.,result);
  }

The wrapper starts by including the Python.h header, which includes necessary commands and a few standard header files: stdio.h, string.h, errno.h, and dstlib.h. Python commands that are included with Python.h almost always begin with Py or py, so they are easily distinguished from the rest of the C code.

You can see that the wrapper has two arguments: self and arg. The argument self is used when the C function implements a built-in method. The arg becomes a pointer to a Python tuple object containing the arguments. Each item of the tuple corresponds to an argument in the call's argument list, and the arguments are Python objects. The small .i. is short for int, which is what we assume .x. is. A long int would be l, a char string would be s, a single character is c, f for float, d for double, o for object (a PyObject) and a tuple means multiple items.

Together, PyArg_ParseTuple() and PyBuildValue() are what convert data between C and Python; you retrieve arguments by using PyArg_ParseTuple and pass results back with Py_BuildValue. PyArg_ParseTuple() is a Python API function that checks the argument types and converts them into C values so that they can be used. It returns true if all arguments have the right type and the components have been stored in the variables whose addresses are passed. An if statement is used to return NULL if an error is detected in the argument list. The wrapper must return any values as Python objects, which is done using the Py_BuildValue(), which returns a new Python object. Of course, everything is a PyObject, an object on the Python heap. If you have a C function that returns no useful argument (void), the Python function must return None.

Once you have written a wrapper, Python needs to know about it, so an initialization function is used when importing. The initialization registers new methods with the Python interpreter and looks something like this:

Static PyMethod exampleMethods[] = {
  {.function., wrap_function, 1},
  {NULL, NULL}
};

void initialize_function(){
  PyObject *m
  m = Py_InitModule(.example., .exampleMethods.);
}

This is, of course, a simple example. For instance, when using C++ instead of C, these initialization functions need to be given C linkage.

After a wrapper exists and there is an initialization function, you can compile, and afterwards your function can be part of your Python's lib directory and be called at any time, just like a native Python module. You might also use a setup file when importing a module. A setup file includes a module name, the location of the C code, and any compile tags needed. The setup file is then preprocessed into a project file or makefile. Of course, the compile and build process varies depending upon the platform, environment, tools, and dynamic/static decision making. Refer to the Python parent documentation or look at the resources at the end of the article if you are unsure how to do this.

The last step, including .function. in your Python code, is the easiest step in the process. This is done with a simple import line to initialize the module; you then can call the function from Python just like calling any other method:

import ModuleToImport
ModuleToImport.function(int)

There are other options available besides the wrapper. For instance, SWIG is an extension wrapper designed to make extension easier, and is used to generate interfaces without having to write a lot of code, primarily for extending with C. There is also a relative of SWIG, named Sip, that focuses on C++. Boost.Python is another tool by David Abrahams; it can be used to write small bits of code to create a shared library.

Out of these, SWIG is probably the most popular, likely because SWIG is capable of connecting programs written in C and C++ to a variety of languages, not just Python but also to Perl, Tcl/Tk, Java, C#, and Ruby. SWIG is copyrighted, freely distributed, and immensely useful because, as you can see, writing wrapper functions can be error prone, especially if the number of functions gets really large.

SWIG automates the process by generating wrapper code from a list of ANSI C function and variable declarations. The language is fairly complex and very complete, supporting preprocessing, pointers, classes, inheritance, and C++ templates. SWIG is normally used on UNIX but also works on Win32. It is typically called from the command prompt and can be used with NMAKE. Modules can be compiled into a DLL form and then dynamically loaded into Python, or set up as a custom-build option in MS Development Studio.

Links/Resources/References






Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Sitemap | Contact Us

Rocket Fuel