Wie nennt man Python-Code von C-Code?
-
20-08-2019 - |
Frage
Ich möchte ein großes C-Projekt mit einigen neuen Funktionalität erweitern, aber ich will es wirklich in Python schreiben. Im Grunde möchte ich Python-Code von C-Code aufzurufen. Python-> C-Wrapper wie SWIG ermöglichen jedoch das Gegenteil, dass C-Module ist das Schreiben und C aus Python aufrufen.
Ich betrachte einen Ansatz, der IPC oder RPC (Ich bin nicht dagegen mehrere Prozesse aufweisen); das heißt, mit meiner pure-Python Komponente Lauf in einem separaten Prozess (auf der gleichen Maschine) und meine C Projekt mit kommunizieren mit ihm durch das Schreiben / Lesen von einem Socket (oder UNIX-Rohr). meine Python-Komponente kann an der Buchse lesen / schreiben zu kommunizieren. Ist das ein sinnvoller Ansatz? Gibt es etwas Besseres? Wie einige spezielle RPC-Mechanismus?
Danke für die Antwort so weit - aber ich möchte auf IPC-basierte Ansätze konzentrieren, da ich meine Python-Programm in einem separaten Prozess, wie mein C-Programm haben wollen. Ich möchte nicht, ein Python-Interpreter einbinden. Dank!
Lösung
Ich empfehle die hier detaillierte Ansätze. Es beginnt mit der Erklärung, wie Strings von Python-Code auszuführen, dann von dort beschrieben, wie eine Python-Umgebung einzurichten mit Ihrem C-Programm zu interagieren, rufen Python-Funktionen von Ihrem C-Code, manipulieren Python Objekte aus Ihrem C-Code, etc.
Bearbeiten : Wenn Sie wirklich die Route der IPC gehen wollen, dann werden Sie wollen eine href verwenden <= "http://docs.python.org/library/struct.html "rel =" noreferrer "> die Struktur Modul oder besser noch, protlib . Die meisten Kommunikation zwischen einem Prozess Python und C dreht sich um Weitergabe structs hin und her, entweder über eine Socket oder durch Shared Memory .
Ich empfehle, die Schaffung eines Command
Struktur mit Feldern und Codes Kommandos und deren Argumente darzustellen. Ich kann nicht viel spezifischere Ratschläge geben, ohne mehr zu wissen, was Sie erreichen wollen, aber im Allgemeinen empfehle ich die protlib Bibliothek, es ist da, was ich zwischen C und Python-Programmen kommunizieren (Disclaimer: ich bin der Autor des protlib).
Andere Tipps
Siehe entsprechende Kapitel im Handbuch: http://docs.python.org/extending/
Im Wesentlichen werden Sie haben die Python-Interpreter in Ihr Programm einbinden.
Haben Sie darüber nachgedacht Wickeln Sie einfach Ihre Python-Anwendung in einem Shell-Skript und es aus mit Ihrer C-Anwendung aufrufen?
Nicht die eleganteste Lösung, aber es ist sehr einfach.
Ich habe keine IPC Ansatz für Python verwendet. Ich würde das C-Programm habe ein Standard-Gabel-exec verwenden, umgeleitet stdin
und stdout
im Kindprozess für die Kommunikation. Eine schöne text-basierte Kommunikation wird es sehr einfach macht das Python-Programm zu entwickeln und zu testen.
Wenn ich beschlossen habe, mit IPC zu gehen, würde ich wahrscheinlich protzen mit XML-RPC - Cross-Plattform, können Sie ganz einfach später das Python-Server-Projekt auf einem anderen Knoten setzen, wenn Sie wollen, viele ausgezeichneten Implementierungen (siehe hier für viele, darunter C und Python diejenigen, und hier für den einfachen XML-RPC-Server, den Teil der Python-Standardbibliothek ist -. nicht so hoch skalierbar als andere Ansätze, aber wahrscheinlich gut und bequem für den Anwendungsfall)
Es kann kein perfekter IPC Ansatz für alle Fälle sein (oder sogar eine perfekte RPC ein, mit allen Mitteln!), Aber die Bequemlichkeit, Flexibilität, Robustheit und eine breite Palette von Implementierungen viel kleinere Mängel überwiegen, in meiner Meinung.
anscheinend Python muß in der Lage sein, um win32 dll zu kompilieren, es wird das Problem lösen
In einer solchen Art und Weise, dass zu Win32-DLLs c # -Code Umwandlung wird es von jedem Entwicklungs-Tool nutzbar machen
Das scheint ganz nett http://thrift.apache.org/ gibt es sogar ein Buch über es.
Details:
Die Apache Thrift Software-Framework für skalierbare Cross-Sprache Dienstleistungen Entwicklung, kombiniert einen Software-Stack mit einer Code-Generierung Motor Dienste zu bauen, die effizient und nahtlos arbeiten zwischen C ++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C #, Kakao, JavaScript, Node.js, Smalltalk, OCaml und Delphi und andere Sprachen.
Ich habe den "Standard" -Ansatz von PyPy von C aufrufen . Es verwendet CFFI die Schnittstelle zu vereinfachen, aber es erfordert PyPy, nicht Python. Lesen und verstehen Sie dieses Beispiel erste, zumindest auf hohem Niveau.
I modifizierte, um das C / PyPy Beispiel mit Python zu arbeiten. Hier ist, wie Python von C nennen CFFI verwenden.
Mein Beispiel ist komplizierter, weil ich drei Funktionen in Python implementiert statt einem. Ich wollte zusätzliche Aspekte abdecken von Daten zurück vorbei und her.
Der komplizierteste Teil wird nun auf das Bestehen der Adresse von api
zu Python isoliert. Dass nur ein einziges Mal durchgeführt werden muss. Danach ist es einfach, neue Funktionen in Python hinzuzufügen.
interface.h
// These are the three functions that I implemented in Python.
// Any additional function would be added here.
struct API {
double (*add_numbers)(double x, double y);
char* (*dump_buffer)(char *buffer, int buffer_size);
int (*release_object)(char *obj);
};
test_cffi.c
//
// Calling Python from C.
// Based on Calling PyPy from C:
// http://doc.pypy.org/en/latest/embedding.html#more-complete-example
//
#include <stdio.h>
#include <assert.h>
#include "Python.h"
#include "interface.h"
struct API api; /* global var */
int main(int argc, char *argv[])
{
int rc;
// Start Python interpreter and initialize "api" in interface.py using
// old style "Embedding Python in Another Application":
// https://docs.python.org/2/extending/embedding.html#embedding-python-in-another-application
PyObject *pName, *pModule, *py_results;
PyObject *fill_api;
#define PYVERIFY(exp) if ((exp) == 0) { fprintf(stderr, "%s[%d]: ", __FILE__, __LINE__); PyErr_Print(); exit(1); }
Py_SetProgramName(argv[0]); /* optional but recommended */
Py_Initialize();
PyRun_SimpleString(
"import sys;"
"sys.path.insert(0, '.')" );
PYVERIFY( pName = PyString_FromString("interface") )
PYVERIFY( pModule = PyImport_Import(pName) )
Py_DECREF(pName);
PYVERIFY( fill_api = PyObject_GetAttrString(pModule, "fill_api") )
// "k" = [unsigned long],
// see https://docs.python.org/2/c-api/arg.html#c.Py_BuildValue
PYVERIFY( py_results = PyObject_CallFunction(fill_api, "k", &api) )
assert(py_results == Py_None);
// Call Python function from C using cffi.
printf("sum: %f\n", api.add_numbers(12.3, 45.6));
// More complex example.
char buffer[20];
char * result = api.dump_buffer(buffer, sizeof buffer);
assert(result != 0);
printf("buffer: %s\n", result);
// Let Python perform garbage collection on result now.
rc = api.release_object(result);
assert(rc == 0);
// Close Python interpreter.
Py_Finalize();
return 0;
}
interface.py
import cffi
import sys
import traceback
ffi = cffi.FFI()
ffi.cdef(file('interface.h').read())
# Hold references to objects to prevent garbage collection.
noGCDict = {}
# Add two numbers.
# This function was copied from the PyPy example.
@ffi.callback("double (double, double)")
def add_numbers(x, y):
return x + y
# Convert input buffer to repr(buffer).
@ffi.callback("char *(char*, int)")
def dump_buffer(buffer, buffer_len):
try:
# First attempt to access data in buffer.
# Using the ffi/lib objects:
# http://cffi.readthedocs.org/en/latest/using.html#using-the-ffi-lib-objects
# One char at time, Looks inefficient.
#data = ''.join([buffer[i] for i in xrange(buffer_len)])
# Second attempt.
# FFI Interface:
# http://cffi.readthedocs.org/en/latest/using.html#ffi-interface
# Works but doc says "str() gives inconsistent results".
#data = str( ffi.buffer(buffer, buffer_len) )
# Convert C buffer to Python str.
# Doc says [:] is recommended instead of str().
data = ffi.buffer(buffer, buffer_len)[:]
# The goal is to return repr(data)
# but it has to be converted to a C buffer.
result = ffi.new('char []', repr(data))
# Save reference to data so it's not freed until released by C program.
noGCDict[ffi.addressof(result)] = result
return result
except:
print >>sys.stderr, traceback.format_exc()
return ffi.NULL
# Release object so that Python can reclaim the memory.
@ffi.callback("int (char*)")
def release_object(ptr):
try:
del noGCDict[ptr]
return 0
except:
print >>sys.stderr, traceback.format_exc()
return 1
def fill_api(ptr):
global api
api = ffi.cast("struct API*", ptr)
api.add_numbers = add_numbers
api.dump_buffer = dump_buffer
api.release_object = release_object
Übersetzen:
gcc -o test_cffi test_cffi.c -I/home/jmudd/pgsql-native/Python-2.7.10.install/include/python2.7 -L/home/jmudd/pgsql-native/Python-2.7.10.install/lib -lpython2.7
Ausführen:
$ test_cffi
sum: 57.900000
buffer: 'T\x9e\x04\x08\xa8\x93\xff\xbf]\x86\x04\x08\x00\x00\x00\x00\x00\x00\x00\x00'
$