2015-02-06 7 views
5

Ich experimentiere gerade mit der FLTK GUI Bibliothek, bin mir aber nicht sicher, was mit Callback-Funktionen los ist. Speziell der Typ Casting ist mir etwas fremd. Das folgende Beispiel zeigt ein einfaches Fenster mit einer Schaltfläche "Drücken". Wenn Sie die Taste drücken, ändert sich die Bezeichnung in "Fertig".Type Casting/Callback Funktionen

Das Label, das an die Callback-Funktion übergeben wird, wird als Typ const char* deklariert und dann wird es gegossen void*, zu geben, aber es ist möglich, dies als std::string zu erklären und warf dann void*? Ich bevorzuge moderne C++ - Zeichenfolgen als char Notation.

Auch, ist die Syntax unten die beste Möglichkeit zum Umwandeln von einem Typ zum anderen? Ich habe gesehen, static_cast<type>() vorher verwendet, aber was ist der sicherste/beste Weg, um eine Besetzung von const char* zu void* oder umgekehrt und warum? Was würde C++ 11 empfehlen?

#include <FL/Fl.H> 
#include <FL/Fl_Window.H> 
#include <FL/Fl_Button.H> 
#include <FL/Fl_Native_File_Chooser.H> 
#include <string> 

void xyz_callback(Fl_Widget* w, void* userdata) 
{ 

    Fl_Button* b = (Fl_Button*)w; //cast widget w to button and assign to b 

    b->label((const char*)userdata); //cast userdata back to type const char* 
} 


int main(int argc, char **argv) 
{ 
    Fl_Window *window = new Fl_Window(340,180); 
    Fl_Button *button = new Fl_Button(20,40,300,100, "label"); 
    button->label("Press"); 
    button->labelfont(FL_BOLD+FL_ITALIC); 
    button->labelsize(36); 
    button->labeltype(FL_SHADOW_LABEL); 
    button->when(FL_WHEN_RELEASE); 

    const char* word = "Done"; 
    button->callback(xyz_callback, (void*)word); //cast word to type void* 

    window->end(); 
    window->show(argc, argv); 
    return Fl::run(); 
} 
+0

aber ist es möglich, dies als std :: string zu erklären und werfen dann auf „void *“? Ja. –

+1

@BartekBanachewicz Möchten Sie die std :: string oder einen Zeiger auf std :: string? – harper

+0

Nun, ein Zeiger darauf. –

Antwort

6

(T)a -Stil Guss, auch als C-Stil Gießen bekannt, es tatsächlich den schlimmste Weg eine explizite Konvertierung in C++ durchzuführen. Das ist, weil es das leistungsfähigste ist — es wird glücklich fast alles umwandeln, schwere Fehler leicht versteckend. Es ist die einzige Form der expliziten Typkonvertierung, die in C verfügbar ist, und wurde als solche von C++ geerbt, sollte aber nie wirklich in Qualitäts-C++ - Code verwendet werden.

Die Besetzung von word zu void* ist unnötig — jeder Zeigertyp Objekt kann implizit in einen Zeiger auf void umgewandelt werden.

Die Rückübertragung auf im Callback ist notwendig, könnte aber mit einem static_cast<const char*>(userdata) erfolgen.

Um die Frage zu beantworten std::string: das hängt von der Lebensdauer ab. Sie können eine std::string* in eine void* konvertieren und an den Rückruf übergeben. Dort würden Sie es zurück in str::string* (und dann abrufen c_str() von ihm, um die label Funktion übergeben). Aber Sie müssen sicherstellen, dass das std::string, auf das gezeigt wird, immer noch am Leben ist (hat den Gültigkeitsbereich nicht verlassen), wenn der Rückruf aufgerufen wird. Wenn Sie es zu einer lokalen Variablen in main machen, sind Sie ziemlich sicher.

Wenn Sie es auf diese Weise tun, wird der Code wie folgt aussehen:

void xyz_callback(Fl_Widget* w, void* userdata) 
{ 
    Fl_Button* b = static_cast<Fl_Button*>(w); 

    b->label(static_cast<std::string*>(userdata)->c_str()); 
} 


int main(int argc, char **argv) 
{ 
    Fl_Window *window = new Fl_Window(340,180); 
    Fl_Button *button = new Fl_Button(20,40,300,100, "label"); 
    button->label("Press"); 
    button->labelfont(FL_BOLD+FL_ITALIC); 
    button->labelsize(36); 
    button->labeltype(FL_SHADOW_LABEL); 
    button->when(FL_WHEN_RELEASE); 

    std::string word = "Done"; 
    button->callback(xyz_callback, &word); 

    window->end(); 
    window->show(argc, argv); 
    return Fl::run(); 
} 
+0

Vielen Dank Angew - das ist genau die Art von Informationen, die ich brauche, um einige Dinge in meinem Kopf zu klären, und ich bin viel glücklicher mit dem Code, den Sie vorgeschlagen haben. Danke, dass du dir die Zeit genommen hast zu antworten! –

+0

@EdwardJeckyll Wenn eine Antwort Ihr Problem löst, sollten Sie es als * akzeptieren * markieren (indem Sie auf das grüne Häkchen daneben klicken, maximal eine akzeptierte Antwort pro Frage). Dies markiert die Frage als gelöst und gibt sowohl dem Antworter als auch Ihnen einen gewissen Ruf. Es ist die [Stack Overflow Art zu sagen "Danke, es hat funktioniert"] (http://stackoverflow.com/help/someone-answers). – Angew

+0

Verstanden, danke Angew. –

0

Wenn Ihr Rückruf ein void* als Sie erwartet einen Zeiger auf etwas passieren kann. Aber der Callback muss wissen, wie er mit dem Argument umgehen soll. Es muss es auf einen verwendbaren Typ zurückgeworfen werden.

Sie können den Rückruf so ändern, dass er einen Zeiger auf std::string erwartet. Wenn Sie ein const char* in dem Rückruf haben wollen, müssen Sie den C-String an den Rückruf übergeben:

button->callback(xyz_callback, str.c_str()); 
+0

minus 1 - jemanden zu sagen, 'c_str()' zu verwenden und die Probleme der Lebensdauerverwaltung nicht zu beschreiben, insb. in einem Rückruf. Wenn Sie Ihren Code in den Code der obigen Frage einfügen, würde das fürchterlich sein. – Yakk

+0

@Yakk Der Rückruf wird synchron aufgerufen. Die Callbacks müssen sowieso die Daten übernehmen. Das Übergeben eines Zeigers an eine lokale Variable hat das gleiche Problem. Aber das ist nicht Teil der Frage. Sie können nicht erwarten, hier ein Buch mit Programmierstil zu lesen. – harper

+0

good point - sie machen ein ':: run()', bevor 'main' beendet wird. – Yakk