C++ CallBack Function

Xervos

Erfahrenes Mitglied
Hallo Leute,

ich habe da mal eine Frage. Ich möchte ganz gerne ein GUI schreiben. Ich habe hier eine Klasse Button erstellt. Die sieht ungefähr so aus: (ob diese genau so bleibt weiß ich noch nicht ein bisschen was muss ich sicher noch machen aber zu 90% sollte es so bleiben)

Code:
class button
{
public:
	button(const std::String &sButtonImageFile,
			float fXposition,
			float fYposition,
			float fLength,
			float fHeight,
			GameState::GameState gsDrawOnGameState);
	
    button();	
	~button();
	
	// Functions for Set the Button Visible/Invisible this flag is stronger then bAvailability
	void SetButtonVisible(); // This function shoule be Called to set the Drawbutton flag Visible
	void SetButtonInVisible(); // This function shoule be Called to set the Drawbutton flag to InVisble 
	bool Visibility(); // This Function Returns if the Button should be Drawn or not
	
	// This Function Just say if the Aktiv/NoAktiv state should be performed
	void SetButtonEnable(); // This Function should be called to Enable the Button 
	void SetButtonDisable(); // This Function should be called to Disable the Button 
	bool Availability(); // This Function Returns if the Button is ClickAble or not
	
	//This Functions should be called to Set the Button Aktiv or not
	void SetButtonAktiv(); //will set the Button to Aktiv 
	void SetButtonInaktiv(); //will set the Button to InAktiv
	bool Activity();
	
	// Set SubRect for the Button and his two States
	void SetInAktivRectAngle(sf::IntRect irInAktivRect); // this Function should be Called to set SubRect to show when Button is not Aktiv -- NO Mouse over
	void SetAktivRectAngle(sf::IntRect irAktivRect); // this Function should be Called to set SubRect to show when Button is Aktiv -- When Mouse over
	
	//Set the GameState when the Button Is Fired
	void SetGameStateWhenButtonFired(GameState::GameState &gsButtonFired); // this Function should be called the State which will be Set when Button is Fired
	
	//ButtonFired Function
	GameState::GameState ButtonFired(); // This Function returns the GameState which should be set when button is fired
	
	// When should the Button be Loaded
	GameState::GameState& GetShowOnGameState();
	
	// Get The Sprite
	sf::Sprite& GetSprite();
	
	//Button Fired
	void ButtonFired();
	
private:
	
	sf::image _iLoadImage;
	sf::Sprite _spButtonSprite;
	
	sf::IntRect _irAktivRect;
	sf::IntRect _irInAktivRect;
	
	bool _bVisibility;
	bool _bAvailability;
	bool _bActivity;
	
	float _fLength;
	float _fHeight;
	
	GameState::GameState _gsLoadOnGameState;
	
	

};

so nun habe ich eine Frage zu der Funktion

Code:
void ButtonFired();

Ich möchte hier individuell bleiben soll heißen das ich mich im Objekt Button nicht darum kümmern muss was ich den da aufrufen muss usw. Ich möchte hier die Möglichkeit bieten das der ersteller des Objekts dem Objekt mitteilt "So und wenn du gedrückst wurdest bitte diese Funktion von mir aufrufen"

zb. MainWindowExit();

Nochmal zur Ansicht.

In meinem Programm gibt es 3 Klassen, Das MainWindow, den ObjektManager und Button.

Die Klasse MainWindow meldet beim ObjektManager an das es einen neuen Button an der stelle X,Y mit dem Image "TEST.png" anlegen soll.

und ruft dazu die Funktion

Code:
bool CreateButton(std::string i_szButtonImageFile, 
                   float fXposition,
		   float fYposition,
		   float fLength,
		   float fHeight);

des ObjecManager auf. Also zb so:

Code:
m_ObjectManager->CreateButton("Sprites\\Gui\\button_start.png",270.00f, 300.00f, 100.00f, 50.00f);

Der ObjectManager legt diesen Button an und legt einen Zeiger auf diese Objekt in eine LIste (wie ich diesen dann wieder löschen will ist in einem anderen Thema drinnen)

ok so der Button wurde erzeugt und angelegt. Aber wie weiß das MainWindow nun das der Button gedrückt wurde?

Ich habe hier schon eine Funktion die MausInputs auffrägt und auswertet. Je nachem ob die MausPosi über dem Button ist und der Linke Mausknopf gedrückt wurde ruft der Objektmanager die Funktion

Code:
ButtonFired()

des Objekts auf. Naja im moment Passiert ja noch nichts, aber wie teile ich dem Button Objekt nun mit welche Funktion er aufrufen soll wenn er gedrückt wurde?

Ich habe mir das ganze so überlegt es mit einer Callback Funktion zu lösen nur bekomme ich das nicht hin. Meine Idee dahinter, ich speicher mir die Adress der Funktion in einer Zeigervariable und rufe diese dann auf. Dem Button ist es ja völlig egal was passiert wenn er gedrückt wird, er hat nur die Aufgabe es irgendwem mitzuteilen das es passiert.

kann mir da einer Helfen ****

lg
 
Hallo,

Du kannst dazu die Funktionsobjekte aus std::tr1 (bzw. boost, wenn dein Compiler std::tr1 noch nicht unterstützt) verwenden. Als kleines Minimalbeispiel:
C++:
#include <functional>
#include <list>

class Button
{
   std::list<std::tr1::function<void (void)>> m_clickCallbacks;
public:
   void AddClickCallback(const std::tr1::function<void (void)>& target)
   {
      m_clickCallbacks.push_back(target);
   }

   void ButtonFired()
   {
      std::list<std::tr1::function<void (void)>>::iterator itr = m_clickCallbacks.begin();
      for( ; itr != m_clickCallbacks.end(); ++itr)
         (*itr)();
   }
};

Natürlich fehlen da noch wichtige Elemente wie Threadsicherheit, aber von der Idee her sollte es klar sein.

PS:
Wenn du Visual Studio 2010 hast ist std::tr1 unterstützt und auch andere Teile von C++0x wie z.B. Lambdas, das würde dann ButtonFired etwas einfacher machen:
C++:
#include <algorithm>
//...
	void ButtonFired()
	{
		std::for_each(m_clickCallbacks.begin(), m_clickCallbacks.end(),
			[](std::tr1::function<void (void)>& s) { s(); }
		);
	}

Gruss
Muepe
 
Ahh cool das wusste ich ja gar nicht das es so auch geht :)

Nur noch eine Frage dazu wie mach ich das dann mit der Übergabe?

zb habe ich eine Funktion "MainWindowExit" in der Klasse MainWindow. Wie übergebe ich das dann ?
 
Hallo Xervos,

Gegeben folgende Funktionsdeklaration:
C++:
class MainWindow
{
public:
   void MainWindowExit();
};

wäre die Übergabe dann folgendermassen:
C++:
MainWindow mainWindow;
Button bt;
bt.AddClickCallback(std::tr1::bind(&MainWindow::MainWindowExit, &mainWindow));
 
Ahh
Cool danke dir :) dann werde ich mir das auch mal anschauen. Im moment geht es super vorran das ganze läuft echt schon ordentlich.

Eine frage habe ich aber schon noch. Also ich lese immer das man Referenzen an eine Funktion übergeben soll. Da stellt sich nun die Frage, ab wann lohnt es sich eine Referenz zu übergeben und ab wann kann ich eine Copy übergeben. Ich meine zb bei einem Int wert, den als referenz zu übergeben kommt mir ein bisschen komisch vor. Aber ich weiß nicht was meint ihr zu dem Thema, bzw. was sagt da die Regel ?
 
Richtige Regeln gibt es nicht, aber man kann sich ein paar Richtlinien zurechtzimmern:

Pointer verwende ich bei Parametern, die optional sein können

Referenzen verwende ich dann, wenn ich das übergebene Objekt manipulieren möchte.

const Referenzen verwende ich dann, wenn das übergebene Objekt nicht manipuliert werden soll, und es größer ist als die Busbreite des Prozessors (soll heißen, int oder char mit Referenz macht wenig Sinn, aber bei größeren Objekten durchaus (std::string, etc.), da diese sonst kopiert werden müssen
 
Hmm also zb so einfache Typen wie Bool, int, float kann ich als Kopie übergeben sobald es etwas Komplexer wird als Referenz. Ich habe mir das Buch C/C++: Von den Grundlagen zur professionellen Programmierung gekauft und da steht drinnen das man es vermeiden sollte Pointer zu übergeben da immer eine Copy übergeben wird am besten ist es die Refernz zu übergeben, also wenn ich zb was verändern will die Refernz und wenn ich nur was nachschauen will in einer Variable oder eine GetFunktion aufrufe dann nehme ich einen pointer habe ich das so richtig verstanden ?
 
Hi

kleine Korrektur: Bei einem Pointer wird NIE eine Kopie der tatsächlichen Variable übergeben.
Kopiert wird maximal ein int für die Adresse.
Wenn man eine 10-KB-Variable hat macht es schon einen Geschwindigkeitsunterschied, ob diese oder 4 bzw. 8 Byte kopiert werden.

Referenzen machen ca. das Gleiche wie Pointer, haben aber eine komfortablere Schreibweise (und dafür wieder Nachteile wie fehlende Möglichkeit zur Pointerarithmetik).

Gruß
 
Zurück