Pregunta

Quiero extender una gran C proyecto con una nueva funcionalidad, pero lo que realmente quiero escribir en Python.Básicamente, quiero llamar a código Python de código C.Sin embargo, Python->C wrappers como TRAGO permitir el CONTRARIO, que es la escritura de los módulos C y llamadas C de Python.

Estoy considerando un enfoque con el IPC o RPC (que no me importa tener varios procesos);es decir, tener mi pura-Python componente ejecuta en un proceso independiente (en la misma máquina) y tener a mi C proyecto de comunicarse con ella por la escritura/lectura de una toma de corriente (o unix de la tubería).mi python componente puede leer/escribir en el socket para comunicarse.Es que un enfoque razonable?¿Hay algo mejor?Como algunos especiales mecanismo de RPC?

Gracias por la respuesta tan lejos - sin embargo, me gustaría centrarme en IPC-enfoques basados en los ya quiero tener mi programa en Python en un proceso independiente como mi programa en C.No quiero incrustar un intérprete de Python.Gracias!

¿Fue útil?

Solución

Recomiendo la los enfoques que aquí se detallan.Comienza explicando cómo ejecutar cadenas de código de Python, luego a partir de ahí los detalles de cómo configurar un entorno de Python para interactuar con el programa en C, llamada Python las funciones de su código C, manipular los objetos de Python a partir de su código en C, etc.

EDITAR:Si realmente quieres ir a la ruta de la cia, entonces usted querrá usar la estructura del módulo de o mejor aún, protlib.La mayoría de la comunicación entre Python y C proceso gira en torno pasando estructuras de ida y vuelta, ya sea a través de un socket o a través de memoria compartida.

Recomiendo la creación de un Command estructura con los campos y los códigos para representar los comandos y sus argumentos.No te puedo dar mucho más el asesoramiento específico y sin saber más acerca de lo que quiere lograr, pero en general recomiendo el protlib de la biblioteca, ya que es lo que utilizan para comunicarse entre C y los programas de Python (descargo de responsabilidad:Yo soy el autor de protlib).

Otros consejos

Consulte el capítulo correspondiente en el manual: http://docs.python.org/extending/

Esencialmente, usted tendrá que integrar el intérprete de python en su programa.

Han considerado que acaba de envolver sus python aplicación en un script de shell y invocarlo desde su aplicación de C?

No es la solución más elegante, pero es muy simple.

No he usado un IPC enfoque para Python<->C de la comunicación, pero debería funcionar bastante bien.Me gustaría tener el programa en C hacer un estándar de la horquilla-exec y el uso redirigido stdin y stdout en el niño, para el proceso de comunicación.Un buen texto basado en la comunicación hará que sea muy fácil para desarrollar y probar el programa en Python.

Si yo había decidido ir con el IPC, probablemente me derrochar con XML-RPC -- cruz-plataforma, permite poner fácilmente el Python proyecto de servidor en un nodo diferente más tarde si lo desea, tiene muchas implementaciones (ver aquí para muchos, incluyendo C y Python, y aquí por el simple XML-RPC server que parte de la biblioteca estándar de Python, no como altamente escalable como otros enfoques, pero probablemente muy bien y conveniente para el caso de uso).

Puede que no sea perfecto IPC enfoque para todos los casos (o incluso un perfecto RPC uno, por todos los medios!), pero la comodidad, flexibilidad, robustez, y una amplia gama de implementaciones superan un montón de defectos de menor importancia, en mi opinión.

al parecer, Python necesita para ser capaz de compilar win32 dll, se va a resolver el problema

De tal manera que la conversión de código de c# para win32 dll va a hacer que sea utilizable por cualquier herramienta de desarrollo

Esto parece bastante agradable http://thrift.apache.org/, hay incluso un libro sobre él.

Detalles:

El Apache Thrift framework de software, para escalable el lenguaje de la cruz servicios de desarrollo, combina un paquete de software con un código de generación de el motor de la creación de servicios que funcionan de manera eficiente y sin problemas entre C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cacao, JavaScript, Node.js, Smalltalk, y OCaml Delphi y otros idiomas.

He usado el "estándar" enfoque de La incrustación de Python en Otra Aplicación.Pero es complicado y tedioso.Cada nueva función en Python es doloroso a implementar.

Vi un ejemplo de Llamar PyPy de C.Utiliza CFFI para simplificar la interfaz, pero requiere de PyPy, no de Python.Leer y comprender este ejemplo, primero, al menos en un nivel alto.

He modificado la C/PyPy ejemplo para trabajar con Python.He aquí cómo llamar a Python desde C usando CFFI.

Mi ejemplo es más complicado porque he implementado tres funciones en Python en lugar de uno.Quería cubrir otros aspectos de la transferencia de datos de ida y vuelta.

La parte complicada es ahora aislado a la aprobación de la dirección de api para Python.Que sólo debe ser aplicado de una sola vez.Después de que es fácil añadir nuevas funciones en Python.

la interfaz.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

Compilar:

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

Ejecutar:

$ 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'
$ 
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top