Lightning talk #8 – Interfacing C++ with other languages

lightning talk

What is Foreign Function Interface?

According to wikipedia:

FFI refers to the ability for code written in one language (the “host language,” such as C++), to access and invoke functions written in another language (the “guest language,” such as Python). The term “foreign” refers to the fact that the functions come from another language and environment.

Depending on the language and its FFI support, you might also be able to access global named variables, automatically convert data types between the host and guest languages, and have code in the guest language invoke functions in the host language as callbacks.

Let’s see an example. Let’s start with simpler things first (common code base)

Trivial FFI: FROM C++ TO C

We will need to take into account the following things:

  • Every object is passed about in C an opaque handle.
  • Constructors and destructors are wrapped in pure functions
  • Member functions are pure functions.
  • Other built-ins are mapped to C equivalents where possible.

Let’s take them one by one.

Every object is passed about in C an opaque handle.

  • We do not have classes in C, so we have to use what we have instead (structs in our case)

Constructors and destructors are wrapped in pure functions

  • We do not have classes in C, so we have to create functions that uses new and returns pointer to struct instead

Member functions are pure functions.

  • We do not have classes in C, so the member functions (which receive the this pointer by the compiler) have to do the same now, as free functions

Other built-ins are mapped to C equivalents where possible.

  • Some things are valid only in C++, and we need to transpose them to C (std::string for example -> use char* instead)
  • Virtual functions: Needs to be called the same as member functions -> turn them into free functions, call through pointer
  • Overloaded functions: C does not have name mangling, so we need to create separate functions (with different names) which delegates the call to the function we need to call

So what’s with the extern?

Extern ensures that the symbols within are “unmangled” – that the compiler emits a binary file with their names undecorated, as a C compiler would do.

As C language definitions are unmangled, the C++ compiler needs to avoid mangling references to these identifiers.

As mentioned previously, name mangling is something done by compiler, and each of them implements it differently. C compilers keep the function names as they are declared, so if we want to have C++ code visible in C code, we need to have a stable API (We need to know what is the function name so we can call it).

Extern C promises that the functions written in that block will keep the same names. C++, through name mangling, change the names of the functions, and if we want to call them through C, we will be unable to find those functions which had their names changed.

Python to c++

Pybind11 module

  • We can create a python interpreter inside our C++ code and call python functions directly
  • We can retrieve data from python calls
  • We can import modules as well

Boost.Python

Having the following c++ struct:

We write the following boost wrapper:

And we can use it directly in python afterwards

SWIG – Simplified Wrapper and Interface Generator

Having the following C++ code:

Creating the following .i (interface) file:

And compiling the code:

It generates a shared library that can further be used in python.

So what we got there?

swig -c++ -python Value.i

This line executes swig and tells it to read the interface file, and compile glue code for C++ and python

g++ -O2 -c  Value.cpp -I/usr/include/python2.4

g++ -O2 -c  Value_wrap.cxx  -I/usr/include/python2.4

These lines compile the C++ Code and the C++ code which wraps the python object

g++ -shared Value.o Value_wrap.o -o _Value.so

This line creates the shared library with the name _Value.so (the file Value.so was created on the first step (the glue code part)). Value.so acts as a proxy and imports the _Value module afterwards.

After we have executed all lines, we can use the library as any python module.

Leave a comment

Your email address will not be published.