boost::function::target gibt Segmentation Fault



  • Hi,
    den ganzen Abend habe ich mich damit beschäftigt, einer normalen GLUT Callbackfunktion einen Funktionspointer mit vorgegebenen Parametern zu übergeben.
    Inzwischen bin ich mein Testcode gelandet und so weit gekommen:

    #include <iostream>
    #include <boost/bind.hpp>
    #include <boost/function.hpp>
    
    struct obj
    {
        void foo(int x) { std::cout << x << std::endl; }
    };
    
    void call(void (*func)())
    {
        func();
    }
    
    typedef void (*func_t)();
    
    int main(int, char**)
    {
        obj x;
        boost::function<void ()> f = boost::bind(&obj::foo, boost::ref(x), 100);
        func_t g = *f.target<func_t>(); // Segmentation Fault
        call(g);
    
        return 0;
    }
    

    Warum bekomm ich da einen Segmentation Fault?!

    Grüße,
    Raz

    Ach ja: Ubuntu 10.10, g++ 4.4.4, boost 1.43



  • Da boost::function um einiges mächtiger ist als normale Funktionszeiger, kannst du erstere nicht immer in letztere umwandeln.

    Die Memberfunktion target() gibt einen Nullzeiger zurück, falls der angegebene Typ nicht mit dem internen übereinstimmt. Der interne Typ ist entweder der Funktionszeiger, mit dem das function -Objekt initialisiert wurde, oder die Adresse des Funktionsobjekts.



  • Okay, danke für die schnelle Antwort. Du hast Recht, target gibt NULL zurück. Dann sag mir mal bitte, warum in dem Beispiel target NULL zurück gibt.

    #include <iostream>
    #include <boost/bind.hpp>
    #include <boost/mem_fn.hpp>
    #include <boost/function.hpp>
    
    struct obj
    {
        void foo(int x) { std::cout << x << std::endl; }
    };
    
    void call(void (*func)())
    {
        func();
    }
    
    typedef void (*func_t)();
    
    int main(int, char**)
    {
        obj x;
        boost::function<void ()> f = boost::bind(boost::mem_fn(&obj::foo), boost::ref(x), 100);
        if(f.target<func_t>() == NULL)
            std::cout << "NULL" << std::endl;
    
        return 0;
    }
    


  • Razoron schrieb:

    Dann sag mir mal bitte, warum in dem Beispiel target NULL zurück gibt.

    Ich habe doch geschrieben, wann target() einen Nullzeiger zurückgibt. Der Zeigertyp auf das von bind() erstellte Funktionsobjekt unterscheidet sich vom Typ des Funktionszeigers.



  • Aber der Typ vom Funktionszeiger und der vom bind zurückgegebenen sollte doch gleich sein, also:

    void (*)()
    


  • Offensichtlich nicht, da bind() ein Funktionsobjekt (also eine Instanz einer Klasse mit überladenem operator() ) zurückgibt.



  • Okay, dann hab ich wohl bind noch nicht richtig verstanden? bind gibt also ein neues Objekt, also ein neue Instanz, bei dem Beispiel vom Typ obj, zurück?

    EDIT: Ah, dein edit gelesen.
    Also müsste das theoretisch so funktionieren:

    #include <iostream>
    #include <boost/bind.hpp>
    #include <boost/mem_fn.hpp>
    #include <boost/function.hpp>
    
    struct obj
    {
        void foo(int x) { std::cout << x << std::endl; }
    };
    
    void call(void (*func)())
    {
        func();
    }
    
    typedef void (*func_t)(int x);
    
    int main(int, char**)
    {
        obj x;
        obj nw = &boost::bind(boost::mem_fn(&obj::foo), boost::ref(x), 100);
        nw(); // Würde nw.foo(100) bewirken?
    
        return 0;
    }
    

    Aber ich bin eh auf dem Holzweg...



  • Razoron schrieb:

    Okay, dann hab ich wohl bind noch nicht richtig verstanden?

    bind() ist recht komplex. Es handelt sich um eine Überladung von sehr vielen Funktionstemplates (zumindest solange keine Variadic Templates aus C++0x unterstützt werden). Dass bind() derart mächtig ist, beruht auf der generischen Erzeugung von Funktionsobjekten. Funktionen hingegen kann man nicht einfach so zusammenbasteln.

    Der Rückgabetyp von bind() ist implementation-defined, aber konvertierbar in ein function -Objekt mit einer passenden Signatur. Du wirst es nicht schaffen, einen Funktionszeiger zu erzeugen, der dieses Funktionsobjekt aufrufen kann.

    Razoron schrieb:

    Also müsste das theoretisch so funktionieren

    Nein. Du darfst nicht die Adresse eines temporären Objekts nehmen, ausserdem sind Zeiger und obj inkompatible Typen und haben eigentlich nichts miteinander zu tun.

    Ich rate dir, die Dokumentation zu function und bind ausführlich anzuschauen.



  • boost::bind gibt ein nach boost::function<void()> konvertierbares Objekt zurück. Das ist eine Funktionsobjekt, dessen operator () die Selbe Signatur hat, wie deine Funktion, abgesehen vom 'gebundenen' Parameter. 😉
    Deshalb ist es auch nicht ratsam, Funktionen zu schreiben, die Funktionszeiger als Parameter nehmen, sondern ein boost::function Objekt.



  • Okay, danke für die Information. Mein Ziel ist es ja, eine Methode einer Klasse mit gebundenen Parametern als ein normalen Funktionspointer an eine GLUT Funktion zu übergeben, z.B. glutDisplayFunc(). Ist das überhaupt möglich oder muss ich die umständliche Variante nehmen, eine statische Funktion schreiben, die dann durch glutGetWindow() und Suchen in einem Set von allen Fenstern die entsprechende Klasse findet?



  • Razoron schrieb:

    Ist das überhaupt möglich

    Nein. Mit C++0x und Lambda-Ausdrücken könntest du das aber hinkriegen, da diese in Funktionszeiger konvertierbar sind (übrigens ein sehr nettes Feature :)).

    Razoron schrieb:

    oder muss ich die umständliche Variante nehmen

    Nimmt dein Callback irgendwelche Parameter, z.B. void* ? Dann könntest du andere Argumente da hineinquetschen. Ansonsten wird es mühsam; ich sehe keine Möglichkeit, die ohne statische oder globale Variablen auskommt.



  • Danke für Aufklärung. Dann ist das wohl das Falsche. Ich hab den Standardt bei meinem Compiler eh schon auf C++0x gesetzt, die auto variablen sind echt praktisch :D. Dann werd ich mich mal mit Lambda-Ausdrücken beschäftigen.

    glutDisplayFunc nimmt void (*)() als Operator.


Anmelden zum Antworten