Python und C/C++ Übergabeparameter

Python in C/C++ embedden, C-Module, ctypes, Cython, SWIG, SIP etc sind hier richtig.
Antworten
Fuchs aus dem Wald
User
Beiträge: 8
Registriert: Donnerstag 7. November 2013, 11:54

Halli hallo,
ich hab grade ein wenig gesucht hier bei euch und auch vorher schon viel gegoogelt. Ich komm nicht auf das richtige. Ich versuch erstmal mein Problem zu beschreiben.
Ich arbeite mit Linux und versuche grade Python, C/C++ bei zu bringen. Mach ich wie folgt:

Code: Alles auswählen

#include <Python.h>
#include <stdio.h>
#include "cache.h"

#ifdef __cplusplus
extern "C" {
#endif
  void initdriver(void);
#ifdef __cplusplus
}
#endif

static PyObject *driver_driverLoad(PyObject *self, PyObject *args){
  if (!PyArg_ParseTuple(args, "")){
    return nullptr;
  }
  printf("Hello world!\n");
  Py_INCREF(Py_None);
  return Py_None;
}

static PyMethodDef driverMethods[] = {
  {"driverLoad", driver_driverLoad, METH_VARARGS, "Loading drivers."},
  {nullptr, nullptr, 0, nullptr},
};

void initdriver(void){
  Py_InitModule("driver", driverMethods);
}
setup,py und alles andere ist auch da und ich kann die Funktion auch in meiner .py aufrufen:

Code: Alles auswählen

import driver

driver.driverLoad()
Jetzt ist mein Problem wie ihr im C/C++ Code seht da steht das #include "cache.h", von dem würde ich gerne eine Instance an die driverLoad übergeben. Also ich brauche diese Instance. Ich weiß nur nicht wie ich sie übergeben kann oder wie ich das dort hin bekomme. Ich mein ich hab diese in der .py ja nicht und kann nicht einfach driver.dirverLoad(Cache_Instance) machen.
Vielleicht kann mir hier jemand helfen. Falls noch nicht klar ist was ich machen will bitte fragen. Manchmal tue ich mir schwer mich zu erklären. :)
Sirius3
User
Beiträge: 17738
Registriert: Sonntag 21. Oktober 2012, 17:20

@Fuchs aus dem Wald: Wenn Cache_Instance in Python verfügbar sein soll, dann mußt Du Cache_Instance innerhalb von C in einer eigenen Python-Klasse kapseln und genauso über das Interface zur Verfügung stellen.
Fuchs aus dem Wald
User
Beiträge: 8
Registriert: Donnerstag 7. November 2013, 11:54

Sirius3 hat geschrieben:@Fuchs aus dem Wald: Wenn Cache_Instance in Python verfügbar sein soll, dann mußt Du Cache_Instance innerhalb von C in einer eigenen Python-Klasse kapseln und genauso über das Interface zur Verfügung stellen.
Wenn ich das richtig verstehe soll ich meine Cache Klasse über mein Interface verfügbar machen. Das Problem ist ja das ich nicht die Klasse dort brauche sondern eine Instance die ich in einer anderen Klasse erzeuge. Normalerweise macht man das ja dann so das man diese per Übergabeparameter an die Funktion übergibt die man braucht, beim aufrufen. Nun rufe ich diese Funktion aber in Python auf und habe die Instance ja nicht zum übergeben.
Vielleicht mache ich auch etwas komplett falsch. :K

Code: Alles auswählen

/* new Cache for this cash box */
  newCache();
  while(loop){/* loop for input */
    FD_SET(file_descriptor, &readfs); /* set testing for source */
    if(FD_ISSET(file_descriptor, &readfs)){ 
      length = read(file_descriptor, buffer, 8);
      CurrCache->setDevice(settings->getDeviceId());
      CurrCache->setData(buffer, length);
      //HIER MÜSSTE JA DIE INSTANCE NACH PYTHON ÜBERGEBEN WERDEN
      write(STDOUT_FILENO, buffer, length);
    }else{
      perror("Error on FD!");
      break;
    }
  }
Vielleicht helfen die weiteren Infos euch ja. Und ich werde noch mal weiter schauen wenn ich die Klasse über mein Interface zur Verfügung stelle.
BlackJack

@Fuchs aus dem Wald: Das sind zu wenig Informationen um helfen zu können. Wir wissen doch überhaupt nicht wie Dein Code aufgebaut ist. Selbst bei dem Kommentar in dem letzten C++-Quelltext stellt sich mir die Frage was an der Stelle in der Schleife denn überhaupt das Objekt (die „instance”) ist und wo die her kommt. Ist das `CurrCache`? Ist das eine globale Variable? Ein Feld eines anderen Objekts wo der Code enthalten ist? Ändert sich das in der Schleife, oder warum muss das *in* der Schleife an Python übergeben werden? Wo wird Python dort denn aufgerufen? Denn nur dann macht es ja Sinn das Objekt an Python zu übergeben.

C oder C++-Code für Python-Erweiterungsmodule würde ich übrigens nur per Hand schreiben wenn es tatsächlich überhaupt nicht anders geht. Das macht relativ viel Arbeit im Vergleich zum erstellen einer C-API die man über `ctypes` ansprechen kann oder Cython das den C-Code aus Python mit einigen syntaktischen Erweiterungen generiert.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Fuchs aus dem Wald hat geschrieben:

Code: Alles auswählen

import driver

driver.driverLoad()
Jetzt ist mein Problem wie ihr im C/C++ Code seht da steht das #include "cache.h", von dem würde ich gerne eine Instance an die driverLoad übergeben. Also ich brauche diese Instance. Ich weiß nur nicht wie ich sie übergeben kann oder wie ich das dort hin bekomme. Ich mein ich hab diese in der .py ja nicht und kann nicht einfach driver.dirverLoad(Cache_Instance) machen.
Falls du keine Instanz von Python aus übergeben möchtest, dann würde ich vorschlagen, du baust die Parameter, die zum Erstellen der Instanz benötigt werden, einfach als Argumente in `driverLoad()` ein. Intern würde aufgrund der Eingaben dann die passende Instanz mit deiner C++-Bibliothek erzeugt werden.
Fuchs aus dem Wald
User
Beiträge: 8
Registriert: Donnerstag 7. November 2013, 11:54

Hey Leute,
ich meld mich erst jetzt, da ich erst noch ein Problem mit meinem Cache hatte.

Also wieder zurück zu meinem Problem. Ich versuche mal mehr Code zu zeigen und mein Problem darzustellen, da ich denke das es noch nicht ganz verstanden wurde was ich meinte. Trotzdem danke für die schnellen und guten Antworten!

Hier ist die Funktion die meinen "Treiber" aufruft:

Code: Alles auswählen

void ExposDriver::RunDriver(ExposDriverModul *driver, int argc, char* argv[], Cache *cache){
  Py_Initialize();

  PySys_SetArgv(argc, argv);
  PyRun_SimpleString(driver->getDriver());

  Py_Finalize();
}
In der Cache Klasse liegen die Daten die an den Treiber übergeben werden sollen. Die Befüllung sieht wie folgt aus:

Code: Alles auswählen

void Cache::setData(unsigned char data[], unsigned int size){
  newDataStruct();
  if(CurrDataStruct->DataRead != NULL){
    delete[] CurrDataStruct->DataRead;
  }
  CurrDataStruct->DataRead = new unsigned char[size];
  memcpy(CurrDataStruct->DataRead, data, size);
}
Und newDataStruct():

Code: Alles auswählen

void Cache::newDataStruct(){
  if(!FirstDataStruct){
    FirstDataStruct = new Data;
    CurrDataStruct = FirstDataStruct;
  }else{
    Data *next = CurrDataStruct->Next;
    next = new Data;
    CurrDataStruct = next;
  }
  CurrDataStruct->Next = nullptr;
}
An die Daten kommt man ran in dem ich mir mit dem Übergabeparameter cache die Funktion cache->FirstDataStruct() aufrufen kann.

So nun möchte ich diese Daten in eine Python Datei übergeben um dort mit den zu arbeiten.(Sind vom Typen unsigned char*) Im Prinzip egal was ich damit anstellen will, da ich nicht weiß wie diese dort ankommen.
Die anderen Sachen hatte ich ja schon oben in meinem ersten Post gepostet. Ich hoffe ihr wisst jetzt was ich meine und habt vielleicht noch einen Tipp.
snafu hat geschrieben: Falls du keine Instanz von Python aus übergeben möchtest, dann würde ich vorschlagen, du baust die Parameter, die zum Erstellen der Instanz benötigt werden, einfach als Argumente in `driverLoad()` ein. Intern würde aufgrund der Eingaben dann die passende Instanz mit deiner C++-Bibliothek erzeugt werden.
Das verstehe ich nicht ganz. Kannst du da noch einmal genauer was zu sagen? Meinst du argc und argv?
Vielen dank soweit. :)
BlackJack

@Fuchs aus dem Wald: Also mir ist mit dem letzten Beitrag erst klar geworden, dass Du den Python-Code aus dem C++-Programm heraus ausführen willst. Bin bis jetzt immer davon ausgegangen, dass Du ein externes Python-Modul in C++ geschrieben hast.

Bisher wolltest Du ein C++-`Cache`-Objekt nur durch reichen, jetzt könnte man vermuten das Du mit dem `Cache`-Objekt auch in Python noch etwas anstellen möchtest‽

Für das reine durch reichen müsstest Du nur einen simplen Wrapper schreiben um so ein `Cache`-Objekt, damit das auf Python-Seite als Python-Objekt-Datenstruktur verwendet werden kann. Davon könntest Du dann zum Beispiel ein Exemplar in der `ExposDriver::RunDriver()`-Methode erstellen und in den globalen Namensraum des Python-Interpreters stecken bevor Du den Python-Code ausführst.

Wenn man auf Python-Seite mit dem `Cache`-Objekt noch irgend etwas anstellen können soll, dann wird dazu zusätzlicher Code nötig, der die Methoden und Datentypen auf Python-Seite nutzbar macht.
Fuchs aus dem Wald
User
Beiträge: 8
Registriert: Donnerstag 7. November 2013, 11:54

Vielen Dank für deine Antwort BlackJack!

Ja genau das meinte ich. Allerdings möchte ich nicht mit dem Cache-Objekt arbeiten sondern mit dem Struct der in dem Cache-Objekt liegt. Aber ich denke ich werde jetzt erstmal das weiter reichen Programmieren und dann weiter machen. Den wenn ich es schon mal weiter gereicht habe dann kann ich ja auch weiter machen.
BlackJack hat geschrieben: Für das reine durch reichen müsstest Du nur einen simplen Wrapper schreiben um so ein `Cache`-Objekt, damit das auf Python-Seite als Python-Objekt-Datenstruktur verwendet werden kann. Davon könntest Du dann zum Beispiel ein Exemplar in der `ExposDriver::RunDriver()`-Methode erstellen und in den globalen Namensraum des Python-Interpreters stecken bevor Du den Python-Code ausführst.
Vielleicht kennt ja jemand ein Beispiel dafür. Oder hat einen Link für mich. Das würde mich noch einmal genau Interessieren. Ich bin da nämlich noch blutiger Anfänger.

Ich probier jetzt erstmal und bei weiteren Fragen melde ich mich noch ein mal.
Danke schön.
BlackJack

@Fuchs aus dem Wald: Die entsprechenden Teile in der Python-Dokumentation behandeln so etwas doch recht ausführlich.

Allerdings wiederhole ich noch mal: Ich würde diese Sachen nicht von Hand in C schreiben sondern Cython das machen lassen. Das ist weniger Boilerplate-Schreibarbeit und sicherer was die Speicherverwaltung und Fehlerbehandlung angeht.
Fuchs aus dem Wald
User
Beiträge: 8
Registriert: Donnerstag 7. November 2013, 11:54

BlackJack hat geschrieben:@Fuchs aus dem Wald: Die entsprechenden Teile in der Python-Dokumentation behandeln so etwas doch recht ausführlich.

Allerdings wiederhole ich noch mal: Ich würde diese Sachen nicht von Hand in C schreiben sondern Cython das machen lassen. Das ist weniger Boilerplate-Schreibarbeit und sicherer was die Speicherverwaltung und Fehlerbehandlung angeht.
Hab eben schon einmal in Cython rein geschaut. Für mich auf den ersten Blick einfacher als die Doku von Python selber.
Ich arbeite auf Linux also hab ich das schon drauf, wenn ich das richtig verstanden habe?
Ach so und wo ist der Unterschied? :)
BlackJack

@Fuchs aus dem Wald: Cython ist sehr wahrscheinlich nicht bereits installiert, sollte aber über die Paketverwaltung der Linux-Distribution nachinstallierbar sein.

Welchen Unterschied meinst Du?
Fuchs aus dem Wald
User
Beiträge: 8
Registriert: Donnerstag 7. November 2013, 11:54

Zwischen Cython und Python?
BlackJack

@Fuchs aus dem Wald: Python ist eine eigenständige Programmiersprache und Cython ist eine Sprache um statisch kompilierte Erweiterungen für CPython zu schreiben. Es versteht Python-Syntax und hat einige Erweiterungen um effizienten C- oder C++-Quelltext zu generieren der dann zu einem Python-Erweiterungsmodul übersetzt wird.
Fuchs aus dem Wald
User
Beiträge: 8
Registriert: Donnerstag 7. November 2013, 11:54

Vielen Dank noch einmal für die vielen Erklärungen heute.

Also wenn ich das jetzt richtig verstehe kann ich mir per Cython eine Funktion schreiben die einen cdef struct erwartet und diese dann in Python benutzen. Dazu werde ich dann über den globalen namens raum meine Daten weiter reichen und kann dann alles benutzten?

Ich werde jetzt erstmal die nächste Zeit damit verbringen mich da weiter einzulesen.
Danke!
Fuchs aus dem Wald
User
Beiträge: 8
Registriert: Donnerstag 7. November 2013, 11:54

EDIT: Ich bin dumm sry hab das file nicht in den Ordner kopiert!
Hey,
hab mich jetzt noch einmal mehr damit beschäftigt und habe es nun doch anders. :)

Nun bin ich aber vor einem neuem Problem. Code kommt am Ende.

Also mein Problem liegt in der "instance". Da bekomme ich immer diesen Fehler:

Code: Alles auswählen

TypeError: this constructor takes no arguments
Call failed
Ich hab es alles so wie in dem Beispiel hier versucht nach zu machen(ganz unten bekommt man die kompletten Daten):
http://www.fh-wedel.de/~si/seminare/ws0 ... ython4.htm
Mein ganzer Kram macht noch nicht viel ich will erstmal das es so Funktioniert und keine Fehler schmeißt. Wenn ich nach dem Fehler google dann bekomme ich nur Antworten die sagen man soll statt "_init_" -> "__init__" schreiben.

diver.cpp:

Code: Alles auswählen

int ExposDriver::RunDriver(ExposDriverModul *driver, Cache *cache, char *filename){
  PyObject   *module = nullptr, *className = nullptr, *driv = nullptr, *instance = nullptr;
  const char *drivername = driver->getName();
  const char *cName = "Driver";
  PyObject   *mName = PyString_FromString(drivername);
  if(!mName){
    PyErr_Print();
  }

  Py_Initialize();

  module = PyImport_Import(mName);
  Py_DECREF(mName);
  if(module){
    className = PyObject_GetAttrString(module, cName);
    if(className && PyCallable_Check(className)){
      instance = PyObject_CallFunction(className, "(s)", filename);
      if(instance){
        driv = PyObject_CallMethod(instance, "parse_to_xml", "");
        if(driv){
          printf("YEAH");
        }else{
          perror("driver");
        }
      }else{
        Py_DECREF(className);
        Py_DECREF(module);
        PyErr_Print();
        fprintf(stderr,"Call failed\n");
        return -1;
      }
    }else{
      if(PyErr_Occurred()){
        PyErr_Print();
      }
      fprintf(stderr, "Cannot find function \"%s\"\n", cName);
    }
    Py_XDECREF(className);
    Py_DECREF(module);
  }else{
    PyErr_Print();
    fprintf(stderr, "Failed to load \"%s\"\n", drivername);
    return -1;
  }

  Py_Finalize();
}
atm.py:

Code: Alles auswählen

#DRV_NAME atm
#DRV_VENDOR
#DRV_VERSION 0.1
print "HALLO"

class Driver():
  file = None

  def __init__(self, file):
    self.file = file

  def parse_to_xml(self):
    print "voll gut!"
Vielen Dank fürs lesen! :)
BlackJack

@Fuchs aus dem Wald: Also als erstes fällt auf, dass das Initialisieren des Python-Interpreters zu spät kommt.

Der Name `className` ist falsch denn das Python-Objekt hinter diesem Objekt ist nicht der Name einer Klasse sondern die Klasse selbst. Klassen sind in Python, wie alles was man an Namen binden kann, auch Objekte.

Bist Du sicher, dass Du das richtige Modul importierst? Der aktuelle Pfad wird zum Beispiel bei einem kleinen Test den ich gerade gemacht habe nicht durchsucht im Gegensatz dazu wenn man Python direkt ausführt.

`Driver` sollte von `object` erben damit man eine „new style”-Klasse bekommt.

Das `file` *Klassenattribut* gehört da nicht hin, würde ich mal sagen. Es wird bei jedem Exemplar von `Driver` sowieso vom Attribut `file` auf dem Exemplar verdeckt.
Antworten