Pergunta

Eu estou lutando um pouco com a API do Python C. Eu estou chamando um método python para fazer algum jogo AI em 60Hz aproximadamente. Ele funciona mais do tempo, mas a cada segundo ou assim a chamada para resultados PyEval_CallObject em um valor NULL retorno. Se eu detectar corretamente o erro e continuar loop, tudo está bem para a próxima segunda ou assim, após o que o erro ocorre novamente.

Eu suspeito que eu estou fazendo algo de errado com contagem ref mas eu não consigo descobrir o que é:

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

Sim, eu estou importando o módulo de cada iteração. Isso é necessário? Se eu armazenar pAiModule como global, eu recebo uma falha do disco depois de cerca de um segundo.

    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
       {

Eu não tenho sido capaz de descobrir como extrair a exceção, no entanto, quer ... sem testar para todas exceção

       }
    }

Am I nem perto de fazer isso certo? Como eu disse, que na sua maioria funciona, mas eu realmente gostaria de entender por que eu estou recebendo um erro.

Agradecemos antecipadamente por qualquer ajuda.

Foi útil?

Solução

Você pode chamar PyImport_Import() tão frequentemente como você gosta, mas você só vai continuar recebendo o mesmo módulo de objeto de volta. Python armazena em cache as importações. Além disso, em vez de criar uma nova string Python e vazamento de referência (e, portanto, o objeto), você deve apenas usar PyImport_ImportModule(), que leva um const char *.

PyImport_Import*() retornar uma nova referência, porém, você deve chamar Py_DECREF() sobre ele quando estiver pronto. Armazenar o módulo em um mundial não deve ser um problema, desde que você possui uma referência a ele (que você faz, aqui.)

Em sua chamada para PyEval_CallObject() você não está verificando o resultado de Py_BuildValue() para erros, e você também não está chamando Py_DECREF() quando você está feito com ele, então você está vazando esse objeto também.

Para converter um float Python para um C dupla, você provavelmente deve apenas chamar PyFloat_AsDouble() vez de mucking com PyArg_Parse() (e lembre-se de teste para exceções)

Para Baixo para a manipulação de erro real: PyErr_ExceptionMatches() só é útil quando você realmente quiser testar se a exceção corresponde alguma coisa. Se você quer saber se ocorreu uma exceção, ou obter o objeto de exceção real, PyErr_Occurred() é o que você deve chamar. Ele retorna a exceção atual tipo (não o objeto de exceção real) como referência emprestado, ou NULL se nenhum está definido. Se você quiser apenas imprimir um rastreamento para stderr, PyErr_Print() e PyErr_Clear() é o que você deseja usar. Para mais inspeção de grão fino do erro real em seu código, PyErr_Fetch() você recebe o objeto de exceção atual eo rastreamento associado a ele (que você recebe a mesma informação que sys.exc_info() em código Python.) Todas as coisas consideradas raramente você quiser obter esse profundamente no manuseamento excepção em código C.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top