PROFDINFO.COM

Votre enseignant d'informatique en ligne

Paramètres, valeurs de retour et tuyauterie

Paramètres

Vous avez travaillé avec les commandes texte et vous avez constaté que la plupart d'entre elles acceptent des paramètres. Un paramètre est un mot qui est écrit après le nom de la commande lorsqu'on l'appelle. On peut passer plusieurs paramètres à une commande en les séparant simplement par des espaces. Par exemple:

dir /w /p c:\windows

Ici, la commande dir reçoit trois paramètres: /w, /p et c:\windows. Les deux premiers sont des "switches" (modificateurs) qui vont modifier le comportement de la commande. Le troisième est un chemin qui sera utilisé par la commande.

Mais saviez-vous que lorsque vous créez un programme C++, il est également possible de l'appeler en lui passant des paramètres, qui seront récupérés par le programme et utilisés comme bon vous semblera?

Tout d'abord, il faut savoir que l'on peut exécuter un de nos programmes à partir de la console. Il suffit de se rendre dans le dossier Debug qui se trouve dans le dossier de la solution après la compilation. Un fichier .exe se trouvera là. Tapez simplement son nom dans la console (n'oubliez pas de le mettre entre guillemets si ce nom contient un espace) et il sera exécuté directement.

Un exemple de démarrage par la console

Sachant cela, il pourrait être intéressant de pouvoir passer des paramètres à notre programme -- par exemple, pour lui demander d'afficher la phrase un certain nombre de fois, et d'utiliser notre nom plutôt que "le monde", en faisant quelque chose comme:

testparametres 10 Georges

Pour cela, il suffit de se rappeler que le main est une fonction un peu comme les autres, sauf qu'elle est automatiquement appelée en premier par le système d'exploitation. Et comme toute fonction, il est possible de lui définir des paramètres.

Au lieu de:

int main()

on écrira:

int main (int argc, char* argv[])

(Vous aurez d'ailleurs possiblement remarqué que les exemples de programmes réalisés avec SDL dans le cours d'algorithmie utilisent cette notation.)

Qu'est-ce que ça signifie? Tout simplement que maintenant, le main aura deux paramètres: un entier, suivi d'un tableau de... un tableau de quoi au juste? Techniquement, c'est un tableau de pointeurs vers des char. Vous ne verrez pas les pointeurs avant encore quelques mois, mais sachez simplement que pour l'instant, vous pouvez le traiter plus ou moins comme un tableau de string.

L'entier argc contient le nombre de paramètres qui ont été reçus par le programme. Le tableau argv[] contient ces paramètres, chacun dans une case. Il faut toutefois savoir que argv[0] contient toujours le nom du programme et que ce "paramètre" est compté dans argc. Par exemple, soit ce programme C++:

 

int main(int argc, char* argv[])
{
   cout << "J'ai recu " << argc << " parametres" << endl;
   for (int i = 0; i < argc; i++)
   {
      cout << argv[i] << endl;
   }
   return 0;
}

Si vous compilez le programme et que vous l'exécutez à partir de la console, vous pouvez alors l'appeler en lui passant des paramètres. Voyez le résultat:

Tests d'un programme avec paramètres

Le nom de l'exécutable (ici, "testparametres.exe" est toujours placé dans argv[0] et est compté dans argc. Autrement dit, argc ne sera jamais égal à 0.

Il est donc facile de faire en sorte que les paramètres reçus changent le comportement de notre programme. Par exemple, on pourrait d'abord faire un "Hello World" qui salue l'usager par son nom, entré en paramètre:

int main(int argc, char* argv[])
{
   cout << "Allo " << argv[1] << endl;
   return 0;
}

Magique!

Test avec un paramètre

Ne resterait plus maintenant qu'à faire une boucle qui nous salue par notre nom, autant de fois qu'on lui demande, pour atteindre notre but. On rappelle qu'on voudrait pouvoir faire testparametres 10 Georges pour se faire dire "Allo Georges" dix fois.

Rien de plus simple. Il faut toutefois savoir comment convertir des chaînes de caractères en nombres entiers -- parce que le nombre 10 n'est pas du tout la même chose que la chaîne "10", dans la mémoire de l'ordinateur.

Très simple: la commande stoi (string to int) accepte un string (ou un char* dans ce cas-ci, c'est "pareil") et retourne un int correspondant, dans le bon format. Il s'agit d'une toute nouvelle fonction, récemment arrivée avec C++11, qui fait partie de la bibliothèque <string>. (Notez qu'il existe aussi stod, stof et stol pour transformer un string en (respectivement) un double, un float et un long.)

Notre code devient donc:

#include <iostream>
#include <string>
using namespace std;

int main(int argc, char* argv[])
{
   int Max = stoi(argv[1]);

   for (int i = 0; i < Max; i++)
   {
      cout << "Allo " << argv[2] << endl;
   }
   
   return 0;
}

Et la magie opère:

Test à 2 paramètres

Comparer les paramètres à des chaînes de texte

On l'a dit tantôt, les paramètres ne sont pas placés dans des string, mais dans des pointeurs de char. C'est très similaire, mais il y a une différence importante: on ne pourra pas comparer un paramètre avec un string. Par exemple, ceci sera toujours faux, peu importe le contenu du premier paramètre:

if (argv[1] == "allo")
{
    // ...
}

Des solutions pour contourner ce problème sans connaître les pointeurs?

1- Mettre le contenu du paramètre dans un string au préalable, et utiliser ce string-là à la place:

string param1;
param1 = argv[1];

if (param1 == "allo")
{
   //...
}

2- Utiliser la méthode strcmp (string compare), qui se trouve dans la bibliothèque <string> et qui compare deux chaînes et retourne 0 si elles sont pareilles:

if (strcmp(argv[1], "allo") == 0)
{
   //...
}

3- Si le paramètre contient un chiffre, utiliser stoi tel que vu précédemment et comparer avec son résultat:

if (stoi(argv[1] == 1) // Fonctionne!
{
   //...
}

if (argv[1] == "1") // Ne fonctionne pas!
{
   //...
}

La valeur de retour

Il est possible de vérifier si le script (ou le programme) qui a été appelé s'est exécuté correctement ou si une erreur est survenue. En effet, la grande majorité des commandes texte retourne une valeur de 0 si tout a bien été, et une valeur > 0 dans le cas d'une erreur. Parfois, la valeur exacte indiquera la nature du problème. D'autres fois, on fonctionnera simplement avec 0 ou 1.

La variable ERRORLEVEL est une variable d'environnement particulière qui contient toujours le résultat de la dernière commande exécutée. Il suffit de vérifier son contenu pour savoir si tout s'est bien passé. Par exemple, si on tape ceci dans la console:

copy a.txt c:\unrepertoirequinexistepas 
echo %ERRORLEVEL%

On obtient 1. Mais si on écrit:

dir
echo %ERRORLEVEL%

On obtient 0

Il est donc possible, dans un script, de tester si la dernière commande a échoué en faisant quelque chose comme:

if %ERRORLEVEL% GEQ 1 (echo Woups) else echo Hourra

Comme c'est une commande relativement courante, il existe une façon abrégée d'arriver au même résultat. On peut écrire:

if ERRORLEVEL 1 (echo Woups) else echo Hourra

Notez l'absence de % autour du nom de la variable et celle de l'opérateur GEQ. Cette syntaxe particulière n'est valable que pour ERRORLEVEL et signifie bel et bien "si ERRORLEVEL est plus grand ou égale à 1" (et non pas simplement égale à 1).

De votre côté, dans un script, vous pouvez utiliser la commande exit /b pour terminer le script avec une valeur de retour donnée. Par exemple:

exit /b 0

termine sans erreur, et:

exit /b 1

termine avec le code d'erreur 1.

(Notez que si vous omettez le /b, la commande exit mettra également fin à l'exécution de cmd.exe, fermant alors votre console.)

Et dans le cas d'un programme C++, vous le faites depuis le début sans savoir pourquoi: le fameux return 0 à la fin du main indiquait au programme de retourner le code de retour 0, signifiant qu'aucune erreur n'était survenue. Il est possible de retourner la valeur que l'on veut. Cette valeur est uniquement pertinente lorsque notre programme est exécuté par un script ou par un autre programme.

Les appels système

Vous en avez déjà utilisé un, peut-être sans le réaliser. Un appel système est une façon, en C++ d'appeler cmd.exe pour lui faire exécuter une commande. Il suffit de faire:

system("commande")

où la commande est une commande de cmd (possiblement suivie de paramètres), qui peut donc être un script, une autre application console, ou une commande texte (comme la célèbre cls, qui efface la console).

On pourrait donc aussi faire:

system ("copy programme.exe c:\windows")

ou:

system ("color CF") // Joyeux Noël

Toute commande utilisable dans la console peut être appelée par vos programmes. Vous pouvez également appeler un script windows (batch), ou un autre programme. La commande system retourne aussi la valeur de retour retournée par la commande appelée. Par exemple, si vous faites ceci:

int s = system ("copy fichierimaginaire.txt c:\ > nul")  
cout << s << endl;

vous devriez voir le code de retour 1, retourné par system, qui lui même l'a reçu de copy (puisqu'on tente de copier un fichier qui n'existe pas).

La tuyauterie de cmd

Vous avez déjà vu la possibilité, dans cmd, de rediriger la sortie d'une commande (ce qu'elle afficherait normalement dans la console) dans un fichier texte:

tasklist > task.txt

Il est aussi possible "d'enchainer" les commandes en utilisant l'opérateur "tube" ou "pipeline", représenté par la barre verticale | (on la trouve généralement à gauche de la touche 1, avec la majuscule).

Le pipeline exécute la commande à sa gauche, puis envoie la sortie de celle-ci à l'entrée de la commande à sa droite, qui est donc exécutée ensuite. L'utilisation classique de cet opérateur sous Windows:

type longtexte.txt | more

La commande type affiche le contenu du fichier texte longtexte.txt à l'écran, normalement, afin qu'on puisse le lire. Mais si le texte est très long, il déroule pendant quelques secondes et on a perdu le début. En l'envoyant plutôt à la commande more, cette dernière l'affiche un écran à la fois, en attendant que vous appuyiez sur une touche pour continuer.

En Windows, la plupart des commandes s'enchaînent mal puisqu'elles envoient du texte comme sortie, et que bien peu acceptent du texte en entrée (et celles qui le font ont besoin de texte bien précis). Vous verrez toutefois qu'en Linux, c'est un outil beaucoup plus puissant.

Vos programmes, lorsqu'ils contiennent des cin, peuvent recevoir des données du clavier, mais peuvent aussi recevoir des données à travers le pipeline, des données qui sont en fait le cout d'un autre programme...

Exercices

1- Créez un programme appelé or qui accepte deux nombres en paramètres. Les deux nombres doivent être des 1 ou des 0.

2- Créez 4 petits programmes tout simples qui lisent un nombre entré au clavier et qui affichent seulement un autre nombre, résultat d'un calcul spécifique. Ces quatre programmes n'ont pas besoin de paramètres. Ils font juste un cin pour lire un nombre et un cout pour afficher un résultat. C'est tout bête. Votre programme ne doit pas poser de question, ni afficher quoi que ce soit d'autre que le résultat du calcul.

  1. Le premier programme double le nombre reçu.
  2. Le deuxième programme met au carré le nombre reçu.
  3. Le troisième programme soustrait 100 au nombre reçu.
  4. Le quatrième programme fait la racine carrée du nombre reçu.

Créez ensuite un (très court) script batch qui utilise vos quatre programmes et des pipelines pour réaliser des opérations à la chaîne afin d'obtenir:

  1. La racine carrée du double d'un nombre auquel on a retranché 100.
  2. Le double du carré d'un nombre.

Montrez le résultat à votre gentil professeur avant de quitter afin d'avoir vos points.