C++ class in Python3

Python to C++ class interface

Stirred, not shaken : C++ & Python 3
ABSTRACT: I show two examples of simple Python3 modules written in C/C++. Then there is an illustrative example of how to build a perfect interface to C++ classes for Python 3.

CODE REPOSITORY: On Github


Introduction.
I always found mixing programming languages together interesting, it appeares to me as a bit of ninja-level programming skill. Since most of the people who use Python use cPython, adding C/C++ to Python is rather straightforward, though not many Python users know how to do it.

BTW: if you have never heard of cPython, you are probably using it – cPython is just the most popular implementation of Python interpreter. cPython is written in C and C++.

One may ask: why would I add C++ to Python? Well, a very good question, since many people go for Python to avoid C++.

If you can accomplish a task using pure Python – perfect, DO NOT use C++. Yet, IMHO there are two reasons to use both the languages in one project: performance and synergy.

As a rule of thumb, it can be stated that well written C++ code is usually faster than well written Python code. So using C++ may be a solution to speed up bottlenecks in Python code. However, if a Python project requires more performance, the standard Python optimisation techniques should be used in the first place. An engineer should profile the code, vectorise what may be vectorised, use list comprehensions instead of loops, etc.

Unfortunately, there are examples where code vectorisation is very difficult, or even impossible, and then embedding C++ code into Python code may be a solution.

I found mixing C++ with Python3 useful because of the synergy it offers. In middle-size and big-size projects, where a part of the project is implemented in Python and an another part (usually the part “closer to the metal”) is implemented in C++, we may enjoy adventages of both of the worlds.


Simplest Python 3 C++ Module.
My example of the simplest Python3 module written in C++ is on Github: simplest_py_module.cpp. This is a “dummy” module, because it provides no methods. On the other hand, it is a fully operational module which can be compiled and loaded to Python.

Module structure.
The basic structure of the module is rather simple. There are three basic parts of every Python module:

  • Initialisation function
  • Module definition structure
  • Methods definition structure

In the current example, the module initialisation function PyInit_simplest just returns a handle to PyModule_Create function which creates the module. The initialisation function PyInit_simplest must always be called PyInit_NAMEOFTHEMODULE. Module creation function PyModule_Create takes an address to a module definition structure as an argument. In this case this structure is named simplestModule.

PyMODINIT_FUNC PyInit_simplest(void)
{
    return PyModule_Create(&simplestModule);
}

Fields in structure simplestModule define the module parameters. Two of these fields are really important in this example – name of the module and a structure which describes the methods which the module provide: simplestFunctions. Structure simplestModule is below:

struct PyModuleDef simplestModule =
{
   PyModuleDef_HEAD_INIT,
   "simplest",             // Name of the module.

   NULL,                   // Docstring for the module - in this case empty.

   -1,                     // Used by sub-interpreters, if you do not know what
                           // it is then you do not need it, keep -1 .

   simplestFunctions       // Structures of type `PyMethodDef` with functions
                           // (or "methods") provided by the module.
};

In the current example the method definition structure (simplestFunctions) is empty, because the current module does not define any methods:

PyMethodDef simplestFunctions[] =
{
};

Compiling and running the module.
Using Python tool `distutils` it the recommended method to compile Python C/C++ modules, though in bigger project I personally use direct gcc/clang call to compile Python modules. Python compilation script for this example (“Simplest module”) is available on Github: compile_SIMPLEST_py_module.py.

To compile the module run (in subdirectory `simplest`):

$ make simplest_py_module

This will create simplest.cpython-3Xm.so file, which is the Python module. Now, in the Python shell it is possible to load the module:

>>> import simplest

…and this is all what this module is able to do 😉


Simple Python 3 C++ Module.

It is time to add a method to the module. Module simple_py_module is available on Github: simple_py_module.cpp

Function and its arguments.
This module provides a single method print_variables. Methods definition structure (simpleFunctions) is no longer empty as in the previous example. It contains a reference to the function the method provides AND a an empty function description at the end:

PyMethodDef simpleFunctions[] =
{
    {"print_variables",
      print_variables, METH_VARARGS,
     "Print variables "},
    {NULL, NULL, 0, NULL}      // Last function description must be empty.
                               // Otherwise, it will create seg fault while
                               // importing the module.
};

Function print_variables returns a pointer to PyObject and takes two arguments. In fact, every function in a C++ Python module should return PyObject* and take two arguments: self and args, both are pointers to PyObject. Then, the real arguments passed to the function from Python are extracted by PyArg_ParseTuple function:

PyObject* print_variables(PyObject* self, PyObject* args)
{
    // Arguments passed from Python
    int iInteger_;
    long int iLongInteger_;
    unsigned int iUnsignedInteger_;
    const char* csString_;
    double dDouble_;

    // Process arguments
    // Description of this function: https://docs.python.org/2/c-api/arg.html
    PyArg_ParseTuple(args, "iLIsd",
                     &iInteger_,          // `i` in "iLIscd"
                     &iLongInteger_,      // `L` in "iLIscd"
                     &iUnsignedInteger_,  // `I` in "iLIscd"
                     &csString_,          // `s` in "iLIscd"
                     &dDouble_            // `d` in "iLIscd"
                     );

There are five arguments passed from Python to this method: integer, long integer, unsigned integer, string and double. All these arguments are printed out. Then, the first argument (integer) is multiplied by two and returned back to Python using Py_BuildValue function:

    // Print the given arguments
    std::cout << "Integer given: " << iInteger_ << std::endl;
    std::cout << "Long integer given: " << iLongInteger_ << std::endl;
    std::cout << "Unsigned integer given: " << iUnsignedInteger_ << std::endl;
    std::cout << "String given: " << csString_ << std::endl;
    std::cout << "Double given: " << dDouble_ << std::endl;

    // Multiply the first argument (Integer) x2 and return it
    int i2Integer = 2 * iInteger_;

    // Return the multiplied argument
    return Py_BuildValue("i", i2Integer);
}

Compiling and running the module.
Python script which uses this module is on Github: use_simple_module.py.

To run the script compile the module first:

$ make simple_py_module

and then run the Python script:

$ python3 use_simple_module.py


Class Example : Car Module.

C++ object.
This example uses a simple C++ object `Car`, available on Github: car.cpp and: car.h.

This object is very simple. It is an abstract car, which can be fueled up with a given amount of fuel and then it can drive for a certain amount of kilometers. Car’s odometer keeps track of a car’s mileage, its value can be read. Every car has a color and fuel economy assigned when an object is created. A C++ program which uses the `Car` object is on Github: use_car.cpp. Public functions in the `Car` class are as follows:

class Car
{
    public:

        // Fuel up the car
        void fuel_up(double);

        // Drive the car
        void drive(unsigned int);

        // What is the mileage?
        void print_mileage() const;

        // Constructor
        Car(std::string, double);

Bottom interface (C++ Python module).
The proposed Python interface to a C++ class is composed of two levels. Structure of the proposed interface is presented on Figure 1. The first level (bottom interface) is a Python module written in Cpp (car2py.cpp): car2py.cpp.

                                      Figure 1: Python interface to a C++ class
Figure 1: Python interface to a C++ class

The bottom interface module is composed of C++ wrapper functions which reflect public functions provided by Car class, with two additional auxiliary functions: construct and delete_object. The construct function allocates an object and returns a pointer to the allocated object in a form of a Python Capsule. Any other wrapper function in the bottom level of the interface uses the pointer to call a proper object and its corresponding public function. The construct function from the current example is below:

PyObject* construct(PyObject* self, PyObject* args)
{
    // Arguments passed from Python
    const char* csCol_;    // Car color
    double dFuelEconomy_;  // Fuel economy

    // Process arguments passes from Python
    PyArg_ParseTuple(args, "sd",
                     &csCol_,
                     &dFuelEconomy_);

    // Allocate new `Car` object
    Car* car = new Car(csCol_, dFuelEconomy_);

    // Create a Python capsule with a pointer to `Car` object
    PyObject* carCapsule = PyCapsule_New((void *)car, "CarPtr", NULL);
    PyCapsule_SetPointer(carCapsule, (void *)car);

    // Return the Python capsule with the pointer to`Car` object
    return Py_BuildValue("O", carCapsule);   // "O" means "Python object"
}

The bottom interface to fuel_up function is below. The first argument passed to the function is a Python Capsule with a pointer to a C++ object. It is followed by an argument to fuel_up function in C++ class (the amount of fuel in liters):

PyObject* fuel_up(PyObject* self, PyObject* args)
{
    // Arguments passed from Python
    PyObject* carCapsule_;   // Capsule with the pointer to `Car` object
    double dLitres_;         // Volume which will be fueled up

    // Process arguments
    PyArg_ParseTuple(args, "Od",
                     &carCapsule_,
                     &dLitres_);

    // Get the pointer to `Car` object
    Car* car = (Car*)PyCapsule_GetPointer(carCapsule_, "CarPtr");

    // Call `fuel_up` function
    car->fuel_up(dLitres_);

    // Return nothing
    return Py_BuildValue("");

Top interface (Python module). The second level (top interface) is a Python class definition (on Github: car.py). This Python class stores a Python Capsule with a pointer to C++ object. The top interface loads the bottom interface as a module:

try:
    import cCar
except:
    strErr = "\n\n`cCar` module not found, "
    strErr += "run `$ make car_py_module`! \n"
    raise RuntimeError(strErr)

Functions of the Python class (the top interface) are Python wrappers to public functions in the C++ class. These Python wrappers call a correct function in the bottom interface (Python C++ module). The first argument passed to the bottom interface functions is a Python Capsule with a pointer to a correct C++ object. Initialisation function for top interface Python class allocates a new object. Hence, any new allocated Python class has its own C++ object allocated in memory:

    def __init__(self, color, fueleconomy):
        self.carCapsule = cCar.construct(color, fueleconomy)

Top interface Python wrapper for fuel_up C++ function is below. It calls a corresponding bottom interface function. The Python Capsule returned by the create function is the first argument:

    def fuel_up(self, litres):
        cCar.fuel_up(self.carCapsule, litres)

Compiling and running the module.
Python script which uses the C++ `Car` class is on Github: use_car.py. Hence to the proposed structure, Python is able to use C++ object in a seamless way. It uses the Car class in the same way the C++ program uses it. This is the beginning of a C++ program which uses the `Car` class:

    // Create two cars
    Car carRed("Blue", 5.5);
    Car carSilver("Silver", 12);

    // Fuel up the cars
    carRed.fuel_up(40);
    carSilver.fuel_up(30)

This is the beginning of a Python script which uses the `Car` class:

    # Create two cars
    carRed = car("Blue", 5.5)
    carSilver = car("Silver", 12)

    # Fuel up the cars
    carRed.fuel_up(40)
    carSilver.fuel_up(30)


To run the example, firstly compile the Python C++ module which is a bottom interface to the class:

$ make car_py_module

Then it is possible to run Python script which uses the class:

$ python3 use_car.py.

To compile and run the C++ program which use the `Car` class:

$ make use_car_cpp
$ ./use_car

Outputs of the C++ program and Python script are the same.

Copyright © 2019 | First Mag designed by Themes4WP