Сбой PyEval_CallObject в цикле - PullRequest
       15

Сбой PyEval_CallObject в цикле

2 голосов
/ 10 февраля 2010

Я немного борюсь с Python C API. Я вызываю метод python для создания игрового ИИ на частоте около 60 Гц. Он работает большую часть времени, но каждую секунду или около того вызов PyEval_CallObject приводит к возвращаемому значению NULL. Если я правильно обнаружу ошибку и продолжу цикл, все будет хорошо в течение следующей секунды или около того, после чего ошибка повторяется.

Я подозреваю, что делаю что-то не так с подсчетом ссылок, но не могу понять, что это:

int script_do_ai(struct game_data_t* gd)
{

    PyObject *pAiModule, *pResult;

    float result=0.0;
    pResult = NULL;

    pAiModule = PyImport_Import(PyString_FromString("ai_script"));

Да, я импортирую модуль каждую итерацию. Это необходимо? Если я сохраню pAiModule как глобальный, через секунду произойдет крутой сбой.

    pResult = PyEval_CallObject(PyObject_GetAttrString(pAiModule, "do_ai"),
                               Py_BuildValue("f", gd->important_float))  
    if (pResult != NULL)
    {       
        PyArg_Parse(pResult, "f", &result);
        Py_DECREF(pResult);
        ConquerEnemies(result);  //you get the idea
    }
    else  //this happens every 75 or so iterations thru the loop
    {
       if (PyErr_ExceptionMatches(PyExc_SomeException))  //? not sure what to do here
       {

Я еще не смог выяснить, как извлечь исключение, либо ... без проверки на каждое исключение

       }
    }

Я даже близок к тому, чтобы сделать это правильно? Как я уже сказал, это в основном работает, но мне бы очень хотелось понять, почему я получаю ошибку.

Заранее благодарю за любую помощь.

1 Ответ

4 голосов
/ 10 февраля 2010

Вы можете вызывать PyImport_Import() столько раз, сколько захотите, но вы просто продолжите возвращать один и тот же объект модуля. Импорт кэшей Python. Кроме того, вместо создания новой строки Python и утечки ссылки (и, следовательно, объекта), вы должны просто использовать PyImport_ImportModule(), что занимает const char *.

PyImport_Import*() вернуть новую ссылку, однако, вы должны вызвать Py_DECREF(), когда закончите. Хранение модуля в глобальном порядке не должно быть проблемой, если у вас есть ссылка на него (что вы делаете здесь).

При вызове PyEval_CallObject() вы не проверяете результат Py_BuildValue() на наличие ошибок, и вы также не вызываете Py_DECREF(), когда закончите с ним, поэтому вы пропускаете этот объект как хорошо.

Чтобы преобразовать число с плавающей точкой Python в двойник C, вам, вероятно, следует просто позвонить PyFloat_AsDouble(), а не копаться с PyArg_Parse() (и иметь в виду, что нужно проверять исключения)

Вплоть до фактической обработки ошибок: PyErr_ExceptionMatches() полезен только тогда, когда вы действительно хотите проверить, соответствует ли исключение чему-либо. Если вы хотите узнать, произошло ли исключение, или получить фактический объект исключения, PyErr_Occurred() - это то, что вы должны вызывать. Возвращает текущее исключение тип (не фактический объект исключения) в качестве заимствованной ссылки или NULL, если ни одно не установлено. Если вы хотите просто напечатать трассировку в stderr, PyErr_Print() и PyErr_Clear() - это то, что вы хотите использовать. Для более детальной проверки фактической ошибки в вашем коде PyErr_Fetch() возвращает вам текущий объект исключения и трассировку, связанную с ним (он получает ту же информацию, что и sys.exc_info() в коде Python). Все вещи считаются вами редко хочу глубоко вникнуть в обработку исключений в C-коде.

...