2010-02-10 3 views
2

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

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

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 
     { 

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

 } 
    } 

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

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

ответ

4

Вы можете позвонить по телефону 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-коде.

+0

+1 спасибо ... Я подумал, что если бы я включил вызов Py_BuildValue, он позаботился бы о протекающей ссылке, но, возможно, нет. Я посмотрю, что PyErr_Fetch дает мне на pyErr_Occured –

+0

Увы, C не сбор мусора :) PyErr_Print() довольно удобен для печати исключений, особенно при отладке подобных случаев. –

+0

Да, слишком много времени в .NET-land для меня. Хорошо, странно. Я добавил «sys.stdout = open ('CONOUT $', 'wt')" в свой скрипт python (для вывода на консоль ... я нахожусь в Win32), и все началось магически. Bizarre. –

Смежные вопросы