Pasando un puntero C alrededor con la API de Python / C

Soy nuevo en la API de Python / C … Intento agregar una nueva funcionalidad a mi progtwig C, en el que puedo incrustar python y simultáneamente extender la funcionalidad para que el intérprete incrustado pueda ejecutar un script que interactúe con un módulo Python extendido escrito como parte de mi progtwig C. Mi progtwig C no tiene variables globales. Me gustaría mantener las cosas de esta manera; Al mismo tiempo, para exponer la funcionalidad de C a python, parece que la función de C extendida al menos necesita acceso a variables globales para acceder al estado del progtwig. ¿Cómo puedo evitar esto?

Por ejemplo, aquí es cómo planeo incrustar desde donde se llama a PYINTERFACE_Initialize

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í está la función extendida:

 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 de 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 apoyar este tipo de comportamiento.

¡Gracias por adelantado! V

    Las cápsulas son básicamente punteros vacíos opacos a python que se pueden 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 adjunte el puntero a su módulo algo como esto (comprobación de errores eliminada) …

     // wrap the methods to be exposed to python in a module // ie 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 Python para recuperar ese puntero X, debe hacer algo como esto (esto debe irse antes de comenzar a consultar 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 solo un nombre para algunas comprobaciones de tipos agregadas: ponga un nombre significativo aquí para su aplicación. Turquía es el nombre del módulo de demostración que inventé en ese momento.

    Ahora suponiendo que, y dependiendo de cómo, haya exportado el turkey_do_something fn al llamar a Py_InitModule (), puede llamar esto así desde un script de python:

     import turkey print turkey.do_something() 

    Verifique esto: http://docs.python.org/2/c-api/arg.html para saber cómo formatear las tuplas y esto .. http://docs.python.org/3.1/c-api/capsule. html para el doco en capsulas

    Suponiendo que desea cierta compatibilidad con versiones anteriores, desea utilizar PyCObjects: http://docs.python.org/c-api/cobject.html

    Si solo desea usar Python 3, puede usar una PyCapsule: http://docs.python.org/c-api/capsule.html

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

    Las PyCapsules son bastante similares, excepto que agregan algunas características, la principal es que te permiten almacenar múltiples punteros en la Cápsula, por lo que es básicamente un dict.

    En su caso específico, donde agrega el puntero, solo querrá hacer esto (se eliminaron los códigos de error y de comprobación de errores):

     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);