Вопрос

Я немного борюсь с API Python C.Я вызываю метод Python для выполнения игрового AI на частоте около 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
       {

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

       }
    }

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

Спасибо заранее за любую помощь.

Это было полезно?

Решение

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top