Kann es eine Funktion ExecProcedure(string name) geben?
-
Ich brauche eine Funktion bool ExecProcedure(string name), die die Prozedur mit dem Namen name aufruft, true zurückgibt bei erfolgreichem Aufruf und false bei Nichtexistenz der Prozedur name. Zum Beispiel statt:
string procedure; cout << "Welche Prozedur wollen Sie ausführen? "; cin >> procedure; switch(procedure) { case "a": a(); break; case "b": b(); break; case "xyz": xyz(); break; // usw., bei vielen Prozeduren kann das richtig nervig werden, es sieht außerdem // wenig elegant aus default: cout << "\nProzedur existiert nicht\n" break; }
ginge dann einfach:
string procedure; cout << "Welche Prozedur wollen Sie ausführen? "; cin >> procedure; if(!ExecProcedure(procedure)) cout << "\nProzedur existiert nicht\n";
Perfekt wäre es, wenn man auch Funktionen ausführen könnte, z. B. bool ExecFunction(string name, type* returnvalue), wobei hinterher in returnvalue der Rückgabewert enthalten ist.
-
Gefährliche Sache, die du da vorhast.
An sich könntest du ein konstrukt à la
(*name)(...)
Verwenden, wobei du ... beispielsweise durch eine referenz vom gewünschten Typen ersetzt.
Das ganze ist aber eine recht heikle Angelegenheit, von der ich abrate.
-
joa...ich bin mal so frei, dir eine halbe, aber funktionierende Lösung zu posten.
Allerdings ohne Return-Wert.
Bei dieser Lösung kommst du ohne jegliches if/else- oder switch-Konstrukt aus (das übrigens in deinem Code *falsch* ist).
Wenn du return werte willst, sind diese eigentlich auch leicht zu ergänzen. sofern der typ immer gleich ist. Im anderen Fall könntest du eine virtuelle Basis-Klasse BaseFunctor bereitstellen oder so... ach, schau mal, ob du das nachvollziehen kannst:#include <iostream> #include <string> #include <map> using namespace std; // testfunktion 1 void hallo() { cout << "hallo\n"; } // testfunktion 2 void answer() { cout << "42\n"; } // *** für nen int-return Wert änderst du folgenden aus // *** typedef int (*iv_func)(); ab typedef void (*vv_func)(); // globle map, die paare string -> funktion speichert map<string, vv_func> functions; // und gibts hier eine Referenz noch mit an, z.b.: // bool exec_procedure(const string& str, int& result) bool exec_procedure(const string& str) { map<string, vv_func>::iterator pos = functions.find(str); // prüfung, ob string vorhanden ist if(pos != functions.end()) { (pos->second)(); // aufruf der funktion // *** entsprechend waere der aufruf dann: // *** result = (pos->second)(); return true; } // negativer return wert -> funktion nicht gefunden return false; } int main() { // map mit den beiden testfunktionen fuellen functions.insert(make_pair("hallo", &hallo)); functions.insert(make_pair("answer", &answer)); string input; cout << "Welche Prozedur wollen sie ausfuehren? "; cin >> input; // *** diese abfrage wuerde dann wie folgt aussehen mit return-Wert: // *** int result; // *** if(!exec_procedure(input, result)) // kommentar s. exec_procedure -> false bedeutet, funktion wurde nicht gefunden if(!exec_procedure(input)) cerr << "Prozedur nicht vorhanden\n"; return 0; }
Grüße, Xantus
Edit:
Das ganze ist aber eine recht heikle Angelegenheit, von der ich abrate.
Bei 17 effektiven LOC ist das doch gar nicht so heikel.
~ Ich hab noch Kommentare zu evt. Code mit Return-Werten hinzugefügt (diese sind mit *** gekennzeichnet). Viel Spaß
-
Vielen Dank für den Code, ich werde es mal probieren, aber ich würde gerne verstehen was du da machst...
Was heißt typedef?
Wieso setzt du einen Funktionsnamen in Klamern und mit einem Stern davor:typedef void (*vv_func)();
Wieso übergibst du den String als Referenz, er soll doch nicht verändert werden??
Ich habe schon mal etwas von Maps gehört, aber ich weiß nicht, was du da mit der Map genau machst.
Tut mir leid, aber ich verstehs echt nicht. Ich könnte mir jetzt auch alles einzeln im Internet zusammensuchen, aber dann hätte ich immer noch kein Gesamtbild. Eine Version, bei dem du die Kommentare zur ExecFunction() weglässt aber du dafür auskomentierst, was die einzelnen Befehle machen, wäre super.
PS:
Bei dieser Lösung kommst du ohne switch-Konstrukt aus (das übrigens in deinem Code *falsch* ist).
Wieso ist mein switch-Konstrukt falsch? Ich kenne das so:
switch(variable) { case wert1: // { hier können geschweifte Klammern stehen, müssen aber nicht function1(); break; // } case wert2: function2(); break(); // die anderen Variablenwerte... default: defaultfunction(); break; // muss hier nicht stehen, fals default am Ende steht, ich machs trotzdem immer } // end switch
-
dein switch konstrukt ist falsch, weil zeichenketten nicht für case nicht verwendet werden können, sondern nur integrale (ganzzahlige) (compile-time) konstanten.
c++Beginner schrieb:
Was heißt typedef?
typedef führt alias-namen für bestimmte typen ein.
typedef int foo; foo f = 42; //same as int f = 42
so wird auch mit
typedef void (*vv_func)();
der alias n ame
vv_func
für den typenvoid(*)()
eingeführt.
void(*)()
ist vom typ "funktionszeiger auf eine funktion, die nichts (void) zurückgibt und keine argumente übernimmt".Ich habe schon mal etwas von Maps gehört, aber ich weiß nicht, was du da mit der Map genau machst.
eine map verbindet im prinzip zwei werte miteinander: einen schlüssel (key) und einen wert, den man mit diesem schlüssel (schnell) finden kann. besonders schnell und ähnlich wie ein switch/case wäre dabei eine hashmap. aber du musst hauptsächlich wissen, dass maps assoziative container sind, die einen (einmaligen) schlüssel mit einem wert in verbindung bringen.
map<string, vv_func>
in diesem fall ist der schlüssel ein string und der wert, auf den der schlüssel verweist ein zeiger auf eine funktion. wenn du als schlüssel nun funktionsnamen speicherst, bekommt jeder funktionsname einen zugehörigen funktionszeiger zugewiesen, über welchen du die passende funktion aufrufen kannst.
ein minimalbeispiel, das auf funktionszeiger verzichtet, wäre in der art:
#include <tr1/functional> #include <map> #include <string> #include <iostream> using namespace std; void hello () { cout << "hello\n"; } void answer () { cout << "42\n"; } int main () { map<string, tr1::function<void()> function_map; //tr1::function "hält" sich einen funktionszeiger auf //eine void () - funktion. //nun wird die map gefüllt: function_map["hello"] = hello; function_map["answer"] = answer; //und so wird eine funktion aufgerufen: function_map["hello"](); }
-
Hi,
da du ja selber lernen willst, gebe ich dir google-stichwörter, mit denen du dir die Syntax recht schnell erklären kannst:c++Beginner schrieb:
Was heißt typedef?
Wieso setzt du einen Funktionsnamen in Klamern und mit einem Stern davor:Google: Funktionspointer / Funktionszeiger
Wieso übergibst du den String als Referenz, er soll doch nicht verändert werden??
Ich übergebe ihn als konstante Referenz. Er soll nicht verändert werden, dafür ist das const da, er soll aber auch nicht kopiert werden (um Laufzeit zu sparen), dafür ist es eine Referenz.
Google: const correctnessIch habe schon mal etwas von Maps gehört, aber ich weiß nicht, was du da mit der Map genau machst.
Die map besteht aus den Key -> Value paaren
string -> funktion
mit
std::map<T, U>::insert
fügst du ein neues paar hinzu. Das erklärt dann auch, was make_pair machtBei dieser Lösung kommst du ohne switch-Konstrukt aus (das übrigens in deinem Code *falsch* ist).
Wieso ist mein switch-Konstrukt falsch?
Weil man nur Integrale Werte (int, long, enum) in einem Switch-Konstrukt vergleichen kann, du vergleichst aber String-Literale:
case "xyz": // sollte einen Fehler erzeugen xyz() break;
wo hängt es denn bei exec_procedure?
Google hints: STL iterator, function pointer (siehe oben), std::map find (in der Referenz deiner Wahl, z.b. cppreference.com oder cplusplus.com)Wenn du kein (gutes) C++ (Einsteiger) Buch hast, ist es drigend an der Zeit, dir eins zuzulegen
- Edit: zu langsam -
-
case "xyz": // sollte einen Fehler erzeugen xyz() // sollte auch einen Fehler erzeugen ;) Flüchtigkeitsfehler break;
Das heißt, immer wenn ich Strings mit festgelegten Werten vergleichen will, muss ich in if/else if/else-Konstrukt verwenden? Das ist aber blöd, gibt es da keine einfachere Lösung?
Wenn du kein (gutes) C++ (Einsteiger) Buch hast, ist es drigend an der Zeit, dir eins zuzulegen
Ich hielt die durchgearbeiteten Bücher für gut Über Maps stand da auch was, was es ist, wusste ich auch noch, aber die einzelnen Funktionen dazu nicht.
"Funktionszeiger" wurden gar nicht erwähnt, nur Pointer auf Objekte und Variablen.
Danke für die Hilfe! Hätte auf keinen Fall gedacht, dass sowas geht.
-
Oh,
ich habe gar nicht gesehen, dass in euren Funktionen die aufzurufenden Prozeduren bekannt sein müssen. Angenommen, sie sind nicht bekannt - fragt mich jetzt nicht wofür das gut ist - aber gibt es da auch eine Lösung?
-
Zu deiner switch-Frage: Nein, if ist da wohl erste Wahl.
Zu deinem zweiten Post: Wie willst du etwas aufrufen, von dem du nicht weißt wie es heißt und wo es sich befindet?
Womöglich geht es mit plattformspezifischen Mittelchen (ich denke da an DLLs), aber mit Standardmitteln wohl nicht.
-
c++Beginner schrieb:
Oh,
ich habe gar nicht gesehen, dass in euren Funktionen die aufzurufenden Prozeduren bekannt sein müssen. Angenommen, sie sind nicht bekannt - fragt mich jetzt nicht wofür das gut ist - aber gibt es da auch eine Lösung?
Was soll eine unbekannte Funktion Deiner Meinung nach denn tun ?
Gruß,
Simon2.
-
Sagen wir es so: Es ist möglich, aber wenn die Funktion bzw der SPeicher nicht existiert bzw nicht belegt ist, hast du ein Problem (:
Das meinte ich mit der heiklen Angelegenheit.
-
Was soll eine unbekannte Funktion Deiner Meinung nach denn tun ?
Keine unbbekante Funktion. Ich meinte, dass ich einmal eine Methode "ExecProcedure" definieren muss und mich dann aber nicht darum kümmern muss, jedes Mal einen neuen Eintrag in der Map zu erzeugen, wenn eine Funktion hinzukommt - dann ist die switch-Variante meistens sogar kürzer.
-
c++Beginner schrieb:
Was soll eine unbekannte Funktion Deiner Meinung nach denn tun ?
Keine unbbekante Funktion. Ich meinte, dass ich einmal eine Methode "ExecProcedure" definieren muss und mich dann aber nicht darum kümmern muss, jedes Mal einen neuen Eintrag in der Map zu erzeugen, wenn eine Funktion hinzukommt - dann ist die switch-Variante meistens sogar kürzer.
Du musst die Funktionen die du verwendest auch bekannt machen. Sei es nun das du jedesmal den Code deiner ExecProcedure um einen neuen if/else-Fall erweiterst oder einen neuen Eintrag in eine map einfügst. Ohne geht es nicht (Eine Funktion hat keine Namen in dem Sinne, das dieser zur Laufzeit noch bekannt wäre).
Irgendwie musst du also deinen Code anpassen.
Das sauberste ist aber tatsächlich die Lösung mit der map.
cu André
-
c++Beginner schrieb:
Was soll eine unbekannte Funktion Deiner Meinung nach denn tun ?
Keine unbbekante Funktion. Ich meinte, dass ich einmal eine Methode "ExecProcedure" definieren muss und mich dann aber nicht darum kümmern muss, jedes Mal einen neuen Eintrag in der Map zu erzeugen, wenn eine Funktion hinzukommt - dann ist die switch-Variante meistens sogar kürzer.
nö, ist sie nicht, da du ja strings vergleichen musst, hast du nicht mal ne switch-Anweisung:
bool ExecProcedure(const std::string& str) { if(str == "hallo") { // zeile 1 hallo(); // zeile 2 return true; // zeile 3 } // zeile 4 else if(str == "answer") { answer(); return true; } else return false; }
das sind jeweils 4-5 Zeilen, wenn es ordentlich eingerückt und übersichtlich sein soll.
Die Map-Variante benötigt nur 1 Zeile:
functions.insert( make_pair("answer", &answer) );
und gerade wenn die Funktionsanzahl 10+ erreicht, tut Beispiel 1 schon in den Augen weh. Und hinzufügen musst du die Funktionen so oder so!
- Edit: zu langsam -
-
Auch ordentlich:
bool ExecProcedure(const std::string& str) { if(str == "hallo") // zeile 1 hallo(); // zeile 2 else if(str == "answer") answer(); else return false; return true; }
-
So, ich habe mal etwas ausprobiert, obwohl ich kein Fan von globalen Variablen bin. Ich übernehme für den Code auch keine Garantien:
Ziel des Codes ist es, mit möglichst wenig gegenseitigen Wissen Funkionen über einen Namen aufzurufen:
// main.cpp
#include "exec_fn.h" int main() { exec::ExecuteFunction("ShowA"); exec::ExecuteFunction("ShowB"); exec::ExecuteFunction("ShowA"); exec::ExecuteFunction("wait"); }
// exec_fn.h
#if !defined(EXEC_FN_HEADER) #define EXEC_FN_HEADER #include <string> namespace exec { typedef void (*function_type)(); void Register(const std::string& functionName, function_type function); void ExecuteFunction(const std::string& functionName); class RegisterFunction { public: RegisterFunction(const std::string& functionName, function_type function) { Register(functionName, function); } }; } #endif
// exec_fn.cpp
#include "exec_fn.h" #include <map> namespace exec { std::map<std::string, function_type>& GetFunctionMap() { static std::map<std::string, function_type> functionMap; return functionMap; } void Register(const std::string& functionName, function_type function) { std::map<std::string, function_type>& functionMap = GetFunctionMap(); if(functionMap.find(functionName) == functionMap.end()) functionMap[functionName] = function; } void ExecuteFunction(const std::string& functionName) { std::map<std::string, function_type>& functionMap = GetFunctionMap(); std::map<std::string, function_type>::iterator pos = functionMap.find(functionName); if(pos != functionMap.end()) (pos->second)(); } }
Zudem noch 3 weitere Dateien die aber alle den gleichen Aufbau haben (Eine void-Funktion ohne Parameter, eine globale Variable...). Ich zeige hier nur die Implementierung von der wait.h/wait.cpp.
// wait.h
#if !defined(WAIT_HEADER) #define WAIT_HEADER void wait(); #endif
// wait.cpp
#include "exec_fn.h" #include <iostream> void wait() { std::cin.clear(); std::cin.ignore(std::cin.rdbuf()->in_avail()); std::cin.get(); } // Hiermit wird die wait-Funktion registriert exec::RegisterFunction wait_("wait", &wait);
Die restlichen Header/Sourcen kann man sich wohl selbst ausdenken...
Ob man dies als Sauber bezeichnen kann wage ich aber zu bezweifeln (Getestet unter VC++ 2005).
cu André
-
c++Beginner schrieb:
Was soll eine unbekannte Funktion Deiner Meinung nach denn tun ?
Keine unbbekante Funktion. Ich meinte, dass ich einmal eine Methode "ExecProcedure" definieren muss und mich dann aber nicht darum kümmern muss, jedes Mal einen neuen Eintrag in der Map zu erzeugen, wenn eine Funktion hinzukommt ..
Ach so: Du möchtest also eine "automatische Externalisierung", sobald Du eine Funktion hinzufügst.
Ich weiß gar nicht, ob mir das erstrebenswert erschiene .... aber da der Compiler das nicht kann, geht's halt auch nicht.
Gruß,
Simon2.