2011-12-08 13 views
9

Jestem nowym interfejsem API Python/C ... Próbuję dodać nowe funkcje do mojego programu C, w którym mogę osadzić w nim pythona i jednocześnie rozszerzyć jego funkcjonalność tak, aby wbudowany interpreter może wykonać skrypt, który będzie współdziałać z rozszerzającym się modułem pythona napisanym w ramach mojego programu C. Mój program C nie ma zmiennych globalnych. Chciałbym zachować rzeczy w ten sposób; W tym samym czasie, aby udostępnić funkcje P pythonowi, wydaje się, że rozszerzona funkcja C potrzebuje dostępu do zmiennych globalnych, aby uzyskać dostęp do stanu programu. Jak sobie z tym poradzić?Przekazywanie wskaźnika C za pomocą interfejsu API Python/C

np. Oto jak ja planuję na osadzanie gdzie PYINTERFACE_Initialize jest wywoływana z głównego

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**  
} 

Oto rozszerzona funkcja:

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

Jest to najczystszy sposób przekazać państwu * ptr wokół? Na pewno nie widzę potrzeby ujawnienia wewnętrznego stanu pytonowi. Zastanawiałem się nad użyciem kapsułek, ale nie wydaje się, aby kapsułki miały na celu wspieranie tego rodzaju zachowań.

Z góry dziękuję! V

Odpowiedz

5

Zakładając, że chcesz trochę wsteczną kompatybilność, którego chcesz użyć PyCObjects: http://docs.python.org/c-api/cobject.html

Jeśli chcesz tylko używać Pythona 2.7, można użyć PyCapsule: http://docs.python.org/c-api/capsule.html

Zasadniczo PyCObject rzeczy zamienia twój nieprzezroczysty wskaźnik na PyObject, który możesz przekazać w obrębie Pythona, a gdy wrócisz do C z jednym, możesz go rozpakować i użyć.

PyCapsules są bardzo podobne, z wyjątkiem dodania kilku cech, z których głównym jest to, że pozwalają na przechowywanie nazwy w kapsule, aby zapewnić poziom bezpieczeństwa typu.

W określonych przypadkach, gdzie można dodać wskaźnik, musisz po prostu chcesz zrobić (sprawdzanie błędów i kod zniszczenie usunięte):

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

Kapsułki są zasadniczo python-nieprzejrzysty void wskaźniki, które można omijać lub kojarzyć z modułami. Są "drogą" do rozwiązania twojego problemu.

Oto przykład, który wykorzystuje instancję x, która nie musi być statyczna. Najpierw dołączyć wskaźnik do modułu coś takiego (sprawdzanie błędów usunięte) ...

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

Następnie w funkcji, którą chcesz wystawiać na pytona w celu uzyskania że x wskaźnik z powrotem zrobić coś takiego (ta rzeczywiście musi przejść przed rozpoczęciem odnosząc się do niego w kodzie powyżej):

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

here „turkey._X_C_API” to tylko nazwa dla pewnego typu kontroli dodanej - umieścić jakąś znaczącą nazwę tu dla swojej aplikacji. Turcja to nazwa modułu demo, którą właśnie wymyśliłem.

Teraz zakładając, że, w zależności od sposobu, masz eksportowane fn turkey_do_something Dzwoniąc Py_InitModule() można nazwać tak ze skryptu Pythona:

import turkey 

print turkey.do_something() 

Sprawdź to: http://docs.python.org/2/c-api/arg.html do jak sformatuj krotki i to .. http://docs.python.org/3.1/c-api/capsule.html dla doco na kapsułkach

Powiązane problemy