Memberzeiger zwischen Klassen
-
Hallo,
ich habe ien Problem und wäre dankbar, wenn mir jemand dabei helfen könnte.
An verschiedenen Stellen in meinem Programm ist eine Ja/Nein-Entscheidung vom Benutzer abzufragen. Diese Stellen können in verschiedenen Klassen, die entsprechende Funktionalitäten aufweisen, auftreten. Von diesen Punkten aus möchte ich eine Klasse CYesNoDialog mit new erzeugen und ihr im Konstruktor Memberzeiger auf entsprechende Methoden der den YesNoDialog erzeugenden Klasse übergeben ( jeweils Memberzeiger auf Methoden für die Yes und No Entscheidungen, damit unterschidliche "Protokolle" abgearbeitet werden können ). Der erzeugte YesNoDialog soll der Klasse CCallback wiederum einen Memberzeiger auf seine Funktion CYesNoDialog::Proceed() übergeben, damit gewährleistet ist, dies bei jedem durchlauf abzufragen. die statische Methode CCallback::Call() wird zentral aufgerufen und alle hinzugefügten "Calls" abgearbeitet.
Mein Problem ist nun, dass sich Memberzeiger verschidener Klassen durch den this-Zeiger nicht Konvertieren lassen. Ich möchte auch nicht alle Memberzeiger statisch machen, da innerhalb derer natürlich nicht-statische Member verarbeitet werden sollen.
Vielleicht sehe ich den Wald vor lauter Bäume nicht, aber irgendwie stecke ich fest. Für Anregungen und Vorschläge, auch wie es effizienter zu bewerkstelligen ginge, wäre ich sehr dankbar!
Mfg Oliver
-
1. Methodenzeiger brauchen ein Objekt.
class Foo { public: void bar(int i) { }; }; // ... typedef int (Foo::*func)(int); // ... void call(Foo& foo, func callback) { (foo.*callback)(0); }
2. Man kann Funktoren einsetzen, um die Bindung mitzutragen.
template<typename FuncT> void call(FuncT func) { func(); } class Foo { public: void bar(int i) { } void callBar() { std::tr1::bind(&Foo::bar, this, 0); } }
Falls kein TR1 vorhanden ist, dann gibt es Boost.Bind. Man kann auch über die Funktoren von
<functional>
etwas zusammenbasteln.3. Um allgemeine Funktionen zu aktzeptieren (Edit: und vor allem um zu speichern :)):
void call(std::tr1::function<void()> func) { func(); } // oder void call2(std::tr1::function<void(int,char)> func) { func(0, '0'); }
Falls kein TR1 vorhanden, dann Boost.Function.
An so ein Function Objekt kann auch ein Funktor Objekt übergeben werden, also zum Beispiel das Resultat aus einem Bind.4. Gut, alles nette Theorie, für dich aber unsinnig. Wieso machst du nicht sowas?
inline bool askYesNo(std::string const& question) { std::cout << question << std::endl; std::string answer; std::cin >> answer; return "ja" == answer; }
Natürlich stark vereinfacht, ohne Fehlerprüfung usw. Wäre doch deutlich besser und einfacher?
Grüssli
-
Hey,
vielen Dank erstmal. Erfolgreich finden kann man ja meist nur wenn man weiß wo oder nach was man suchen soll. Daher habe ich deinen Eintrag leider nicht gefunden.
Ich probiere es jetzt mal mit BOOST Threads. POSIX Threads wären mir allerdings lieber. Möchte das aber ungern nachbauen haha
Weißt du ob es mit APR Threads auch dynamisch geht?
-
@LaDispute,
Pst: Du bist im falschen Thread
Ich gebe dir eine Antwort in deinem Thread *g*Grüssli
-
Vielen Dank für die Antwort!
Leider bin ich nicht wirklich schlauer geworden. Vielleicht verstehe ich es mit etwas spezifischerem Code. Um es nochmal auszuführen:
Eine beliebiege Klasse X erzeugt mit class *CYesNo = new CYesNo einen Abfrage-Dialog. Dem Konstruktor sollen u.a. Methodenzeiger für den jeweiligen Antwortabfang übergeben werden ( also &X::Yes und &X::No ( beide Typ void X::xxx (void*) ) als ausführende Methoden ). Der Abfragedialog hat eine Funktion int CYesNo::Proceed(), die bei jedem Programmdurchlauf durch die Klasse CCallback abgefragt werden soll. Darum möchte ich der Methode void CCallback::PushCaller (*pCaller) () den Memberzeiger auf die Methode des aktuellen Objektes CYesNo::Proceed() übergeben. An dieser Stelle tritt der Konvertierungsfehler auf, klar, da unterschiedliche this-Zeiger. Ausserdem kann der Dialog von verschiedenen Stellen im Programm ( also durchaus unterschiedliche Klassen X, Y Z,... ) aufgerufen werden, sodass erneut Konvertierungen erforderlich sind. Wie kann ich dies möglichst flexibel lösen?
Mit tr1 und boost habe ich bisher keine Erfahrungen, ist dies hierfür zwingend notwendig?
Deinen 4. Ansatz verstehe ich nicht ganz, Zumal die Abfrage grafisch erfolgen und aus der Methode heraus direkt umgesetzt werden soll. Im Hintergrund sollen weitere Programmabläufe statt finden, daher der Callback.
Vielleicht könntest Du in kurzform die notwendigen Deklarationen und Konvertierungen aufzeigen und dabei die 3 Klassen einbeziehen. Ich wäre wirklich frah, wenn es sich auf diese Weise lösen liesse.
Mfg und danke nochmal
Oliver
-
Hi S_Oliver,
10 Zeilen Code sagen mehr als 1000 Worte ... ich habe mich da in Deiner Schilderung verheddert (besonders, wo da bestimmt auch noch Tippfehler drin sind).
Schreib doch mal ein kurzes, aussagekräftiges Beispiel (in cpp-Tags), das genau den Fehler produziert...Gruß,
Simon2.
-
Hier nochmal der etwas ausführlichere Code der Problematik.
Die includes habe ich weggelassen, auch könnten Fehler in der Syntax sein, da ich den Code aus dem Ärmel geschüttelt habe.
Die Klasse X soll auf eine überprüfte Kondition hin den Dialog auslösen.class X { public: void Func(); void DoYes (void*); void DoNo (void*); }; void X::Func() { .... if ( Condition ) { CYesNoDialog *YesNo = new CYesNoDialog ( &X::DoYes, &Y::DoNo, "Frage" ); } } class YesNoDialog { public: int Proceed(); YesNoDialog( void (*pYes) (void*), void (*pNo) (void*), std::string szInfoText ); private: void (*pYesCall) (void*); void (*pNoCall) (void*); }; CYesNoDialo::YesNoDialog( void (*pYes) (void*), void (*pNo) (void*), std::string szInfoText ) { pYesCall = pYes; pNoCall = pNo; CCallback::PushCaller( &CYesNoDialog::Proceed ); .... }
bei jedem Durchlauf fragt der Dialog die Yes/No-Konditionen ab und soll die verknüpften Member aufrufen, die dem Konstruktor übergeben wurden.
int CYesNoDialog::Proceed() { if ( YesCondition ) { pYesCall(void*); /*soll zur Methode X::DoYes() springen, aber variabel bleiben, z.B. andere Klasse, amdere Methode*/ return 1; } else if ( NoCondition ) { pNoCall(void*); /*soll zur Methode X::DoNo() springen, aber variabel bleiben, z.B. andere Klasse, amdere Methode*/ return 2; } return 0; } class CCallback { private: static std::vector < void* > m_vCaller; public: static void Proceed(); static void PushCaller ( void (*pCall) (void*) ) { m_vCaller.push_back ( (void*)pCall ); };
Der Callback fragt alle zugefügten Caller ab:
std::vector < void* > m_vCaller; void CCallback::Proceed() { for ( int i = 0; i < m_vCaller.size(); i++ ) m_vCaller.at(i)(); }
Jetz kann an einer beliebigen Stelle ( Methode) der Klasse X ( oder einer anderen ) die Abfrage auftreten. Di Klasse A ist die Hauptklasse, die alle anderen aufruft:
int main() { CA MyA; while() { CCallback::Proceed(); MyA.DoThisAndThat(); /*ruft z.B. die Methode X::Func() auf*/ } return 0; }
Zu den Inkompatibilitäten ( this-Zeiger ) kommt noch die elegante Zerstörung der YesNo-Dialoge. delete this (nicht aufgeführt) erscheint mir fragwürdig.
Das größte Problem bleibt wie gesagt die Konvertierung der Methodenzeiger.
Ich hoffe, das Problem wurde nocheimel deutlich und jemand kann mir ein paar konkrete Tipps geben.Vielen Dank und freundliche Grüße
Oliver
-
Keiner mehr einen Tipp für mich? Habe wirklich keine Ahnung wie ich das Problem ( auf diese Art ) elegant lösen kann!