Programmstruktur wiederholen mit anderen Funktionen
-
Booah cool, danke für die Mühe

So hatte ich mir das vorgestellt, wusst nur nicht wie umsetzen.Jetzt muss ich mir noch die boost::function0 reinziehn, sind das sowas wie Funktionszeiger?
-
Ja, aber allgemeiner. boost::function nimmt auch Funktionsobjekte (also Instanzen von Klassen mit einem operator() ) entgegen. Deshalb kannst du einfach Menu-Objekte als Untermenüs registrieren.
-
Hey Danke!
You made my day
-
Wow voll der Vollprofi.

-
Ja und ich bin voll der nOOP. Der Optimismus verfliegt schnell.
Bin wohl zu blöd boost unter vs2005EE zu installieren.
Ich blick bei dem Wirrwarr in der Doku nicht durch.
Gibt es nicht was aus der STL das auch gehen würd?
-
Kopier den Ordner "boost" aus dem Zip-Packet nach C:\Programme\Microsoft Visual Studio 8\VC\include
-
Und das soll alles sein?
Was sollen denn die Seitenlangen beschreibungen in der Doku?btw: wär boost nicht mal nen Artikel, oder mindestens nen FAQ eintrag wert?
hab mit der forensuche irgendwie Nichts gescheites gefunden.
-
Einige Libraries muss man kompilieren und nur das macht die Installation ein bisschen kompliziert. Für boost::function und viele andere Sachen ist das aber nicht nötig. :schland:
-
Hi .filmor
Ich hab jetzt nach längerer Pause (ja es ist Sommer
)
versucht deine Klasse umzusetzen. Echt ne tolle Sache
Funktioniert fast alles:// Header #include <boost/function.hpp> #include <map> class Menu { public: typedef boost::function0<void> functor_type; typedef std::pair<std::string, functor_type> option_type; typedef std::map<int, option_type> map_type; typedef map_type::const_iterator map_iter; Menu( map_type map ) : map_( map ) {} Menu() {} ~Menu() {} void register_option( const option_type& option ); void register_option( const option_type& option, int position ); void operator() () const; private: map_type map_; }; // Implementation // ... void Menu::operator() () const { bool done = false; do { int n = 1; for( map_iter it = map_.begin(); it != map_.end(); ++it ) { std::cout << '[' << it->first << "] " << it->second.first << std::endl; ++n; } int selection; std::cin >> selection; if( ( map_iter it = map_.find( selection ) ) != map_.end() ) { it->second; done = true; } else { std::cout << "Please make a selection between 1 and " << n - 1 << std::endl; } } while( !done ); }aber bei der Zeile
if( ( map_iter it = map_.find( selection ) ) != map_.end() )motzt der Compiler.
c:\..menu.cpp(28) : error C2146: syntax error : missing ')' before identifier 'it'
c:\..menu.cpp(28) : error C2065: 'it' : undeclared identifier
c:\..menu.cpp(28) : error C2143: syntax error : missing ';' before '!='
c:\..menu.cpp(28) : warning C4390: ';' : empty controlled statement found; is this the intent?
c:\..menu.cpp(28) : error C2059: syntax error : ')'
c:\..menu.cpp(28) : error C2143: syntax error : missing ';' before '{'Ich kann aber nicht finden wo mein Fehler ist, hab die Zeile aus deinem Beispiel übernommen. Da fehlt doch auch keine Klammer oder?
Ne andere Frage wäre, wofür du das int n brauchst in der for Schlaufe:
for (map_type::const_iterator i = _map.begin (), int n = 1; i != _map.end (); ++i, ++n) std::cout << '[' << i->first << "] " << i->second.first << '\n';Danke dann auch schonmal im voraus
-
Seltsamerweise geht es, wenn ich den Iterator vorher deklariere:
map_iter it; if( ( it = map_.find( selection ) ) != map_.end() ) { ...any ideas

Übrigens, um die Funktion aufzurufen, müsste es dann auch so heissen:
if( ( it = map_.find( selection ) ) != map_.end() ) { it->second.second(); // <-- !!! done = true; }it->second wäre ja der option_type
it->second.second ist erst der Functor, und den Operator() brauchts ja auch noch
-
Sorry, wenn ich hier weiter frage, aber will nicht nen neuen Thread aufmachen, gehört ja eigentlich dazu.
Ich hab nun also meine tolle dynamische Klasse "Menu" (c) .filmor

Registriere zum testen in der Main() die Menus "zu Fuss", klappt alles wunderbar. Hab noch die Option 0 für exit reingebaut, die die Do While Schlaufe beendet, damit man nach ausführen der Option wieder zum Menu zurückkommt.Menu.cpp (ausschnitt)
void Menu::operator() () const { bool done = false; do { std::cout << endl << title_ << std::endl << std::endl; int n = 1; for( map_iter it = map_.begin(); it != map_.end(); ++it ) { std::cout << '[' << it->first << "] " << it->second.first << std::endl; ++n; } std::cout << std::endl << "[0] Exit" << std::endl; int selection; std::cin >> selection; map_iter it; if( ( it = map_.find( selection ) ) != map_.end() ) { it->second.second(); } else if( selection == 0 ) { done = true; } else { std::cout << "Please make a selection between 0 and " << n - 1 << std::endl; } } while( !done ); }Main.cpp
#include <iostream> #include <string> #include <conio.h> #include "Defs.h" #include "Menu.h" #include "Menustructure.h" using namespace std; typedef boost::function0<void> functor_type; typedef pair<string, functor_type> option_type; void option1_function() { cout << "option1" << endl; } void option2_function() { cout << "option2" << endl; } void sub1_option1_function() { cout << "sub1_option1" << endl; } void sub1_option2_function() { cout << "sub1_option2" << endl; } int main() { cout << "Welcome to the Game Test" << endl << "Hit enter to enter main menu" << endl << endl; cin.get(); // Erstelle Menus und registriere Optionen Menu sub1_menu( " Sub1 Menu" ); option_type sub1_option1( "Sub1 Option1", sub1_option1_function ); option_type sub1_option2( "Sub1 Option2", sub1_option2_function ); sub1_menu.register_option( sub1_option1 ); sub1_menu.register_option( sub1_option2 ); Menu main_menu( "Main Menu" ); option_type option1( "Sub1 Menu", sub1_menu ); option_type option2( "Option2", option2_function ); main_menu.register_option( option1 ); main_menu.register_option( option2 ); main_menu(); //------------------------------------------------------ cout << endl << "Hit enter to close window" << endl; cin.clear(); cin.ignore( cin.rdbuf()->in_avail() ); cin.get(); return 0; }Da ich gerne möglichst sauberes und wiederverwendbares OOP lernen will, und sowenig wie möglich in der Main() haben möchte, wollt ich die ganze Menuhierarchie auslagern. Ich dachte an eine statische Klasse. Hab ich andere Möglichkeiten, als alles im Source hard zu codieren? Ich dachte dabei auch an structs mit Titel, den Optionen und Funktionen. Nur ändert ja die Anzahl der Optionen jeweils. Mach ich einfach ne Struktur für jedes Submenu, oder mach ich ne Map (map_type) die ich dann befülle?
Sorry ich hoff das ist jetzt nicht unverständlich.
-
Opptimist schrieb:
aber bei der Zeile
if( ( map_iter it = map_.find( selection ) ) != map_.end() )motzt der Compiler. Ich kann aber nicht finden wo mein Fehler ist, hab die Zeile aus deinem Beispiel übernommen.
Opptimist schrieb:
Seltsamerweise geht es, wenn ich den Iterator vorher deklariere:
map_iter it; if( ( it = map_.find( selection ) ) != map_.end() ) { ...any ideas

Das liegt daran, dass dein Compiler (VC6?) die erste Syntax wohl nicht unterstützt. Ich hab den Code nicht getestet, es macht aber nichts, wenn du den Iterator darüber erzeugst, die Zuweisung kannst du dir aber trotzdem sparen:
map_iter it = map_.find( selection ); if( it != map_.end() ) ...Konstruktoraufrufe (und genau das ist das erste!) sind günstiger als Zuweisungen.
Opptimist schrieb:
Übrigens, um die Funktion aufzurufen, müsste es dann auch so heissen:
if( ( it = map_.find( selection ) ) != map_.end() ) { it->second.second(); // <-- !!! done = true; }it->second wäre ja der option_type
it->second.second ist erst der Functor, und den Operator() brauchts ja auch noch
Alles ungetesteter Code...

Opptimist schrieb:
Ne andere Frage wäre, wofür du das int n brauchst in der for Schlaufe:
for (map_type::const_iterator i = _map.begin (), int n = 1; i != _map.end (); ++i, ++n) std::cout << '[' << i->first << "] " << i->second.first << '\n';Das ist ein "Überbleibsel" aus der ersten Art, wie ich die Nummerierung gehandhabt habe. Ich bin aber zur Überzeugung gekommen, dass die jetzige Art besser ist. Dieser Teil sollte (mit deiner Nomenklatur) so aussehen:
for (map_type::const_iterator i = map_.begin (); i != map_.end (); ++i) std::cout << '[' << i->first << "] " << i->second.first << '\n';Opptimist schrieb:
Da ich gerne möglichst sauberes und wiederverwendbares OOP lernen will, und sowenig wie möglich in der Main() haben möchte, wollt ich die ganze Menuhierarchie auslagern.
Lobenswert, aber Menüstrukturen sind eigentlich ziemlich "programmabhängig", also schwer wiederzuverwenden. Wenn es allgemeine Aufgabe sind, kannst du dafür ja einfach Klassen erstellen, deren Objekte im Konstruktor irgendwelchen spezifischen Krams entgegennehmen.
Opptimist schrieb:
Ich dachte an eine statische Klasse.
So etwas (bzw. das Gegenteil) gibt es nicht! Alle Klassen sind "statisch", denn C++ ist eine statisch typisierte Sprache.
Opptimist schrieb:
Hab ich andere Möglichkeiten, als alles im Source hard zu codieren?
Ja, siehe oben. Du kannst auch die Menüstrukturen aus einer Datei parsen, die dann auszuführende Funktion ist aber so schwer zu beschreiben. Für "vorgefertigte" Objekte wie oben erwähnt ist das aber durchaus gangbar.
Opptimist schrieb:
Ich dachte dabei auch an structs mit Titel, den Optionen und Funktionen. Nur ändert ja die Anzahl der Optionen jeweils. Mach ich einfach ne Struktur für jedes Submenu, oder mach ich ne Map (map_type) die ich dann befülle?
Sorry ich hoff das ist jetzt nicht unverständlich.
Leider nicht, ich habs zumindest nicht verstanden :(.
-
Danke. Schon klar, dass de nicht getestet hast. Wollt nur drauf Hinweisen, falls andere Anfänger auch drüber stolpern :p
Und sorry fürs Verwirren. Ev. mach ichs auch viel zu kompliziert. Also ich hab jetzt was halbweg brauchbares. Wenigstens ist die main() wieder schlanker
Also erst mal hab ich ne Struct gemacht, und nen weiteren Ctor für die Menu Klasse:
Menu::Menu( Menustructure& menu ) : title_( menu.title ), map_( menu.options ) {} Menu::Menu( std::string title, map_type map ) : title_( title ), map_( map ) {} Menu::Menu( std::string title ) : title_( title ) {}Menustructure.h
struct Menustructure { std::string title; map_type options; }; extern Menu main_menu;Menustructure.cpp
Hier ist dann die Festlegung der Menus, also die einzige Stelle wo man ändern muss, für neue Menuhierarchie#include <boost/assign/list_of.hpp> #include <iostream> // ---- Sub1 Menu ------------------------------------------------------------- void sub1_option1_function() { std::cout << std::endl << "sub1_option1" << std::endl << std::endl; } void sub1_option2_function() { std::cout << std::endl << "sub1_option2" << std::endl << std::endl; } option_type sub1_option1( "Sub1 Option1", sub1_option1_function ); option_type sub1_option2( "Sub1 Option2", sub1_option2_function ); Menustructure sub1_menu_structure = { "Sub1 Menu ", boost::assign::map_list_of( 1, sub1_option1 )( 2, sub1_option2 ) }; Menu sub1_menu( sub1_menu_structure ); // ---- Main Menu ------------------------------------------------------------- void option2_function() { std::cout << std::endl << "option2" << std::endl << std::endl; } option_type option1( "Sub1 Menu", sub1_menu ); option_type option2( "Option2", option2_function ); Menustructure main_menu_struct = { "Main Menu ", boost::assign::map_list_of( 1, option1 )( 2, option2 ) }; Menu main_menu( main_menu_struct );Kann man ev. noch schöner lösen, über Kritiken würd ich mich freuen. Mir gefällt irgendwie nicht, das alle Funktionen und Menuobjekte global sind.
Aber in der main() reicht nun ein einfacher Aufruf um die ganze Hierarchie zu starten:
int main() { cout << "Welcome to the Game Test" << endl << "Hit enter to enter main menu" << endl << line << endl; cin.get(); main_menu(); cout << line << endl << "Hit enter to close window" << endl; cin.clear(); cin.ignore( cin.rdbuf()->in_avail() ); cin.get(); return 0; }
-
Du kannst die Funktionen und Objekte doch einfach in eine Klasse (bzw. einen Namespace) packen. Wenn du die Funktionen in eine Klasse packst, so solltest du die Funktionen dann statisch deklarieren.
-
Th schrieb:
Du kannst die Funktionen und Objekte doch einfach in eine Klasse (bzw. einen Namespace) packen. Wenn du die Funktionen in eine Klasse packst, so solltest du die Funktionen dann statisch deklarieren.
Ja an ne Klasse dachte ich auch. Warum statisch?
Sollte dann wohl nur ein Objekt der Klasse erzeugen oder? Etwa ein Singleton?
-
Statisch, damit du eben kein Objekt erzeugen musst. Statische Methoden gehören der Klasse, sind also mehr oder weniger normale Funktionen.
-
Ah ok, geschnallt, danke.
Dann kann ich die Optionen (also die Funktionen, auf die die Funktoren zeigen) im private Bereich verstecken. Werd mich gleich mal dransetzen.