Wie am besten eine Eventklasse implementieren?
-
Nein, das meinte ich nicht. Ich möchte mehrere Eventhandles einrichten können, also z.B. wenn Taste 'a' gedrückt wird, soll eine bestimmte Funktion aufgerufen werden. Gleichzeitig soll aber auch Taste 'y' überwacht werden, der dann wiederrum eine eigene, aufzurufende Funktion zugeordnet wird. Das klappt in der ersten Lösung mit den Funktionszeigern auch ganz gut, ich könnte in ein Array jede einzelne Adresse der aufzurufenden Funktionen abspeichern, das gleiche ebenfalls mit den Eventkeys (Tasten), die dann alle parallel überwacht werden.
Mit dem Interface jedoch bereitet es mir Schwierigkeiten. Ich habe nur eine von der Basisklasse abgeleitete Klasse (DerivedClass), die die Methoden redefiniert, die später von AbstractAPI aufgerufen werden können. Und jetzt stell dir vor, ich möchte nun mehrere Eventhandles einrichten, es steht mir aber nur eine abgeleitete Klasse zur Verfügung, die die Methoden auch nur einmal redefiniert. Deshalb kann ich letztendlich für jede zu überwachende Taste nur diese einzige Methode aufrufen, für jede weitere müsste ich wieder eine neue, von der Basisklasse abgeleitete Klasse schreiben, um die Neuversion der Methoden zu redefinieren.
-
.filmor schrieb:
Was sind eigentlich die Voraussetzungen? Wenn zB alle Ereignisse bereits zur Compilezeit bekannt sind, dann solltest du möglicherweise Abstand von Laufzeitpolymorphie nehmen.
Die Ereignisse sind natürlich nicht zur Compilezeit bekannt, der Compiler kann ja nicht wissen, wann ich welche Tasten drücken werde. Abgefragt wird der Eingabebuffer mit der WinAPI Funktion GetAsyncKeyState ().
ghorst schrieb:
das "(*this).HandleFunc = static_cast <void*> (HandleFunc);" ist böse, weil das verhalten undefiniert ist.
Deshalb suche ich ja eine bessere Lösung.
ghorst schrieb:
ansonste eleganter ist wohl eine klasse zu schreiben, in der die funktion über eine definierte schnittstelle aufgerufen wird oder halt einen functor zu übergeben.
Klingt gut, könntest du mir das detaillierter erklären?
ghorst schrieb:
ist mir völlig schleierhaft. derive ist von typ DerivedClass*, da musst du nichts casten.
Da gebe ich dir Recht, das war noch ein Anfangsfehler, den ich nicht beseitigt habe.
ghorst schrieb:
zum eigentliche thema: elegant finde ich persönlich die verwendung signal-slot zum handling von events, da man sich den spaß mit den funktionspointern spart.
Und was bedeutet das konkret? Hast du evtl. nen Pseudocode dafür?

.filmor schrieb:
Besser wäre es, du würdest einen Funktionswrapper wie Boost.Function oder std::tr1::function (ist dasselbe, aber letzteres ist zb beim GCC4 dabei während du für Ersteres Boost oder Teile davon installieren musst.
Zwar ist Boost eine tolle Sache, aber ich will von fertigen Libraries in meinem Projekt Abstand nehmen. Gerade die Selbstimplementation ist das reizende an der ganzen Sache.
-
Hat niemand mehr was zu sagen?

-
CodeOriginator schrieb:
Nein, das meinte ich nicht. Ich möchte mehrere Eventhandles einrichten können, also z.B. wenn Taste 'a' gedrückt wird, soll eine bestimmte Funktion aufgerufen werden. Gleichzeitig soll aber auch Taste 'y' überwacht werden, der dann wiederrum eine eigene, aufzurufende Funktion zugeordnet wird. Das klappt in der ersten Lösung mit den Funktionszeigern auch ganz gut, ich könnte in ein Array jede einzelne Adresse der aufzurufenden Funktionen abspeichern, das gleiche ebenfalls mit den Eventkeys (Tasten), die dann alle parallel überwacht werden.
Und wenn du alle Tastenevents haben willst, dann musst du ganz viele Funktionen registrieren. Alle solchen Eventhandler/Callbackfunktionen die ich bisher gesehen hab, haben es anderst gemacht. Die haben immer den KeyCode mitgegeben.
Außerdem wäre es ja auch nicht so schlimm, mehrere Implementierungen für das Interface zu haben, wenn du bei deiner Vorgehensweise bleiben willst.
-
Außerdem wäre es ja auch nicht so schlimm, mehrere Implementierungen für das Interface zu haben, wenn du bei deiner Vorgehensweise bleiben willst.
Die Sache ist ja die, ich suche nach einer optimaleren Lösung. Das mit dem Interface hat (in meiner Version) eben den Nachteil, dass ich die Basisklassenmethoden pro abgeleiteter Klasse nur einmal redefinieren kann. Ich könnte wenigstens noch der bei einem auftretenden Event aufgerufenen Funktion die eben gedrückte Taste per Parameter übergeben, und innerhalb der Funktion mittels switch/case Konstrukten für jeden erwünschten Buchstaben reagieren. Etwa so:
void EventFunktion (char *GedrueckteTaste) { case 'A': // ... break; // usw... }Wenn mir aber trotzdem noch jemand ein konkretes Codebeispiel liefern könnte (-> ghorst!), dann wäre ich sehr erfreut darüber.
-
CodeOriginator schrieb:
Zwar ist Boost eine tolle Sache, aber ich will von fertigen Libraries in meinem Projekt Abstand nehmen. Gerade die Selbstimplementation ist das reizende an der ganzen Sache.
Boost ist in großen Teilen ziemlich „low level“. Du bekommst dort mit Sicherheit keine fertige Lösung für dein Problem. Aber Boost.Function (oder auch std(!)::tr1::function) solltest du auf jeden Fall verwenden. Du schränkst dich mit dem Alles-selbst-implementieren zu sehr ein (die stdlib willst du ja auch nicht selbst bauen, hoffe ich zumindest).
Du kannst ja auf dessen Basis etwas Boost.Signals-mäßiges bauen.CodeOriginator schrieb:
.filmor schrieb:
Was sind eigentlich die Voraussetzungen? Wenn zB alle Ereignisse bereits zur Compilezeit bekannt sind, dann solltest du möglicherweise Abstand von Laufzeitpolymorphie nehmen.
Die Ereignisse sind natürlich nicht zur Compilezeit bekannt, der Compiler kann ja nicht wissen, wann ich welche Tasten drücken werde. Abgefragt wird der Eingabebuffer mit der WinAPI Funktion GetAsyncKeyState ().
Natürlich weiß er nicht, welche Tasten du /drückst/, aber er weiß, welche gedrückt werden /können/.
CodeOriginator schrieb:
ghorst schrieb:
das "(*this).HandleFunc = static_cast <void*> (HandleFunc);" ist böse, weil das verhalten undefiniert ist.
Deshalb suche ich ja eine bessere Lösung.
Entweder Boost.Function oder per Template.
CodeOriginator schrieb:
ghorst schrieb:
ansonste eleganter ist wohl eine klasse zu schreiben, in der die funktion über eine definierte schnittstelle aufgerufen wird oder halt einen functor zu übergeben.
Klingt gut, könntest du mir das detaillierter erklären?
Bist du dir sicher, dass du schon sicher genug in der Sprache bist?
CodeOriginator schrieb:
ghorst schrieb:
zum eigentliche thema: elegant finde ich persönlich die verwendung signal-slot zum handling von events, da man sich den spaß mit den funktionspointern spart.
Und was bedeutet das konkret? Hast du evtl. nen Pseudocode dafür?

Boost.Signals oder libsigc++. Letztlich ist das nichts anderes, als ein noch weiter als Boost.Function verallgemeinerter Funktionszeiger, der beliebig viele aufzurufende Funktionen hat. Etwas in der Art
slot s; s.add (funktion1); s.add (funktion2); s (); // funktion1 und funktion2 werden nacheinander aufgerufen, das wäre der callback
-
Wie .filmor bereits angesprochen hat, währe ne Templateklasse eine Lösung.
-
CodeOriginator schrieb:
ghorst schrieb:
das "(*this).HandleFunc = static_cast <void*> (HandleFunc);" ist böse, weil das verhalten undefiniert ist.
Deshalb suche ich ja eine bessere Lösung.
ich denk, du hast mich falsch verstanden: deine "lösung" ist schlicht falsch, weil sie der standard verbietet.
den passenden link hat bspw finix hier geliefert.CodeOriginator schrieb:
Klingt gut, könntest du mir das detaillierter erklären?
definierte schnittsetelle heißt, basisklasse definieren und darin eine virtual funktion einsetzen, die man dann bspw in dem eventhandler aufruft. functor heißt, den operator() überladen.
ansonsten hat .filmor alles wichtige gesagt.
-
verschick event objekte, die informationen darüber mit sich tragen, was für ein event eingetreten ist. beispiel:
struct KeyPressedEvent { int key; }; class KeyListener { public: virtual void keyPressed(const KeyPressedEvent &event) = 0; };so sind die key events erstmal neutral, sie machen nichts weiter, als informationen darüber zu enthalten, dass eine taste gedrückt wurde und welche taste gedrückt wurde. den listener steht es frei, die gedrückten tasten zu interpretieren, wie es nötig ist.
-
thordk schrieb:
verschick event objekte, die informationen darüber mit sich tragen, was für ein event eingetreten ist. beispiel:
Danke, genau so habe ich es geregelt. Also wenn jeder meint, dass die Lösung mit dem Interface eine gute Lösung ist, dann lasse ich das mal so.