Do tej pory koncentrowaliśmy się na umożliwianiu wywołania z Pythona funkcji w C. Użyteczna jest też możliwość odwrotna: wywoływanie funkcji Pythona z C. Ma to zastosowanie szczególnie w przypadku bibliotek obsługujących tak zwane funkcje zwrotne ("callback"). Jeśli interfejs w C wykorzystuje mechanizm funkcji zwrotnych odpowiadający mu interfejs w Pythonie często musi również udostępniać programistom taki mechanizm. Implementacja będzie wymagać wywoływanie funkcji zwrotnych Pythona z funkcji zwrotnych w C. Można również wyobrazić sobie i inne zastosowania.
Na szczęście bez problemu można wywołać interpreter Pythona w sposób rekurencyjny jak również istnieje standardowy interfejs wywoływania funkcji Pythona (nie będziemy jednak zajmować się zagadnieniem wywoływania parsera Pythona z określonym napisem na wejściu -- zainteresowanych odsyłamy do kodu źródłowego: opcja -c wywołania interpretera Pythona w pliku Python/pythonmain.c).
Wywołanie funkcji języka Python jest łatwe. Najpierw program w Pythonie musi w jakiś sposób przekazać do programu w C obiekt fukcji. Aby tego dokonać należy zastosować funkcję (lub inny rodzaj interfejsu). Kiedy taka funkcja zostanie wywołana przechowujemy wskaźnik do wspomnianego obiektu funkcji w zmiennej globalnej albo gdziekolwiek wyda nam się stosowne (nie należy zapominać o zwiększeniu licznika odwołań -- wywołujemy Py_INCREF() z argumentem będącym naszym obiektem). Dla przykładu następująca funkcja może być częścią definicji modułu:
static PyObject *my_callback = NULL;
static PyObject *
my_set_callback(PyObject *dummy, PyObject *args)
{
PyObject *result = NULL;
PyObject *temp;
if (PyArg_ParseTuple(args, "O:set_callback", &temp)) {
if (!PyCallable_Check(temp)) {
PyErr_SetString(PyExc_TypeError, "parameter must be callable");
return NULL;
}
Py_XINCREF(temp); /* Add a reference to new callback */
Py_XDECREF(my_callback); /* Dispose of previous callback */
my_callback = temp; /* Remember new callback */
/* Boilerplate to return "None" */
Py_INCREF(Py_None);
result = Py_None;
}
return result;
}
Ta funkcja musi zostać zarejestrowana w interpreterze z wykorzystaniem flagi METH_VARARGS. Procedurę tę opisuje rozdział 1.4: "Tablica metod modułu i funkcje inicjujące". Funkcja PyArg_ParseTuple() i jej argumenty są opisane w rozdziale 1.7: "Wydobywanie parametrów w funkcjach rozszerzeń".
Makra Py_XINCREF() oraz Py_XDECREF() zwiększają/zmniejszają licznik odwołań obiektu i są bezpieczne w przypadku obecności wskaźników NULL (należy zwrócić uwagę, że temp nie będzie miał wartości NULL w naszym przykładzie). Więcej informacji na ten temat można znaleźć w rozdziale 1.10 "Liczniki odwołań".
Później, gdy trzeba wywołać funkcję, wywołujemy funkcję w C PyEval_CallObject(). Funkcja ta oczekuje dwóch argumentów będących wskaźnikami do obiektów Pythona: obiektu funkcji Pythona i listy argumentów. Lista argumentów musi być zawsze obiektem krotki, której długość jest równa liczbie argumentów. Aby wywołać funkcję Pythona bez argumentów przekazujemy pustą krotkę. Aby wywołać ją z jednym argumentem przekazujemy krotkę jednoelementową. Funkcja Py_BuildValue() zwraca krotkę jeśli jej napis formatujący zawiera zero lub więcej kodów formatujących pomiędzy nawiasami, na przykład:
int arg;
PyObject *arglist;
PyObject *result;
...
arg = 123;
...
/* Time to call the callback */
arglist = Py_BuildValue("(i)", arg);
result = PyEval_CallObject(my_callback, arglist);
Py_DECREF(arglist);
PyEval_CallObject() zwraca wskaźnik na obiekt Pythona: to jest wartość zwrócona z funkcji w Pythonie. PyEval_CallObject() jest "neutralny" z punktu widzenia licznika odwołań w stosunku do swoich argumentów. W podanym przykładzie została utworzonanowa krotka dla obsłużenia listy argumentów. Jej licznik odwołań zostaje zmniejszony przy pomocy Py_DECREF() natychmiast po wyjściu z funkcji.
Wartość zwrócona z PyEval_CallObject() jest "nowa": jest to albo zupełnie nowy obiekt, albo istniejący obiekt którego licznik odwołań został zwiększony. Dlatego, o ile nie mamy zamiaru zachować jej w zmiennej globalnej, należy w jakiś sposób zmniejszyć licznik odwołań obiektu (za pomocą makra Py_DECREF()), nawet jeśli nie jesteśmy zainteresowani tym wynikiem (a wręcz szczególnie w takiej sytuacji!).
Zanim jednak tego dokonamy należy sprawdzić, czy wartością wynikową funkcji nie jest NULL. Jeśli taka sytuacja ma miejsce, oznacza to, że funkcja w Pythonie zakończyła się wywołując wyjątek. Jeśli kod w C, który wywołał PyEval_CallObject() jest wywoływany z Pythona, powinien teraz zwrócić wartość wskazującą na błąd aby interpreter Pythona mógł wyświetlić stos wywołań lub też aby kod w Pythonie mógł obsłużyć wywołany wyjątek. Jeśli takie zachowanie jest niemożliwe lub niepożądane należy wyczyścić informację o wyjątku za pomocą PyErr_Clear(). Na przykład:
if (result == NULL)
return NULL; /* Pass error back */
...use result...
Py_DECREF(result);
W zależności od pożądanego interfejsu do funkcji zwrotnej w Pythonie może wystąpić potrzeba podania funkcji PyEval_CallObject() listy argumentów. W niektórych przypadkach listę argumentów przekazuje również program w Pythonie za pośrednictwem tego samego interfejsu który posłużył do utworzenia funkcji zwrotnej. Lista argumentów może zostać zapisana i wykorzystana tak samo jak obiekt funkcji. W innych przypadkach być może będzie trzeba utworzyć nową krotkę, którą przekażemy funkcji jako listę argumentów. Najprostszym sposobem na to jest wywołanie funkcji Py_BuildValue(). Na przykład jeśli chcemy przekazać kod zdarzenia, możemy posłużyć się następującym kodem:
PyObject *arglist;
...
arglist = Py_BuildValue("(l)", eventcode);
result = PyEval_CallObject(my_callback, arglist);
Py_DECREF(arglist);
if (result == NULL)
return NULL; /* Pass error back */
/* Here maybe use the result */
Py_DECREF(result);
Warto zwrócić uwagę na umieszczenie "Py_DECREF(arglist)" natychmiast po wywołaniu funkcji, przed sprawdzeniem błędu! Na uwagę zasługuje też fakt, że kod nie jest całkiem kompletny: Py_BuildValue() może spowodować sytuację braku pamięci i ta sytuacja też powinna zostać obsłużona.
Zajrzyj do Informacji na temat tej publikacji... aby pomóc w jej rozwoju.