Python/C - [[],[],[],...] an Python übergeben

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Benutzeravatar
windows97
User
Beiträge: 24
Registriert: Freitag 10. März 2006, 13:34

Python/C - [[],[],[],...] an Python übergeben

Beitragvon windows97 » Montag 13. März 2006, 14:50

Guten Tag allerseits!
Ich hab mal wieder ein Prob mit der C/API.
Ich würde gern mit

Code: Alles auswählen

PyObject_CallMethod( pInstance, "setARB", "XXX", YYY)

die Methode

Code: Alles auswählen

def setARB(self, ARBGraph=[[1, 11.20, 15], [2, 6, 15], [3, 6, 50], [5, 12, 1], [6, 0, 0]])

aufrufen.

ARBGraph habe ich mal definiert, damit ihr seht, wie die Übergabe formatiert sein muss.

Code: Alles auswählen

[int, float, int]


Mein Problem liegt zum einen im Aufbau der Liste in C++ (YYY oben).
Baue ich mir da am Besten eine verkettete Liste, die wiederum Elemente enthält. -> Oder gibt es da bessere Lösungen?
Zum Anderen weiß ich nicht, wie ich XXX dann formatiere. Eigentlich müsste es doch "[[ifi]]" sein, gell?

Ich sag schonmal "Danke"!
Benutzeravatar
windows97
User
Beiträge: 24
Registriert: Freitag 10. März 2006, 13:34

Beitragvon windows97 » Dienstag 14. März 2006, 12:38

Nach einer kurzen Nacht und viel Kaffee dann hier die Lösung für evtl. Interessierte.

Code: Alles auswählen

int powersupply::setARB( ARB_Point Graph[], int ARB_Length )
{
   PyObject *response = NULL;
   PyObject *PyArgs, *PyARB_Point;
   PyArgs = PyList_New(0);
   // Append the Port-States to the PythonList PyArgs
   for ( int i = 0; i < ARB_Length; i++ )
   {
      PyARB_Point = PyList_New(0);
      PyList_Append( PyARB_Point, Py_BuildValue ( "i", Graph[i].Position ));
      PyList_Append( PyARB_Point, Py_BuildValue ( "f", Graph[i].Voltage ));
      PyList_Append( PyARB_Point, Py_BuildValue ( "i", Graph[i].Time ));
      PyList_Append( PyArgs, PyARB_Point );
   }
   response = PyObject_CallMethodObjArgs( pInstance, Py_BuildValue( "s", "setARB" ), PyArgs, NULL);
   if ( response == NULL )
   {
      return ERROR_RETURN;
   }
   else
   {
      Py_DECREF( response );
      if ( PyArgs != NULL )      Py_DECREF( PyArgs );
      if ( PyARB_Point != NULL )   Py_DECREF( PyARB_Point );
      return 0;
   }
}


Gruß Daniel
Benutzeravatar
Joghurt
User
Beiträge: 877
Registriert: Dienstag 15. Februar 2005, 15:07

Beitragvon Joghurt » Dienstag 14. März 2006, 16:54

Statt Py_BuildValue wäre doch PyInt_FromLong bzw. PyFloat_FromDouble einfacher, oder?
modelnine
User
Beiträge: 670
Registriert: Sonntag 15. Januar 2006, 18:42
Wohnort: Celle
Kontaktdaten:

Beitragvon modelnine » Mittwoch 15. März 2006, 00:11

Auch hier gilt, was ich in einem anderen Thread schon mal gesagt hatte: alle Funktionen die Du bemühst geben einen Fehler-Indikator zurück; zum Beispiel kann es passieren dass Py_BuildValue den Wert aus welchen Gründen auch immer eben nicht bauen kann, oder aber PyList_Append einen Fehler wirft weil zum Beispiel der Speicher zu knapp ist.

Es ohne entsprechende Fehlerprüfung zu lassen produziert fahrlässigerweise Core-Dumps, die man durchaus verhindern könnte, indem man einfach:

Code: Alles auswählen

if( !( data = Py_BuildValue(...) ) )
    goto cleanup;
if( !PyList_Append(PyARB_Point,data) )
    goto cleanup;
data = NULL;
cleanup:
Py_XDECREF(data);
Py_XDECREF(PyARB_Point);


usw. in den Code einbaut. Gute Beispiele hierzu liefern die Python-Module, die aktiven Gebrauch von goto machen um an eine Fehlerbehandlungsstelle zu springen, in der im Endeffekt alle Referenzen freigegeben werden um die Routine nach einem Fehler sauber zu verlassen. So kriegt man nämlich dann den entsprechenden Traceback in Python selbst (zum Beispiel einen MemoryError), und sieht nicht

"Speicherzugriffsfehler, Programm beendet"

auf der Konsole.
--- Heiko.
Benutzeravatar
windows97
User
Beiträge: 24
Registriert: Freitag 10. März 2006, 13:34

Beitragvon windows97 » Mittwoch 15. März 2006, 08:02

Oh, eine goto-Anweisung in C?
--> Da steht jeder dritte Informatik-Prof kurz vorm Herzinfarkt. :lol:
Nichts gegen diese Personengruppe, aber ich hab schon etliche erlebt, die mit erhobenem Finger gepredigt haben, bloß keine GOTOs verwenden.

Danke für die Tipps ich werd sie später mal mit reinbringen. Und dann noch später posten.

Gruß Daniel
Benutzeravatar
windows97
User
Beiträge: 24
Registriert: Freitag 10. März 2006, 13:34

Beitragvon windows97 » Mittwoch 15. März 2006, 15:55

So und hier dann auch die Lösung die hoffentlich allen gefällt.
Ist natürlich etwas größer geworden. Ich hoffe ich habe jetzt alles beachtet.

Code: Alles auswählen

int powersupply::setARB( ARB_Point Graph[], int ARB_Length )
{
   PyObject *PyArgs = NULL, *PyARB_Point = NULL;
   PyObject *PyData = NULL, *PyResponse = NULL;
   PyARB_Point = PyList_New(0);
   PyArgs = PyList_New(0);
   int retValue = -1;
   // Append the Port-States to the PythonList PyArgs
   for ( int i = 0; i < ARB_Length; i++ )
   {
      PyARB_Point = PyList_New(0);
      PyData = PyInt_FromLong( Graph[i].Position );
      // Standard Error Handling
      if ( PyData != NULL )                           
      {
         if ( PyList_Append( PyARB_Point, PyData ) == -1 )
         {
            goto error_occured;
         }
      }
      else
      {
         goto error_occured;
      }
      PyData = PyFloat_FromDouble( Graph[i].Voltage );
      if ( PyData != NULL )
      {
         if ( PyList_Append( PyARB_Point, PyData ) == -1 )
         {
            goto error_occured;
         }
      }
      else
      {
         goto error_occured;
      }
      PyData = PyInt_FromLong( Graph[i].Time );
      if ( PyData != NULL )
      {
         if ( PyList_Append( PyARB_Point, PyData ) == -1 )
         {
            goto error_occured;
         }
      }
      else
      {
         goto error_occured;
      }
      if ( PyList_Append( PyArgs, PyARB_Point ) == -1 )
      {
         goto error_occured;
      }
   }
   PyResponse = PyObject_CallMethodObjArgs( pInstance, Py_BuildValue( "s", "setARB" ), PyArgs, NULL);
   if ( PyResponse == NULL )
   {
      goto error_occured;
   }
   else
   {
      retValue = 0;
   // goto jumps always here to handle the error exception
   error_occured:
      Py_XDECREF( PyResponse );
      Py_XDECREF( PyArgs );
      Py_XDECREF( PyARB_Point );
      Py_XDECREF( PyData );
      return retValue;
   }
}


Gruß Daniel
icepacker
User
Beiträge: 49
Registriert: Dienstag 15. November 2005, 18:48

Beitragvon icepacker » Mittwoch 15. März 2006, 19:55

na das ist jetzt aber echt übel, so viele goto's :shock:
mach doch eine funktion error_occured und lass die dann an den betreffenden
stellen aufrufen...
ansonsten siehts gut aus!
(muss mich auch mal an der kombi C/python versuchen :) )

lg eiswolf
ubuntu linux !!
modelnine
User
Beiträge: 670
Registriert: Sonntag 15. Januar 2006, 18:42
Wohnort: Celle
Kontaktdaten:

Beitragvon modelnine » Mittwoch 15. März 2006, 20:11

Das ist immer noch nicht korrekt. Du prüfst einige Aufrufe immer noch nicht, wie zum Beispiel PyList_New(), o.Ä.

Ich schreib heute abend wenn ich zu Hause bin mal eine Fassung die alle Fehler korrekt abfängt...

Und: eine Funktion aufzurufen ist hier nicht angebracht, das es ja nur darum geht die lokalen Variablen (also den Stack) der Funktion aufzuräumen wenn ein Fehler passiert.
--- Heiko.
icepacker
User
Beiträge: 49
Registriert: Dienstag 15. November 2005, 18:48

Beitragvon icepacker » Mittwoch 15. März 2006, 20:17

modelnine hat geschrieben:Und: eine Funktion aufzurufen ist hier nicht angebracht, das es ja nur darum geht die lokalen Variablen (also den Stack) der Funktion aufzuräumen wenn ein Fehler passiert.

sorry aber,
warum ist das jetzt ein argument gegen eine funktion?
ubuntu linux !!
modelnine
User
Beiträge: 670
Registriert: Sonntag 15. Januar 2006, 18:42
Wohnort: Celle
Kontaktdaten:

Beitragvon modelnine » Mittwoch 15. März 2006, 21:28

Das ist ein Argument eben keine Funktion einzusetzen, da die Funktion die Du aufrufst logischerweise nicht auf den Stack der aufrufenden Funktion (ausser durch ueble Hacks) zugreifen kann. Sprich: die Fehlerbehandlung, und im besonderen das aufraeumen des eigenen Muells, will man im Normalfall nicht weiterdelegieren an eine andere Funktion, sondern selbst erledigen.
--- Heiko.
Benutzeravatar
Joghurt
User
Beiträge: 877
Registriert: Dienstag 15. Februar 2005, 15:07

Beitragvon Joghurt » Mittwoch 15. März 2006, 21:37

Wenn du schon C++ nutzt, warum wirfst du nicht einfach eine Exception, wenn ein Fehler auftritt und fängst diese dann ab? Dann sparst du dir das goto
Benutzeravatar
windows97
User
Beiträge: 24
Registriert: Freitag 10. März 2006, 13:34

Beitragvon windows97 » Mittwoch 15. März 2006, 21:47

Joah, dass das übelst aussieht, weiß ich selber...
Hab erst überlegt, es mit einer INLINE-FKT zu realisieren, aber dann wärs wohl noch unübersichtlicher geworden (hab ich mir gedacht).

Throw-Catch kann ich auf jeden Fall versuchen. - Danke!

Das PyList_New() teste ich nicht, da hast Recht. - Man, nächstens wird alles in C geschrieben, dann gibts den Rotz net! :wink:

Ich bedanke mich aber schonmal für die rege Beteiligung! *thumpsup*
modelnine
User
Beiträge: 670
Registriert: Sonntag 15. Januar 2006, 18:42
Wohnort: Celle
Kontaktdaten:

Beitragvon modelnine » Mittwoch 15. März 2006, 21:52

In der folgenden Art und Weise sieht das Ding richtig und gut aus:

Code: Alles auswählen

int powersupply::setARB( ARB_Point Graph[], int ARB_Length )
{
    PyObject *PyArgs = NULL, *PyARB_Point = NULL;
    PyObject *PyData = NULL, *PyResponse = NULL;
    int retValue = -1;

    /* Argumentliste bauen. */
    if( !( PyArgs = PyList_New(0) ) ) goto end_error;

    // Append the Port-States to the PythonList PyArgs
    for ( int i = 0; i < ARB_Length; i++ )
    {
        if( !( PyARB_Point = PyTuple_New(0) ) ) goto end_error;

        /* Position in Tupel einbauen. */
        if( !( PyData = PyInt_FromLong( Graph[i].Position ) ) ) goto end_error;
        PyTuple_SET_ITEM(PyARB_Point,0,PyData);
        PyData = NULL;

        /* Spannung in Tupel einbauen. */
        if( !( PyData = PyFloat_FromDouble( Graph[i].Voltage ) ) ) goto end_error;
        PyTuple_SET_ITEM(PyARB_Point,1,PyData);
        PyData = NULL;

        /* Zeit in Tupel einbauen. */
        if( !( PyData = PyInt_FromLong( Graph[i].Time ) ) ) goto end_error;
        PyTuple_SET_ITEM(PyARB_Point,2,PyData);
        PyData = NULL;

        if ( PyList_Append( PyArgs, PyARB_Point ) ) goto end_error;
        Py_DECREF(PyARB_Point);
        PyARB_Point = NULL;
    }

    /* Funktion aufrufen. */
    if( !( PyData = Py_BuildValue( "s", "setARB" ) ) ) goto end_error;
    PyResponse = PyObject_CallMethodObjArgs( pInstance, PyData, PyArgs, NULL);
    if( PyResponse ) retValue = 0;

end_error:
    Py_XDECREF(PyResponse);
    Py_XDECREF(PyData);
    Py_XDECREF(PyARB_Point);
    Py_XDECREF(PyArgs);

    /* Fehler-Status zurücksetzen... */
    PyErr_Clear();

    return retValue;
}


Kurz zur Erklärung: alle Python C-API Aufrufe werden jetzt auf Rückgabewert geprüft, und es werden keine Referenzen hängen gelassen. Ich hab die innere Liste durch Tupel ersetzt, weil es dann nämlich ein wenig weniger Fehlerprüfung geben muß; wenn ein Tupel mit PyTuple_New erfolgreich erstellt wurde kann beim Setzen des Tupel-Elements nichts mehr schiefgehen, und diese Methode stiehlt auch gleich die Referenz auf das Python-Objekt was ich setze.

Während ich bei PyList_Append die Referenz explizit freigeben muß, weil PyList_Append eine neue Referenz für die Liste erzeugt (damit das erstellte Tupel irgendwann mal wieder freigegeben wird).

HTH!
--- Heiko.
Benutzeravatar
windows97
User
Beiträge: 24
Registriert: Freitag 10. März 2006, 13:34

Beitragvon windows97 » Mittwoch 15. März 2006, 22:28

Hi Heiko!

Also ich habs jetzt nurmal kurz überflogen! - Hab morgen keine Zeit und werds mir deswegen erst Freitag antun (denke ich).

Aber an dieser Stelle schonmal "Danke!".

Gruß Daniel
BlackJack

Beitragvon BlackJack » Mittwoch 15. März 2006, 22:30

windows97 hat geschrieben:Oh, eine goto-Anweisung in C?
--> Da steht jeder dritte Informatik-Prof kurz vorm Herzinfarkt. :lol:


Die anderen 2 sehen das ``goto`` hier kontrolliert als Ersatz für Ausnahmen benutzt wird. Denn das auslösen einer Ausnahme ist im Grunde ja auch eine Sprunganweisung aus dem "strukturierten Programmcode" zu einem "except-Label".

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder