Mardi 31 Aout 2010
(Re^n)-belote
Bon, comme d'habitude, je remets mon ouvrage sur le métier, mais cette fois en C, pour l'assembleur et la lib générale qui sert aux softs en ligne de commande sous PedroM.
Comme d'habitude aussi, je repars aussi par la gestion de la ligne de commande. Comme d'habitude, j'essaye de faire en sorte de me simplifier la vie au maximum.
En assembleur, j'utilisais une lib pour aider à sa gestion, mais ce n'était pas optimal : chaque programme l'utilisant aurait eu à réimplémenter un code de gestion similaire pour utiliser la lib. Cette fois, le but est d'éviter ça.
Le principe : le programme appelle une fonction de la lib, ManageArgs, avec quelques arguments, dont deux très important :
- la liste des switches valides en tant qu'options
- la liste des fonctions à appeler si l'on lit un de ces switches
Il y a aussi une fonction de callback appelée après différents évènements (succès du parsing d'un switch, switch non trouvé, argument non switch). La fonction de callback renvoie une valeur qui permet au programme de dire à la lib de continuer à tourner ou d'interrompre le traitement. Le programme devient en quelque sorte une librairie appelée par la véritable librairie. Fabuleux \o/
Voici le code. Pas de commentaires, mais un descriptif :
Au final, un programme peut se faire de la manière suivante :
- une fonction main() qui appelle le gestionnaire
- pleins de fonctions pour gérer chaque switch
- une fonction pour traiter un argument qui n'est pas un switch (dans mon cas, l'assemblage d'un source).
Ca change donc complètement ma vision impérative habituelle, pour faire du programme quelque chose de très fonctionnel. C'est rigolo, j'espère juste que j'arriverai à coder dans cette optique.
Il y a aussi deux fonctions, SkipArgs et GetCurrentArgPtr, pour récupérer le pointeur vers l'argument couramment traité (pour afficher un switch invalide par exemple), ou pour indiquer au gestionnaire qu'on a géré un argument à la mano et qu'il doit l'ignorer.
Finalement, je me demande une chose : dois-je chercher à faire un assembleur, ou juste une lib de gestion de ligne de commande ? Je me demande vraiment...
edit -> je n'arrivais pas à coder l'appel d'une fonction dont le pointeur était dans la pile, en utilisant une va_list. Godzil a trouvé la solution ! Merci à lui \o/
Comme d'habitude aussi, je repars aussi par la gestion de la ligne de commande. Comme d'habitude, j'essaye de faire en sorte de me simplifier la vie au maximum.
En assembleur, j'utilisais une lib pour aider à sa gestion, mais ce n'était pas optimal : chaque programme l'utilisant aurait eu à réimplémenter un code de gestion similaire pour utiliser la lib. Cette fois, le but est d'éviter ça.
Le principe : le programme appelle une fonction de la lib, ManageArgs, avec quelques arguments, dont deux très important :
- la liste des switches valides en tant qu'options
- la liste des fonctions à appeler si l'on lit un de ces switches
Il y a aussi une fonction de callback appelée après différents évènements (succès du parsing d'un switch, switch non trouvé, argument non switch). La fonction de callback renvoie une valeur qui permet au programme de dire à la lib de continuer à tourner ou d'interrompre le traitement. Le programme devient en quelque sorte une librairie appelée par la véritable librairie. Fabuleux \o/
Voici le code. Pas de commentaires, mais un descriptif :
ManageArgs
/**************************************************** * ManageArgs * * Handle switches of the command line * * Args * CmdLine Ptr to a CMDLINE structure * OptList List of valid options, without their sign ("switch1\0switch2\0...\switchN\0") * FuncList List of functions to execute for each option * Callback Function called after each switch parsed * * Returns PDT_NO_MORE_ARGS The command line is fully parsed * PDT_SWITCH_OK Switch is valid, its routine has been executed * PDT_INVALID_SWITCH Switch doesn't exist in OptList * PDT_NOT_A_SWITCH Current arg doesn't begin with + or - * * Callback Provides PDT_SWITCH_OK Switch is valid, its routine has been executed * PDT_INVALID_SWITCH Switch doesn't exist in OptList * PDT_NOT_A_SWITCH Current arg doesn't begin with + or - * Returns PDT_CONTINUE_MANAGEMENT ManageArgs continue to parse command line * PDT_EXIT_MANAGEMENT ManageArgs stop to parse command line and returns * last provided arg * * SwitchFunc Provides the sign of the current switch (+ or -) * ****************************************************/ int pdtlib__ManageArgs (CMDLINE* CmdLine, const char* OptList, int (*Callback)(int Status), void (*SwitchFunc)(char Sign), ...) { typedef void (*funcptr)(char); va_list FuncList; const char* ArgPtr; char Sign; while (CmdLine->argc > CmdLine->argit + 1) { va_start (FuncList, SwitchFunc); CmdLine->argit ++; Sign = *CmdLine->argv[CmdLine->argit]; if (Sign == '-' || Sign == '+') { ArgPtr = OptList; while (*ArgPtr != 0) { if (strcmp (CmdLine->argv[CmdLine->argit] + 1, ArgPtr) == 0) { va_arg(FuncList, funcptr)(Sign); va_end(FuncList); if (Callback(PDT_SWITCH_OK) == PDT_STOP_MANAGEMENT) return PDT_SWITCH_OK; break; } else ArgPtr += strlen(ArgPtr) + 1; } if (*ArgPtr == 0 && Callback(PDT_INVALID_SWITCH) == PDT_STOP_MANAGEMENT) { va_end(FuncList); return PDT_INVALID_SWITCH; } } else { if (Callback(PDT_NOT_A_SWITCH) == PDT_STOP_MANAGEMENT) { va_end(FuncList); return PDT_NOT_A_SWITCH; } } } va_end(FuncList); return PDT_NO_MORE_ARG; }
Au final, un programme peut se faire de la manière suivante :
- une fonction main() qui appelle le gestionnaire
- pleins de fonctions pour gérer chaque switch
- une fonction pour traiter un argument qui n'est pas un switch (dans mon cas, l'assemblage d'un source).
Ca change donc complètement ma vision impérative habituelle, pour faire du programme quelque chose de très fonctionnel. C'est rigolo, j'espère juste que j'arriverai à coder dans cette optique.
Il y a aussi deux fonctions, SkipArgs et GetCurrentArgPtr, pour récupérer le pointeur vers l'argument couramment traité (pour afficher un switch invalide par exemple), ou pour indiquer au gestionnaire qu'on a géré un argument à la mano et qu'il doit l'ignorer.
Finalement, je me demande une chose : dois-je chercher à faire un assembleur, ou juste une lib de gestion de ligne de commande ? Je me demande vraiment...
edit -> je n'arrivais pas à coder l'appel d'une fonction dont le pointeur était dans la pile, en utilisant une va_list. Godzil a trouvé la solution ! Merci à lui \o/
lol
Sinon, mon perfectionnisme me perdra, faut que j'accepte d'avancer même si ce que j'ai fait n'est pas parfait et qu'une meilleure idée me vient...
"Finalement, je me demande une chose : dois-je chercher à faire un assembleur, ou juste une lib de gestion de ligne de commande ? Je me demande vraiment..." -> ben d'après toi, qu'est ce qui est le plus utile ? La gestion de la ligne de commande, c'est un mini-problème intéressant, mais ça ne reste qu'un mini-problème...
enum { PDT_STOP_MANAGEMENT, PDT_SWITCH_OK, PDT_INVALID_SWITCH, PDT_NOT_A_SWITCH, PDT_NO_MORE_ARG };
typedef struct { char argc; char argit; char *argv[];} CMDLINE;
typedef void (*funcptr)(void);
int pdtlib__ManageArgs (CMDLINE* CmdLine, const char* OptList, int (*Callback)(int Status), void (*SwitchFunc)(char Sign), ...)
{
const char* ArgPtr;
char Sign;
va_list FuncList;
funcptr ptrf;
while (CmdLine->argc > CmdLine->argit + 1)
{
va_start (FuncList, SwitchFunc);
CmdLine->argit ++;
Sign = *CmdLine->argv[CmdLine->argit];
if (Sign == '-' || Sign == '+')
{
ArgPtr = OptList;
while (*ArgPtr != 0)
{
if (strcmp (CmdLine->argv[CmdLine->argit] + 1, ArgPtr) == 0)
{
ptrf = va_arg(FuncList,funcptr);
if (ptrf != NULL)
ptrf();
va_end(FuncList);
if (Callback(PDT_SWITCH_OK) == PDT_STOP_MANAGEMENT)
return PDT_SWITCH_OK;
break;
}
else
ArgPtr += strlen(ArgPtr) + 1;
}
if (*ArgPtr == 0 && Callback(PDT_INVALID_SWITCH) == PDT_STOP_MANAGEMENT)
{
va_end(FuncList);
return PDT_INVALID_SWITCH;
}
}
else
{
if (Callback(PDT_NOT_A_SWITCH) == PDT_STOP_MANAGEMENT)
{
va_end(FuncList);
return PDT_NOT_A_SWITCH;
}
}
}
return PDT_NO_MORE_ARG;
}