2011-12-08 14 views
9

Im nuevo en la API de Python/C ... Estoy tratando de agregar una nueva funcionalidad a mi programa C, donde puedo incrustar Python en ella y ampliar al mismo tiempo la funcionalidad para que El intérprete incorporado puede ejecutar un script que interactuará con un módulo de extensión de python escrito como parte de mi programa C. Mi programa C no tiene variables globales. Me gustaría mantener las cosas de esta manera; Al mismo tiempo, para exponer la funcionalidad C a python, parece que la función C extendida necesita acceso a variables globales para acceder al estado del programa. ¿Cómo puedo evitar esto?Pasando un puntero C con la API de Python/C

p. Ej. Aquí es cómo planeo incorporar en PYINTERFACE_Initialize principal se llama desde

void PYINTERFACE_Initialize(State *ptr, FILE *scriptFile, const char* scriptFileName) 
{ 
    Py_Initialize(); 
    PyObject *module = Py_InitModule("CInterface", CInterfaceMethods); 
    if (PyRun_SimpleFileEx(scriptFile, scriptFileName, 1) != 0) 
    { 
     printf("PYINTERFACE script execution failed!\n"); 
    } 
    **//ADD State *ptr TO module**  
} 

aquí es la función ampliada:

static PyObject* 
CInterface_GetStateInfo(PyObject *self, PyObject *args) 
{ 
    const char *printStr; 
    double stateInfo; 
    State *ptr; 

    if(!PyArg_ParseTuple(args, "s", &printStr)) 
    { 
     return NULL; 
    } 
    printf("Received %s\n", printStr); 

    **//RETRIEVE State *ptr FROM self**  

    stateInfo = ptr->info; 
    return Py_BuildValue("d", currentTime); 
} 

Es esta la forma más limpia para pasar el Estado * ptr alrededor? Ciertamente no veo la necesidad de exponer el estado interno a Python. He pensado en usar cápsulas, pero no parece ser la intención de las cápsulas para apoyar este tipo de comportamiento.

¡Gracias de antemano! V

Respuesta

5

Suponiendo que desea cierta compatibilidad hacia atrás, que desea utilizar PyCObjects: http://docs.python.org/c-api/cobject.html

Si sólo desea utilizar Python 2.7, se puede utilizar una PyCapsule: http://docs.python.org/c-api/capsule.html

Básicamente, el PyCObject cosas convierten tu puntero opaco en un PyObject que puedes pasar dentro de python y cuando vuelves a C con uno, puedes desenvolverlo y usarlo.

PyCapsules son bastante similares, excepto que añaden algunas características, la principal es que le permiten almacenar un nombre en la cápsula para proporcionar un nivel de seguridad de tipo.

En su caso específico, donde se agrega el puntero, vas a querer hacer esto (código de comprobación de errores y la destrucción eliminado):

PyObject *pystate = PyCObject_FromVoidPtr(State, NULL); 
PyObject *dict = PyModule_GetDict(module); 
PyDict_SetItemString(dict, "CStateObject", pystate); 

# To retrieve it from self (assuming that it is an object) 
PyObject *pystate = PyObject_GetAttrString(self, "CStateObject"); 
State *state = (State *)PyCObject_AsVoidPtr(pystate); 
10

cápsulas son punteros void básicamente pitón-opaco que pueda pasar o asociar con módulos. Ellos son "el camino" para resolver su problema.

Aquí hay un ejemplo que usa una instancia x que no tiene que ser estática. Primero conecte el puntero a su módulo de algo como esto (comprobación de errores eliminado) ...

// wrap the methods to be exposed to python in a module 
// i.e. this is a list of method descriptions for the module 
static PyMethodDef InitializeTurkeyMethods[] = { 

    // this block describes one method.. turkey.do_something() 
    {"do_something", 
    turkey_do_something, // fn pointer to wrap (defined below) 
    METH_VARARGS, 
    "do something .. return an int."}, 

    {NULL, NULL, 0, NULL} // sentinel. 
}; 


int init(X * x) { 

    // initialize embedded python scripting .. 
    // (this method a no-op on second or later calls). 
    Py_Initialize(); 

    // initialize the turkey python module 
    PyObject * module = Py_InitModule("turkey", InitializeTurkeyMethods); 

    // Create a capsule containing the x pointer 
    PyObject * c_api_object = PyCapsule_New((void *)x, "turkey._X_C_API", NULL); 

    // and add it to the module 
    PyModule_AddObject(module, "_X_C_API", c_api_object); 
} 

Luego, en la función que desea exponer a Pitón con el fin de conseguir que el puntero X volver haces algo como esto (este en realidad tiene que pasar antes de empezar a referirse a ella en el código anterior):

static PyObject* turkey_do_something(PyObject *self, PyObject *args) {  

    if(!PyArg_ParseTuple(args, ":turkey_do_something")) 
     return NULL; 

    // get the x pointer back from the capsule 
    X * x = (X*)PyCapsule_Import("turkey._X_C_API", 0);  

    // call some fn on x 
    return Py_BuildValue("i", x->some_fn_that_returns_an_int()); 
} 

aquí "turkey._X_C_API" es sólo un nombre para algunas comprobaciones de tipo agregado - poner un poco de nombre significativo aquí para su aplicación. Turquía es un nombre de módulo de demostración que inventé en ese momento.

Ahora el supuesto de que, y dependiendo de cómo, que ha exportado el fn turkey_do_something al llamar Py_InitModule() se puede llamar a esto como esto de un script en Python:

import turkey 

print turkey.do_something() 

Comprobar esto: http://docs.python.org/2/c-api/arg.html de cómo formatee las tuplas y esto .. http://docs.python.org/3.1/c-api/capsule.html para el doco en cápsulas

Cuestiones relacionadas