Präprozessor-Befehl
-
Hallo,
ich habe hier vor einiger Zeit mal berichtet, dass ich an einem kleinen Parser für Kommandozeilen-Parameter geschrieben habe.
Mittlerweile bin ich mit meinem Programm zufrieden, habe jetzt allerdings noch das Problem, dass die im Programm nötigen Abfragen nicht so kurz sind, wie ich es mir wünschen würde. Aktuell sieht eine Abfrage etwa so aus:if(_cmd_line_switch && (_string_compare( "t" , argv['t' - 97] ) || _string_compare( "T" , argv['T' - 65] )) && _string_length(argv['t' - 97]) > 1 )
Ich habe nun keine richtige Vorstellung, wie ich diese lange Bedingungsprüfung elegant verkürzen kann. Ich nutze zwar ein paar winzige Makros, aber keine, die mit Variablen umgehen könnten. Ein Vorschlag (mit kurzer Erklärung?) eurerseits würde mich erfreuen.
Wenn ich das ganze mit einem Unterprogramm versuche, dann habe ich das Problem, dass alle zur Abfrage nötigen Variablen (inkl. argc und argv) jedesmal mit übergeben werden müssen - oder kann ich vielleicht dafür sorgen, dass diese standardmäßig übergeben werden und ich mich nie wieder darum kümmern muss? So etwas ist mir bisher nämlich auch nicht begegnet.
-
sowas macht man doch nicht mit präprozessor anweisungen!
hier bietet sich eine switch-case abfrage an.
-
Ich hätte am liebsten ein Makro oder eine kleine Funktion, da ich meinen kleinen Parser in alle möglichen Programme übernommen habe.
Ich habe gar nicht bedacht, dass es wohl etwas seltsam aussieht, wie ich mein argv behandele: Das Feld besteht bei mir aus 26 Einträgen (für jeden Buchstaben einer), die entweder "0" oder eben "xy" enthalten, wobei x für einen Buchstaben, y für eine Zahl steht.Nun muss ich zur Prüfung, ob ein Parameter übergeben wurde, einfach argv['x' - 97] ansprechen und finde dort die entsprechenden Informationen. Die Großbuchstaben werden übrigens im selben Feld-Element gespeichert.
Das eigentlich variable an dieser Abfrage stellt also (im Beispiel) nur das "t" bzw. das "T" dar. Ansonsten muss ich diese Abfrage immer wieder kopieren.
Das ganze mit switches zu machen stelle ich mir ebenso aufwändig vor, das wird meinen Quelltext nicht viel übersichtlicher machen.Meine aktuelle Vorstellung:
#define _CMD_LOOKUP(x) _cmd_line_switch && (_string_compare( "(x)" , argv['(x)' - 97] ) || _string_compare( "(x - 32)" , argv['(x - 32)' - 65] )) && _string_length(argv['(x)' - 97]) > 1 ) ... if(_CMD_LOOKUP(t));
Da ich gerade noch an anderer Front kämpfe, habe ich das nicht ausprobiert, ich grübele auch noch an einer Funktions-Variante, das scheitert aber noch immer daran, dass ich bei jedem Aufruf zahlreiche Parameter übergeben müsste (4 aktuell), das ganze aber kurz und aussagekräftig erschein soll - ein Parameter muss also reichen.
EDIT: Ich habe meinen Makro-Vorschlag gerade mal ausprobiert, funktioniert natürlich nicht, ich bekomme folgende Fehlermeldungen:
..\Test.c:50:17: warning: multi-character character constant ..\Test.c:50:17: warning: character constant too long for its type ..\Test.c:50:17: warning: multi-character character constant
Zeile 17 entspricht hierbei der Zeile mit dem Makro ;).
Ich versuche jetzt mal eine Kombination meiner beiden Ideen, vielleicht geht es ja so schöner...
EDIT2: Mein Zwischenstand scheint schon zu funktionieren, ist aber nicht sonderlich hübsch:
#define _CMD_PTEST(x) (_cmd_ptest_call( (x) , argc , argv , _cmd_line_switch)) short _cmd_ptest_call(char x , int argc , char ** argv , short _cmd_line_switch) { char * buffer1 = calloc(2 , sizeof(char)); char * buffer2 = calloc(2 , sizeof(char)); buffer1[0] = x; buffer2[0] = x - 32; if(_cmd_line_switch && (_string_compare( buffer1 , argv[buffer1[0] - 97] ) || _string_compare( buffer2 , argv[buffer2[0] - 65] )) && _string_length(argv[buffer1[0] - 97]) > 1) { free(buffer1); free(buffer2); return(1); } else { free(buffer1); free(buffer2); return(0); } } int main(int argc, char * argv[]) { ............ ............ if(_CMD_PTEST('e')) Zahl = _str_to_double(argv['e' - 97]); }
So sieht zumindest die main-Funktion nicht mehr so übel aus, über Verbesserungsvorschläge würde ich mich aber weiterhin freuen.
-
Hi!
Ich weiss nicht genau, was du vorhast, habs nur kurz überflogen.
Aaaber:
es sieht mir viel zu umständlich aus, von dem calloc für einen Buchstaben ganz zu schweigen!
char* argv_has_c(int argc, char* argv[], char c) { while( --argc >= 0 ) if ( toupper(argv[argc][0]) == toupper(c) ) return argv[argc]; return NULL; } int main(int argc, char* argv[]) { char* s = NULL; char c = 'a'; if ( NULL != ( s = argv_has_c (argc, argv, c)) ) puts(s); return 0; };
-
P.S.
Den Programmpfad willst du vermutlich nicht checken, dann ist eher ein
while( --argc >= 1 ) angebracht.
-
Dein Vorschlag lässt mich also auf der Suche nach einem Zeichen durch das ursprüngliche argv-Array laufen. Im Prinzip unterscheidet sich die Probe doch nicht von meinem
_string_compare( buffer1 , argv[buffer1[0] - 97]
oder? Nur muss ich schon nicht mehr durch das Array laufen, weil ich weiß, wo das Zeichen zu finden wäre. Zusätzlich müsste ich bei deiner (und meiner) Methode auf die weiteren Bedingungen prüfen, das wird den Quelltext also nur wenig übersichtlicher machen.
Ich muss dir allerdings Recht geben bei der Sache mit den callocs, das ist wirklich hässlich. Ich habe es vorhin nur erstmal so hingebastelt, dass ich den Code kompilieren und ausführen konnte. _string_compare benötigt Pointer auf char(-arrays), ich hatte einige Schwierigkeiten, mein "x" einfach dort einzusetzen. Ohne die allocs und frees wäre die Funktion fast schon angenehm kurz :).
-
Ja, klar, in meiner Verion wird das Array durchlaufen, das macht eine reihenfolgeunabhängige Eingabe von Programmschaltern möglich.
Wie sieht denn deine _string_compare funktion aus, der zweite Parameter kann negativ werden, das könnte eventuell zum Crash führen.
-
Der zweite Parameter ist ein Pointer, der dürfte nicht negativ werden können. Falls du den Zugriff auf das argv meinst, der kann nicht negativ sein, da im buffer-Feld ja ein char >=97 steht.
Das ist meine Vergleichsfunktion:short _string_compare (char * shortString1 , char * String2) { unsigned int i = 0; while(shortString1[i] != '\0') { if(shortString1[i] != String2[i]) return(0); i++; } return(1); }
EDIT:Wie kann ich denn aus dem x im ersten Argument einen char* machen? Der &-Operator funktioniert hier nicht.
char x = 'e'; _string_compare( x , argv[x - 97] )
Wenn das irgendwie ginge, könnte ich nämlich auf dei callocs verzichten und meine Funktion um einiges verkürzen.
EDIT2: Ich hab's mir jetzt einfach gemacht und die Vergleichsfunktion geändert. Damit betrachte ich dieses Problem mal als gelöst, Verbesserungsvorschläge wären mir aber trotzdem lieb :).
#define _CMD_PTEST(x) (_cmd_ptest_call( (x) , argc , argv , _cmd_line_switch)) short _char_compare (char c1 , char * c2) { if(c1 != c2[0]) return(0); return(1); } short _cmd_ptest_call(char x , int argc , char ** argv , short _cmd_line_switch) { if(_cmd_line_switch && (_char_compare( x , argv[x - 97] ) || _char_compare( x - 32 , argv[x - 97] )) && _string_length(argv[x - 97]) > 1) return(1); else return(0); } int main(int argc , char * argv[]) { ... if(_CMD_PTEST('e')) Zahl = _str_to_double(argv['e' - 97]); ... }
Das ist zwar alles etwas umständlich, aber von jetzt an kann ich bequem auf meine Parameter zugreifen :).