C++-virtuelle Methode in Python ableiten

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.
Antworten
franzf
User
Beiträge: 78
Registriert: Samstag 29. August 2009, 10:21

Hallo,

Ich steht grad vor einem größeren Problem. Ich möchte mein Programm um Scripting erweitern. Es existiert bereits eine Plugin-Infrastruktur, welche ich jetzt auf Pythonscripte ausweiten will. Dafür wollte ich (eben wie auch in C++) die vorgegebenen virtuellen Methoden implementieren.

Nur gibt es jetzt Probleme beim Aufrufen in meinem Programm :/

Ich wollte natürlich erst klein anfangen ;)
Hier mal etwas Code:
class.h

Code: Alles auswählen

#ifndef MY_CLASS_H
#define MY_CLASS_H

#include <string>

class Tester
{
    protected:
        virtual std::string runTest() const;
    public:
        std::string test() const;
};

#endif // MY_CLASS_H
class.cpp

Code: Alles auswählen

#include "class.h"

std::string
Tester::runTest() const
{
    return "Tester::test()";
}

std::string
Tester::test() const
{
    return runTest();
}
testapp.py

Code: Alles auswählen

from Test import Tester

class PyTester(Tester):
    def __init__(self):
        print "init PyTester"
        #Tester::__init__(self)

    #def test(self):
        #print "call runTest"
        #return self.runTest()

    def runTest(self):
        print "runTest"
        return "PyTester::test()"

def create():
    return PyTester()
testapp.cpp

Code: Alles auswählen

#include <Python.h>
#include <string>
#include <iostream>
using namespace std;

int main()
{
    Py_Initialize();
    PyObject* module = PyImport_ImportModule("testapp");
    PyObject* factory = PyObject_GetAttrString(module, "create");
    PyObject* pytester = PyEval_CallObject(factory, 0);
    char method[] = "test";
    PyObject* ret = PyObject_CallMethod(pytester, method, 0);
    string str = PyString_AsString(ret);
    std::cout << str << std::endl;
    Py_Finalize();
}
Das Python-Modul wird mit SWIG erstellt.
Mein Problem ist, dass natürlich test() nicht via Python definiert ist, sondern eben im von SWIG generierten Modul.

Code: Alles auswählen

class Tester(_object):
    __swig_setmethods__ = {}
    __setattr__ = lambda self, name, value: _swig_setattr(self, Tester, name, value)
    __swig_getmethods__ = {}
    __getattr__ = lambda self, name: _swig_getattr(self, Tester, name)
    __repr__ = _swig_repr
    def test(self): return _Test.Tester_test(self)
    def __init__(self): 
        this = _Test.new_Tester()
        try: self.this.append(this)
        except: self.this = this
    __swig_destroy__ = _Test.delete_Tester
    __del__ = lambda self : None;
Wenn ich method auf "runTest" setze, klappt das auch wunderbar. Aber eben nicht mit "test".
Kann mir vllt. jemand sagen, ob das so überhaupt geht? Und wenn ja, was es zu beachten gibt.

Vielen Dank
Franz
franzf
User
Beiträge: 78
Registriert: Samstag 29. August 2009, 10:21

OK, einen Schritt weiter :)
Es lag nur an den const-Methoden!
Wenn ich in "class Tester" in class.h das const entferne, geht mein testapp.
Drauf bin ich gekommen durch die doofe Idee, testapp mal in der Python-Console laufen zu lassen.

Code: Alles auswählen

>>> t.test()                                                          
Traceback (most recent call last):                                    
  File "<stdin>", line 1, in <module>                                 
  File "build/Test.py", line 76, in test                              
TypeError: in method 'Tester_test', argument 1 of type 'Tester const *'
Da ich auf das const nicht verzichten will, schau ich mal wie ich wie ich es in meinem class.i schaffe der Klassenmethode trotz Definition in class.h noch ein "immutable" aufzudrüccken, ohne die ganze Klassendefinition nochmal abzutippen :)
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Hallo und naträglich noch ein "Willkommen im Forum!",

das mit den const und SWIG finde ich auch etwas problematisch / umständlich. Wenn Du eine Lösung hast, fände ich's gut, wenn Du sie hier postest.

Danke,
Christian
franzf
User
Beiträge: 78
Registriert: Samstag 29. August 2009, 10:21

Thx für das nette Willkommen.

Und Entschuldigung für meinen Noobismus, ist meine erste Berührung mit SWIG und überhaupt Python-Integrierung.
Das Problem war lustigerweise am Ende nicht mehr das const (das wird scheinbar echt recht gut von SWIg unterstützt, zu mindest in meiner v1.3.39). Ich hab nochmal etwas rumgedreht und auf einmal kam nicht mehr

Code: Alles auswählen

TypeError: in method 'Tester_test', argument 1 of type 'Tester const *'
sondern

Code: Alles auswählen

TypeError: in method 'Tester_test', argument 1 of type 'Tester *'
Keine Ahnung, woher welcher Fehler kam, alles blieb irgendwie nicht reproduzierbar...

Am Ende stand dann nur noch das Problem, dass die Python-Implementierung der virtuellen Methode auch wirklich aufgerufen wird.
Die Lösung ist recht simpel, Verwendung von directors im swig-file:

Code: Alles auswählen

%module(directors="1") Test
%{
    #include "class.h"
%}

%feature("director");
%include "std_string.i"
%include "class.h"
Ich hab einfach mal das ganze Projekt ins WWW gepackt:
http://www.alpine-art.de/files/swig_test_1.zip
Ich bin bisher eigentlich recht zufrieden :) Mal schauen welche Steine sich noch in den Weg legen (für inner classes brauch ich sicher noch nen Workaround...). Ansonsten werden sich sicher noch auf Seite der python-C-API einige Fragen auftun, ich freu mich schon auf Konvertieren von PyObject*, um meine python-Plugins als normale C++-Objekte gewrappt in den Wust des Frameworks zu werfen :D

Grüße
Franz
Antworten