Wie Callback implementieren?



  • Wenn du statisch linkst, ist alles benötigte bei deinem Programm dabei. Dadurch wird die .exe allerdings viel größer, oft einige MB.



  • Nanni schrieb:

    Das hört sich irgendwie sehr kompliziert an. Ich dachte ich kann die exe einfach jemandem schicken und die läuft dann.

    Nein. Im Normalfall laufen .exes erstmal nur auf dem Betriebssystem, für das sie gebaut wurden. Programme, die mit MSVC gebaut werden, brauchen zum Laufen normalerweise die passende Redistributable.

    Ich frage mich allerdings, ob derartige probleme für dich wirklich schon relevant sind. Deinen Fragen nach zu urteilen gehe ich erstmal nicht davon aus, dass du schon produktionsreife Software geschrieben hast, die du an möglichst viele Kunden verkaufen willst. Ich vermute eher, dass du die .exe ein paar Kumpels schicken möchtest um ein wenig zu posen 😉 Sonst könntest du auch einfach deinen Code zippen und verschicken, wenn der Adressat einen Compiler hat (da macht die Frage nach der C++0x-Kompatibilität dann auch wieder Sinn). Am Besten machst du dich mal kundig, was du dabei beachten musst, das gehört dann aber eher ins Compilerforum.



  • Oder die benötigten DLLs im gleichen Verzeichnis mitliefern. Wobei du dann die CRT auch statisch linken könntest.



  • So, habs jetzt mal probiert mit diesem std::functional, aber leider kriege ich eine Fehlermeldung (ohne Zeilenangabe und sau kryptisch 😡 )

    // Hauptklasse
    class Foo {
       typedef std::function<void (const InputData& inputData, float deltaTime)> InputCallback;
       InputCallback        mInputCallback;
    
    public:
       void setInputCallback(InputCallback	inputCallback) {
          mInputCallback = inputCallback;
       }
    };
    
    // Handlerklase:
    class App {
    private:
       Foo* foo;
    
       void inputHandler(const oox::InputData& inputData, float deltaTime);
    
       void bar() {
          foo->setInputCallback( std::bind(&App ::inputHandler, this) ); // kommentiere ich das aus, gibts keinen Fehler
       }
    }
    

    Fehlermeldung (nur der Anfang):

    :\microsoft visual studio 10.0\vc\include\xxresult(28): error C2825: '_Fty': must be a class or namespace when followed by '::'
    1> d:\microsoft visual studio 10.0\vc\include\xxresult(40) : see reference to class template instantiation 'std::tr1::_Result_type1<__formal,_Fty,_Arg0>' being compiled
    1> with
    1> [
    1> __formal=false,
    1> _Fty=void (__thiscall fmd::Application::* const )(const ple::InputData &,float),
    1> _Arg0=std::tr1::_Nil &
    1> ]
    1> d:\microsoft visual studio 10.0\vc\include\xxresult(597) : see reference to class template instantiation 'std::tr1::_Result_of1<_Fty,_Farg0>' being compiled
    1> with
    1> [
    1> _Fty=void (__thiscall fmd::Application::* const )(const ple::InputData &,float),
    1> _Farg0=std::tr1::_Nil &
    1> ]
    1> d:\microsoft visual studio 10.0\vc\include\xrefwrap(28) : see reference to class template instantiation 'std::tr1::_Result_of<_Ty>' being compiled
    1> with
    1> [
    1> _Ty=void (__thiscall fmd::Application::* const (std::tr1::_Nil &))(const ple::InputData &,float)
    1> ]
    1> d:\microsoft visual studio 10.0\vc\include\xxbind1(273) : see reference to class template instantiation 'std::tr1::result_of<_Fty>' being compiled
    1> with
    1> [
    1> _Fty=void (__thiscall fmd::Application::* const (std::tr1::_Nil &))(const ple::InputData &,float)
    1> ]
    1> d:\microsoft visual studio 10.0\vc\include\xxbind0(10) : see reference to class template instantiation 'std::tr1::_Bind1<_Callable,_Arg0>::_Return<_Barg0,_Barg1,_Barg2,_Barg3,_Barg4,_Barg5,_Barg6,_Barg7,_Barg8,_Barg9>' being compiled
    1> with
    1> [
    1> _Callable=std::tr1::_Callable_pmd<void (__thiscall fmd::Application::* const )(const ple::InputData &,float),fmd::Application>,
    1> _Arg0=fmd::Application *,
    1> _Barg0=std::tr1::_Nil &,
    1> _Barg1=std::tr1::_Nil &,
    1> _Barg2=std::tr1::_Nil &,
    1> _Barg3=std::tr1::_Nil &,
    1> _Barg4=std::tr1::_Nil &,
    1> _Barg5=std::tr1::_Nil &,
    1> _Barg6=std::tr1::_Nil &,
    1> _Barg7=std::tr1::_Nil &,
    1> _Barg8=std::tr1::_Nil &,
    1> _Barg9=std::tr1::_Nil &
    1> ]
    1> d:\microsoft visual studio 10.0\vc\include\functional(394) : see reference to class template instantiation 'std::tr1::_Bind_base<_Ret,_BindN>' being compiled

    Weiß jemand was falsch ist?
    Und wieso sind in C++ die Fehlermeldungen oft so ein riesen Haufen kryptischer Scheiß? 😡 😡



  • das liegt an den templates
    die machen die übersicht von den fehlermeldungen immer zunichte, und wenn wie in deinem fall, auch noch 9 template parameter genutzt werden, sieht das schonmal echt aus wie ein haufen ägyptischer hieroglyphen



  • Skym0sh0 schrieb:

    das liegt an den templates
    die machen die übersicht von den fehlermeldungen immer zunichte, und wenn wie in deinem fall, auch noch 9 template parameter genutzt werden, sieht das schonmal echt aus wie ein haufen ägyptischer hieroglyphen

    Allerdings. Ich kann C++ ja eigentlich einigermaßen gut, aber diesen %($"!?= Template Fehlermeldung steig ich regelmäßig aus. Drecks Templates!

    Wenns schon so anfängt mit std::bind(), dann lass ichs. Ich nehm jetzt einfach die Handler Klasse und fertig.



  • Wenn du hinter dem &App im bind-Aufruf tatsächlich ein Leerzeichen stehen hast, solltest du das mal entfernen.

    Warum heißt es einmal oox::InputData, einmal nur InputData?



  • manni66 schrieb:

    Wenn du hinter dem &App im bind-Aufruf tatsächlich ein Leerzeichen stehen hast, solltest du das mal entfernen.

    Habe ich nicht. Habs nur falsch abgetippt-

    manni66 schrieb:

    Warum heißt es einmal oox::InputData, einmal nur InputData?

    Eigentlich ist der Code aufgesplittet auf .h und .cpp und im Header deklariere ich den Namespaces explizit und im .cpp habe ich eine using Direktive.



  • Nanni schrieb:

    manni66 schrieb:

    Wenn du hinter dem &App im bind-Aufruf tatsächlich ein Leerzeichen stehen hast, solltest du das mal entfernen.

    Habe ich nicht. Habs nur falsch abgetippt-

    manni66 schrieb:

    Warum heißt es einmal oox::InputData, einmal nur InputData?

    Eigentlich ist der Code aufgesplittet auf .h und .cpp und im Header deklariere ich den Namespaces explizit und im .cpp habe ich eine using Direktive.

    Wie bitteschön soll jemand etwas sinnvolles zu deinen Fehlermeldungen sagen, wenn du nicht den Code zeigst, der zu den Meldungen führt?



  • manni66 schrieb:

    Nanni schrieb:

    manni66 schrieb:

    Wenn du hinter dem &App im bind-Aufruf tatsächlich ein Leerzeichen stehen hast, solltest du das mal entfernen.

    Habe ich nicht. Habs nur falsch abgetippt-

    manni66 schrieb:

    Warum heißt es einmal oox::InputData, einmal nur InputData?

    Eigentlich ist der Code aufgesplittet auf .h und .cpp und im Header deklariere ich den Namespaces explizit und im .cpp habe ich eine using Direktive.

    Wie bitteschön soll jemand etwas sinnvolles zu deinen Fehlermeldungen sagen, wenn du nicht den Code zeigst, der zu den Meldungen führt?

    Die relevanten Stellen sind gepostet.

    Übrigens ist die Fehlermeldung 5mal so lange als das was ich zitiert habe. Und dennoch so hilfreich wie ein Kropf.



  • Ich würd auch sagen, da fehlt was. Poste doch Mal alles... Bei den Dingern muss man soweit runterscrollen, bis die einen auf den eigenen Code verweisen, dann ist das auch nicht weiter kompliziert zu lesen, das darüber ist halt überflüssig.



  • Zu den Fehlermeldungen bei Templates, lass die beim Visual Studio nicht die Ausgabe sondern die Fehlerliste anzeigen, da sind alle Fehler schön verstämdlich für jedermann aufgelistet, auch bei Templates 🙂



  • Du bindest in deinem Beispiel nur "this", also einen Parameter. Die Funktion hat - aus Sicht von bind() - aber 3 Parameter: das implizite this, was du bindest, und noch zwei weitere.
    Bei bind() musst du aber immer alle Parameter angeben.
    Für Parameter die du nicht binden (festlegen) willst, sondern die Parameter bleiben sollen, gibt es dazu die Platzhalter _1, _2 etc. im Namespace std::placeholders.

    Die Nummer bezieht sich dabei auf die Nummer des Parameters der *neuen* Funktion.

    Also

    // foo->setInputCallback( std::bind(&App ::inputHandler, this) );
    foo->setInputCallback( std::bind(&App::inputHandler, this, std::placeholders::_1, std::placeholders::_2) );
    

    Bzw. alternativ

    using namespace std::placeholders;
    // foo->setInputCallback( std::bind(&App ::inputHandler, this) );
    foo->setInputCallback( std::bind(&App::inputHandler, this, _1, _2) );
    

    So sollte es IMO funktionieren.



  • manni66 schrieb:

    Wenn du hinter dem &App im bind-Aufruf tatsächlich ein Leerzeichen stehen hast, solltest du das mal entfernen.

    Das Leerzeichen an der Stelle ist zwar unüblich, aber durchaus erlaubt.



  • hustbaer schrieb:

    Du bindest in deinem Beispiel nur "this", also einen Parameter. Die Funktion hat - aus Sicht von bind() - aber 3 Parameter: das implizite this, was du bindest, und noch zwei weitere.
    Bei bind() musst du aber immer alle Parameter angeben.
    Für Parameter die du nicht binden (festlegen) willst, sondern die Parameter bleiben sollen, gibt es dazu die Platzhalter _1, _2 etc. im Namespace std::placeholders.

    Die Nummer bezieht sich dabei auf die Nummer des Parameters der *neuen* Funktion.

    Also

    // foo->setInputCallback( std::bind(&App ::inputHandler, this) );
    foo->setInputCallback( std::bind(&App::inputHandler, this, std::placeholders::_1, std::placeholders::_2) );
    

    Bzw. alternativ

    using namespace std::placeholders;
    // foo->setInputCallback( std::bind(&App ::inputHandler, this) );
    foo->setInputCallback( std::bind(&App::inputHandler, this, _1, _2) );
    

    So sollte es IMO funktionieren.

    Danke 👍

    Mit den Änderung klappts tatsächlich. Hab ja gesagt, dass der relevante Code gepostet ist. 🙄

    Hab mich aber dennoch für den InputHandler als Klasse entschieden, weil ich die ganze Syntax mit bind potthässlich finde

    dev->setInputCallback( std::bind(&App::inputHandler, this, std::placeholders::_1, std::placeholders::_2 ) );
    

    vs

    dev->setInputCallback( mInputHandler );
    


  • std::bind() ist konsistent, übersichtlich und vor allem sehr flexibel, wenn man das Grundprinzip verstanden hat. Kommt mir wie ein schlechter Deal vor, nur wegen der Syntax wieder zum InputHandler zurückzugreifen. Zumal du im Endeffekt wahrscheinlich einiges mehr an Code (grösstenteils Boilerplate) benötigst, um das Gleiche zu erreichen.

    Aber natürlich machst du den Namensraum std::placeholders lokal bekannt, sonst musst du dich über umständliche Platzhalter nicht wundern...



  • bind ist für den Aufrufer wesentlich flexibler. Er kann ganz einfach seine eigenen Klasse und Funktionen (jeweils mit eigenem Interface) ohne nennenswerten Aufwand als Callback registrieren, ohne jedesmal umständlich eine neue Klasse von InputHandler abzuleiten (das ist eher Java-Stil, weil es da nicht besser geht). Und wenn dem Benutzer die bind -Syntax nicht gefällt (das hat ja gar nichts mit dir zu tun, du willst ja nur eine entsprechende std::function ), kann er immernoch einen eigenen Funktor schreiben (Klasse mit überladenem ()-Operator).



  • ipsec schrieb:

    Und wenn dem Benutzer die bind -Syntax nicht gefällt (das hat ja gar nichts mit dir zu tun, du willst ja nur eine entsprechende std::function ), kann er immernoch einen eigenen Funktor schreiben (Klasse mit überladenem ()-Operator).

    Oder, da sich Nanni ja scheinbar schon mit std::function im C++0x-Bereich bewegt, auch einfach ein lambda:

    foo->setInputCallback([&](const InputData& inputData, float deltaTime)
                                { inputHandler(inputData, deltaTime); }
                              );
    


  • Nanni schrieb:

    Und wieso sind in C++ die Fehlermeldungen oft so ein riesen Haufen kryptischer Scheiß? 😡 😡

    C++ ist die wohl am schwierigst zu parsende Sprache der Welt. Ein Wunder, dass ein Compiler den Code überhaupt übersetzen kann. Das geht, da eindeutig definiert ist (im C++ Standard) was richtig ist und was nicht. Der Compiler gibt dir die Informationen, die er hat. Woher soll er wissen, dass hier Typ XY hineingehört? Und sei froh, dass er dir so viel Information gibt, wie möglich. Er könnte genauso sagen "error in file XY".


Anmelden zum Antworten