C++ ObjectWrapper für Python

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.
DonnerCobra
User
Beiträge: 53
Registriert: Mittwoch 9. April 2008, 19:35

C++ ObjectWrapper für Python

Beitragvon DonnerCobra » Donnerstag 29. Mai 2008, 10:47

Hallo, ich möchte gerne einen ObjectWrapper schrieben. Ich möchte auch kein Boost nutzen, sondern das ganze mit der StandardBibliothek erreichen...



Enhalten ist 1x eine Funktion die etwas ausgibt. Und zwar "println" und dann wollte ich noch versuchen mittels ein paar Samples aus dem Internet einen ObjectWrapper für mein Objekt zu schreiben. Leider scheiter ich jedoch. Kann mir jemand helfen?

Danke!! Oder wo ich eventuell alternative SourceCodes herbekomme damit ich mir das ganze an einem Beispiel klarmachen kann.



Code: Alles auswählen


#include <Python/Python.h>
#include <string.h>

using namespace std;

//Function to print out something///////////////////////////////////////////
PyObject* println(PyObject* pSelf, PyObject* pArgs)
{
   char* s = NULL;
   if (!PyArg_ParseTuple(pArgs, "s", &s)) return NULL;
   printf("%s",s);
   
   Py_INCREF(Py_None);
   return Py_None;
}

static PyMethodDef myMethods[] = {
    {"println", println, METH_VARARGS},
    {NULL, NULL, 0, NULL}
};








////////CLASS WRAPPER!!!!!//////////////////////////////////////////////

class Numbers
{
public:
    Numbers(int first, int second)
   {
      m_first = first;
      m_second = second;
   }
    double NumMemberMult(void){ return m_first*m_second;}
private:
    int m_first;
    double m_second;
};

static void PyDelNumbers(void *ptr)
{
    //std::cout<<"Called PyDelNumbers()\n"; //Good to see once
    Numbers * oldnum = static_cast<Numbers *>(ptr);
    delete oldnum;
    return;
}

PyObject *wrap_new_Numbers(PyObject *, PyObject* args)
{
    //First, extract the arguments from a Python tuple
    int arg1;
    int arg2;
    int ok = PyArg_ParseTuple(args,"ii",&arg1,&arg2);
    if(!ok) return 0;
 
    //Second, dynamically allocate a new object
    Numbers *newnum = new Numbers(arg1, arg2);

    //Third, wrap the pointer as a "PyCObject" and
    // return that object to the interpreter
    return PyCObject_FromVoidPtr( newnum, PyDelNumbers);
}
////////////////////////////////////

int main (int argc, char **argv) {

   Py_Initialize();
   
   PyObject *m = Py_InitModule("c4d", myMethods);
   Py_INCREF((PyObject *)&wrap_new_Numbers);
    PyModule_AddObject(m, "Numbers", (PyObject *)&wrap_new_Numbers);

   PyRun_SimpleString("from c4d import *;\nprintln('ich bin ein test');\ntest = Numbers(3,4)");

   Py_Finalize();
    return 0;
}
DonnerCobra
User
Beiträge: 53
Registriert: Mittwoch 9. April 2008, 19:35

Beitragvon DonnerCobra » Freitag 30. Mai 2008, 07:31

Hallo,

es hat alles geklappt und ich kann in Python nun ein Objekt erzeugen vom Typ Numbers.

heißt t = Numbers(3,4); jedoch funktioniert



Mit welchem Befehl füge ich denn hinzu, dass Python die Attribute (Member) meiner Klasse kennt? Mit PyMethodDef jedenfalls nicht, oder?


Danke sehr :)
Benutzeravatar
Spaten
User
Beiträge: 52
Registriert: Samstag 27. Mai 2006, 11:35
Wohnort: Bremen
Kontaktdaten:

Beitragvon Spaten » Dienstag 11. November 2008, 20:28

Hallo DonnerCobra,

entschuldigung, dass ich diesen alten Thread noch einmal belebe,
aber ich bin im Moment mit einem ähnlichen Problem beschäftigt.
Ich möchte eine C++ Klasse für Python wrappen und habe mich
an deinem Beispiel orientiert. Das ganze lässt sich auch kompilieren
und linken, allerdings bekomme ich bei der Ausführung eine
Speicherzugriffsverletzung (segmentation fault) in der Zeile mit

Py_INCREF((PyObject *)&wrap_new_Numbers);

bzw. in der danach (PyModule_AddObject), wenn ich die Zeile davor auskommentiere.

Du hast gesagt, dass es bei dir funktioniert und es würde mich interessieren,
ob du noch etwas an dem oben geposteten Code verändert hast, damit es läuft.

Wäre echt nett, auch wenn jemand anders vielleicht einen Tipp hätte,
was die Zugriffsverletzung hervorrufen könnte.


Ich nutze DevCpp und Python 2.5
Python-Version: 2.5
Benutzeravatar
HWK
User
Beiträge: 1295
Registriert: Mittwoch 7. Juni 2006, 20:44

Beitragvon HWK » Mittwoch 12. November 2008, 15:19

Der Zeiger auf die C-Funktion wrap_new_Numbers wird als Zeiger auf ein PyObject verwendet. Das muss wohl zwangsläufig zu Fehlern führen.
MfG
HWK

Edit: Diese beiden Links sollten Dir eigentlich ausreichend Anregungen geben: Link 1 und Link 2
Qubit
User
Beiträge: 75
Registriert: Dienstag 7. Oktober 2008, 09:07

Beitragvon Qubit » Mittwoch 12. November 2008, 17:38

Spaten hat geschrieben:Wäre echt nett, auch wenn jemand anders vielleicht einen Tipp hätte,


Würde jetzt auf die Schnelle sagen..

MyNumbers.cpp

Code: Alles auswählen

#include </usr/include/python2.5/Python.h>
#include <string.h>


PyObject *println(PyObject *self, PyObject *args)
{
    char *s = NULL;
    if (!PyArg_ParseTuple(args, "s", &s)) return NULL;
    printf("%s\n",s);

    Py_INCREF(Py_None);
    return Py_None;
}

class Numbers
{
public:
    Numbers(int first, double second)
       {m_first = first; m_second = second;}
    double NumMemberMult(void){ return m_first*m_second;}
private:
    int m_first;
    double m_second;
};

static void PyDelNumbers(void *ptr)
{
    Numbers * oldnum = static_cast<Numbers *>(ptr);
    delete oldnum;
    return;
}

PyObject *wrap_Numbers_MemberMult(PyObject *self, PyObject *args)
{
    // First, get the PyCObject from the args tuple
    PyObject *pynum = 0;
    int ok = PyArg_ParseTuple( args, "O", &pynum);
    //"O" is for Object
    if(!ok) return NULL;

    // Convert the PyCObject to a void pointer:
    void * temp = PyCObject_AsVoidPtr(pynum);
    // Cast the void pointer to a Numbers pointer:
    Numbers * thisnum = static_cast<Numbers *>(temp);

    //Second, make the function call
    double result = thisnum->NumMemberMult();

    //Third, build & return a Python float object
    return Py_BuildValue("d",result);
}

PyObject *wrap_new_Numbers(PyObject *self, PyObject *args)
{
    //First, extract the arguments from a Python tuple
    int arg1;
    double arg2;
    int ok = PyArg_ParseTuple(args,"id",&arg1,&arg2);
    if(!ok) return 0;

    //Second, dynamically allocate a new object
    Numbers *newnum = new Numbers(arg1, arg2);

    //Third, wrap the pointer as a "PyCObject" and
    // return that object to the interpreter
    return PyCObject_FromVoidPtr( newnum, PyDelNumbers);
}

////////////////

static PyMethodDef myFunc[] = {
    {"println", println, METH_VARARGS},
    {"Numbers",wrap_new_Numbers, METH_VARARGS},
    {"mult",wrap_Numbers_MemberMult, METH_VARARGS},
    {NULL, NULL, 0, NULL}
};



PyMODINIT_FUNC initMyNumbers()
{
    Py_Initialize();

    PyObject* m;
    m = Py_InitModule("MyNumbers", myFunc);
}


setup.py

Code: Alles auswählen

from distutils.core import setup, Extension

mynum = Extension('MyNumbers',
                    sources = ['MyNumbers.cpp'])

setup (name = 'MyNumbers',
       version = '0.1',
       description = 'This is a test package',
       ext_modules = [mynum])


python setup.py install

Code: Alles auswählen

>>> from MyNumbers import *
>>> n = Numbers(9,9)
>>> n
<PyCObject object at 0xb7de9440>
>>> mult(n)
81.0
>>>
Benutzeravatar
Spaten
User
Beiträge: 52
Registriert: Samstag 27. Mai 2006, 11:35
Wohnort: Bremen
Kontaktdaten:

Beitragvon Spaten » Donnerstag 13. November 2008, 07:38

Vielen Dank für die Info!

Hat geklappt, danke :D
Python-Version: 2.5
booth
User
Beiträge: 44
Registriert: Mittwoch 31. Oktober 2007, 21:30
Wohnort: Durham, U.K.

Beitragvon booth » Dienstag 10. Februar 2009, 23:14

Hallo,

ich habe eine Frage zur Verwendung von "PyArg_ParseTuple".

Ich moechte ein Array/Liste/Tuple (eigentlich wurst was es am Ende wird) an ein C++-Array weitergeben.

Wenn ich jetzt ein Tuple in Python erzeuge:

Code: Alles auswählen

 
def testFunc():
    return tuple([1,2])

und im C++-Code nehme ich das mit der Zeile

Code: Alles auswählen

pValueTuple = PyObject_CallObject(pFunc, pArgs);
PyObject *testTuple;
if (PyTuple_Check(pValueTuple)){
    PyArg_ParseTuple(pValueTuple, "O", &testTuple);
    std::cout << PyTuple_Check(testTuple) << std::endl;
}


Dabei sind pValueTuple, pFunc und pArgs PyObject *

Dann gibt es erzeugt das einen Windows Fehler. Die einzige Moeglichkeit wie man das ohne Fehler zum laufen kriegt (wenigstens die einzige die ich gefunden habe) ist z.B.

Code: Alles auswählen

return tuple([1])


zurueck zugeben. Allerdings gibt der Test ob "testTuple" zurueck, dass es sich _nicht_ um ein Tuple handelt (0). Sobald man ein mehrelementiges Tuple ausgibt stuerzt das Program ab.

Irgendwer eine Idee? Oder eine andere Moeglichkeit ein Array aus Python an C++ zu uebergeben?

Vielen Dank fuer die Antworten
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Beitragvon CM » Mittwoch 11. Februar 2009, 11:25

Hoi,

sorry, aber ich kapiere Deine Frage schon nicht. Möchtest Du vielleicht wissen wie man eine Liste / einen Tuple als Parameter an eine Funktion in C/C++ übergibt?

Falls nicht und niemand anders hier weiß, was Du möchtest, solltest Du vielleicht schreiben, was die testFunc mit dem C-Code zu tun hat - das ist quasi ein boolean auf dem Terminal ausgeben. Oder was ist der Zweck?

Gruß,
Christian
booth
User
Beiträge: 44
Registriert: Mittwoch 31. Oktober 2007, 21:30
Wohnort: Durham, U.K.

Beitragvon booth » Mittwoch 11. Februar 2009, 12:39

CM hat geschrieben:Hoi,
Möchtest Du vielleicht wissen wie man eine Liste / einen Tuple als Parameter an eine Funktion in C/C++ übergibt?
Christian


Genau das moechte ich. Es laeuft einfach drauf raus, dass ich ein Numpy-Array (2d) ausgebe und es an C/C++ uebergebe.

Ich habe gesehen, dass die Funktionen PyArg_Parse() und PyArg_ParseTuple() gibt. Wenn ich aus python also ein tuple ausgebe, dann kann ich das mit PyArg_Parse(pythonTuple, "O", &pyObj) uebergeben (das geht erstaunlicherweise mit PyArg_ParseTuple nicht!). Was ich dann in pyObj habe ist auch tatsaechlich ein Tuple.

Inzwischen gehe ich den unschoenen/komplizierten Weg und initalisiere ein 2d-C/C++-Array und schreibe die Elemente des Tuples einzeln mit PyTuple_GetItem().

Ich dachte es gibt eine elegante Moeglichkeit direkt das Numpy-Array aus python an ein C/C++-Array zurueck zu geben. Habe aber bisher keine gefunden.

Richtig bloed wird es, wenn man ein Tuple von Tuplen zurueck geben moechte, das geht zwar, aber man muss PyArg_Parse(pythonTuple, "OOO..", &pyObj1, &pyObj2, &pyObj3,...) aufrufen. Wenn man sich jetzt aber nicht auf die Anzahl der Tuple-Elemente in dem zurueckgegebenen Tuple festlegen moechte, dann geht das so nicht (weil man ja PyArg_Parse sagen muss wieviele Elemente da in welchem Format kommen). Auch hier waere ein Funktionsaufruf PyArg_Parse(numpyArray,"?", &initalized2dArray). Nur das es "?" eben nicht gibt. :roll:

Fazit: Ich bekomme es hin, es ist aber nicht optimal (Geschwindigkeit etc.) und auch nicht schoen.
Benutzeravatar
HerrHagen
User
Beiträge: 430
Registriert: Freitag 6. Juni 2008, 19:07

Beitragvon HerrHagen » Mittwoch 11. Februar 2009, 13:43

Ich dachte es gibt eine elegante Moeglichkeit direkt das Numpy-Array aus python an ein C/C++-Array zurueck zu geben. Habe aber bisher keine gefunden.

Klar geht das. Hir mal ein Ausschnitt aus einem Modul das ich mal geschrieben hab:

Code: Alles auswählen

static PyObject *_ImageProcessing_median(PyObject *self, PyObject *args)
{
    PyArrayObject *a;      //in
   PyArrayObject *mask;    //in
   PyArrayObject *b;      //out
   int cx, cy;           //in, center point mask

   if(!PyArg_ParseTuple(args, "O!O!ii", &PyArray_Type, &a, &PyArray_Type, &mask, &cy, &cx)) return NULL;

   int * mask_ptr = PyArray_DATA(mask);

Die ersten beiden Argumente sind numpy.array's. Die numpy API stellt eine Reihe von Makros und Funktionen bereit, die dir den Zugriff auf das Array ermöglichen. Wenn du einen Pointer auf die eigentlichen Daten haben willst, geschieht das so wie in Zeile 10. Hier findest du die Doku der Zugriffsmethoden. Damit die C-API richtig funktioniert, darfst du folgendes nicht vergessen.

Code: Alles auswählen

#include <Python.h>
#include "numpy/arrayobject.h"  // !!!!!

...

// Modul-Initialisierung
PyMODINIT_FUNC init_ImageProcessing(void)
    {
    Py_InitModule("_ImageProcessing", _ImageProcessingMethods);
    import_array(); // !!!!!!!
    }


MFG HerrHagen
booth
User
Beiträge: 44
Registriert: Mittwoch 31. Oktober 2007, 21:30
Wohnort: Durham, U.K.

Beitragvon booth » Mittwoch 11. Februar 2009, 14:33

HerrHagen hat geschrieben:

Code: Alles auswählen

   if(!PyArg_ParseTuple(args, "O!O!ii", &PyArray_Type, &a, &PyArray_Type, &mask, &cy, &cx)) return NULL;

   int * mask_ptr = PyArray_DATA(mask);


Habe ich eigentlich so gemacht. Bei der letzten Zeile bin ich mir nicht sicher ob das mit 2d-Arrays funktioniert. Muss ich mal noch ausprobieren.

HerrHagen hat geschrieben:

Code: Alles auswählen

#include <Python.h>
#include "numpy/arrayobject.h"  // !!!!!

...

// Modul-Initialisierung
PyMODINIT_FUNC init_ImageProcessing(void)
    {
    Py_InitModule("_ImageProcessing", _ImageProcessingMethods);
    import_array(); // !!!!!!!
    }



Vielen Dank fuer den Link. Die Modulinitialisierung kapiere ich noch nicht. Wahrscheinlich brauch ich das aber so auch nicht, weil ich kein static-Funktion benutze. Der ganze Code befindet sich in einer Funktion in einer DLL. Wie gesagt, ich muss mal rumprobieren und nachdenken. Vielen Dank fuer die Hilfe.
Benutzeravatar
HerrHagen
User
Beiträge: 430
Registriert: Freitag 6. Juni 2008, 19:07

Beitragvon HerrHagen » Mittwoch 11. Februar 2009, 14:46

Bei der letzten Zeile bin ich mir nicht sicher ob das mit 2d-Arrays funktioniert.

Ja! Die Daten liegen dann (wenn du ein C-Array gemacht hast, nicht FORTRAN) Zeile für Zeile hintereinander. Du kannst das mit numpy.require(a, dtype='int32', requirements='CA') auch fordern.
Am besten solltest du aber auf die Daten nicht direkt über den DATA-Pointer sondern über die PyArray_GETPTR2(PyObject* obj, <npy_intp> i, <npy_intp> j) Funtkion gehen.
Die Modulinitialisierung kapiere ich noch nicht. Wahrscheinlich brauch ich das aber so auch nicht, weil ich kein static-Funktion benutze.

Die ist per Definition zwingend für jedes Erweiterungsmodul! Genauso wie alle Funktionen die du von Python aus nutzen willst static sein müssen. Ich hab nur die Stellen mit !!!! markiert, die zusätzlich bei der Benutzung der numpy-API anfallen. Schau dir einfach mal die Beispiele auf der scipy-Seite an.
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Beitragvon CM » Mittwoch 11. Februar 2009, 14:54

SCNR:
booth hat geschrieben:Genau das moechte ich. Es laeuft einfach drauf raus, dass ich ein Numpy-Array (2d) ausgebe und es an C/C++ uebergebe.

Genau *das* stand da vorher nicht. Aber schön, dass es jetzt gelöst ist ;-).

Gruß,
Christian
booth
User
Beiträge: 44
Registriert: Mittwoch 31. Oktober 2007, 21:30
Wohnort: Durham, U.K.

Beitragvon booth » Mittwoch 11. Februar 2009, 16:11

CM hat geschrieben:SCNR:
booth hat geschrieben:Genau das moechte ich. Es laeuft einfach drauf raus, dass ich ein Numpy-Array (2d) ausgebe und es an C/C++ uebergebe.

Genau *das* stand da vorher nicht. Aber schön, dass es jetzt gelöst ist ;-).


Wir brauchen mehr Praezesion :D

Ich muss mir erstmal die ganzen Beispiele etc. ansehen. Wenn ich nur nicht so langsam waere... :roll:
booth
User
Beiträge: 44
Registriert: Mittwoch 31. Oktober 2007, 21:30
Wohnort: Durham, U.K.

Beitragvon booth » Donnerstag 12. Februar 2009, 10:26

Das ist ja alles noch viel komplizierter als ich gedacht habe ;)

HerrHagen hat geschrieben:

Code: Alles auswählen

static PyObject *_ImageProcessing_median(PyObject *self, PyObject *args)
{
    PyArrayObject *a;      //in
   PyArrayObject *mask;    //in
   PyArrayObject *b;      //out
   int cx, cy;           //in, center point mask

   if(!PyArg_ParseTuple(args, "O!O!ii", &PyArray_Type, &a, &PyArray_Type, &mask, &cy, &cx)) return NULL;

   int * mask_ptr = PyArray_DATA(mask);



Ich vestehe noch nicht wozu ich eigentlich ein Modul brauche. Kann ich Zeile 9 nicht einfach in der main()-Funktion aufrufen?

HerrHagen hat geschrieben:

Code: Alles auswählen

#include <Python.h>
#include "numpy/arrayobject.h"  // !!!!!

...

// Modul-Initialisierung
PyMODINIT_FUNC init_ImageProcessing(void)
    {
    Py_InitModule("_ImageProcessing", _ImageProcessingMethods);
    import_array(); // !!!!!!!
    }



Wahrscheinlich muss das Argument "_ImageProcessing_median" sein, oder? Gibt es bei dir dann noch eine Zeile ala:

Code: Alles auswählen

static PyMethodDef _ImageProcessingMethods[]


Und zu guter Letzt (vorerst). Wieso kann ich import_array() nicht einfach nach Py_Initialize(); aufrufen. Mein Compiler (mingw) meldet "statement with no value, in function returning 'int'". Was auch immer das heisst.

vG, U.

Wer ist online?

Mitglieder in diesem Forum: Bing [Bot]