Pointer auf Klassenfunktionen
-
Hallo Leute, habt ihr vielleicht eine Ahnung, wieso der folgende Code nicht funktioniert ? Ich habe die Fehlermeldungen des Compilers jeweils über der Fehlerzeile als Kommentar geschrieben...
// ****************************************** headers ***************************************** #include <conio.h> #include <stdio.h> // ******************************************************************************************** // ***************************************** Keyboard ***************************************** class Keyboard{ private: void (Keyboard::*onKeyPressedAction)(int key); public: Keyboard(void); ~Keyboard(void){} void setKeyPressedAction(void (Keyboard::*onKeyPressedAction)(int key)); void update(void); }; Keyboard::Keyboard(void){ this->onKeyPressedAction = 0x00; } void Keyboard::setKeyPressedAction(void (Keyboard::*onKeyPressedAction)(int key)){ this->onKeyPressedAction = onKeyPressedAction; } void Keyboard::update(void){ // error C2064: term does not evaluate to a function if(this->onKeyPressedAction) this->onKeyPressedAction(kbhit()); } // ******************************************************************************************** // ******************************************* Game ******************************************* class Game{ private: Keyboard keyb; int cUpdate; void keyPressedAction(int key); public: Game(void); ~Game(void){} void update(void); }; Game::Game(void){ // error C2276: '&' : illegal operation on bound member function expression this->keyb.setKeyPressedAction(reinterpret_cast<void (Keyboard::*)(int)>(&this->keyPressedAction)); } void Game::update(void){ this->cUpdate++; } void Game::keyPressedAction(int key){ printf("Die Taste \"%s\" wurde gedrückt, dies ist die %i. Taste, die Sie gedrückt haben...",key,this->cUpdate); } // ******************************************************************************************** // ******************************************* main ******************************************* int main(void){ Game g; while(true) g.update(); return 0; } // ********************************************************************************************
Lieber Gruss Ishildur
-
Vielleicht so?
if(this->onKeyPressedAction) (this->*onKeyPressedAction)(kbhit()); this->keyb.setKeyPressedAction(reinterpret_cast<void (Keyboard::*)(int)>(&keyPressedAction));
-
Achtung!
// ****************************************** headers ***************************************** #include <conio.h> #include <cstdio> //cstdio ist die "neue version" von stdio.h // ******************************************************************************************** // ***************************************** Keyboard ***************************************** class Keyboard{ private: void (Keyboard::*onKeyPressedAction)(int key); //onKeyPressedAction ist also ein Zeiger, der nur auf Elementfunktionen //der Klasse Keyboard zeigen darf, die ein int als Parameter haben und //nichts zurückgeben public: Keyboard(void); ~Keyboard(void){} void setKeyPressedAction(void (Keyboard::*onKeyPressedAction)(int key)); void update(void); }; Keyboard::Keyboard(void){ this->onKeyPressedAction = 0x00; } void Keyboard::setKeyPressedAction(void (Keyboard::*onKeyPressedAction)(int key)){ this->onKeyPressedAction = onKeyPressedAction; //hm warum nicht einfach nen anderen namen nehmen? } //... Game::Game(void){ // error C2276: '&' : illegal operation on bound member function expression keyb.setKeyPressedAction(reinterpret_cast<void (Keyboard::*)(int)>(&keyPressedAction)); //hier ginge auch: &Game::keyPressedAction //und da sieht man schon das problem. //dieser reinterpret_cast wird in die hose gehen. } void Game::update(void){ cUpdate++; //es muss nicht immer this-> sein } //...
Keyboard::setKeyPressedAction akzeptiert nur Zeiger auf Elementfunktion der Klasse Keyboard. Auch ein reinterpret_cast hilft hier nicht viel.
-
Ich dachte halt, dass der Klassenpointer auch immer 4 Bytes gross ist. Soviel ich weiss gibt es eben keine generische Klassenzeiger in der Art void (void::*onKeyPressed)(int key);
Kennst du vielleicht Delphi ? Dort haben Sie dieses Problem irgendwie gelöst!
-
Gibt es denn keine Möglichkeit, diese Programmarchitektur zu realisieren ??
-
Reichen dir die Anregungen nicht, die du in diesem Thread bekommen hast?
-
Also eine Antwort habe ich dort nicht gefunden, nein. Ich interessiere mich für den technischen Hintergrund der Programmarchitektur des Delphi - Framework, welches auch in der VCL von Borland verwendet wird.
Ich bin überzeugt davon, dass es eine Möglichkeit gibt, doch reichen meine Kenntnisse nicht aus. Aus diesem Grund habe ich auch in dieses Forum geschrieben. Zuerst schrieb ich ins Spieleprogrammier - Forum, weil ich dachte, dass dort die Leute anzutreffen sind, welche kaum auf Bibliotheken zurückgreifen würden und somit über die nötigen Hintergrundkenntnisse verfügen würden. Als das nichts brachte, versuchte ich es noch in diesem Forum, da ich dachte, dass sich hier andere Leute herumtreiben und wer weiss, vielleicht hätte ich ja Glück...
Ich weiss nicht so recht, ob du mir einen Vorwurf machst, aber falls ja, wieso erklärst du mir nicht, welche "Anregung" denn als eine Antwort auf meine eigentliche Frage zu interpretieren ist ??
-
Hallo!
Wie wäre es denn mit einem Interface? Du definierst Dir eine Klasse die (für Dein Beispiel) nur eine abstrakte Methode enthält. Objekte die diese Methode implementieren sollen werden von dieser Klasse (dem Interface) abgeleitet. Über einen Zeiger auf das Interface kannst Du die Methode dann ansprechen.
Viele Grüße
Thomas
-
Ja das Problem wäre dann nur, dass ich am Schluss meine Game Klasse etwa von 100 anderen ableiten müsste, nein ich denke, nicht dass dies eine Lösung wäre, aber danke dir trotzdem.
Übrigens, ich suche nicht nach Alternativen, sondern ich möchte es wirklich so wie im source - code machen. Es geht nur um das korrekte Type - Casting...
-
Ishildur schrieb:
Ich interessiere mich für den technischen Hintergrund der Programmarchitektur des Delphi - Framework, welches auch in der VCL von Borland verwendet wird.
Naja, Delphi ist eine andere Sprache, da kannst du nicht erwarten, dass es in C++ genauso funktioniert. Beim Borland C++ Builder funktioniert es zwar genauso, aber die haben ihren Compiler dementsprechend erweitert. In "reinem" C++ muss man das mit Templates lösen.
Hier hast du was zu lesen: http://www.gotw.ca/gotw/083.htm
Ich würde aber die Interface-Variante nehmen.
-
Ishildur schrieb:
Ja das Problem wäre dann nur, dass ich am Schluss meine Game Klasse etwa von 100 anderen ableiten müsste
100? Was soll die Klasse denn alles für Events verarbeiten? Da würde ich mich eher ranmachen und das Design nochmal überdenken.
-
Nein nein, da hast du was falsch verstanden!
Die Keyboard Klasse wäre ja nur eine von sehr sehr vielen Klassen, die Events versenden würde...Ich versuche meine Frage etwas allgemeiner zu Formulieren:
Unter C wird oft mit Callback Funktionen gearbeitet. Was ich möchte, wäre eine Callback Funktion, die aber eine Memberfunktion einer Klasseninstanz sein kann.Oder Stell dir mal folgendes vor:
// ******************** Konventionell ********************
class Tetris{
public:
bool isThereABlockImpact(void)
};Tetris t1,t2;
while(true){
if(t1.isThereABlockImpact() || t1.isThereABlockImpact()) DoSomething();
}
// *******************************************************// ****************** Meine Architektur ******************
class Tetris{
public:
void (*onBlockImpactAction)(Tetris *pTetris);
};onBlockImpact(Tetris *pTetris){
DoSomething();
}Tetris t1,t2;
t1.onBlockImpactAction = t2.onBlockImpactAction = onBlockImpact;
// *******************************************************Die gesammte WinApi, sowie viele Bereiche der DirectX Api funktionieren nach diesem Prinzip. Ich möchte nun noch einen Schritt weitergehen, indem ich innerhalb der Callback funktionen auch auf Membervariablen von Klasseninstancen zugreifen kann...
-
Ishildur schrieb:
Unter C wird oft mit Callback Funktionen gearbeitet. Was ich möchte, wäre eine Callback Funktion, die aber eine Memberfunktion einer Klasseninstanz sein kann.
Ja, schon klar. Aber so einfach ist das nicht, da Memberfunktionen beim Aufruf an ein Objekt gebunden sind.
Schau dir den Link an, den ich dir gegeben habe.
-
Templates kommen für mich leider nicht in Frage, da dies eine Bibliothek wird, die in einer DLL ausgeliefert werden soll...
Du hast geschriben, eine memberfunktion sei an ein Objekt gebunden. Technisch gesehen ist sie doch nur an einen 32 - Bit Wert gebunden, und mit einem reinterpret_cast sollte es doch nicht allzuschwer sein das ganze zu casten.
Ich meine folgendes geht ja auch:
struct Point{ int xPos,yPos; }; struct Dimension{ int xDir,yDir; }; int main(){ Point p = {5,15}; Dimension *pDim = reinterpret_cast<Dimension*>(&p); std::cout << pDim->xDir; };
Also muss mann doch so etwas auch mit einer Memeberfunktion machen können ??
-
theoretisch.
eine memberfunktion bekommt immer als unsichtbares argument den this zeiger mitübergeben. sie zu casten wäre also gleich(ähnlich)bedeutend damit, folgendes zu tun:void foo (int) {} void bar (double,double) {} int main () { void (*a) (int) = reinterpret_cast<void(*)(int)>(&bar); (*a)(10); //bar erwartet aber 2 argumente. }
damit wird die funktion sehr unwahrscheinlich funktionieren, auch wenn nur ein typ eines parameters geändert wird. der compiler sieht da aber keinen unterschied.
womöglich gibt es aber sogar welche, die das zulassen.
ich bezweifle außerdem, dass der code durch solche dinge einfacher und wartbarer wird.
-
Verstehe nicht wo das Problem mit den Templates ist, die gibts ja nur in einer kleinen Callback-Klasse. Deine Library kann ja weiterhin in einer DLL stehen.
Dieses rumgecaste ist doch alles nicht das wahre.
-
Ein Methodenzeiger in Delphi sind eigentlich zwei Zeiger, einer auf eine Instanz und einer auf eine Methode. Ein C++-Elementfunktions"zeiger" kann alles Mögliche sein und ist deshalb nicht einmal implizit nach void* konvertierbar, auf jeden Fall aber wird kein Objekt mitgespeichert. Die beiden funktionieren technisch also völlig verschieden.
Wenn du eine Delphi-ähnliche Lösung suchst, kommt boost::function von www.boost.org dem Ganzen sehr nahe, hat vielleicht sogar mehr Vorteile (man kann es an beliebige Funktoren, also auch globale Funktionen binden) als Nachteile (auf 10 Parameter beschränkt, evtl. langsamer?). Um eine Elementfunktion als ein Callback zu binden, das als boost::function gespeichert wird, kannst du dann boost::mem_fn oder boost::bind verwenden.