Boost.preprocessor String-Vergleich



  • hi Gate! zeig mal etwas code, damit wird besser sehen wie du das machst.

    bye



  • also grob gesagt hab ich das ganze folgerndermaßen gemacht:

    Ich hab wxPython mit meinen wx-libs kompiliert, damit ich wxWidgets in python und c++ benutzen kann (tut aber hier nichts zur Sache so richtig).

    Und dann so:

    import ai
    
    class MyPlugin(ai.IGeneralPlugin):
    	def GetName(self):
    		return "MyPlugin from Python"
    
    	def GetVersion(self):
    		return "vPython10"
    
    	def GetDescription(self):
    		return "Ein kelines einfaches python-plugin..."
    
    	def GetFileName(self):
    		return "PythonPlugin.py"
    
    	def DisplayHelp(self):
    		wx.MessageBox("Hi, dies dies die kleine Beschreibung zum Plugin...")
    
    	def Shutdown(self):
    		# nothing to do
    		return 0
    
    	def Startup(self):
    		#nothing to do
    		# hier sollte dann Code rein, der das plugin im progreamm verfügbar macht,
                    # bspw. Befehle zu den Menüs hinzufügen
                    # dazu gibts den Befehl ai.getApp().Addcommand(Befehlsummer, "Titel")
                    print "Habe das PyPlugin geladen!"
    
    		return 0
    
    global generalpi
    
    def Startup(a):
    	global generalpi
    	print "Startup in Pythonplugin!"
    	app = ai.getApp()
    	wndmain = ai.getWndMain()
    
    	generalpi = MyPlugin()
    	app.AddGeneralPlugin(generalpi)
    	return 1
    
    print "Datei geladen..."
    
    // mit den Befehlen und Events hab ich das so gemacht,
    // der selbe Code funktioniert auch in python, habs nur noch nicht benutzt
    app->AddCommand(ID_PRINTIMAGE, ("Drucken..."), tr(""), bm);
    app->PushEventHandler(this);
    
    // wobei this von wxEvtHandler abgeleitet ist und eine Methode OnPrint anbietet.
    // mit bind müsste das aber auch gehen.
    

    Aber ein bisschen Code, welches dein Problem zeigt, wäre schon nicht schlecht



  • maxxi schrieb:

    hi Gate! zeig mal etwas code, damit wird besser sehen wie du das machst.

    bye

    Okay, ich versuchs einfach mal darzustellen:

    struct Observer
    {
      virtual void Update( Subject* ) = 0;
      virtual ~Observer(){};
    };
    

    Das ist erstmal die abstrakte Basisklasse des Observers wovon meine Cppklassen sowie die entsprechenden Pythonklassen erben sollen, damit sie auf Veränderungen im Programm reagieren können, indem sie jewils die Update() Methode implementieren.
    So, um sie für Python verfügbar zu machen, wird sie gewrappt:

    struct Listener : Observer, boost::python::wrapper<Observer>
    {
        Listener()
        {
         m_self = NULL;
        }
        Listener( PyObject* self_) : m_self(self_)
        {
            Py_XINCREF(m_self);
        }
        Listener( PyObject* self_, const Listener& copy ) : Observer(copy), m_self(self_)
        {
            Py_XINCREF(m_self);
        }
        Listener( PyObject* self_, const Observer& copy ) : Observer(copy), m_self(self_)
        {
            Py_XINCREF(m_self);
        }
        ~Listener() { Py_XDECREF(m_self); }
    
        virtual void Update(Subject*)
        {
            this->get_override("Update")();
        }
        PyObject *m_self;
    };
    

    Dabei bin ich mir allerdings nicht sicher, ob die ganzen Copy-Ctors notwendig sind aber bei meiner Recherche zu dem Problem hatte ich das dann so einfach mal übernommen.
    Für Python wird der Wrapper dann wie folgt verfügbar gemacht:

    class_<Listener, std::auto_ptr<Listener>, boost::noncopyable>("Listener")
              .def("Update",pure_virtual(&Listener::Update))
             ;
    

    Folgend dann die Registrierungsmethode der Pythonobserver:

    void Subscribe(Listener* listener,const std::basic_string<wxChar>& sVar)
    {
        //Hier wird deque anstatt vector genutzt, da bei Letzterem die Elemente nicht an der selben
        //Speicheradresse gehalten werden. Da aber eine Konstante Adresse für den Aufruf der Updatefunktion
        //vom VarManager aus nötig ist muss dieser Container genutzt werden.
        static std::deque<Listener >Listeners;
        Listener L = *listener;
        Listeners.push_back(L);
        VarManager::Get()->Subscribe(sVar, &Listeners.back());
    }
    

    Noch für auto_ptr<> gewrappt:

    void Subscribe_wrapper(std::auto_ptr<Listener> l, const std::basic_string<wxChar>& sVar)
    {
        Subscribe(l.get(), sVar);
        l.release();
    }
    

    Soweit also von Seiten des Cpp Programms. Der Varmanager ruft die Updatemethoden der Observer auf. Inzwischen erhalte ich bei Pythonoberservern allerdings immer unbehandelte exceptions, wenn Update aufgerufen wird. Durch einkapseln des Update Aufrufs mit try catch( ... ) konnte ich die exception auch nicht auffangen merkwürdig.
    Hier nochmal die Pythonseite:

    class CfgListener(LoW.Listener):
            def Update(self,Subject):
                '''Hier weiterer Code der aufgerufen werden soll
    
    CfgListener = CfgListener()
    LoW.Cfg.Subscribe(CfgListener,"Event")
    

    Nun wie gesagt, momentan sieht es so aus, das ne unbehandelte Exception fliegt, wenn Update() aufgerufen wird. Ich war schon soweit, dass der Update Aufruf funktionierte, es aber zum Absturz kam wenn Code in eine andere Methode als der Update Methode ausgelagert wurde. Slicing?
    Wie gesagt wäre für jeden Hinweis dankbar, wie ich mein Vorhaben vernünftig umsetzen könnte.



  • kannst du die exception denn mit try {} catch(...) {} fangen?
    Evtentuell wird eine Exception vom Typ boost::error_already_set geworfen? Wenn ja, kannst du danach mit PyErr_Print (oder so ähnlich) den Fehler ausgeben.



  • Maxi schrieb:

    kannst du die exception denn mit try {} catch(...) {} fangen?
    Evtentuell wird eine Exception vom Typ boost::error_already_set geworfen? Wenn ja, kannst du danach mit PyErr_Print (oder so ähnlich) den Fehler ausgeben.

    Also ich hab schon versucht die Exceptions zu fangen, bis jetzt aber ohne Erfolg. Dazu hab ich den Update() Aufruf vom Subject aus mit try catch( ... ) eingeschlossen und auch im Listener selbst in die Update Methode try catch benutzt. Beides hat leider nicht geklappt. Wenn ich nur wüsste, wo die Exception fliegt.
    Hier übrigens die Fehlerbehandlung mit PyErr_Print:

    if ( boost::python::handle_exception( boost::bind(exec_file_test, sFile) ) )
        {
            if (PyErr_Occurred())
            {
                wxLogError( wxT("Fehler aufgetreten:") );
                PyErr_Print();
                boost::python::object sys(boost::python::handle<>(PyImport_ImportModule("sys")));
                boost::python::object err = sys.attr("stderr");
                std::string err_text = boost::python::extract<std::string>(err.attr("getvalue")());
                wxLogMessage(err_text.c_str());
            }
            else
            {
                wxLogError("A C++ exception was thrown  for which "
                            "there was no exception translator registered.");
            }
        }
    

    Das dunktioniert soweit, sprich ich kann mir die Python Fehlermeldungen im Programm ausgeben lassen.



  • So, ich konnte den Ursprung der Exception nun genau bestimmen:

    struct Listener : Observer, boost::python::wrapper<Observer>
    {
        [...]
    
        virtual void Update(Subject* s)
        {
            this->get_override("Update")(s); //<-- Hier wird die Exception geworfen
        }
        [...]
    };
    

    Das blöde ist nur, dass selbst wenn ich die Exceptionbehandlung hinzufüge, kann ich die Ausnahme nicht auffangen:

    struct Listener : Observer, boost::python::wrapper<Observer>
    {
        [...]
    
     virtual void Update(Subject* s)
        {
            try
            {
            this->get_override("Update")(s);
            }
            catch( ... )
            {
                wxMessageBox("Fehler");
            }
        }
        [...]
    };
    

    Ich bin ehrlich gesagt überfragt, was ich an der Stelle falsch mache. SCheinbar ist der Aufruf von this->get_override("Update")(s); falsch, aber ich weiß nicht wie es denn richtig aussehen müsste.



  • versuch ma noch das hier, dann kannst du es noch weiter eingrenzen:

    void Update(Subject* s)
    {
    	if(override f = get_override("Update"))
    		f(s);
    // oder auch eventuell f(boost::ref(s));
    	else
    		Listener::Update(s);
    }
    


  • Maxi schrieb:

    versuch ma noch das hier, dann kannst du es noch weiter eingrenzen:

    void Update(Subject* s)
    {
    	if(override f = get_override("Update"))
    		f(s);
    // oder auch eventuell f(boost::ref(s));
    	else
    		Listener::Update(s);
    }
    

    Also vielen Dank für den Tipp, aber sobald f(s) oder f(boost::ref(s)); aufgerufen werden fliegt die Exception. Aber immerhin funktioniert scheinbar die get_override() Funktion. Fangen kann ich sie aber wiedermal nicht.
    Die Frage die ich mir gerade stelle ist, ob das denn dann überhaupt eine Exception ist.
    Die Nachrichti die ich bekomme lautet zumindest wie folgt:

    Runtime Error!
    Program: blabla.exe
    This application has requested the runtime to terminate it in an unusual way. Please Contact the application's support team for more information.

    Ich mein, das ist doch eine Nachricht einer nicht gefangenen Exception oder?



  • Gate schrieb:

    Ich mein, das ist doch eine Nachricht einer nicht gefangenen Exception oder?

    Nicht zwingend. Führ das Programm mal im Debug-Modus aus, dann solltest du mehr Informationen darüber bekommen.



  • So endlich,
    ich hab nun herausgefunden, warum die Exception fliegt.
    Die Übergabe von Paramtern vom C++ Programm zu Python funktioniert nicht.
    Eigentlich hatte die Python Updatemethode ja einmal den self und dann den Subject Paramter. Mittels

    this->get_override("Update")(s);
    

    wollte ich das Subject an die Methode übergeben. Irgendwie funktioniert das nicht. Wenn ich also im pythonskript der Updatemethode nur noch den self paramter definiere und dann vom Programm aus

    this->get_override("Update")();
    

    aufrufe geht es ohne exception.
    Hat nun vllt. jemand ne Idee wie ich es hinbekomme doch noch den Subject Parameter zu übergeben?



  • was passiert, wenn du das machst:

    Subject* s = ...; // als parameter oder sonstwas.
    
    boost::python::object o(s)
    this->get_override("Update")(o);
    

    So kommst du weiter in den Fehler rein, wenn die Exception in der Zeile mit python::object fliegt



  • Maxi schrieb:

    was passiert, wenn du das machst:

    Subject* s = ...; // als parameter oder sonstwas.
    
    boost::python::object o(s)
    this->get_override("Update")(o);
    

    So kommst du weiter in den Fehler rein, wenn die Exception in der Zeile mit python::object fliegt

    Ah, das ist interessant, die Exception oder was auch immer, ich nenns jetzt mal Runtime Error, fligt beim Aufruf von

    boost::python::object o(s)
    

    Also tippe ich mal, dass die Konvertierung vom Subject zu einem Pythonbjekt fehlschlägt. Liege ich damit soweit richtig? und wichtiger, was kann ich tun damit es klappt?



  • Ja, scheint so, als ob Subject* nicht an python übergeben werden kann.
    zeig doch mal deine deklaration von Subject und wie du das in boost.python registrierst.
    Und probiere mal boost::python::object(boost::ref(s))



  • Maxi schrieb:

    Und probiere mal boost::python::object(boost::ref(s))

    Hey, vielen Dank. Das hat nun endlich geklappt. Allerdings bleibt nun meine Frage, aufgrund derer ich überhaupt den Thread wieder hochgeholt habe:
    Gibt es eine Möglichkeit das Slicing zu verhindern?
    Als Beispiel:

    class CSVExporter(LoW.Listener):
            def Update(self,Subject):
                l = ["Ja", "Nein"]
                Choice = LoW.Choice("Sollen die Fachdaten als CSV Datei exportiert werden?",l)
                if Choice == "Ja":
                    DoIt()
    
            def DoIt():
                [...]
    

    Sobald hier nun innerhalb der Update Methode DoIt() aufgerufen wird, gibt es den gleichen Runtime Error wie schon zuvor.
    Das dürfte ja daran liegen, dass im Programm selbst nur Listener gespeichert werden können, die aber eben keine weitere Methoden wie eben z.B. DoIt definiert haben. Ein typisches Slicing Problem. Also hat damit vllt. jemand Erfahrungen und Ideen wie ich den Fehler umgehen kann?


Anmelden zum Antworten