2010-02-10 9 views
2

Je me bats un peu avec l'API Python C. J'appelle une méthode python pour faire du jeu IA à environ 60hz. Cela fonctionne le plus du temps mais chaque seconde ou alors l'appel à PyEval_CallObject aboutit à une valeur de retour NULL. Si je détecte correctement l'erreur et continue en boucle, tout va bien pour la prochaine seconde ou plus, après quoi l'erreur se produit à nouveau.PyEval_CallObject échouant en boucle de temps en temps

Je pense que je suis en train de faire quelque chose de mal avec le comptage ref mais je ne peux pas savoir ce qu'il est:

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

Oui, je suis d'importer le module chaque itération. Est-ce nécessaire? Si je stocke pAiModule comme global, j'obtiens un crash dur après environ une seconde.

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 
     { 

Je n'ai pas été en mesure de savoir comment extraire l'exception encore, que ce soit ... sans test pour tous les exception

 } 
    } 

Suis-je encore près de faire ce droit? Comme je l'ai dit, cela fonctionne surtout mais j'aimerais vraiment comprendre pourquoi je reçois une erreur.

Merci d'avance pour toute aide.

Répondre

4

Vous pouvez appeler PyImport_Import() aussi souvent que vous le souhaitez, mais vous continuerez à récupérer le même objet module. Python met en cache les importations. En outre, au lieu de créer une nouvelle chaîne Python et de fuir la référence (et donc l'objet), vous devez simplement utiliser PyImport_ImportModule(), ce qui prend un const char *.

PyImport_Import*() Renvoyer une nouvelle référence, cependant, vous devez appeler Py_DECREF() lorsque vous avez terminé. Stocker le module dans un global ne devrait pas être un problème, tant que vous possédez une référence à celui-ci (que vous faites, ici.)

Dans votre appel à PyEval_CallObject() vous ne vérifiez pas le résultat de Py_BuildValue() pour les erreurs, et vous n'appelez pas non plus Py_DECREF() lorsque vous en avez terminé, vous fuyez donc également cet objet.

Afin de convertir un flotteur Python à un double C, vous devriez probablement appeler PyFloat_AsDouble() au lieu de déblayage environ avec PyArg_Parse() (et garder à l'esprit pour tester des exceptions)

vers le bas pour la gestion des erreurs réelles: PyErr_ExceptionMatches() n'est utile que lorsque vous voulez réellement tester si l'exception correspond à quelque chose. Si vous voulez savoir si une exception s'est produite ou si vous obtenez l'objet d'exception réel, vous devez appeler PyErr_Occurred(). Il renvoie l'exception actuelle de type (pas l'objet d'exception réel) en tant que référence empruntée, ou NULL si aucun n'est défini. Si vous voulez juste imprimer un retraçage à stderr, PyErr_Print() et PyErr_Clear() sont ce que vous voulez utiliser. Pour une inspection plus fine de l'erreur réelle dans votre code, PyErr_Fetch() vous obtient l'objet d'exception actuel et le retraçage associé (il vous donne les mêmes informations que sys.exc_info() en code Python.) Tout bien considéré, vous voulez rarement obtenir profondément dans la gestion des exceptions dans le code C.

+0

+1 merci ... Je me suis dit que si je soulignais l'appel à Py_BuildValue, il prendrait soin de la référence de fuite, mais peut-être pas. Je vais voir ce que PyErr_Fetch me donne sur pyErr_Occurred –

+0

Hélas, C n'est pas ramassé garbage :) PyErr_Print() est assez pratique pour imprimer des exceptions, en particulier lors du débogage des cas comme celui-ci. –

+0

Ouais trop de temps en. NET-terrain pour moi. Ok, c'est bizarre. J'ai ajouté "sys.stdout = open ('CONOUT $', 'wt')" à mon script python (pour sortir en console ... je suis dans Win32) et tout a fonctionné comme par magie. Bizarre. –