Fließende Kommunikation zwischen Python und C/C++Python erweitern und einbetten
Von
Rainer Grimm *
10 min Lesedauer
Es gibt viele gute Gründe, Python entweder um C/C++ zu erweitern oder in C/C++ einzubetten – grundsätzlich verstehen sich Python und C/C++ nämlich blendend. Am Anfang steht aber die Qual der Wahl: Welche Technik oder welches Framework sollte zum Einsatz kommen? Wann ist es ratsam, Python zu erweitern, wann sollte man es eher in C++ einbetten?
Das mit Abstand am häufigsten verwendete Python (CPython) ist bereits in C implementiert und dürfte vielen Entwicklern bereits geläufig sein. Doch wann ist es nötig, das verwendete Python zu erweitern? Wann ist es eher ratsam, statt in C eine einbettung in C++ vorzunehmen?
Bevor dieser Artikel in die Untiefen der Kommunikation zwischen Python und C/C++ abtaucht, gilt es zuerst, die elementare Frage zu klären: Was bedeutet es, Python um C/C++ zu erweitern beziehungsweise Python in C/C++ einzubetten? Vereinfacht gesagt verhalten sich die beiden Techniken spiegelbildlich zueinander.
Das Erweitern geschieht in den folgenden drei Schritten:
Zunächst muss man die Werte von Python nach C/C++ konvertieren.
Anschließend kann man die konvertierten Werte verwenden, um die C/C++-Funktionalität auszuführen.
Zuletzt lassen sich die Ergebnisse von C/C++ nach Python konvertieren.
Das Einbetten hingegen funktioniert genau umgekehrt: Zuerst werden die Werte von C/C++ nach Python konvertiert, und am Ende steht das Konvertieren der Ergebnisse von C/C++ nach Python. Das Erweitern und Einbetten von Python ist kein Selbstzweck, sondern bietet eine Reihe von Vorteilen. Der Einfachheit halber beschränkt sich folgende Aufzählung auf drei Vorteile.
Don't repeat yourself (DRY).
Optimierung des performanzkritischen Teils der Applikation.
Um den Umfang dieses Artikels nicht zu sprengen, werden sich alle Beispiele in diesem Dokument auf Linux beziehen. Die entsprechenden Schritte lassen sich aber auch auf Windows entsprechend durchführen.
Direkte Kommunikation aus Python mit Shared Libraries dank ctypes
Bild 1: Shared Library aus Linux ausführen.
(Bild: Rainer Grimm)
Mit dem Modul ctypes lässt sich eine Shared Librarys oder DLL direkt und komfortabel aufrufen. ctypes wird in der Python-Dokumentation als eine Bibliothek für fremde Funktionen beschrieben. Sie stellt mit C kompatible Datentypen zur Verfügung und ermöglicht den Aufruf von Funktionen in den DLLs oder Shared Libraries. Außerdem dient sie dazu, diese Bibliotheken in reinen Python-Code zu verpacken.
Bild 1 zeigt, wie die Shared Library libhelloWorld.so sich direkt laden und die Funktion "helloWorld.helloWorld()" ausführen lässt.
Python direkt erweitern
Für das direkte Erweitern der Python-API und den unmittelbaren Zugriff auf die Python-Laufzeit bietet Python Funktionen, Makros und Variablen an. Die Python-API wird durch die Headerdatei Python.h angeboten und lässt sich nur unter CPython einsetzen. Eigene Erweiterungsmodule in C/C++ für Python zu schreiben ist eine durchaus anspruchsvolle Aufgabe – daher wird das folgende Kapitel in pragmatischer Manier die bekannte Shared Library helloWorld als Erweiterungsmodul implementieren, und folgende Listing stellt das neue Erweiterungsmodul vor.
Zum Verständnis des Listings sind einige Erklärungen hilfreich:
Python.h erlaubt den Zugriff auf die Python-API, sie muss die erste Headerdatei sein.
static PyMethodDef HelloWorld[ ] definiert die Methodentabelle. Dabei besteht ein Eintrag{"helloWorld", method_helloWorld, METH_VARARGS, "Hello"} aus dem Namen der Python-Methode helloWorld, dem Namen der korrespondierenden C-Funktion method_helloWorld, der Aufrufkonvention der C-Funktion METH_VARARGS und dem Dokumentationsstring der Python-Methode. Eine Methodentabelle kann mehr als einen Eintrag besitzen, der letzte Eintrag {NULL, NULL, 0, NULL} schließt die Tabelle ab.
static struct PyModuleDef helloWorldModule steht für die Definition des Moduls. Das Modul benötigt einen Namen helloWorld sowie den Dokumentationsstring des Moduls Hello World message, und es referenziert die bereits definierte Methodentabelle HelloWorld.
PyMODINIT_FUNC sorgt für die Initialisierung des Moduls.
Nun gilt es noch ein Paket zu bauen, das das Modul enthält. Mit dem Python-Modul disutils lässt sich diese Aufgabe für Linux problemlos umsetzen. Für das Erzeugen eines Pakets ist eine setup.py-Datei notwendig. Die Datei setup.py im folgenden Listing stellt eine minimale Konfigurationsdatei für das Paket helloWorld dar.
Nachdem die Namen des Moduls disutils bekannt sind, lässt sich das Paket beschreiben. name steht für den Namen des Pakets. Neben dem Namen des Pakets ist auch der Inhalt des Moduls genau zu spezifizieren. Das erledigt das Attribut ext_modules. Das Paket besteht aus dem gleichnamigen Modul helloWorld, das wiederum aus der C-Datei helloWorldModule.c besteht. Ein Paket kann mehrere Module enthalten.
Mittels Aufruf python setup.py build_ext --inplace lässt sich das Paket bauen.
Bild 2 zeigt die lange Kommandozeile, die notwendig ist, um das Paket auf Linux zu erstellen.
Bild 3: Verwendung des Shared Library auf Linux.
(Bild: Rainer Grimm)
Mit dem Flag --inplace wird in dem aktuellen Verzeichnis eine Shared Library erzeugt, die sich direkt verwenden lässt. Bild 3 stellt die Verwendung des Shared Library auf Linux vor.
Interfaces zu C/C++ mit SWIG erzeugen
Der Simplified Wrapper and Interface Generator (SWIG) erzeugt Interfaces, sodass C beziehungsweise C++ mit anderen Programmiersprachen kommunizieren können. SWIG spricht mehrere Sprachen wie beispielsweise C#, D, Java, JavaScript, Perl, Python, PHP oder Ruby. SWIG dient als Wrapper zwischen Python und C beziehungsweise C++.
Der leichteste Einstieg in SWIG ist es, ein Erweiterungsmodul zu implementieren, was hier mit einem einfachen helloWorld-Beispiel demonstriert wird. Das Erzeugen eines Erweiterungsmoduls mit SWIG beginnt mit einer einfachen SWIG-Interfacedatei, die als hello.i im folgenden Listing dargestellt ist.
Die Datei hello.i enthält den Namen des Moduls helloWorld und spezifiziert mit SWIG_FILE_WITH_INIT, dass die zu erzeugende Erweiterung eine Python-Erweiterung ist. Letztere fügt den Initialisierungscode für das Modul hinzu. Außerdem umfasst die Datei die Headerdatei der C-Quellen helloWorld.h und eine Deklaration der C-Funktion namens extern void helloWorld(). Das Kommando swig erzeugt mithilfe der Interfacedatei die zwei Dateien helloWorld_wrap.c und helloWorld.py. Bild 4 zeigt den dabei ablaufenden Build-Prozess.
Stand: 08.12.2025
Es ist für uns eine Selbstverständlichkeit, dass wir verantwortungsvoll mit Ihren personenbezogenen Daten umgehen. Sofern wir personenbezogene Daten von Ihnen erheben, verarbeiten wir diese unter Beachtung der geltenden Datenschutzvorschriften. Detaillierte Informationen finden Sie in unserer Datenschutzerklärung.
Einwilligung in die Verwendung von Daten zu Werbezwecken
Ich bin damit einverstanden, dass die Vogel IT-Medien GmbH, Max-Josef-Metzger-Straße 21, 86157 Augsburg, einschließlich aller mit ihr im Sinne der §§ 15 ff. AktG verbundenen Unternehmen (im weiteren: Vogel Communications Group) meine E-Mail-Adresse für die Zusendung von Newslettern und Werbung nutzt. Auflistungen der jeweils zugehörigen Unternehmen können hier abgerufen werden.
Der Newsletterinhalt erstreckt sich dabei auf Produkte und Dienstleistungen aller zuvor genannten Unternehmen, darunter beispielsweise Fachzeitschriften und Fachbücher, Veranstaltungen und Messen sowie veranstaltungsbezogene Produkte und Dienstleistungen, Print- und Digital-Mediaangebote und Services wie weitere (redaktionelle) Newsletter, Gewinnspiele, Lead-Kampagnen, Marktforschung im Online- und Offline-Bereich, fachspezifische Webportale und E-Learning-Angebote. Wenn auch meine persönliche Telefonnummer erhoben wurde, darf diese für die Unterbreitung von Angeboten der vorgenannten Produkte und Dienstleistungen der vorgenannten Unternehmen und Marktforschung genutzt werden.
Meine Einwilligung umfasst zudem die Verarbeitung meiner E-Mail-Adresse und Telefonnummer für den Datenabgleich zu Marketingzwecken mit ausgewählten Werbepartnern wie z.B. LinkedIN, Google und Meta. Hierfür darf die Vogel Communications Group die genannten Daten gehasht an Werbepartner übermitteln, die diese Daten dann nutzen, um feststellen zu können, ob ich ebenfalls Mitglied auf den besagten Werbepartnerportalen bin. Die Vogel Communications Group nutzt diese Funktion zu Zwecken des Retargeting (Upselling, Crossselling und Kundenbindung), der Generierung von sog. Lookalike Audiences zur Neukundengewinnung und als Ausschlussgrundlage für laufende Werbekampagnen. Weitere Informationen kann ich dem Abschnitt „Datenabgleich zu Marketingzwecken“ in der Datenschutzerklärung entnehmen.
Falls ich im Internet auf Portalen der Vogel Communications Group einschließlich deren mit ihr im Sinne der §§ 15 ff. AktG verbundenen Unternehmen geschützte Inhalte abrufe, muss ich mich mit weiteren Daten für den Zugang zu diesen Inhalten registrieren. Im Gegenzug für diesen gebührenlosen Zugang zu redaktionellen Inhalten dürfen meine Daten im Sinne dieser Einwilligung für die hier genannten Zwecke verwendet werden. Dies gilt nicht für den Datenabgleich zu Marketingzwecken.
Recht auf Widerruf
Mir ist bewusst, dass ich diese Einwilligung jederzeit für die Zukunft widerrufen kann. Durch meinen Widerruf wird die Rechtmäßigkeit der aufgrund meiner Einwilligung bis zum Widerruf erfolgten Verarbeitung nicht berührt. Um meinen Widerruf zu erklären, kann ich als eine Möglichkeit das unter https://contact.vogel.de abrufbare Kontaktformular nutzen. Sofern ich einzelne von mir abonnierte Newsletter nicht mehr erhalten möchte, kann ich darüber hinaus auch den am Ende eines Newsletters eingebundenen Abmeldelink anklicken. Weitere Informationen zu meinem Widerrufsrecht und dessen Ausübung sowie zu den Folgen meines Widerrufs finde ich in der Datenschutzerklärung.
Während die Datei helloWorld_wrap.c der Low-Level-Wrapper ist, der mit der restlichen Applikation gelinkt wird, enthält die Python-Datei helloWorld.py den High-Level-Code, der sich aus Python importieren lässt. Das Modul benötigt nicht nur die dafür erforderlichen C- und Python-Wrapper, die durch swig erzeugt wurden, sondern auch die eigentliche C-Funktionalität.
Das nächste Listing zeigt die Headerdatei helloWorld.h zu der korrespondierenden Quellcodedatei helloWorld.c:
Jetzt gilt es nur noch, das Erweiterungsmodul helloWorld zu bauen und zu verwenden. Mit dem Python-Modul disutils geht das leicht von der Hand.
C++11 und Python mit pybind11 nahtlos verbinden
Der Name ist Programm, denn pybind11 unterstützt eine nahtlose Zusammenarbeit zwischen C++11 und Python. Nahtlos heißt in dem konkreten Fall, dass sich sowohl C als auch C++ um Python erweitern und andererseits C/C++ in Python einbinden lassen.
pybind11 ist vollständig in die Headerdateien implementiert. Damit steht die gesamte Funktionalität durch das Inkludieren von Headerdateien direkt zur Verfügung.Die Vielseitigkeit von pybind11 ist beeindruckend. So lassen sich Lambda-Ausdrücke verwenden, Funktionen können ihre Argumente per Value, Referenz oder Zeiger erhalten und lassen sich überladen. Klassen können Methoden und Attribute besitzen, lassen sich mehrfach ableiten und erlauben Virtualität. Mit der Standard Template Library (STL) und weiteren Bibliotheken von C++11 lässt pybind praktisch keine Wünsche offen.
Seminar-Tipp: C++11 und C++14
„C++11 feels like a new language“. Dieses berühmte Zitat von Bjarne Stroustrup, dem Vater von C++, bringt es auf den Punkt. Nicht nur der Sprachumfang von 800 Seiten hat sich fast verdoppelt. Auch die Art und Weise, wie Sie in modernes C++ programmieren ist fundamental anders. In dem Seminar lernen Sie, was modernes C++ (C++11 und C++14) auszeichnet. Sie werden die Veränderungen der Kernsprache, die neue Multithreading-Fähigkeit von C++ und die vielen neuen Bibliotheken kennen lernen. Das Seminar „C++11 und C++14“ liefert den Teilnehmern einen praktischen „Leitfaden für Programmierer zu modernem C++“.
Die Funktionalität von pybind11 lässt sich am einfachsten an einem Beispiel aufzeigen. Das folgende Listing stellt eine C++-Funktion vor, für die pybind11 eine Python-Bindung erzeugt:
#include <pybind11/pybind11.h>int add(int i, int j) { return i + j;}PYBIND11_MODULE(function, m) { m.def("add", &add, "A simple add function");}
Die C++/Python-Bindung besteht aus den folgenden Komponenten
#include <pybind11/pybind11.h> erzeugt die C++11/Python-Bindung
PYBIND11_MODULE wird durch die import-Anweisung aufgerufen
function ist der Name des Erweiterungmoduls, durch das sich das Modul in Python importieren lässt
m ist eine Variable vom Typ py::module_, die als Interface für die Bindung verwendet wird
m.def macht Python mit der Funktion bekannt und bindet sich an das Erweiterungsmodul function. Dabei ist das erste Argument der Name der Python-Funktion, das zweite die Adresse der C-Funktion und das letzte Argument der Dokumentationsstring der Python-Funktion.
Bild 5: Verwenden des Moduls function.
(Bild: Rainer Grimm)
Das Bauen des Programms unter Linux ist mit python3-config fast schon ein Kinderspiel. Zunächst gilt es, die Headerdateien zu ermitteln python3 -m pybind11 -–includes , dann sind mit folgendem Befehl die Suffixe zu bestimmen: python3-config --extension-suffix . Daraus resultieren Kommandozeilenbefehle für das Bauen des Erweiterungsmoduls, und das Ergebnis sollte wie folgt aussehen: c++ -O3 –Wall –std=c++11 -shared -fPIC $(python3 -m pybind11 --includes) function.cpp -o function$(python3-config --extension-suffix). Mit den vorgestellten Kommandozeilenbefehlen lässt sich das Modul function erstellen, importieren und verwenden (siehe Bild 5).
Python in C/C++ einbetten
Die Anforderung, Python in C oder C++ einzubetten sind dieselben, die es bei der Erweiterung von Python um C/C++ zu meistern galt. Lediglich die Reihenfolge ist beim Einbetten auf den Kopf gestellt. Zunächst sind die Werte von C/C++ nach Python zu konvertieren. Anschließend gilt es, die konvertierten Werte zum Ausführen der Python-Funktionalität zu verwenden. Zuletzt lassen sich die Ergebnisse dann von Python nach C beziehungsweise C++ konvertieren.
Prinzipiell bieten sich zwei Möglichkeiten an, um Python-Code in C/C++ auszuführen. Zum einen lässt sich der Python-Code als String oder als Modul verpacken und vollständig ausführen. Darüber hinaus ist es möglich, explizit Code aus einem Python-Modul auszuführen. Diese zweite Variante ist deutlich anspruchsvoller und kann daher nicht in diesem Artikel vorgestellt werden.
Die größte Hürde beim Einbetten besteht darin, die richtigen Compiler- und Linkeroptionen zu bestimmen. Mit dem Executable python-config lassen sich die Optionen für den Compiler (python-config --clflags) sowie für den Linker (python-config --ldflags) auf Linux bestimmen.
Das direkte Ausführen eines Python-Strings oder Python-Moduls sollte nun leicht von der Hand gehen.
Python-Strings direkt ausführen
Das einfache C-Programm showTimeString.cpp im folgenden Listing dient als erstes Beispiel.
Mit der Headerdatei <Python.h> stehen die Anweisungen in dem main-Programm zu Verfügung. Py_Initialize initialisiert den Python-Interpreter. PyRun_SimpleString führt den Python-String direkt aus, der zuerst das Modul time lädt, um anschließend die aktuelle Uhrzeit auszugeben. Py_Finalize fährt den Interpreter wieder runter.
Python-Module direkt ausführen
Ähnlich einfach ist das Ausführen eines Python-Moduls in dem folgenden C-Programm. Der Quellcode im folgenden Listing stellt das Python-Modul showTime.py vor.
import timeprint(time.ctime(time.time()))
Ähnlich wie bei dem Python-String im vorherigen Listing importiert das Python-Modul auch das Modul time und stellt die aktuelle Uhrzeit dar. Deutlich komfortabler ist hingegen das C-Programm showTimeModule.cpp im Folgenden, das das Python-Modul lädt und ausführt.
Das Programm öffnet in diesem Fall die Datei showTime.py und führt das Python-Modul mithilfe des Befehls PyRun_SimpleFile direkt aus (Bild 6).
Persönliches Fazit
Auf die Frage, welche Lösung für das Erweitern von Python um C/C++ zu empfehlen ist, gibt es keine einfache Antwort.
Für das Erweitern um eine bestehende dynamische Bibliothek ist das Python-Modul ctypes die erste Wahl.
Sollen C beziehungsweise C++ nicht nur mit Python, sondern zudem mit weiteren Skriptsprachen kommunizieren, spielt SWIG seine volle Stärke aus.
Den Autor hat pybind11 am stärksten beeindruckt. Mit diesem Framework ist es möglich, C++-Funktionalität mit Python anzusprechen.
Dieser Beitrag stammt mit freundlicher Genehmigung des Autors aus dem Tagungsband des ESE Kongress 2021.
* Rainer Grimm ist seit vielen Jahren als Softwarearchitekt, Team- und Schulungsleiter tätig und spricht auf zahlreichen Fachkonferenzen. Seit 2016 steht er auf selbstständigen Beinen. Insbesondere das Vermitteln von Wissen zu modernem C++ ist ihm eine Herzensangelegenheit. Seine Bücher "C++11 für Programmierer", "C++" und "C++-Standardbibliothek" für die kurz und gut Reihe sind beim Verlag O'Reilly und Hanser erschienen. Seine englischsprachigen Werke "C++20", "The C++ Standard Library" und "Concurrency with Modern C++” sind in mehrere Sprachen übersetzt worden.