Wie Callback implementieren?
-
Sers,
In meiner Klasse soll der Anwender meine Lib einen Callback registrieren können.
Bis jetzt hab ich das über einen Funktionszeiger gemacht:typedef void (*InputCallback)(const InputData& inputData, float deltaTime); class InputSys { public: setInputCallback(InputCallback* callback); };Das klappt auch bei freien Funktionen, nur Methoden kann man natürlich so nicht registriere. Wie könnte ich das ändern, so dass der Anwender auch Methoden registrieren kann (also ein Foo::myHandler)?
-
hab selbst so gut wie keine erfahrung damit, aber würde man sowas nicht mit templates hinbekommen, also so ungefähr:
template<typename CB> class Foo { typedef void (*CB::InputCallback)(const InputData& inputData, float deltaTime); void setCallback(InputCallback callback); };(delegates heissen doch methoden zeiger glaub ich, oder?)
achja den zusätzlichen zeiger in deienr methoden deklaration ist unnötig, damit hättest du einen zeiger auf einen funktionszeiger
-
Nanni schrieb:
Wie könnte ich das ändern, so dass der Anwender auch Methoden registrieren kann (also ein Foo::myHandler)?
Es gibt in C++ Methodenzeiger. Das Problem ist nur, dass du mit einem Methodenzeiger alleine nicht weiter kommst, da du auch ein Objekt brauchst, auf dem du die Methode aufrufen musst.
Eine allgemeine Lösung wäre vermutlich eine std::function (C++0x) bzw. boost::function (C++03)
-
Sowas ist ganz einfach. Du nimmst eine std::function wie von pumuckl schon erwähnt. Das geht so:
#include <functional> #include <iostream> void call_my_hello_function(const std::function<void()>& function) // Diese Funktion nimmt eine std::function als Parameter { function(); } void free_hello() { std::cout << "Hello, World!" << std::endl; } struct human { void operator () () { std::cout << "Hello, World!" << std::endl; } void sayHello() { std::cout << "Hello, World!" << std::endl; } }; int main() { human h; call_my_hello_function([](){std::cout << "Hello, World!" << std::endl;}); // Lambda-Funktion call_my_hello_function(free_hello); // Freie Funktion call_my_hello_function(h); // operator () call_my_hello_function(std::bind(&human::sayHello, &h)); // Memberfunktion }Und hier mit Ausgabe: http://ideone.com/c5VZx
-
Wenn ich dieses C++0x Ding std::function benutze, läuft das dann auf allen Rechnern? Man braucht dann ja ein C++0x Redistributable (Ich entwickle unter Win7 + VS2010)
Das std::function sieht schon nicht schlecht aus. Aber ich glaub ich benutze eher eine Handlerklasse:
class InputHandler { public: virtual void processInput(InputData& id, float time) = 0; }; class Foo { setInputHandler(InputHandler* ih); }
-
Nanni schrieb:
Man braucht dann ja ein C++0x Redistributable (Ich entwickle unter Win7 + VS2010)
Du brauchst dann das VS2010 Redistributable. Da du sowieso alle benötigten Biblitoheken mit deiner Software mitliefern solltest bzw. angeben solltest, wo sie im Zweifel zu bekommen sind, sollte das nicht das Hindernis sein.
In dem Fall läuft das dann natürlich nur unter Win7 - für andere Betriebssysteme müsstest du es entweder mit einem passenden Compiler auf dem Zielsystem übersetzen bzw. mit einem Crosscompiler für das entsprechende Zielsystem. In beiden Fällen sind ggf. auch passende Redistributables/Laufzeitbibliotheken für das Zielsystem zu finden.
-
pumuckl schrieb:
Nanni schrieb:
Man braucht dann ja ein C++0x Redistributable (Ich entwickle unter Win7 + VS2010)
Du brauchst dann das VS2010 Redistributable. Da du sowieso alle benötigten Biblitoheken mit deiner Software mitliefern solltest bzw. angeben solltest, wo sie im Zweifel zu bekommen sind, sollte das nicht das Hindernis sein.
In dem Fall läuft das dann natürlich nur unter Win7 - für andere Betriebssysteme müsstest du es entweder mit einem passenden Compiler auf dem Zielsystem übersetzen bzw. mit einem Crosscompiler für das entsprechende Zielsystem. In beiden Fällen sind ggf. auch passende Redistributables/Laufzeitbibliotheken für das Zielsystem zu finden.
Das hört sich irgendwie sehr kompliziert an. Ich dachte ich kann die exe einfach jemandem schicken und die läuft dann.
Ich habe in den Projekteinstellungen bei "Runtime Library" "Multi-Threaded DLL" eingestellt. Dann ist doch der ganze C++ Runtime Code in einer .dll, die es auf jedem Windows Rechner standardmäßig geben sollte?

-
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 compiledWeiß 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 hieroglyphenAllerdings. 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.