Boost.preprocessor String-Vergleich



  • Hallo!

    Eigentlich eine recht einfach zu stellende Frage, aber die Lösung entzieht sich meinem Wissen 🙂

    Also, ich brauche ein Makro welches zu return expandiert, wenn sein Argument nicht void ist. Habe es so versucht:

    #define meins(arg) BOOST_PP_EXPR_IF(BOOST_PP_NOT_EQUAL(arg, void), return)
    

    Leider expandiert es nur ins tiefste boost-gemakroe und sonst nix. Kann man irgendwie strings vergleichen? habe dazu leider nix gefunden, weder in der doku noch in mailinglisten etc.



  • In welchem Zusammenhang brauchst du das denn? Vielleicht gibts ne elegantere Lösung als Makro-Gefrickel.



  • hehe, die Frage wurde mir in #cpp auch schon gestellt 🙂

    Ich schreibe ein Pluginsystem, welches um python-plugins erweitert werden kann. Dazu müssen Klassen in Python von meinen abstrakten Plugin-Basisklassen erben. Dazu braucht man in boost.python wrapperklassen, welche alle virtuellen funktionen der Klassen wrappen müssen, von denen in python abgeleitet wird. Damit wird die Polymorphie garantiert. Dazu müssen sehr viele funktionen geschrieben werden, die alle in etwa gleich aussehen:

    // zwei Beispiele ( das folgende sind memberfunktionen der wrapperklasse):
    
    std::string GetName() const
    {
    	return this->get_override("GetName");
    }
    
    virtual void DisplayHelp() const
    {
            // Nur bei void darf eben kein return stehen :)
    	this->get_override("DisplayHelp")();
    }
    

    Um das zu generieren möchte ich boost::preprocessor verwenden, klappt ja auch ziemlich gut eigentlich, nur der string-vergleich nicht so. Bis jetzt habe ich eine dritte Variable im preprocessorcode der angibt, ob return void ist oder nicht, damit kann ich dann das IF-Makro füttern.

    PS: Mit template sgeht das ganze nicht, dadie funktionen verschiedene Namen haben 🙂


  • Administrator

    Ich bin mir jetzt nicht mehr sicher, aber ich dachte return void, wäre erlaubt, zumindest wenn eine Funktion aufgerufen wird und nicht void steht. Also konkret:

    void foo() { /* ... */ };
    
    void bar() { return foo(); };
    
    int main()
    {
        bar();
        return 0;
    }
    

    Der MSVC2005 schluckt es jedenfalls, aber das könnte natürlich mal wieder daran liegen, dass er nicht ganz Standardkonform ist 🙂

    Falls du im übrigen keine Antwort zu den Preprocessor-Makros hier bekommst, da sie wohl nicht so oft verwendet werden, frag mal in der Mailing-List von Boost nach 😉

    Grüssli



  • hm, komisch. Ich dachte immer, das wird nicht geschluckt. Also bei templates geht das jedenfalls nicht mitm msvc2005, dass man da ein return void macht. Deswegen dacht ich hier gehts auch nicht.

    Naja... vielleicht findet sich ja noch jemand, der das verbessern kann 🙂 Wenn nicht frag ich mal bei boost direkt an.



  • Maxi schrieb:

    hm, komisch. Ich dachte immer, das wird nicht geschluckt. Also bei templates geht das jedenfalls nicht mitm msvc2005

    Genau wegen der Templates gibt es das Feature aber eigentlich. Ich kann's jetzt in VC2005 nicht nachvollziehen, im Nachfolger geht es aber AFAIR.



  • Hi,
    ich muss ds Thema noch einmal hochholen, da es in diesem Forum scheinbar keine PNs gibt, zumindest hab ich sie jetzt nicht gefunden. Also das geht hauptsächlich an Maxi.
    Und zwar bin ich auch gerade dabei ein Pluginsystem für mein C++ Programm zu schreiben, wobei halt die Plugins in Python geschrieben werden können. Dazu habe ich das Observer-pattern genutzt, um eine Art Eventsystem aufzubauen. Sprich: In Python leitet man von meiner C++ Observerklasse ab, kann sich dann für gewisse Events registrieren mit einer Instanz eben jener Klassen und sobald die Events in meinem Programm auftreten, werden die Update Methoden der Pythonklassen aufgerufen. Das hat soweit einigermaßen geklappt. Nur ist Problem war glaub ich das Slicing. Meine Register-Methode, die die Pythonobserver aufnimmt und den Events zu ordnet kann ja nur Observer erwarten. Daraus folgt, dass wenn in Python der abgeleiteten Observerklasse weitere Methoden hinzugefügt worden sind und diese von der Updatemethode aufgerufen werden, das ganze Programm abstürzt, da das abstrakte Observerinterface ja keine zusätzlichen Methoden kennt.
    Daher nun meine Frage: Wie hast du ds Pluginsystem realisiert, hattest du ähnliche Probleme und wie hast du sie gelöst?

    Wäre für jede Antwort dankbar.



  • 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))


Anmelden zum Antworten