This demo project evalutes designs for a two-way Python/C++ interface for projects that have C++ main loops and are mainly written in C++.
The interface design was created for openage, but it is general-purpose. My Thanks go to #Python on irc.freenode.net, whose advice was vital for finding sane design decisions.
- The application becomes a library (
libtest.so). - The entry point (main method) is written in Python.
.pxdfiles are generated from annotated.hfiles (seepxdgen.py;.pxdfiles are the Cython equivalent of header files).- The interface is written in
.pyxfiles, which are compiled to.cppfiles by Cython; those.cppfiles can then be compiled to Python extension modules; they are linked againstlibtest.so. - Pure python modules can import those extension modules at run-time; that way they can access any C++ functions and objects that are wrapped there.
- Function pointers are used to call Python functions from C++; they are defined in the C++ code (
py_functions.cpp) and made available to Python throughpy_functions.h..pyxmodules that want to provide a function define acdeffunction with a matching signature andexcept*, and assign it to the C++ function pointer during initialization. - Before making calls into the C++ code, all
.pyxmodules that provideC++function pointers must be imported - C++ methods that are called from Python must be declared
except+to enable proper exception translation (automatically done by Cython). - Exceptions raised by Python methods that are called from C++ are translated to C++ exceptions by the
PyFuncfunction pointer wrapper that is defined in and used bypy_functions.h.
mic@mic ~/git/cythondemo $ ./run --help
LD_LIBRARY_PATH=. python3 -m demo --help
usage: __main__.py [-h] [--thatnumber THATNUMBER]
optional arguments:
-h, --help show this help message and exit
--thatnumber THATNUMBER
mic@mic ~/git/cythondemo $ ./run --thatnumber=17
LD_LIBRARY_PATH=. python3 -m demo --thatnumber=17
exception during exctest: python exception: <class 'Exception'>: your mom is fat
square of 17: 289
type p to enter interactive python interp, anything else to add strings
> teststring
processed input: teststring
> p
>>> if_strings.callbacks.append(lambda s: s.upper())
>>>
> teststring
processed input: TESTSTRING
> p
>>> if_strings.callbacks.append(lambda s: len(s)/0)
>>>
> teststring
error processing line: python exception: <class 'ZeroDivisionError'>: division by zero
> kthxbai
python3 pxdgen.py main.h -o main.pxd
python3 pxdgen.py py_functions.h -o py_functions.pxd
cython --gdb --cplus -3 --fast-fail if_math.pyx
cython --gdb --cplus -3 --fast-fail if_main.pyx
cython --gdb --cplus -3 --fast-fail if_strings.pyx
g++ -std=c++11 -fPIC -I/usr/include/python3.4m --shared main.cpp py_functions.cpp -o libtest.so
g++ -std=c++11 -fPIC -I/usr/include/python3.4m --shared if_main.cpp libtest.so -o if_main.so
g++ -std=c++11 -fPIC -I/usr/include/python3.4m --shared if_math.cpp libtest.so -o if_math.so
g++ -std=c++11 -fPIC -I/usr/include/python3.4m --shared if_strings.cpp libtest.so -o if_strings.so