wake-up-neo.com

Aufruf von C / C ++ aus Python?

Was wäre der schnellste Weg, um eine Python -Bindung an eine C- oder C++ - Bibliothek zu erstellen?

(Ich benutze Windows, wenn dies wichtig ist.)

479
shoosh

Sie sollten sich Boost.Python ansehen. Hier ist die kurze Einführung von ihrer Website:

Die Boost Python Library ist ein Framework für die Anbindung von Python und C++. Sie können damit schnell und nahtlos C++ - Klassenfunktionen und -Objekte für Python verfügbar machen und umgekehrt, ohne spezielle Tools - nur Ihren C++ - Compiler. Es ist so konzipiert, dass C++ - Schnittstellen nicht intrusiv umbrochen werden, sodass Sie den C++ - Code zum Umbrechen überhaupt nicht ändern müssen, sodass Boost.Python ideal für das Bereitstellen von Bibliotheken von Drittanbietern für Python ist. Durch die Verwendung fortschrittlicher Metaprogrammiertechniken in der Bibliothek wird die Syntax für Benutzer vereinfacht, sodass der Wrapping-Code wie eine Art deklarative Schnittstellendefinitionssprache (IDL) aussieht.

155
Ralph

ctypes ist Teil der Standardbibliothek und daher stabiler und allgemein verfügbar als swig , was mir immer Probleme bescherte.

Bei ctypes müssen Sie die Kompilierungszeitabhängigkeit von Python berücksichtigen, und Ihre Bindung funktioniert für alle python, die ctypes enthalten, nicht nur für die, mit der sie kompiliert wurde.

Angenommen, Sie haben eine einfache C++ - Beispielklasse, mit der Sie in einer Datei mit dem Namen foo.cpp kommunizieren möchten:

#include <iostream>

class Foo{
    public:
        void bar(){
            std::cout << "Hello" << std::endl;
        }
};

Da ctypes nur mit C-Funktionen kommunizieren können, müssen Sie diejenigen angeben, die sie als externes "C" deklarieren.

extern "C" {
    Foo* Foo_new(){ return new Foo(); }
    void Foo_bar(Foo* foo){ foo->bar(); }
}

Als nächstes müssen Sie dies in eine gemeinsam genutzte Bibliothek kompilieren

g++ -c -fPIC foo.cpp -o foo.o
g++ -shared -Wl,-soname,libfoo.so -o libfoo.so  foo.o

Und schließlich müssen Sie Ihren python Wrapper schreiben (z. B. in fooWrapper.py)

from ctypes import cdll
lib = cdll.LoadLibrary('./libfoo.so')

class Foo(object):
    def __init__(self):
        self.obj = lib.Foo_new()

    def bar(self):
        lib.Foo_bar(self.obj)

Sobald Sie das haben, können Sie es wie nennen

f = Foo()
f.bar() #and you will see "Hello" on the screen
606
Florian Bösch

Am schnellsten geht das mit SWIG .

Beispiel von SWIG Tutorial :

/* File : example.c */
int fact(int n) {
    if (n <= 1) return 1;
    else return n*fact(n-1);
}

Schnittstellendatei:

/* example.i */
%module example
%{
/* Put header files here or function declarations like below */
extern int fact(int n);
%}

extern int fact(int n);

Erstellen eines Python Moduls unter Unix:

swig -python example.i
gcc -fPIC -c example.c example_wrap.c -I/usr/local/include/python2.7
gcc -shared example.o example_wrap.o -o _example.so

Verwendungszweck:

>>> import example
>>> example.fact(5)
120

Beachten Sie, dass Sie Python-Dev haben müssen. Auch in einigen Systemen befinden sich python Header-Dateien in /usr/include/python2.7, je nachdem, wie Sie sie installiert haben.

Aus dem Tutorial:

SWIG ist ein ziemlich vollständiger C++ - Compiler, der nahezu alle Sprachfunktionen unterstützt. Dies umfasst Vorverarbeitung, Zeiger, Klassen, Vererbung und sogar C++ - Vorlagen. Mit SWIG können auch Strukturen und Klassen in Proxy-Klassen in der Zielsprache gepackt werden, wodurch die zugrunde liegende Funktionalität auf ganz natürliche Weise verfügbar gemacht wird.

51
Ben Hoffstein

Ich habe meine Reise in der Python <-> C++ - Bindung von dieser Seite mit dem Ziel begonnen, übergeordnete Datentypen (mehrdimensionale STL-Vektoren mit Python-Listen) zu verknüpfen :-)

Nachdem ich die auf ctypes und boost.python basierenden Lösungen ausprobiert habe (und kein Software-Ingenieur bin), habe ich sie als komplex empfunden, wenn eine Bindung von Datentypen auf hoher Ebene erforderlich ist gefunden SWIG viel einfacher für solche Fälle.

In diesem Beispiel wird daher SWIG verwendet, und es wurde unter Linux getestet (aber SWIG ist verfügbar und wird auch unter Windows häufig verwendet).

Ziel ist es, Python eine C++ - Funktion zur Verfügung zu stellen, die eine Matrix in Form eines 2D-AWL-Vektors annimmt und einen Durchschnitt jeder Zeile (als 1D-AWL-Vektor) zurückgibt.

Der Code in C++ ("code.cpp") lautet wie folgt:

#include <vector>
#include "code.h"

using namespace std;

vector<double> average (vector< vector<double> > i_matrix) {

  // Compute average of each row..
  vector <double> averages;
  for (int r = 0; r < i_matrix.size(); r++){
    double rsum = 0.0;
    double ncols= i_matrix[r].size();
    for (int c = 0; c< i_matrix[r].size(); c++){
      rsum += i_matrix[r][c];
    }
    averages.Push_back(rsum/ncols);
  }
  return averages;
}

Der entsprechende Header ("code.h") lautet:

#ifndef _code
#define _code

#include <vector>

std::vector<double> average (std::vector< std::vector<double> > i_matrix);

#endif

Wir kompilieren zuerst den C++ - Code, um eine Objektdatei zu erstellen:

g++ -c -fPIC code.cpp

Wir definieren dann eine SWIG-Interface-Definitionsdatei ("code.i") für unsere C++ - Funktionen.

%module code
%{
#include "code.h"
%}
%include "std_vector.i"
namespace std {

  /* On a side note, the names VecDouble and VecVecdouble can be changed, but the order of first the inner vector matters! */
  %template(VecDouble) vector<double>;
  %template(VecVecdouble) vector< vector<double> >;
}

%include "code.h"

Mit SWIG generieren wir einen C++ - Schnittstellenquellcode aus der SWIG-Schnittstellendefinitionsdatei.

swig -c++ -python code.i

Wir kompilieren schließlich die generierte C++ - Schnittstellenquelldatei und verknüpfen alles miteinander, um eine gemeinsam genutzte Bibliothek zu generieren, die direkt von Python importiert werden kann (auf die "_" kommt es an):

g++ -c -fPIC code_wrap.cxx  -I/usr/include/python2.7 -I/usr/lib/python2.7
g++ -shared -Wl,-soname,_code.so -o _code.so code.o code_wrap.o

Wir können die Funktion jetzt in Python Skripten verwenden:

#!/usr/bin/env python

import code
a= [[3,5,7],[8,10,12]]
print a
b = code.average(a)
print "Assignment done"
print a
print b
46
Antonello

Schauen Sie sich Pyrex oder Cython an. Sie sind Python-ähnliche Sprachen für die Schnittstelle zwischen C/C++ und Python.

28
Jason Baker

Es gibt auch pybind11, das wie eine Lightweight-Version von Boost.Python ist und mit allen modernen C++ - Compilern kompatibel ist:

https://pybind11.readthedocs.io/en/latest/

25
Tom Wenseleers

Dieser Artikel, in dem behauptet wird, dass Python alles ist, was ein Wissenschaftler braucht sagt im Grunde genommen: Zuerst muss alles in Python prototypisiert werden. Wenn Sie dann einen Teil beschleunigen müssen, verwenden Sie SWIG und übersetzen Sie diesen Teil nach C.

18
Yuval F

Ich habe es nie benutzt, aber ich habe gute Dinge über ctypes gehört. Wenn Sie versuchen, es mit C++ zu verwenden, müssen Sie die Namensverfälschung mit extern "C" umgehen. Danke für den Kommentar, Florian Bösch.

15
John

Verwenden Sie für modernes C++ cppyy: http://cppyy.readthedocs.io/en/latest/

Es basiert auf Cling, dem C++ - Interpreter für Clang/LLVM. Bindungen werden zur Laufzeit ausgeführt und es ist keine zusätzliche Zwischensprache erforderlich. Dank Clang unterstützt es C++ 17.

Installiere es mit pip:

    $ pip install cppyy

Laden Sie bei kleinen Projekten einfach die entsprechende Bibliothek und die gewünschten Header. Nehmen Sie den Code aus dem ctypes-Beispiel, ist dieser Thread, aber aufgeteilt in Header- und Codeabschnitte:

    $ cat foo.h
    class Foo {
    public:
        void bar();
    };

    $ cat foo.cpp
    #include "foo.h"
    #include <iostream>

    void Foo::bar() { std::cout << "Hello" << std::endl; }

Kompiliere es:

    $ g++ -c -fPIC foo.cpp -o foo.o
    $ g++ -shared -Wl,-soname,libfoo.so -o libfoo.so  foo.o

und benutze es:

    $ python
    >>> import cppyy
    >>> cppyy.include("foo.h")
    >>> cppyy.load_library("foo")
    >>> from cppyy.gbl import Foo
    >>> f = Foo()
    >>> f.bar()
    Hello
    >>>

Große Projekte werden durch das automatische Laden der vorbereiteten Reflektionsinformationen und der zu erstellenden cmake-Fragmente unterstützt, sodass Benutzer installierter Pakete einfach Folgendes ausführen können:

    $ python
    >>> import cppyy
    >>> f = cppyy.gbl.Foo()
    >>> f.bar()
    Hello
    >>>

Dank LLVM sind erweiterte Funktionen möglich, z. B. die automatische Instanziierung von Vorlagen. So setzen Sie das Beispiel fort:

    >>> v = cppyy.gbl.std.vector[cppyy.gbl.Foo]()
    >>> v.Push_back(f)
    >>> len(v)
    1
    >>> v[0].bar()
    Hello
    >>>

Anmerkung: Ich bin der Autor von cppyy.

15
Wim Lavrijsen

Ich denke, CFFI für python kann eine Option sein.

Das Ziel ist es, C-Code aus Python aufzurufen. Sie sollten dazu in der Lage sein, ohne eine dritte Sprache zu lernen: Für jede Alternative müssen Sie ihre eigene Sprache (Cython, SWIG) oder API (ctypes) lernen. Daher haben wir versucht, davon auszugehen, dass Sie Python und C kennen, und die zusätzlichen API-Teile zu minimieren, die Sie lernen müssen.

http://cffi.readthedocs.org/en/release-0.7/

13
mrgloom

Die Frage ist, wie man eine C-Funktion von Python aus aufruft, wenn ich das richtig verstanden habe. Dann sind C-Typen die beste Wahl (übrigens portabel für alle Python-Varianten).

>>> from ctypes import *
>>> libc = cdll.msvcrt
>>> print libc.time(None)
1438069008
>>> printf = libc.printf
>>> printf("Hello, %s\n", "World!")
Hello, World!
14
>>> printf("%d bottles of beer\n", 42)
42 bottles of beer
19

Eine ausführliche Anleitung finden Sie unter mein Blog-Artikel .

7
Jadav Bheda

Eines der offiziellen Python Dokumente enthält Details zu Erweiterung von Python mit C/C++ . Auch ohne die Verwendung von SWIG ist dies recht einfach und funktioniert unter Windows einwandfrei.

7

Zunächst sollten Sie sich für einen bestimmten Zweck entscheiden. Die offizielle Python Dokumentation zu Erweiterung und Einbettung des Python Interpreters wurde oben erwähnt, ich kann eine gute Übersicht über binäre Erweiterungen hinzufügen . Die Anwendungsfälle können in 3 Kategorien unterteilt werden:

  • Beschleunigermodule : um schneller zu laufen als der entsprechende reine Python Code in CPython läuft.
  • Wrapper-Module : um vorhandene C-Schnittstellen für Python-Code verfügbar zu machen.
  • Systemzugriff auf niedrigerer Ebene : Zugriff auf Funktionen auf niedrigerer Ebene der CPython-Laufzeit, des Betriebssystems oder der zugrunde liegenden Hardware.

Um anderen Interessierten eine breitere Perspektive zu bieten und da Ihre erste Frage etwas vage ist ("zu einer C- oder C++ - Bibliothek"), denke ich, dass diese Informationen für Sie interessant sein könnten. Auf dem obigen Link können Sie die Nachteile der Verwendung von binären Erweiterungen und deren Alternativen nachlesen.

Abgesehen von den anderen vorgeschlagenen Antworten können Sie Numba ausprobieren, wenn Sie ein Beschleunigermodul möchten. Es funktioniert "durch Generieren von optimiertem Maschinencode mithilfe der LLVM-Compilerinfrastruktur zum Zeitpunkt des Imports, zur Laufzeit oder statisch (mithilfe des enthaltenen Pycc-Tools)".

5

Cython ist definitiv der richtige Weg, es sei denn, Sie erwarten, Java Wrapper zu schreiben. In diesem Fall ist SWIG möglicherweise vorzuziehen.

Ich empfehle die Verwendung des Befehlszeilendienstprogramms runcython, um die Verwendung von Cython zu vereinfachen. Wenn Sie strukturierte Daten an C++ übergeben müssen, sehen Sie sich die Protobuf-Bibliothek von Google an.

Hier sind ein paar Beispiele, die ich gemacht habe und die beide Werkzeuge verwenden:

https://github.com/nicodjimenez/python2cpp

Hoffe, es kann ein nützlicher Ausgangspunkt sein.

5
nicodjimenez

Sie können Scapix Language Bridge verwenden, um automatisch Python Bindungen direkt aus C++ - Headern als Teil des Builds zu generieren.

Fügen Sie Ihrer CMakeLists.txt-Datei einen Aufruf von scapix_bridge_headers() hinzu, und erstellen Sie dann Ihr Projekt mit cmake -DSCAPIX_BRIDGE=python. Siehe ganz Beispiel .

Haftungsausschluss: Ich bin der Autor von Scapix Language Bridge .

0
Boris Rasin