ArmA: Gestion des exceptions (try, throw, catch)

Je vais te parler aujourd’hui de la gestion des exceptions sous Arma. Dans l’optique de l’amélioration continuelle de la fiabilité de tes scripts, BIS a mis à ta disposition 3 commandes try, throw, catch pour gérer les exceptions.

Si tu as déjà utilisé ces commandes dans un autre langage, tu dois déjà connaitre leur puissance. Malheureusement Sous Arma, ces commandes ne fonctionnent pas de la même façon (..) Ces commandes ont été implémenté à l’époque d’ARMA1, et n’ont depuis pas évolué. Il est d’ailleurs très rare de les rencontrer dans le code de la communauté.

Bien commençons, et regardons ce qu’est censé faire un gestionnaire d’exception:
Un système de gestion d’exceptions permet de gérer les conditions exceptionnelles pendant l’exécution du programme. Lorsqu’une exception se produit, l’exécution normale du programme est interrompue et l’exception est traitée.

De la théorie à la pratique
Regardons tout d’abord un peu le prototype de cette structure de contrôle:

 try //begin of try-catch block 
  { //block, that can throw exception } 
 catch
  { //block, that process an exception. Exception is described in _exception variable };

Le code à l’intérieur du scope try {} est toujours exécuté par défaut. Le code dans le scope catch {} est seulement exécuté si une exception provenant de l’intérieur du scope try {} a été déclenché par la commande throw.

La variable magique _exception dans le scope catch {} contiendra la valeur passée par la commande throw.

Le plus important à comprendre, est que l’exécution du scope try {} s’arrête dès que la commande throw est exécuté (équivalent à un exitwith{}).

Regardons cet exemple:

try {
    if !(isNull _unit) then {throw _unit};
    /*...code...*/
} catch {
    diag_log format ["Unit  is not null", _exception];
};

Ce script va déclencher une exception si _unit n’est pas null. L’exception sera ensuite attrapée par le scope catch {} qui va insérer une ligne dans les logs pour écrire l’exception. Le reste du /*…code…*/ dans le scope try {} ne sera pas exécuté.

Allons plus loin …

En pratique, l’implémentation de ces commandes devrait permettre d’intercepter les erreurs remontées au moment du runtime. Hors pour Arma, comme le montre, l’exemple ci-dessous, ça n’est pas le cas.

private _array = [];
try {
    _test = _array select 1;
} catch {
    hint "je gere une exception quand le tableau est vide";
};
// ne renverra que le message d'erreur arma division par 0, montrant que l’exception n'est pas interceptée.

Le moteur ne génère donc pas lui même ses exceptions. L’intérêt de ces commandes en devient par conséquent clairement limité (..)

La seule et unique chose que tu vas donc pouvoir faire est de déclencher toi même les exceptions pour des erreurs que tu considères comme applicative, et que tu gères normalement directement dans ton application avec des if.

Comment utiliser ces commandes alors ?
Ces commandes peuvent être une alternative élégante aux commandes break ou exitwith pour sortir d’une fonction, d’une boucle. En effet, il peut être intéressant de réaliser un traitement à l’extérieur d’une fonction pour rétablir le fonctionnement nominal du programme principal.

exemple:

fnc_external = {
    while { true } do {
        /* mon code */
        if(_variable isEqualTo 0) then { throw 1;};
        if(_variable isEqualTo 1) then { throw 2;};
    };  
};

// ton programme principal
_resultat = try {
    call fnc_external;
} catch {
    switch _exception do { 
        case 1: {/* ton code pour gerer l exception*/};
        case 2: {/* ton code pour gerer l exception*/};
    };
};

Autre utilisation de ces commandes

Comme ces commandes se comportent comme des structures de contrôle classique, il est possible d’en détourner l’usage

exemple:

_taste = "dunno";
_fruit = try {
    if (_taste == "sweet") then {throw "a cherry"};
    if (_taste == "sour") then {throw "a lemon"};
    "a good day"
} catch {_exception};
hint ("Have " + _fruit); //Have a good day

De cette façon, on peut utiliser ces commandes comme on le ferait avec une commande switch {}; Le bloc catch {} renvoie finalement le résultat des différents case décrit dans le bloc try {}, et le default est retourné directement.

Il est également possible de simuler le fonctionnement d’une commande call de cette façon:

_a = 1; _b = 2;
_c = try {
    throw [_a, _b];
} catch {
    (_exception select 0) + (_exception select 1);
}; //3

ou même de faire appel à des fonctions extérieures de cette façon:

fnc_log_error = {
    diag_log format ["Error: %1", _exception];
};
try {
    if !(assert false) then {throw "log whatever"};
} catch fnc_log_error;

Cela sera tout pour cet article sur le try {} catch {}, n’hésite pas à me faire savoir si tu utilises ces commandes, voir d’une façon différente à celle que j’ai indiqué au dessus 🙂

Cet article a été initialement rédigé par Killzone Kid, en grande partie complété, et corrigé par Code34

Votre commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l’aide de votre compte WordPress.com. Déconnexion /  Changer )

Photo Google

Vous commentez à l’aide de votre compte Google. Déconnexion /  Changer )

Image Twitter

Vous commentez à l’aide de votre compte Twitter. Déconnexion /  Changer )

Photo Facebook

Vous commentez à l’aide de votre compte Facebook. Déconnexion /  Changer )

Connexion à %s