Passing a Python callback to C++ in Cython

Hi,

in these days my work involve the porting of one of mine C++ library to Python, to accomplish this I’ve used Cython. The use of Cython is simple and very well documented but I’ve encountered a problem porting a callback from C++ to Python so I’ve decided to write down a post about my problem and the solution that I’ve adopted.

C++ Library

The library that I’ve wrote is a client library used to communicate with a server, it provide a class with a very poor API, the meaning of this library is out of scope but below you can find the basic actions provided by the library.

class ClientLibrary{
    ClientLibrary();
    init(std::function<void(const unsigned int &, 
                            const std::string &)> log = nullptr,
         unsigned int log_level=NORMAL_LVL);
    connect(const std::string &ip, unsigned int port);
    disconnect();
    ...  // others methods
};

As said it has a simple API, just one function has need of explanation, the init(). This method initializes the library (or class) itself, it accept a function pointer and an unsigned int. The first parameter is used in order to let the user specify its own  custom log method and the second to set the level of the log in which is interested.

Let the user to specify its own log method is important, in this way it can save it on file or handle some casuistry in some way. The constraint is that the function specified must accept as first parameter an unsigned int and for second parameter a string, these are the log level  and log message.

Problem

The problem is that the porting in Python use Python code (well… kinda obvious) which C++ library don’t understand. In fact even if you use Cython some where you call the C++ code of the original library, in this case it means that when the user pass the callback in Python the C++ part get angry because it expects a function pointer.

Solution

To avoid it I’ve created a  Men In the Middle function with the same interface expected from a log callback function in C++ which call the Python callback.

# create a MIM function for log callback
# it has the same interface expected from C++ version
cdef void log_mim(unsigned int log_level, const string message)
    pylog_cb(log_level, message)

# Python class
cdef class PyClient:
    ....
    def init(self, log_cb=None, log_lvl=0):

        if log_cb is not None:
            # Python 2.x support
            if not callable(log_cb):
                raise TypeError("""The first parameter MUST
                                   be a function.""")
            # store the Python callback on global variable
            # pylog_cb
            global pylog_cb
            pylog_cb = log_cb
        return self.client_ptr.init(log_mim, log_lvl)

The init method accept a callback  log_cb and the log level log_lvl, the first is stored on the global variable pylog_cb. At the end it will call the C++ init passing the function log_mim. When the C++ code will call the callback log_mim (C++ function pointer) it will call the Python pylog_cb.

Conclusion

This solution to “callback problem” works pretty well, the MIM function allows to avoid the incompatibility between the 2 languages.

On PyClient is missing all the source code about the allocation/deallocation of the object and how to link Python and C++ code cause it is beyond the scope of the post.

Annunci

Informazioni su AlessandroPischedda

I have obtained a Master degree in Computer Engineering at "Università di Pisa" in December 2012. After some work experience now I'm working at Nextworks as SW Engineer.
Questa voce è stata pubblicata in programming e contrassegnata con , , , . Contrassegna il permalink.

2 risposte a Passing a Python callback to C++ in Cython

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione / Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione / Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione / Modifica )

Google+ photo

Stai commentando usando il tuo account Google+. Chiudi sessione / Modifica )

Connessione a %s...