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!

War es hilfreich?

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.

<-> C-Kommunikation, aber es sollte funktionieren ziemlich gut

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'
$ 
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top