Présentation du langage Vala
La puissance du C et la simplicité du C# grâce à Vala
Date de publication : 8 Octobre 2008
Par
Nicolas Joseph (home) (Blog)
Vala est un nouveau langage de programmation. Enfin c'est ainsi qu'il est
présenté sur le
site officiel.
Dans ce tutoriel, nous allons donc découvrir ce qu'est vraiment Vala
ainsi que les possibilités qu'il propose.
1 commentaire ·
I. Public concerné
II. Qu'est ce que Vala ?
III. Vala in a nuts
III-A. Les classes
III-A-1. Constructeur
III-A-2. Destructeur
III-B. Interfaces
III-C. Les classes abstraites
III-D. Les propriétés
III-E. Génériques
III-F. Foreach
III-G. Délégué et expressions lambda
III-H. Les signaux
III-I. Les exceptions
IV. Utilisez vos anciennes bibliothèques
V. FAQ
V-A. Commet fonctionnent les callback ?
V-B. ref, out, weak et transfert de propriété, de quoi s'agit-il ?
V-C. L'avenir de vala
V-D. Comment utiliser le fichier config.h ?
V-E. Quel système de construction utiliser ?
VI. Références
VII. Remerciements
I. Public concerné
 |
Je réserve ce tutoriel à un public averti, non pas que ce langage soit compliqué,
mais il reste relativement jeune et il n'est pas rare de devoir mettre
les mains dans le cambouis. Cependant cela devient de plus en plus rare et
pour une application simple un débutant ne devrait pas rencontrer de problème.
|
II. Qu'est ce que Vala ?
Vala est un nouveau langage de programmation. Nouveau puisqu'il existe
depuis 2006 (comparé au langage C qui existe depuis le début des années 70),
et aussi nouveau par sa conception puisqu'il ne s'agit pas réellement d'un
langage de programmation.
La première chose que l'on remarque lorsque l'on voit du code écrit en Vala
c'est la forte similitude avec le C#, aussi bien au niveau syntaxe que
fonctionnalités. La deuxième chose, plus étonnante, c'est que le compilateur
Vala, va générer un code dans un langage intermédiaire, comme peuvent le
faire le C# ou le Java.
A première vue, ça a l'odeur, la couleur et le goût du C#. S'il n'existait pas
mono
l'initiative serait intéressante. Mais alors où se trouve l'intérêt d'un tel langage ? Toute
l'astuce de Vala réside dans le langage intermédiaire utilisé, comme je le laisse sous-entendre dans
le titre de l'article, il s'agit bien sûr du C ! Du C à l'aide de la bibliothèque GObject.
Pour en finir avec cette présentation, le mariage entre le C#, pour l'écriture, et le C, pour la compilation,
procure à Vala un certain nombre n'avantages, dont voici un résumé :
-
Une facilité d'écriture : il est indéniable que le C# reste plus lisible que le C,
-
Un langage réellement orienté object : alors que le C/GObject accuse parfois d'une syntaxe bancale
vis-à-vis de la POO (en particulier pour la définition d'une classe), Vala permet de corriger ceci,
-
Une rapidité d'exécution proche du C pur : comme le code est traduit en C, on se retrouve avec des
vitesses d'exécution proche de celle d'un programme écrit en C(1),
-
La compatibilité binaire avec le C est conservée ce qui permet de ré-utiliser de nombreux outils et bibliothèques
éprouvées,
-
Ne dépend pas d'une machine binaire pour être exécuté.
Maintenant que les présentations sont faites, passons à la pratique.
III. Vala in a nuts
Nous nous contenterons ici d'une noix
(2) pour aborder la syntaxe du
langage Vala, tout simplement parce qu'elle est proche du C# et
les habitués du C++ ou de Java devraient s'y retrouver très
rapidement.
Voici la liste des fonctionnalités proposées par le langage :
- Les classes
- Les interfaces
- Les classes abstraites
- Les propriétés
- Les génériques
- Foreach
- Les expressions lambda
- Les signaux
- Les types nullables
- La gestion assistée de la mémoire
- Les exceptions
Histoire de vous familiariser avec ce langage et en guise de
pense bête, le vais détailler rapidement chaque fonctionnalité
avec un exemple de quelques lignes.
III-A. Les classes
Il est possible de créer des classes, qu'elles soient
publiques ou privées (portée limitée au fichier). Il est
aussi possible d'utiliser l'héritage (simple uniquement).
Pour créer une classe, il faut qu'elle hérite (directement
ou par l'intermédiaire de sa classe mère) de GLib.Object
(3) :
Pour finir, il est possible d'inclure une classe dans un
espace de nom, soit en utilisant le mot clé namespace
soit en préfixant le nom de la classe.
Voici quelques exemples pour illustrer ceci :
namespace Dvp
{
public MaClasse : Object
{
|
private Dvp.MaClasse : Object
{
|
 |
La GLib étant la bibliothèque standard de Vala, il est inutile
d'écrire GLib.Object, l'utilisation du namespace est implicite.
|
III-A-1. Constructeur
La construction d'un objet est quelque peu différente
par rapport à ce qui existe dans d'autres langages.
Voici la séquence de création d'un objet :
- L'utilisateur demande création d'un nouvel object grâce à l'opérateur new,
- Le système appelle le constructeur de classe pour l'ensemble de la hiérarchie en partant de la classe de base,
- Le système appelle le constructeur d'instance associé à notre classe.
Le constructeur de classe porte le nom
construct et ne possède pas de portée, type
de retour ou paramètre :
public class MaClasse
{
construct
{
|
Concernant le constructeur d'instance, il porte le nom
de la classe sans type de retour :
public class MaClasse
{
public MaClasse ()
{
|
Il est possible de spécifier un ou plusieurs paramètres
cependant il est uniquement possible de modifier les
propriétés de l'objet dans cette partie (nous
reviendrons plus tard sur les propriétés) :
public class MaClasse
{
public string label { ... }
public MaClasse (string label)
{
this.label = label;
|
Pour finir avec ce type de constructeur il est possible
de le surcharger en utilisant un suffixe :
public class MaClasse
{
public string label { ... }
public MaClasse.with_label (string label)
{
this.label = label;
}
public MaClasse ()
{
this.label = "default";
}
|
III-A-2. Destructeur
Comme très souvent, le destructeur porte le nom de la
classe précédé d'un tild :
public class MaClasse
{
~MaClasse ()
{
}
|
III-B. Interfaces
La déclaration d'une interface reste très classique,
les champs à implémenter étant spécifiés comme abstraits.
interface MonInterface
{
public abstract void foo ();
|
Il est possible de profiter de l'héritage entre interface
et une classe peut implémenter une ou plusieurs interfaces
de la même manière que pour l'héritage :
class Dvp.SuperClass : MaClass, MonInterface
{
public override void foo ()
{
|
III-C. Les classes abstraites
Si vous avez compris les interfaces, inutile de s'étendre
sur les classes abstraites :
abstract class AbstractClass
{
public abstract void foo ();
public void bar ()
{
|
III-D. Les propriétés
La syntaxe des propriétés est très riche, je vous renvoie
au
manuel de référence
pour un aperçu complet des possibilités.
Voici tout de même un exemple simple de propriété :
class MaClass : Object
{
private string _prop;
public string prop
{
get
{
return this._prop;
}
set
{
this._prop = value:
}
}
public static int main (string[] args)
{
var c = new MaClass ();
c.prop = "test";
print ("%s\n", c.prop);
return 0;
}
}
|
 |
Pour pouvoir bénéficier des propriétés, la classe doit hériter
de GLib.Object.
|
Pour simplifier l'écriture de propriétés "standard" (c'est-à-dire
comme ci-dessus, qui ne font que manipuler une variable privée),
Vala propose un mécanisme de propriétés automatiques qui
cache cette partie du code :
class MaClass : Object
{
public string prop
{
get;
set;
}
}
|
en plus de la visibilité de la propriété, il est possible
d'affiner la visibilité des assesseurs :
class MaClass : Object
{
public string prop
{
get;
private set;
}
}
|
Ce qui rendra la propriété lisible par tous, mais modifiable
uniquement pas les instances de la classe.
Pour finir, si vous souhaitez qu'une propriété ne soit modifiée
uniquement lors de la construction de l'objet, vous pouvez remplacer
l'assesseur set par construct :
class MaClass : Object
{
public string prop
{
get;
construct;
}
public MaClass (string prop)
{
this.prop = prop;
}
}
|
De plus ceci permet de mettre en place la valeur de la
propriété avant l'appel au constructeur de classe
construct (par défaut, la valeur n'est modifiée
qu'ensuite).
Ceci n'étant pas très clair, voici un exemple qui devrait
vous convaincre de l'utilité de ce mot clés :
class MaClass : Object
{
public string prop
{
get;
construct;
}
public string prop2
{
get;
set;
}
public MaClass (string prop, string prop2)
{
this.prop = prop;
this.prop2 = prop2;
}
construct
{
print ("MaClass.prop = %s\n", this.prop);
print ("MaClass.prop2 = %s\n", this.prop2);
}
}
public class Main
{
public static int main (string[] args)
{
new MaClass ("1", "2");
return 0;
}
}
|
MaClass.prop = 1
MaClass.prop2 = (null)
|
III-E. Génériques
Vala supporte les génériques (ou template pour les adeptes du C++),
de manière très classique :
class Wrapper<T> : Object { ... }
new Wrapper<Object> ();
|
Les différentes bibliothèques (en particulier la glib),
ont été enrichies avec les génériques qui n'existent pas en C.
Leur utilisation n'est pas obligatoire (par exemple pour les listes)
mais cela améliore la lisibilité du code est permet d'utiliser
certaines fonctionnalités du langage, comme le foreach.
III-F. Foreach
Pour illustrer mes propos précédents, voici un exemple avec
les listes :
List<string> files;
foreach (string file in files)
{
print ("%s\n", file);
}
|
 |
Le foreach est utilisable avec les tableaux, les listes
(simple ou doublement chaînées), les tables de hachages et
toutes les classes qui implémente l'interface Gee.Iterable.
|
III-G. Délégué et expressions lambda
Les délégués (ou delegates, en anglais), sont utilisés à la
place des pointeurs de fonctions et on les rencontre le
plus fréquemment pour la gestion des événements (signaux).
Pour associer une fonction callbask à un signal, il suffit
de l'ajouter :
var win = new Gtk.Window ();
win.destroy += Gtk.main_quit;
|
La même syntaxe est utilisée pour déconnecter la fonction :
win.destroy -= Gtk.main_quit;
|
Et plutôt que de créer une fonction pour les signaux demandant
peu de traitement, il est préférable d'utiliser les
expressions lambda qui permettent d'inclure le corps de la
fonction directement :
win.destroy += (s) => {
print ("Quit\n");
Gtk.main_quit ();
};
|
III-H. Les signaux
Nous venons de voir comment intercepter un signal, nous allons
donc nous attarder sur la création d'un signal pour notre objet.
C'est extrêmement simple, il suffit de déclarer que votre objet
envoi un signal à l'aide du code suivant :
public signal void sig_1 (int a);
|
Ensuite, votre objet émet ce signal tout naturellement :
Et pour finir, si vous souhaitez intercepter le signal, il suffit
d'utiliser les delegates ou les fonctions lambda :
o.sig_1 += (s, a) => {
print ("sig_1 : %d\n", a);
};
|
 |
Le premier paramètre correspond à l'objet qui à lancé le
signal (s pour sender), il est passé explicitement.
|
III-I. Les exceptions
Pour ce qui est de la gestion des exceptions, nous retrouvons
une syntaxe traditionnelle :
class MaClass : Object
{
public void foo () throws Error { ... }
public static int main (string[] args)
{
try
{
foo ();
}
catch (Error e)
{
warning (e.message);
}
finally
{
}
return 0;
}
}
|
Par contre pour la création et le lancement d'une exception,
nous devons passer par les spécificités de la GLib
(4).
Il faut commencer par créer un nouveau domaine d'erreur avec les
différents types d'erreur possible :
public errordomain MyError
{
ERROR_1,
ERROR_2
}
|
Et ensuite, nous pouvons créer et lancer notre exception :
throw new MyError.ERROR_1 ("Error message");
|
IV. Utilisez vos anciennes bibliothèques
Comme précisé en introduction, il est possible de réutiliser vos
bibliothèques écrites en C pour vos nouveaux développements en Vala.
Pour cela, il suffit de créer un fichier d'en-tête vapi. Ce fichier
contient simplement la déclaration des classes. Il est bien sûr
nécessaire que votre interface corresponde à une approche objet
(même s'il est possible de corriger certaines imperfections grâce
à l'annotation CCode).
S'il est possible d'écrire ces fichiers à la main pour de petites bibliothèques, cela n'est pas envisageable
pour les interfaces plus conséquentes (par exemple pour GTK+). Il existe donc le programme gobject-introspection
qui pourra vous aider dans cette tâche.
V. FAQ
Voici une mini-FAQ qui vous permettra de répondre à quelques questions qui ne se trouvent pas forcement
sur le site officiel.
V-A. Commet fonctionnent les callback ?
Comme précisé ci-dessus pour intercepter un signal, il faut
utiliser les delegates. La connexion est extrêmement
simple, cependant le prototype de la fonction callback possède
quelques subtilités non précisées dans la documentation.
Premièrement, il faut utiliser l'annotation Callback,
ceci permet au compilateur de créer un swrapper afin d'assouplir
la liste des arguments. Deuxièmement il est possible d'utiliser
indifféremment une méthode de classe ou d'instance pour gérer le
signal. Voici quelques exemples de callback :
public class TestCb : Object
{
[Callback]
private void cb_1 ()
{
}
[Callback]
private void cb_2 (Gtk.Button sender)
{
}
[Callback]
private static void cb_3 ()
{
}
public static int main (string[] args)
{
Gtk.Button btn = new Gtk.Button.with_label ("Click!");
btn.clicked += this.cb_1;
btn.clicked += this.cb_2;
btn.clicked += TestCb.cb_3;
}
}
|
Le compilateur se chargera de passer la référence vers l'instance
si nécessaire et de compléter la liste des arguments.
V-B. ref, out, weak et transfert de propriété, de quoi s'agit-il ?
Voici des mots clés peu connus des développeurs C.
Le mot clé ref est utilisé pour spécifier qu'un
paramètre est passé par référence, donc peut être modifié par la fonction.
Le mot clé out précise que le paramètre est utilisé pour stocker
une valeur de retour.
Le mot clé weak désigne une référence en la définissant
comme faible, c'est à dire que si toutes les références sur un objet
sont faibles, l'objet sera détruit.
Le transfert de propriété, indiqué par le caractère #, permet de
transférer la propriété d'une référence d'une variable à une
autre. Par exemple :
Normalement, la référence de l'objet bar est copiée dans
foo et le nombre de références augmentée d'un. Ici, foo
va contenir la référence de bar puis bar sera mis
à null sans que le compteur de référence soit incrémenté.
Ceci est particulièrement utilisé lorsqu'une fonction retourne
une chaîne de caractère créée localement, afin que celle-ci
continue d'exister après le retour de la fonction.
 |
Ceci se retrouve en C# dans les propriétés avec le mot clés new.
|
V-C. L'avenir de vala
Vala est encore jeune et cela se ressent lorsqu'on développe une
application importante : il n'est pas rare de devoir modifier
les fichiers vapi à la main pour corriger un problème généralement dû
à une ambiguïté dans le nommage des fonctions en C.
Au fils de mes développements j'ai constitué un patch
pour le fichier gtk+-2.0.vapi que vous pouvez trouver
ici.
Il contient les bugs qu'il est difficile de corriger.
V-D. Comment utiliser le fichier config.h ?
Les développeurs du monde Linux, en particulier ceux familiers
des autotools connaissent le fameux fichier config.h. Pour
pouvoir l'utiliser, il suffit de créer un fichier d'interface vapi ressemblant
à l'exemple suivant :
[CCode (cprefix = "", lower_case_cprefix = "", cheader_filename = "config.h")]
namespace Config
{
public const string PACKAGE;
public const string PACKAGE_BUGREPORT;
public const string PACKAGE_NAME;
public const string PACKAGE_STRING;
}
|
V-E. Quel système de construction utiliser ?
Pour les adeptes des autotools, il est parfaitement possible
de les utiliser avec vala. Le programme vala-gen-project
vous génère tous les fichiers nécessaires pour débuter un projet
avec les autotools.
Ou, si comme moi, vous trouvez les autotools repoussant,
il existe
waf
qui prend aussi en compte le langage Vala.
VI. Références
VII. Remerciements
Merci à
Adrien Artero pour la relecture attentive de cet article.
| (1) | http://code.google.com/p/vala-benchmarks/wiki/BenchResults |
| (2) | En référence à
la série In a nutsheel
des éditions O'Reilly. |
| (3) | Il est possible de s'en affranchir mais dans
ce cas nous obtenons une simple structure de données et
devont nous passer de nombreuses fonctionnalités. |
| (4) | Vous
pouvez lire Gestion des erreurs en C avec la GLib
afin de comprendre comment gérer les exceptions en C |


Copyright © 2008 Nicolas Joseph. Aucune reproduction, même partielle, ne peut être faite
de ce site et de l'ensemble de son contenu : textes, documents, images, etc
sans l'autorisation expresse de l'auteur.
Sinon vous encourez selon la loi jusqu'à 3 ans de prison et jusqu'à 300 000 E
de dommages et intérêts. Droits de diffusion permanents accordés à developpez LLC.
Cette page est déposée à la
SACD.