In this article, I will show you how to use C or C++ dynamic libraries from Python, by using the ctypes module from the Python standard library. ctypes is a foreign function library for Python that provides C compatible data types. Although it is mostly used to consume C and C++ libraries, you can use ctypes with libraries written in any language that can export a C compatible API, e.g. Fortran, Rust.
The advantage of using ctypes is that it is already included with your Python installation and that, in theory, you can call any C or C++ shared or dynamic libraries. Another advantage of using ctypes is that you don’t need to recompile the library in order to be able to use it from Python.
A disadvantage of ctypes is that you need to manually wrap the data and functions from the foreign library in Python code. Also for C++ libraries you will need to have the exported functions wrapped in an extern C block. If you need to use a heavy OOP C++ library from Python, I recommend that you look into pybind11.
You can find the source files and image examples used here on the GitHub repo for this article.
Probably the simplest example of using ctypes is to show you how to directly call functions from the system C library. In the next piece of code I will use ctypes to find the system C library and make it available from Python:
Once you have a handle on the system library, you can call any functions from it. Here, I’m showing examples of using the puts and printf C functions:
Please note the use b to convert the Python string to a byte string as expected by C.
This is what I see on a macOS machine when I run the above script:
An interesting note here is that, since a Python string is immutable, you can’t change it from C. If you want to pass a mutable string to C from Python, you will need to use the create_string_buffer function provided by ctypes. Example:
If you are careful, you can even do pointer arithmetic. Please note that this is not a good idea! Say that we want to add 4 O after the above 5 X in the mut_str. We’ll start by getting the address of the mut_str, add 5 to this and convert it to a char pointer:
In the next example I assume that you have Clang or GCC installed on your machine. The code should work the same on Linux, Windows or macOS. If you need to install GCC on Windows check this article. The MSVC compiler is a bit particular about the way you need to annotate functions in a shared library. I will show you how to use it in the next part of this article.
Let’s assume that you keep your function declarations in a header file named mylib.h:
and here is the implementation of the above three functions:
First function simply prints a message from C, second function receives as arguments two floats and returns their sum. Last function receives an array of integers and the array length, prints the array as received and than modifies the array.
If you want to build the above code as a shared library, with Clang on macOS, you can use these commands:
On a Linux system with GCC you can use:
On Windows, assuming that you have GCC installed:
Once we have the shared library, we can write a Python wrapper, mylib.py, that loads the library and exports the functions. Let’s start, like before with the code that loads the library:
Please note that we don’t specify the library extension, find_library will match the system library extension. This makes our code portable on most operating systems. A possible problem is that find_library uses the system library search path and the local folder. If you want to restrict the search path only to the local folder use something like:
Another potential problem is that, on UNIX like systems, find_library will search for both libmylib and mylib unless you specify the search path. When you search for your own libraries, it is probably a good ideas to prefer:
instead of the more general:
Next, let’s make the functions available at the module level:
Please note the way we specify the types of the input, argtypes, and output, restype, for the last two C functions. While this is not always required, it is usually a good idea to be specific and it will give us more meaningful error messages if we try to pass the wrong data type.
OK, let’s use the Python wrapper now. Create a new file named test_mylib.py and add the next code:
This is what I see, if I run the above code on my machine:
If you pass the wrong data type to test_add, you’ll get an error, e.g.:
This is how you define a fixed sized array in Python using ctypes:
You can also initialize the array from other Python objects, e.g. for a list:
Let’s add an extra test to test_mylib.py:
This is what I see if I run the above code on my machine:
In general, when you want to interface Python with C++ you are better off with using a dedicated library like pybind11. However, if all you need is to call a couple of C++ functions from Python and the C++ library has a C API, you can use ctypes.
In order to exemplify the approach, I will use a small BMP image file manipulation I wrote a few months ago. You can find the original article here, if you want to read it.
For the C API, I will define functions to read an image from disk, write an image to disk, create an image in memory, get the pixel data as a one dimensional array, fill a rectangular region of the image and finally a function to release the resources used by the library.
Here is the header of the library we want to call from Python:
What is different versus our previous examples is that, this time, the header file contains a struct and functions to manipulate it. The handle is an opaque pointer to the underlying C++ class that is not visible from C.
Let’s start by writing a Python wrapper around this library. The file is named cbmp.py. The initial boilerplate code that loads the library is almost identical with what we’ve used before:
ctypes has a helper Struct class from which we can inherit. There is basically a one to one mapping to the original C struct:
What is interesting in the above code is that we can map the names of the C structure and data types by using a list of pairs name type.
Next, we can write the wrapping code for the six functions declared in cbmp.h:
The library can read and write BMP images with three or four channels. The fourth channel is the transparency channel. The actual pixel data is stored as a contiguous one dimensional array of unsigned chars and can be directly accessed from Python, by using the BMP_pixels function. A BMP image typically has the channels in BGR or BGRA order.
Here are some examples of using the library from Python:
If you want to learn more about Python, I recommend reading Python Crash Course by Eric Matthes, the book is intended for beginners: