Muffin 3/Le langage D

De Magazine fedora-fr
Aller à : navigation, rechercher
Par : JonathanMercier
Article en cours de relecture !
InProgress tip.png
Le présent article est actuellement en cours de relecture.
Adressez-vous à la personne en charge pour toute proposition ou modification.
Relecteur / Validateur : [[Utilisateur:{{{1}}}|{{{1}}}]]

Sommaire

Le langage D

Introduction

Le langage D est un langage de programmation système qui se présente comme le successeur des langages C et C++. Son objectif est d'allier la puissance du C et du C++ à la simplicité et à la productivité de langages comme Ruby et Python. Le célèbre slogan lié à ce langage est :

Si un langage peut récupérer 90 % de la puissance du C++ mais seulement 10 % de sa complexité, cela vaut largement le coup.

Walter Bright

Les paradigmes supportés par ce langage sont les suivants :

  • impératif [1]
  • orienté objet [2]
  • méta-programmation [3]
  • programmation par contrat [4]

Fedora 14

La célèbre distribution Fedora garde un temps d'avance sur ses concurrentes. En effet, elle propose à ses utilisateurs un environnement de travail pour le langage D dernier cri. De nombreux jours de travail ont permis des améliorations significatives autour de ce langage. Et comme à son habitude, Fedora vous propose les dernières versions de ces outils. Avec un compilateur basé sur LLVM nommé ldc et une bibliothèque standard pour développer des applications en D : tango. Contrairement aux autres distributions, les outils sont pleinement fonctionnels et activement maintenus. De plus, le travail de la communauté Fedora ne s'est pas arrêté en si bon chemin. Celle-ci a également intégré :

  • la bibliothèque mango qui tourne autour du réseau
  • la bibliothèque derelict qui permet de développer des applications OpenGl en D

Fedora c'est génial !

À qui s'adresse ce langage ?

  • Aux programmeurs qui sont lassés d'utiliser des analyseurs de codes ou des outils similaires pour réussir à éliminer des bogues.
  • À tout ceux qui en ont assez des messages d'erreur incompréhensibles lors de la compilation de code C++.
  • À tout ceux qui veulent utiliser la programmation objet sans avoir la complexité du C++.
  • Aux codeurs qui aiment la puissance du C++ mais qui sont frustrés par le temps de débogage nécessaire.
  • À ceux qui veulent intégrer les tests unitaires et activer des sections de code en mode débogage.
  • Aux équipes qui écrivent des applications avec un million de lignes de code.
  • Aux développeurs qui pensent que le langage doit fournir suffisamment de fonctionnalités pour pallier la nécessité de continuer à manipuler les pointeurs.
  • Aux programmeurs qui ont besoin de manipulations avancées sur les nombres. Le langage D intègre de nombreuses fonctionnalités pour cela.
  • Aux équipes qui doivent écrire en partie leurs applications dans des langages de script comme Python et Ruby pour gagner du temps de développement.

À qui ne s'adresse pas ce langage ?

Aux puristes. D est un langage orienté pratique et non théorique, chaque fonctionnalité est pensée avec cet objectif plutôt qu'un idéal théorique. Par exemple, D est construit et fournit une sémantique de façon à éliminer la nécessité de l'utilisation des pointeurs pour les tâches ordinaires. Mais les pointeurs sont toujours là, parce que leur utilisation est parfois obligatoire.

Ce dont vous avez besoin

  • Un compilateur :
# yum install ldc
  • La bibliothèque standard du langage D :
# yum install tango-devel

Soit en une seule commande :

# yum install ldc tango-devel

Les types

Note 1 : les valeurs suivantes ont été obtenues sur un système 64 bits.

Note 2 : vous pouvez générer ces résultats avec ce fichier Programme_D_pour_connaitre_la_taille_des_types

Type Description Min Max
byte Valeur entière sur 1 octet (8 bits) -128 127
ubyte Valeur entière positive sur 1 octet (8 bits) 0 255
short Valeur entière sur 2 octets (16 bits) -32768 32767
ushort Valeur entière positive sur 2 octets (16 bits) 0 65535
int Valeur entière sur 4 octets (32 bits) -2147483648 2147483647
uint Valeur entière positive sur 4 octets (32 bits) 0 4294967296
long Valeur entière sur 8 octets (64 bits) -9223372036854775808 9223372036854775807
ulong Valeur entière positive sur 8 octets (64 bits) 0 18446744073709551615
float Valeur numérique sur 4 octets (32 bits) 1.18e-38 3.40e+38
ifloat Valeur numérique imaginaire pure sur 4 octets (32 bits) 1.18e-38i 3.40e+38i
double Valeur numérique sur 8 octets (64 bits) 2.23e-308 1.80e+308
idouble Valeur numérique imaginaire pure sur 8 octets (64 bits) 2.23e-308i 1.80e+308i
real Le plus grand numérique supporté par le processeur soit 16 octets (128 bits) 3.36e-4932 1.19e+4932
ireal Le plus grand numérique supporté par le processeur soit 16 octets (128 bits) 3.36e-4932i 1.19e+4932
char Un caractère imprimable encodé en UTF-8 sur 1 octet (8bits) 0 255
wchar Un caractère imprimable encodé en UTF-16 sur 2 octets (16bits) 0 65535
dchar Un caractère imprimable encodé en UTF-32 sur 4 octets (32bits) 0 4294967293
cfloat Nombre complexe de 2 valeurs flottantes (float) 1.18e-38 +1.18e-38i -nan
cdouble Nombre complexe de 2 doubles 2.23e-308 +2.23e-308i 1.19e+4932
creal Nombre complexe réel 3.36e-4932 +3.36e-4932i 1.19e+4932

Les éditeurs pour développer en D

Emacs

Pour cela, vous avez besoin de ce fichier: Fichier pour ajouter le support D dans Emacs.

  1. Démarrez emacs, puis faites "ouvrir" ou "créer un fichier D".
  2. Tapez M-x (méta x soit alt + x).
  3. Entrez d-mode (valider avec la touche entrée).

Gedit

Il n'y a rien à faire, il reconnait nativement le langage D et active la coloration syntaxique.

Scite

Il possède lui aussi la coloration syntaxique. Petite particularité: l'explorateur de fichier possède un filtre pour les fichiers sources et ne considère pas les fichiers .d comme tels ! Par conséquent, vous ne pouvez pas les voir. Il suffit de permuter le filtre sur "tout fichier".

Vim

Ce célèbre éditeur n'a rien à envier à ses semblables et propose lui aussi nativement la coloration syntaxique pour le langage D.

Code::blocks

Cet IDE supporte le langage D et propose l'auto-complétion ainsi que quelques autres fonctionnalités. Malheureusement, il ne supporte pas encore le compilateur ldc.

Eclipse et le plugin Descent

À travers ce plugin, on retrouve le confort de l'utilisation d'un IDE supportant le langage D. Tout comme Code::blocks, il ne supporte pas nativement le compilateur ldc.

Vous trouverez toutefois la procédure de mise en place ici : [5].

Votre premier programme

Avant de commencer

Avant de commencer, vous devez savoir que tous les programmes doivent être composés d'un main.

Qu'est-ce que le main ?

Le main est la fonction principale du programme. Pour faire court, c'est le point de départ du programme.

Qu'est-ce qu'une fonction ?

Une fonction est composée des éléments suivants :

  1. un en-tête : celui-ci va faire office de carte d'identité de la fonction.
  2. un corps : le contenu de la fonction, c'est à dire ce qui est exécuté lors de l'appel de la fonction. Il est délimité par des accolades {}.
  3. une valeur de retour : le résultat que la fonction va retourner.

L'en-tête de la fonction main peut s'écrire de plusieurs manières. En voici quelques-unes :

  • une fonction principale retournant un entier et ne prenant pas d'argument
int main (){
…
}
  • une fonction principale retournant un entier et pouvant prendre des arguments de la ligne de commande
int main (char[][] args){
…
}
  • une fonction principale ne retournant aucune valeur et ne prenant pas d'argument
void main (char[][] args){
…
}

Bonjour le monde

Et oui, on ne va pas casser la tradition ! (Bon d'accord, j'ai francisé cette tradition.)

Un exemple simple

Je ne vais pas vous faire languir plus longtemps. Créez un fichier hello.d et écrivez-y les lignes de code suivantes :

import tango.io.Stdout;
void main(){
    Stdout("Bonjour le monde").nl;// j'imprime sur la sortie standard
}

Simple, vous ne trouvez pas ?

Compilation et exécution

Pour compiler, faites simplement :

$ ldc hello.d

Pour l'exécuter:

$ ./hello.d

Je vous laisse le plaisir d'observer le résultat.

Quelques explications
  • Sur la 1ère ligne, on indique que l'on a besoin du module Stdout se trouvant dans <path>tango/io/Stdout.d</path>.

Nous y reviendrons plus tard. Sachez simplement que tous les modules D que vous aurez installés sous Fedora sont dans <path>/usr/include/d/</path>.

Soit ici : <path>/usr/include/d/tango/io/Stdout.d</path>.

  • La 2ème ligne, vous l'aurez reconnue, est notre fonction principale. Elle ne prend pas d'argument et ne retourne aucune valeur.
  • La 3ème ligne est dans la fonction principale, (remarquez les {}) et demande d'écrire sur la sortie standard "Bonjour le monde" suivi d'un retour à la ligne (.nl pour new line/nouvelle ligne).

Il existe beaucoup de façons différentes d'écrire sur la sortie standard mais ne vous éparpillez pas ! On aurait pu souhaiter ne pas vouloir de retour à la ligne à la fin de notre phrase, cela se fait aussi simplement :

Stdout("Bonjour le monde");

Note: n'oubliez pas de recompiler votre code pour voir le changement.

  • La 4ème ligne vient fermer le bloc de la fonction principale.
Les commentaires

J'ai remarqué dans l'exemple précédent // j'imprime sur la sortie standard. Qu'est ce que cela veut dire ? Ceci est un commentaire. En effet, vous pouvez laisser des explications dans votre code (c'est d'ailleurs très fortement recommandé). Il existe plusieurs formes de commentaires :

Commentaire sur une ligne

// Mon commentaire
/// Ceci est une documentation

Commentaire multi-lignes

/*
  Ceci est un commentaire
*/
/*
 * Ceci est un commentaire
 */
/+
  Ceci est un commentaire
+/
/+
 + Ceci est un commentaire
 +/
/**
  Ceci est une documentation
*/
/**
 * Ceci est une documentation
 */
/++
 + Ceci est une documentation
 +/

Vous remarquerez qu'il est parfois écrit « documentation » au lieu de « commentaire ». En effet, on peut documenter son code. Ainsi, un programme externe va lire votre code source et générer automatiquement la documentation. Pour les connaisseurs, c'est le même principe que Javadoc ou Doxygen. Nous y reviendrons plus loin dans ce chapitre.

Les opérateurs arithmétiques

+ permet d'additionner deux variables numériques. - permet de soustraire deux variables numériques. * permet de multiplier deux variables numériques. / permet de diviser deux variables numériques. % permet de renvoyer le reste de la division euclidienne de deux variables de type numérique ; cet opérateur s'appelle le modulo. Par exemple :

import tango.io.Stdout;
void main(){
    uint a = 2;
    uint b = 2;
    Stdout.formatln("une addition: {} + {} = {}", a, b, a+b);
    Stdout.formatln("une soustraction: {} - {} = {}", a, b, a-b);
    Stdout.formatln("une multiplication: {} x {} = {}", a, b, a*b);
    Stdout.formatln("une division: {} / {} = {}", a, b, a/b);
    Stdout.formatln("le modulo: {} % {} = {}", a, b, a%b);
}

Une histoire d'incrémentation et décrémentation

Dans la même catégorie, il existe l'incrémentation et la décrémentation :

  • l'incrémentation consiste à augmenter de 1 la valeur de la variable.
  • la décrémentation consiste à diminuer de 1 la valeur de la variable.

Pour cela, il y a plusieurs façons de le faire. Prenons le cas de l'incrémentation :

int a = 2;
a = a + 1;
a += 1;
a++;
++a

Vous remarquez que les 2 dernières formes sont plus rapides à écrire (oui, le programmeur est un fainéant).

Y a-t-il une différence entre ++a et a++ ?

Oui, il y en a une et elle est subtile :

  • a++ : utilise la valeur de a, puis l'incrémente
  • ++a: incrémente la valeur de a, puis l'utilise

Par exemple :

import tango.io.Stdout;
void main(){
    uint a = 2;
    Stdout.formatln("une incrémentation: {} + {} = {}", a, b, a++);
    a = 2;
    Stdout.formatln("une incrémentation: {} + {} = {}", a, b, ++a);
    a = 2;
    Stdout.formatln("une décrémentation: {} - {} = {}", a, b, a--);
    a = 2;
    Stdout.formatln("une décrémentation: {} - {} = {}", a, b, --a);
}

Autres raccourcis

On peut également utiliser les raccourcis pour la multiplication, la division et le reste de la division euclidienne. un court exemple :

uint a = 5;
a *= 2;
a /= 2;
a %= 2;

Attention

Je vous mets en garde sur le type utilisé. Ce dernier peut avoir des conséquences graves ! En effet, jusqu'ici on a utilisé le type uint et nos résultats étaient des entiers. Mais ceci n'est pas systématiquement vrai. Par exemple, diviser 1 par 3 donne un résultat décimal ne pouvant pas correspondre à un entier positif (uint). Par conséquent, vous devez utiliser un autre type plus approprié, comme le type float ou double :

double a = 1;
a /=3; // a vaut 0.33333333
int b = 1;
int c = 3;
double d = b / c; // d vaut 0.33333333

Les opérateurs de comparaison

Il est fréquent dans un programme que l'on ait de besoin de comparer. Pour cela, en D, il est possible de tester différents cas :

  • tester une égalité
3 == 3; // cette expression retournera vrai
3 == 4; // cette expression retournera faux

  • tester une différence
3 != a; // 3 est différent de 4 donc cette expression retournera vrai
  • comparer a ou b
a || b // si l'une des expressions est vraie, l'expression globale sera vraie
  • comparer a et b
a && b // si l'une des expressions est fausse, l'expression globale sera fausse
  • savoir si a est strictement plus petit que b
a < b
  • savoir si a est plus petit ou égal à que b
a <= b
  • savoir si a est strictement plus grand que b
a > b
  • savoir si a est plus grand ou égal à b
a >= b
  • savoir si a est identique à b
a is b
  • savoir si a n'est pas identique à b
a !== b

Notes

Les comparateurs d'identités sont particuliers et méritent plus d'explications. Si l'on compare :

  • des types entre eux (par exemple int)
int[1] a = 2;
int[1] b = a;
a is b; // retournera vrai

Lorsque l'on compare des types avec ce comparateur, il agit comme si c'était le comparateur ==

  • des objets
Personne jean = new Personne("jean");
Personne paul = new Personne("paul");
jean is paul; // retournera faux

Lorsque l'on compare des objets entre eux, ce comparateur va dire si oui ou non c'est le même objet qui est référencé. On reviendra plus tard sur les références, gardez-les à l'esprit et n'hésitez pas à revenir dessus.

Le comparateur !== est la négation du comparateur === donc

Personne jean = new Personne("jean");
Personne paul = new Personne("paul");
jean !is paul; // retournera vrai

Les conditions

Dans le chapitre précédent, nous avons vu les opérateurs de comparaison. Maintenant, nous allons les utiliser avec les conditions. Rien de plus simple ! Commençons de suite.

Avec les mots clés if et else

int a = 2;
if ( a == 2 ){ // test une égalité avec l'opérateur == , si a vaut 2
    Stdout("la variable a vaut bien 2").nl;
}
else if ( a == 3){ // sinon si la variable a vaut 3
    Stdout("la variable a vaut bien 3").nl;
}
else{ // sinon (pour tous les autres cas)
    Stdout.formatln("la variable a vaut {}",a);
}

C'était un exemple pour vous dégourdir les méninges. Complexifions légèrement avec divers exemples d'utilisation :

int a = 2;
int b = 3;
if ( a == 2 && b == 3 ){ // si a vaut 2 et b vaut 3. Les 2 conditions doivent être vraies pour rentrer dans le bloc
    Stdout.formatln("la variable a vaut {} et la variable b vaut {}", a, b);
}
if ( a == 2 || b ==3 ){ // si a vaut 2 ou bien si b vaut 3. Au moins une des 2 conditions doit être vraie pour rentrer dans le bloc
    Stdout.formatln("la variable a vaut {} et la variable b vaut {}", a, b);
}
if ( a > 1 && b >= 3 ){ // si a est strictement plus grande que 1 et b plus grande ou égale à 3. Les 2 conditions doivent être vraies pour rentrer dans le bloc
    Stdout.formatln("la variable a vaut {} et la variable b vaut {}", a, b);
}
if ( a <= 2 || b < 3){ // si a est plus petite ou égale à 2 ou bien si b est strictment plus petite que 3. Au moins une des 2 conditions doit être vraie pour rentrer dans le bloc
    Stdout.formatln("la variable a vaut {} et la variable b vaut {}", a, b);
}

Avec les mots clés switch et case

Vous avez vu précédemment la possibilité d'imbriquer des si - sinon de la manière suivante :

int a = 4;
if ( a == 0){
…
}
else if ( a == 1){
…
}
else if ( a == 2){
…
}
else if ( a == 3){
…
}
else if ( a == 4){
…
}
else {
…
}

Sachez qu'il existe une manière plus élégante d'écrire cela à l'aide des mots clés switch et case :

int a = 4;
switch (a){ // on met la variable à tester
    case 1: // cas où la valeur est 1
        …
        break
    case 2: // cas où la valeur est 2
        …
        break
    case 3: // cas où la valeur est 3
        …
        break
    case 4: // cas où la valeur est 4
        …
        break
    default: // cas où la valeur est différente
        …
}

Pourquoi retrouve-t-on break à chaque fois ? Admettons 2 choses pour imager:

  1. que la variable a vaut 3
  2. que le mot clé break soit absent

D'après l'exemple précédent, on entrerait dans le bloc à partir du cas 3 et on effectuerait tous les cas suivants, c'est à dire ici le cas 4 et le cas par défaut. Dans la majeure partie des cas, on n'a pas besoin d'exécuter 2 règles, c'est pour cela que l'on met l'instruction break afin qu'il quitte après avoir effectué son traitement. Un dernier exemple du même type pour la route :

int a = 1;
switch (a){ // on met la variable à tester
    case 1: // cas où la valeur est 1
        …
    case 2: // cas où la valeur est 2
        …
    case 3: // cas où la valeur est 3
        …
        break
    case 4: // cas où la valeur est 4
        …
        break
    default: // cas où la valeur est différente
        …
}

Ici, on va rentrer dans le cas 1, 2 et 3 puis sortir.

Ce comportement est similaire en C et en C++. Mais en D, on peut également utiliser des chaînes de caractères :

char[] prenom = "jonathan";
switch (prenom){ // on met la variable à tester
    case "jonathan": // cas où la valeur est "jonathan"
        …
        break
    case "jean": // cas où la valeur est "jean"
        …
        break
    case "paul": // cas où la valeur est "paul"
        …
        break
    case "remi": // cas où la valeur est "remi"
        …
        break
    default: // cas où la valeur est différente
        …
}

Les tableaux

Déclaration

Il y a 5 façons de déclarer un tableau :

Code Description
int* p; Pointeur vers les données
int[3] s; Tableau statique
int[] a; Tableau dynamique
int[char[]] x; Tableau associatif
int[][] m; Matrice

Les pointeurs

int* p;

Ceci est un simple pointeur vers des données, il représente la même chose qu'en C. Pour ces raisons, je vous invite à lire la partie La mémoire, une question d'adresse sur le Site du Zéro.

Les tableaux statiques

int[3] s;

Un tableau statique est un tableau dont on spécifie la taille lors de sa déclaration, taille qui ne changera plus. Ici, on crée un tableau dans lequel on peut stocker 3 entiers. La taille d'un tableau statique est fixée au moment de la compilation.

Exemples
int[3] s;
s[0] = 50;
s[1] = 24;
s[2] = 98;

Les tableaux dynamiques

int[] a;

Un tableau dynamique est un tableau dont la taille peut varier. Lors de sa déclaration, on ne spécifie pas sa taille. En fait, c'est le ramasse-miette (garbage collector) qui gère ce tableau au moyen d'un pointeur.

Exemples
int[] a;
a.length = 2; // on donne une taille de 2
a[0] = 5;
a[1] = 8;
a.length = a.length + 5 // ici 2+5 = 7 soit la nouvelle taille du tableau
a[2] = 5;
a[3] = 7;
a[4] = 9;
a[5] = 3
a[6] = 1;

Il est conseillé de faire le minimum possible de redimensionnements de tableaux afin de garder de bonnes performances. Ainsi, on évitera :

int[] a;
for ( i = 0 , i < 50, i++){
    a.length = a.length + 1;
    a[i] = 0;
}

On utilisera de préférence :

int[] a;
for ( i = 0 , i < 50, i++){
    if (a.length == i){
        a.length = a.length * 2; // si la taille du tableau est égale à l'indice i, on double sa taille;
    }
    a = 0;
}
a.length = i; // Afin d'économiser de la mémoire, on ajuste exactement la taille du tableau au besoin soit la valeur de l'indice i

Les tableaux associatifs

int[char[]] x;

Un tableau associatif est un tableau dans lequel on associe 2 valeurs ensemble. Ici, on associe une chaîne de caractères à un entier.

Astuce: pour comprendre ce que que représente un tableau associatif, je vous conseille de le lire de la droite vers la gauche. Soit :

Le tableau associatif x contient des chaînes de caractères qui sont associées à un entier.

Exemples
int[char[]] x;
x["pomme"] = 2;
x["poire"] = 5;
x["orange"] = 7;

Dans l'exemple ci-dessus, on associe un fruit à un nombre (pratique pour connaître la quantité restante de chaque fruit).

Les propriétés

Propriétés des tableaux statiques
Propriétés Descriptions
.sizeof Retourne la taille du tableau multipliée par le nombres d'octets pour chaque élément du tableau.
.length Retourne le nombre d'éléments dans le tableau. Cette valeur ne peut pas être modifiée pour les tableaux statiques.
.ptr Retourne un pointeur sur le premier élément du tableau.
.reverse Inverse l'ordre des éléments (le premier devient le dernier) et renvoie le tableau inversé.
.sort Trie les éléments du tableau et renvoie le tableau trié.
.dup Crée un tableau dynamique de la même taille et copie tous les éléments dans ce tableau puis renvoie ce tableau.
Propriétés des tableaux dynamiques
Propriétés Descriptions
.sizeof Retourne la taille de la référence du tableau dynamique, qui est de 8 sur un ordinateur 32 bits.
.length Permet d'obtenir ou de changer la taille du tableau.
.ptr Retourne un pointeur sur le premier élément du tableau.
.reverse Inverse l'ordre des éléments (le premier devient le dernier) et retourne le tableau inversé.
.sort Trie les éléments du tableau et renvoie le tableau trié.
.dup Crée un tableau dynamique de la même taille, et copie tous les éléments dans ce tableau puis retourne ce tableau.

Le slicing

Le slicing est une technique particulière qui permet de copier une série d'éléments d'un tableau dans un autre. Voici un exemple de cette technique :

int[10] a = [1,2,3,4,5,6,7,8,9,10]; // déclare un tableau de 10 entiers
int[] b; // déclare un tableau dynamique
int[] c; // déclare un tableau dynamique
int[3] d; // déclare un tableau statique de 3 éléments
b = a[1..3]; // le fameux slicing, on copie ici les éléments des indices 1 à 3 dans le tableau b. Rappel: le premier indice est le 0 a[0]
c = a[4..$]; // ici on copie tous les éléments à partir de l'indice 4 jusqu'à la fin
d[0..2] = 5; // ici on assigne la valeur 5 à tous les éléments du tableau. Cela équivaut à d[0] = 5 d[1] = 5 d[2] = 5

Concaténation de tableaux

int[10] a = [1,2,3,4,5,6,7,8,9,10]; // déclare un tableau de 10 entiers
int[10] b = [11,12,13,14,15,16,17,18,19,20]; // déclare un tableau de 10 entiers
int[] c;
c = a ~ b; // soit c contient 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20

On peut combiner le slicing et la concaténation

int[10] a = [1,2,3,4,5,6,7,8,9,10]; // déclare un tableau de 10 entiers
int[10] b = [11,12,13,14,15,16,17,18,19,20]; // déclare un tableau de 10 entiers
int[] c;
c = a ~ b[0..5]; // soit c contient 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15

Les matrices

Peu de langages supportent nativement les matrices, mais le langage D en fait partie.

Qu'est-ce qu'une matrice ?

Mieux qu'un long discours, voici un exemple :

0 0 0 
0 0 0 
0 0 0 
0 0 0 

Ceci est une matrice de 4 colonnes par 3 lignes :

uint[3][4] matrix; // déclaration de la matrice
foreach (ligne; matrix)
{
   ligne[0..$] = 0;// on remplit la matrice de 0 en utilisant le slicing, c'est pratique !
}

Les chaînes de caractères

Les langages de programmation doivent bien gérer les chaînes de caractères. Le C et le C++ ne sont pas très bons pour cela. La première difficulté est de gérer la mémoire, les traitements temporaires, de constamment scanner les chaînes de caractères pour rechercher la terminaison par "\0" et de fixer la taille.

Les tableaux dynamiques en D suggèrent la solution la plus évidente. Une chaîne est simplement un tableau dynamique de caractères. Les chaînes littérales sont juste un moyen facile d'écrire des tableaux de caractères.

char[] str = "Le D c'est génial"; // déclaration et initialisation
char[] str1 = str.dup; // copie de la chaîne de caractères
str += " et je l'ai adopté"; // on ajoute à la suite de la chaîne str
char str2[] = "Vive le D";
char str3 = str ~ str2; // on concatène str et str2, grâce au ~ et on stocke le tableau résultant dans str3

Par défaut les chaînes de caractère sont encodées en utf8. Il existe également les types:

  • wchar
  • wchar[]
  • dchar
  • dchar[]

Si une chaîne est ambiguë et peut correspondre à plusieurs types, il faut préciser.

Si on veut de l'UTF-32, on peut faire un cast (voir « Les conversions de type (cast) ») :

(wchar [])"J'aime le D";

Ou si on veut de l'UTF-32 : 
(dchar[])"J'aime le D";
char c;
wchar u;
dchar a;
c = "b"; // le caractère b (UTF-8) est assigné à c
u = "b" // le caractère b (UTF-16) est assigné à u
a = "b" // le caractère b (UTF-32) est assigné à a
u = \n; // un retour à la ligne est assigné à u

Les tableaux associatifs

Le langage D intègre nativement les tableaux associatifs. Précédemment, vous avez eu un court exemple de leur utilisation. Voici quelques autres exemples :

int[char[]] b; // association d'une chaîne de caractère à un entier
b["pomme"] = 3 // on met 3 pommes
uint nombre =  b["pomme"]; // renvoie le nombre de pommes
delete  b["pomme"]; // on supprime la clé "pomme"
Propriété
Propriétés Descriptions
.sizeof Renvoie la taille de la référence vers le tableau associatif.
.length Renvoie le nombre de valeurs dans le tableau associatif. À la différence des tableaux dynamiques, on ne peut pas changer sa valeur.
.keys Renvoie un tableau dynamique contenant les clés du tableau associatif.
.values Renvoie un tableau dynamique contenant les valeurs du tableau associatif.
.rehash Réorganise le tableau associatif pour gagner en performance. Retourne une référence du tableau réorganisé.

Dépassement de la taille du tableau

Le dépassement de la taille limite du tableau constitue une erreur fréquemment rencontrée. Le compilateur vous annoncera « Array index out of bounds ». Vous apprendrez plus loin comment attraper et gérer les exceptions.

Important

En D comme dans d'autres langages tels que le Python, les tableaux et les objets sont passés par référence et non par copie.

Qu'est-ce que cela veut dire ?

Pour faire simple, une référence est une entité qui pointe vers son original. Si l'on modifie la référence, cela modifie l'original. C'est un comportement qui permet d'éviter de recopier un tableau (ce qui peut être long) mais qui, si l'on ne fait pas attention, peut générer des erreurs. Gardez bien cela à l'esprit, c'est capital !

Si le besoin d'une copie et non d'une référence se fait sentir, ils vous suffit de la demander avec la propriété dup :

int [5] a = 3; // les 5 colonnes valent 3
int [] b = a; // b est une référence de a, si je modifie b le tableau a sera modifié
int [] c = a.dup; // c est une copie de a, je peux modifier c sans modifier a
Exemple
import tango.io.Stdout;

void ajoute(uint nombre, uint[] tableau)
{
    foreach(indice,element;tableau){
        tableau[indice] = element + nombre;
    }
}
void main()
{
    uint[] montableau = [1,2,3,4,5,6];
    ajoute(2, montableau);
    Stdout(montableau).nl;
}

Vous voyez que votre tableau a été modifié à votre insu vous avez sur le terminal:

[3, 4, 5, 6, 7, 8]

Vous vous retrouvez avec le tableau modifié par la fonction ajoute. Si vous ne voulez pas ce comportement, vous devez envoyer une copie du tableau. Voici un exemple:

 import tango.io.Stdout;

void ajoute(uint nombre, uint[] tableau)
{
    foreach(indice,element;tableau){
        tableau[indice] = element + nombre;
    }
}
void main()
{
    uint[] montableau = [1,2,3,4,5,6];
    ajoute(2, montableau.dup);
    Stdout(montableau).nl;
}

Ce qui donne:

[1, 2, 3, 4, 5, 6]


Si le besoin d'une copie et non d'une référence se fait sentir, ils vous suffit de la demander avec la propriété dup :

int [5] a = 3; // les 5 colonnes valent 3
int [] b = a; // b est une référence de a, si je modifie b le tableau a sera modifié
int [] c = a.dup; // c est une copie de a, je peux modifier c sans modifier a

Les boucles

Tant que (while)

import tango.io.Stdout
void main()
{
    uint = 0;
    while (i < 10)
    {
        Stdout(i).nl;
        i++;
    }
}

Tant que i est plus petit que 10, on boucle et on imprime sur la sortie standard la valeur courante de i.

Faire tant que (do … while)

Cette boucle est semblable à la précédente, seulement ici on garantit au moins une fois le passage dans la boucle.

import tango.io.Stdout
void main()
{
    uint i = 10;
    do{
        Stdout(i).nl;
        i++;
    }
    while(i < 10)
}

Pour (for)

On peut effectuer la déclaration d'une variable, définir les conditions de la boucle et définir une action exécutée à chaque début de boucle (généralement l'incrémentation de cette variable) dans la déclaration de la boucle for :

for (uint i = 0; i < 10; ++i)
{
    Stdout(i).nl;
}

Pour chaque (foreach)

uint[5] a = [1,5,4,6,8];
foreach(element;a)
{
     Stdout(element).nl;
}

Pour chaque élément de a, on imprime sa valeur sur la sortie standard.

On peut également connaître le nombre d'itérations dans la boucle foreach. Par exemple, connaître le numéro de ligne ou l'indice du tableau en cours de traitement.

uint[5] a = [1,5,4,6,8];
foreach(compteur,element;a) // Notons que element est de même type que a, ici de type uint[] 
{
     Stdout.formatln("numéro {} valeur {}",compteur, element);
}

Aller à (goto)

Le goto n'est à utiliser que dans des cas précis. Il ne faut surtout pas en abuser et la plupart du temps les autres types de boucles suffisent. Depuis le temps que je programme je n'ai eu à l'utiliser qu'une seule fois. Le goto permet d'aller directement à un endroit du code défini par une étiquette :

import tango.io.Stdout;

void main()
{
    uint i = 0;
    Stdout("Bonjour").nl;
    monEtiquette:
    i++;
    Stdout.formatln("Valeur de i {}", i);
    if ( i < 2 )
    {
        goto monEtiquette;
    }
    Stdout("Fin").nl;
}

Les conversions de type (cast)

En langage D, il existe 2 manières d'effectuer une conversion de type :

  • avec le mot clé cast
  • avec le module tango.util.Convert

La première façon de faire est générique et permet de caster (convertir) tout et n'importe quoi. C'est l'équivalent du dynamic_cast pour les connaisseurs du C++. La seconde manière est restreinte au type cité dans un chapitre précédent mais est plus sûre. Exemples :

import module tango.util.Convert;
short a = 1;
float b = 0.5;
int c = to!(int)(a); // on utilise le template nommé "to" provenant du module Convert et on spécifie le type int
int d = cast(int)a; 
double e = to!(double)(b); // on utilise le template nommé "to" provenant du module Convert et on spécifie le type double
double f = cast(double)(b);

On utilise le module Convert uniquement pour utiliser le template "to" (le mot clé cast n'a besoin d'aucun module).

Lire les entrées clavier

import tango.io.Console;

void main()
{
    Cout("Entrer votre nom: ")();
    char[] nom = Cin.get;
    Cout("Salut ")(nom).newline;
}

Demander le nom et l'âge:

import tango.io.Console;
import tango.util.Convert;
import tango.io.Stdout;

void main()
{
    Cout("Entrer votre nom: ")();
    char[] nom = Cin.get;
    Cout("Entrer votre age: ")();
    uint age = to!(uint)(Cin.get);
    Stdout.formatln("Salut {} tu as donc {} ans", nom , age);    
}

On doit convertir ce qui a été saisi par l'utilisateur (char[]) en uint.

Parser les arguments de la ligne de commande

La bibliothèque tango propose un module très pratique pour parser les arguments de la ligne de commande. Il existe un tutoriel en anglais ici. Je vais vous présenter la manière dont je l'utilise :

import text.Arguments  : Arguments;
import tango.io.Stdout : Stdout, Stderr;
import tango.text.Regex: Regex;
import monApplication.Parser;
bool verbose = false;
const float numVersion = 0.1;
void verboseMode(char[] message)
{
    if (verbose)
        Stdout.formatln("\033[0;31m'{}'\033[0;0m",message);
}
void about()
{
    Stdout.formatln("monApplication version \033[0;31m'{}'\033[0;0m", numVersion);
    Stdout.formatln("monApplication est un super programme écrit en D.");
}
void usage(Arguments getopt)
{
    Stdout.formatln("\033[0;31mUsage:\033[0;0m ./monApplication [options] --input files");
    Stdout.formatln("Options:");
    getopt.help((char[] param, char[] text){Stdout.format("\t--{}    {}\n",param, text);});
}
int main (char[][] args)
{
    char[] inputFile;
    char[] outputFile;
    
    /**
     * Définit les options du programme
     */
    auto getopt = new Arguments;
    getopt("help").params(0).aliased('h').help("- Display this message");
    getopt("verbose").params(0).aliased('v').help("- Enable verbose mode");
    getopt("input").params(1).aliased('i').help("- Path to input file");
    getopt("output").params(1).aliased('o').help("- Path to output file");
    getopt("version").params(0).help("- Display metatool version");
    /**
     * Parse la ligne de commande
     */
     getopt.parse(args);
     /**
      * Active ou non le mode verbeux
      */
    if (getopt("verbose").set)
    {
        verbose = true;
    }
    /**
     * Affiche l'aide si demandé
     */
    if (getopt("help").set)
    {
        usage(getopt);
    }
    /**
     * Affiche la version si demandée
     */
    else if (getopt("version").set)
    {
        about();
    }
    /**
     * Affiche un message d'erreur et quitte le programme si l'option --input n'est pas presente
     */
    else if (!getopt("input").set)
    {
        Stderr.formatln("\033[0;31mWarning:\033[0;0m option --input absent!");
        usage(getopt);
        scope(failure) Stderr.formatln("Programme arrêté subitement");
    }
    else{
        /**
         * Stocke le nom du fichier soumis par l'option --input
         */
        inputFile = getopt("input").assigned[0];
        verboseMode("Input file: "~inputFile);

        /**
         * Stocke le nom du fichier soumis par l'option  --output
         */
        if (getopt("output").set){
            outputFile = getopt("output").assigned[0];
        }
        else{ // Si l'option --output est absente, il prend le nom du fichier "input" et remplace l'extention par .out
            auto filePattern= new Regex(r"\.[a-zA-Z0-9_]+");
            if (filePattern.test(inputFile)){
                outputFile = filePattern.replaceLast(inputFile,".out");
            }
            else{
                outputFile = inputFile ~ ".out";
            }
        }
        verboseMode("Output file: "~outputFile);
        /**
         * Parse le fichier input
         */
        auto parser = new Parser(inputFile);
        parser.parse();
        parser.check();
        
    }
    scope(exit) Stdout.formatln("quit");
    scope(success) Stdout.formatln("Programme se termine avec succès");
    return 1;
}

Les classes

Le langage D permet de faire de la programmation orientée objet. Pour cela, il faut passer par l'utilisation des classes. On distingue 2 choses : la classe et l'objet. Par exemple, un humain peut être une classe et Roger un objet. En fait, la classe est la définition, soit dans l'exemple donné ici la définition d'un humain. Un humain est un mammifère avec 2 jambes, 2 bras, un nom et un âge. On dit que Roger est une instance de humain. Passons côté programmation.

class Humain{
…
}

Par convention le nom d'une classe s'écrit avec la 1ère lettre en majuscule.

Les attributs

Si on reprend l'exemple précédent, les attributs de la classe humain sont :

  • le nombre de jambes
  • le nombre de bras
  • le nom
  • l'âge

Soit :

class Humain
{
 private:
     uint _nbJambes;
     uint _nbBras;
     char[] _nom;
     uint _age;
…
}

Vous remarquerez l'utilisation du mot "private" qui signifie que l'on ne peut accéder à tout ce qui suit depuis l'extérieur de l'objet, par opposition à public. Regardez l'exemple qui suit :

class Humain
{
 private:
     uint _nbJambes;
     uint _nbBras;
     char[] _nom;
     uint _age;
 public:
     getNbJambes(){
         return _nbJambes;
     }
…
}

Humain robert = new Humain(); // création d'un objet robert de type Humain
uint nbJambes = robert._nbJambes; // Erreur: ceci ne peut pas fonctionner car _nbJambes est un attribut privé. On ne peut pas accéder à cette valeur.
uint nbJambes = robert.getNbJambes(); // renvoie le nombre de jambes de l'objet robert

Par convention, on préfixe les attributs par un tiret bas (underscore) : "_".

Les méthodes

Les méthodes d'une classe correspondent aux actions. Par exemple, on peut implémenter la méthode "avancer". Pour cela, il faut stocker la position de l'humain, soit :

class Humain
{
 private:
     uint _nbJambes;
     uint _nbBras;
     char[] _nom;
     uint _age;
     int _x;
     int _y;
 public:
     getNbJambes(){
         return _nbJambes;
     }
     avancer(int x, int y){
         _x += x;
         _y += y;
     }
…
}

Humain robert = new Humain(); // création d'un objet robert de type Humain
robert.avancer(2,3);

Dans cet exemple, il y a 2 méthodes :

  1. getNbJambes
  2. avancer

On fait avancer robert de +2 et +3 sur la grille (abstrait).

Constructeur et Destructeur

Les constructeurs et les destructeurs sont des méthodes particulières.

Constructeurs

Le constructeur permet de construire un objet. La méthode utilisée s'appelle "this". Si l'on reprend l'exemple précédent :

class Humain
{
 private:
     uint _nbJambes;
     uint _nbBras;
     char[] _nom;
     uint _age;
     int _x;
     int _y;
 public:
     getNbJambes()
     {
         return _nbJambes;
     }
     avancer(int x, int y)
     {
         _x += x;
         _y += y;
     }
     this(nbJambes, nbBras, nom, age, x, y)
     {
         _nbJambes = nbJambes;
         _nbBras = nbBras;
         _nom = nom;
         _age = age;
         _x = x;
         _y = y;
     }
…
}

Humain robert = new Humain(2, 2, "robert", 28, 3, 5); // création d'un objet robert de type Humain

Dans l'exemple précédent, on créé un objet robert de type Humain en précisant qu'il a 2 jambes, 2 bras, que son nom est robert, qu'il a 28 ans et se trouve en x:3 y:5.

Destructeur

Le destructeur est appelé par le ramasse-miette (« garbage collector ») pour libérer la mémoire et supprimer l'objet. On peut explicitement demander la destruction de l'objet avec le mot clé delete. Le nom de la méthode destructeur est ~this. Soit:

class Humain
{
 private:
     uint _nbJambes;
     uint _nbBras;
     char[] _nom;
     uint _age;
     int _x;
     int _y;
 public:
     getNbJambes()
     {
         return _nbJambes;
     }
     avancer(int x, int y){
         _x += x;
         _y += y;
     }
     this(nbJambes, nbBras, nom, age, x, y)
     {
         _nbJambes = nbJambes;
         _nbBras = nbBras;
         _nom = nom;
         _age = age;
         _x = x;
         _y = y;
     }
     ~this(){
     }
…
}

Humain robert = new Humain(2, 2, "robert", 28, 3, 5); // création d'un objet robert de type Humain
delete robert;

Si on ne supprime pas l'objet robert explicitement avec le mot clé delete , le ramasse-miette (garbage collector) le supprimera tout seul quand l'objet ne sera plus utilisé. On peut donc, selon nos choix, gérer (où l'on a) la mémoire.

Vous avez remarqué ? Le destructeur est vide. En effet aucune variable n'a été créée par le mot clé new . Par conséquent, le ramasse-miette pourra faire son travail quand il sera appelé.

L'héritage

L'héritage consiste à spécialiser une classe : par exemple, un magicien est un humain. Afin d'éviter de réécrire le code de la classe humain, on réutilise le code et on effectue un héritage de la manière suivante :

class Magicien : Humain
{
private:
    uint _mana;
public:
    this(uint mana,char[] nom, uint age, x, y)
    {
        super(2, 2, nom, age, x, y); // construction de l'humain
        _mana = mana;
    }
    ~this()
    {
    }
}
Magicien gandalf = new(200, "gandalf", 7000, 156, -54);

Vous remarquerez l'utilisation du mot-clé super . Celui-ci permet d'appeler le constructeur de la classe mère (c'est à dire ici le constructeur Humain). Pour construire un magicien, on doit lui donner une quantité de mana (pouvoirs magiques), un nom, un âge et sa position dans l'espace.

Les Classes Abstraites

Une classe abstraite est une classe que l'on ne peut pas instancier (c'est à dire dont on ne peut créer un objet). Par exemple, on peut décider que la classe mammifère est abstraite. En effet, un mammifère en tant que tel n'existe pas (par exemple, l'Homme est un mammifère et n'existe qu'en tant que tel).

La mise en place d'une classe abstraite se fait de la manière suivante :

abstract class Mammifere
{
    private:
        uint _temperatureCorporelle;
        uint _taille;
        char[] _espece;
    public:
        this(uint temperatureCorporelle, uint taille, char[] espece)
        {
            _temperatureCorporelle = temperatureCorporelle;
            _taille = taille;
            _espece = espece;
        }
        ~this()
        {
        }
        getTemperatureCorporelle()
        {
            return _temperatureCorporelle;
        }
        getTaille()
        {
            return _taille;
        }
        getEspece()
        {
            return _espece;
        }
}

Ainsi, on ne pourra pas écrire :

Mammifere homme = new Mammifere(37, 170, "Homo sapiens");

Mais on devra créer une classe Homme qui héritera de la classe abstraite Mammifère.

Les interfaces

Le langage D ne supporte pas l'héritage multiple. Pour contourner cela, on utilise les interfaces. Une interface liste des fonctions que doit obligatoirement utiliser une classe. Reprenons la classe Humain et demandons à celle-ci d'implémenter les méthodes suivantes:

  • seReposer
  • esquiver
  • marcher
  • courir

Vous en conviendrez, ces méthodes peuvent être utilisées par d'autres classes que les Humains mais par l'utilisation d'une interface, on standardise le nom de ces méthodes et on évitera ainsi de se retrouver avec du code comme :

  • repos se_reposer regenere
  • esquiver evite
  • marcher marche
  • courir seDepecher

On déclare une interface comme cela :

interface actions{
    void seReposer();
    void esquiver();
    void marcher();
    void courir();
}

Et on l'implémente à une classe ainsi :

class Humain : actions, Mammifere
{
 private:
     uint _nbJambes;
     uint _nbBras;
     char[] _nom;
     uint _age;
     int _x;
     int _y;
 public:
     getNbJambes()
     {
         return _nbJambes;
     }
     avancer(int x, int y)
     {
         _x += x;
         _y += y;
     }
     this(nbJambes, nbBras, nom, age, x, y)
     {
         super(37, 170, "Homo sapiens");
         _nbJambes = nbJambes;
         _nbBras = nbBras;
         _nom = nom;
         _age = age;
         _x = x;
         _y = y;
     }
     ~this(){
     }
    void seReposer(){
    …
    }
    void esquiver(){
    …
    }
    void marcher(){
    …
    }
    void courir(){
    …
    }
…
}

Je vous invite également à lire le tutoriel traitant de la notion d'interface sur le Site du Zéro (Bien que le tutoriel en question soit rédigé pour Java, la syntaxe est proche et l'utilisation des interfaces est identique).

Les exceptions

La gestion des erreurs en D est syntaxiquement identique à celle de Java. Par conséquent, vous pouvez jeter un œil sur le Site du Zéro pour en apprendre plus.

Le langage D propose une classe Exception que vous pourrez proposer en héritage à vos propres classes de gestion d'erreur. Vous trouverez la définition de cette classe dans /usr/include/d/object.di . Cette classe étant définie dans object.d, elle n'a pas besoin d'être importée.

Pour commencer, nous allons voir un cas plus simple, celui du dépassement de la taille d'un tableau :

import tango.core.Exception;
import tango.io.Stdout;

void main()
{
    try 
    {
        int[5] array;
        for (uint i = 0; i <= 10; i++)
        {
            array[i] = 5;
        }
    }
    catch (ArrayBoundsException e)
    {
        Stdout("Vous avez dépassé la taille du tableau !").nl;
        Stdout.formatln("Erreur : {}", e.toString);
    }
}

On définit un tableau de 5 éléments et on essaie d'aller sur le 6ème (cela déclenche l'erreur) :

  • Lorsque le programme atteint une valeur de 6 pour i, une exception de type ArrayBoundsException est levée.
  • Le bloc catch contient justement un objet de type ArrayBoundsException en paramètre. Nous l'avons appelé e (par convention).
  • L'exception étant capturée, l'instruction du bloc catch s'exécute !
  • Le message d'erreur personnalisé s'affiche alors à l'écran.

Note : la définition de l'exception ArrayBoundsException se trouve dans le module tango.core.Exception (c'est pour cela qu'on l'importe ici).

Voyons maintenant le cas de la division par zéro, avec une classe Exception personnalisé:

import tango.io.Stdout;

class DivideByZeroException : Exception 
{
    this( char[] file, long line ){
        super("Division par zéro interdite !", file, line );
    }
}

void main()
{
    float a = 3;
    float b = 0;
    float c;
    try
    {
        if (b ==0)
            throw new DivideByZeroException(__FILE__,__LINE__);
        c = a/b;
    }
    catch (DivideByZeroException e)
    {
        Stdout("Division par zéro impossible !").nl;
        Stdout.formatln("Erreur: {}", e.toString);
    }
}

Les templates

Un template permet d'écrire une fonction ou une classe qui peut accepter n'importe quel type de paramètre et qui retourne également n'importe quel type de variable. Prenons en exemple une fonction pour additionner deux nombres. Traditionnellement, vous feriez :

int addition (int a, int b)
{
 return a + b;
}
float addition(float a, float b)
{
return a + b;
}
float addition(int a, float b)
{
return a + b;
}

et ainsi de suite. On remarquera que c'est fastidieux et pour pas grand chose !

En D avec la notion de template, on peut simplifier cela de la manière suivante :

typeof(T + U) Addition(T = typeof(element1), U = typeof(element2))(T element1, U element2)
{
    return element1 + element2;
}

Explication :

  1. on met en type de retour le type retourné par element1 + element2, par exemple si on additionne un entier avec un flottant le résultat sera un flottant.
  2. le nom du template.
  3. les types que prend le template. Ici il peut prendre 2 types différents, T ou U (T et U peuvent être de même type). Par défaut, les types T et U sont respectivement du type de element1 et element2 .
  4. on retourne le résultat de l'addition.

Exemple:

import tango.io.Stdout;

typeof(T + U) Addition(T = typeof(element1), U = typeof(element2))(T element1, U element2)
{
    return element1 + element2;
}

void main()
{
    int a   = 3;
    int b   = 5;
    float c = 3.14;
    Stdout.formatln("{} + {} = {}", a, b, Addition !()(a,b));
    Stdout.formatln("{} + {} = {}", a, c, Addition !()(a,c));
}

Note: un template se remarque par identifiant!(type)(paramètres)

Ici, nous n'avons pas précisé de type, le template prendra donc les types par défaut de T et U. On pourrait demander, lorsque l'on additionne un entier par un flottant de considérer le dernier comme un entier.

Pour cela, il nous suffit de changer la dernière ligne :

  • Stdout.formatln("{} + {} = {}", a, c, Addition!()(a,b));

par

  • Stdout.formatln("{} + {} = {}", a, c, Addition!(uint, uint)(a,c));

Et d'observer le résultat !

Autres mots-clés du langage D

Le langage D propose également des petits mots-clés qui font sa richesse.

auto

Ce mot clé permet de déterminer automatiquement le type d'une variable. Par exemple :

auto file = new File("pathToTxt");

La variable file sera de type File.

assert

Ce mot-clé permet d'effectuer des tests et, si un échec se produit, il lève une exception (par exemple, on souhaite ici vérifier que maFonction() retourne 1).

assert(maFonction() == 1);

debug

Vous avez la possibilité de mettre des portions de code dans des blocks debug.

Utilisation :

debug(niveauDebug){

}
debug(debugIdentifiant){

}

Cas d'utilisation : ceci permet aux équipes de développement de travailler sur une version orientée développeur, où l'on pourra afficher de nombreux messages d'information ainsi que des tests.

Ces portions de code seront compilées si et seulement si l'option -d-debug <niveauDebug/debugIdentifiant> est précisée.

$ ldc *.d -d-debug 3

ou encore

$ ldc *.d -d-debug graphicEngineTeam

Ainsi, on activera les portions de debug des blocs de niveau 3 ou, dans l'autre cas, ceux dont l'identifiant est "graphicEngineTeam".

unittest

Comme vous le voyez, le langage D n'en finit pas d'intégrer de superbes fonctionnalités nativement ! Un test unitaire permet de vérifier le comportement de votre code. Par exemple, vous avez une classe magicien avec des méthodes comme :

  • lancerBouleDeFeu
  • soin
  • marcher
  • couvrir

À la suite de la définition de la classe, on écrit un exemple d'utilisation permettant de garantir son fonctionnement.

class Magicien : Humain
{
    private:

    public:
        this(){
            
        }
        ~this(){
            
        }
        lancerBouleDeFeu(){
            
        }
        marcher(){
            
        }
        couvrir(){
            
        }
        soin(){
            
        }
}
unittest
{
    Magicien gandalf = new Magicien();
    gandalf.lancerBouleDeFeu();
    gandalf.marcher();
    gandalf.marcher();
    gandalf.couvrir();
    gandalf.soin();
}

Les tests unitaires sont compilés si l'option -unittest est utilisée au moment de la compilation.

$ ldc Magicien.d -unittest

version

Ce mot-clé permet de proposer différentes versions de votre logiciel (par exemple, une version familiale, une version professionnelle, une version « Black », etc. Au lieu d'avoir trois programmes différents, vous pouvez mettre le code spécifique à chaque version dans le bloc version correspondant, par exemple :

import tango.io.Stdout;
uint FAMILIALE      = 0;
uint PROFESSIONNELLE= 1;
uint BLACK          = 2;

void main()
{
    Stdout("Bonjour").nl;
    version(FAMILIALE)
    {
        Stdout("Bienvenue dans la version familiale").nl;
    }
    version(PROFESSIONNELLE)
    {
        Stdout("Bienvenue dans la version professionnelle").nl;
    }
    version(BLACK){
        Stdout("Bienvenue dans la version black edition").nl; 
    }
}

Admettons que vous avez enregistré ce code dans le fichier version.d. Pour tester les cas :

1/ Sans spécifier la version :

$ ldc version.d
$ ./version

2/ Avec la version FAMILIALE :

$ ldc -d-version FAMILIALE version.d
$ ./version

3/ Avec la version PROFESSIONNELLE :

$ ldc -d-version PROFESSIONNELLE version.d
$ ./version

4/ Avec la version BLACK :

$ ldc -d-version BLACK version.d
$ ./version

5/ Utiliser 2 versions :

$ ldc -d-version PROFESSIONNELLE -d-version BLACK version.d
$ ./version

Il existe des versions automatiquement détectées et par conséquent réservées, comme :

  • le type de compilateur:
    • dmd identifiant -> DigitalMars
    • ldc identifiant -> LDC
  • l'architecture du processeur identifiant :
    • x86
    • X86_64
  • la plate-forme identifiant :
    • linux
    • Win32
    • Windows
    • darwin
    • freebsd
    • solaris
    • Posix
    • GNU

pragma

pragma(msg, "Bonjour");

Compilation et débogage en ligne de commande

Compilation

Pour compiler un programme D, il vous aurez besoin du compilateur ldc et de la bibliothèque standard tango.

Le compilateur ldc offre de nombreuses options de compilation. Vous pouvez lister toutes ces options en tapant :

$ ldc --help

Je vous présente l'usage courant que j'ai du compilateur :

ldc -w -g -O2 src/*.d -od build/ -of build/monProgramme -D -Dd doc/

Ne vous inquiétez pas, les différents arguments sont très simples :

  • l'option -w pour activer les warnings afin que le compilateur donne des messages clairs pour améliorer votre code.
  • l'option -g pour générer les informations de debug, très utile quand on recherche un bogue dans notre programme.
  • l'option -O2 permet d'optimiser le code machine (il existe 5 niveaux O0 O1 O2 O3 O4 O5).
  • ensuite vient le chemin vers le dossier contenant les fichiers sources *.d
  • l'option -od spécifie dans quel répertoire mettre les fichier .o (binaires).
  • l'option -of spécifie le nom et l'emplacement du fichier exécutable, ici build/monProgramme
  • l'option -D permet de générer la documentation.
  • l'option -Dd spécifie le dossier où sera mise la documentation.

Débogage

Pour déboguer une application écrite en D, vous pouvez utiliser gdb, tout simplement ! Il existe deux cas de figure de l'utilisation de gdb :

  • déboguer les erreurs de compilation
$ gdb --args <ligne de commande pour compiler>
(gdb) run

soit d'après l'exemple précédent :

$ gdb --args ldc -w -g -O2 src/*.d -od build/ -of build/monProgramme -D -Dd doc/
(gdb) run
  • déboguer le fichier exécutable d'une application D (valable si l'option -g est utilisée) :
$ gdb monProgramme
(gdb) run

La documentation

Je vous ai déjà donné un aperçu dans Les commentaires. Le compilateur ldc offre la possibilité de générer de la documentation et de la formater pour avoir une belle mise en page page html.

La syntaxe

La documentation est structurée en sections.

Qu'est-ce qu'une section ? Sémantiquement parlant, une section est une chaîne de caractères suivie par ':' (attention, les sections sont sensibles à la casse).

Résumé

La section « résumé » est une section n'étant pas soumise à une chaîne de caractères suivie par ':'. Cette section correspond simplement au premier paragraphe et est optionnelle.

/**
 * Ceci est un résumé qui 
 * tient sur un paragraphe
 */

Description

La section « description » est une section n'étant pas soumise à une chaîne de caractères suivie par ':'. Cette section correspond au paragraphe qui suit la section « résumé », tant qu'il ne rencontre aucune section spécifiée par une chaîne de caractères suivie par ':'.

/**
 * Ceci est un résumé qui 
 * tient sur un paragraphe
 *
 * Ceci est une description
 * paragraphe n°1
 *
 * Ceci est une description
 * paragraphe n°2
 */

Auteurs

Cette section liste les auteurs de la manière suivante :

/**
 * Authors: bioinfornatics J. Mercier, bioinfornatics@gmail.com
 */

Bogues

Cette section liste les bogues connus de la manière suivante :

/**
 * Bugs: aucun bogue signalé.
 */

Date

/**
 * Date: September 25, 2010
 */

Dépréciation

/**
 * Deprecated: cette fonction est dépréciée. Utilisez maintenant la nouvelle fonction foo().
 */

Exemples

Cette section permet de documenter avec des exemples. Un exemple d'utilisation de cette section :

/**
 * Examples:
 * Stdout.formatln("3"); // Écrit "3" sur la sortie standard
 */

Historique

Cette section permet d'écrire les différentes modifications effectuées sur la portion de code à la manière d'un « changelog ».

/**
 * History:
 *     version 1 est la version initiale
 *     version 2 ajoute les fonctionnalités suivantes : support de la 3D, utilisation d'un nouveau moteur graphique
 */

Licence

Cette section permet d'écrire la licence sous laquelle est publiée le code.

/**
 * License: GPLv3+
 */

Retour

Cette section détaille la valeur retournée par la fonction. Cette section est évidement inutile pour les fonctions de type void.

/**
 * Returns: Le contenu du fichier
 */

Voir aussi

Cette section décrit les choses se rapprochant du code documenté. Il peut notamment contenir des liens (URL).

/**
 * See_Also:
 *    foo, bar, http://www.dsource.org/projects/tango/docs/stable/
 */

Standards

Cette section permet de spécifier si la partie documentée est en accord avec un quelconque standard que l'on aura défini.

/**
 * Standards: Conforme avec DSPEC-1234
 */

Exception

Cette section permet de lister les exceptions et les circonstances qui les déclenchent.

/**
 * Throws: EcritException est déclenché si rencontre un echec
 */
void EcrireFichier(char[] nomFichier) { … }

Version

Cette section permet de spécifier la version courante du bloc documenté.

/**
 * Version: 1.6a
 */

Sections spéciales

Copyright

Cette section informe sur le copyright. La macro COPYRIGHT prend la valeur de la section quand cette dernière est renseignée. Ce comportement particulier apparait seulement quand cette section est utilisée pour documenter un module.

/**
 * Copyright: Domaine Publique
 */
module foo;

Params

Cette section permet de documenter les paramètres que prend une fonction. Pour cela, on doit lister chaque paramètre. Chaque ligne commence par le nom de la variable suivi de "=" puis de sa description. La description peut s'étendre sur plusieurs lignes.

/**
 * foo fait plein de choses
 * Params:
 *     x = est utilisé
 *         pour un truc
 *     y = est utilisé pour d'autres choses
 */
void foo (int x, int y){
…
}

Macros

La section macros suit la même logique que la section params, soit une suite de nom = valeur. Le nom est le nom de la macro et la valeur est le texte de remplacement.

/**
 * Macros:
 *     FOO = maintenant il est temps
 *           de faire de bonnes choses
 *     BAR = bar
 *     MAGENTA = <font color=magenta></font>
 */

Mise en évidence

Avec du code HTML

La documentation autorise l'écriture de portions de code html

 /**
  * <ul>
  * <li> <a href="http://www.dsource.org/projects/tango/">Tango</a></li>
  * <li> <a href="http://www.dsource.org/projects/tango/docs/stable/">Tango Documentation</a></li>
  * </ul>
  */

Annexes

Programme D pour connaître la taille des types

import tango.io.Stdout;
void main() 
{
    Stdout.formatln("byte\t{}\t\t\t\t{}\t\t\t{}\t{}"    , byte.min      , byte.max      , byte.sizeof   , byte.alignof);
    Stdout.formatln("ubyte\t{}\t\t\t\t{}\t\t\t{}\t{}"   , ubyte.min     , ubyte.max     , ubyte.sizeof  , ubyte.alignof);
    Stdout.formatln("short\t{}\t\t\t\t{}\t\t\t{}\t{}"   , short.min     , short.max     , short.sizeof  , short.alignof);
    Stdout.formatln("ushort\t{}\t\t\t\t{}\t\t\t{}\t{}"  , ushort.min    , ushort.max    , ushort.sizeof , ushort.alignof);
    Stdout.formatln("int\t{}\t\t\t{}\t\t{}\t{}"         , int.min       , int.max       , int.sizeof    , int.alignof);
    Stdout.formatln("uint\t{}\t\t\t\t{}\t\t{}\t{}"      , uint.min      , uint.max      , uint.sizeof   , uint.alignof);
    Stdout.formatln("long\t{}\t\t{}\t{}\t{}"            , long.min      , long.max      , long.sizeof   , long.alignof);
    Stdout.formatln("ulong\t{}\t\t\t\t{}\t{}\t{}"       , ulong.min     , ulong.max     , ulong.sizeof  , ulong.alignof);
    Stdout.formatln("float\t{}\t\t\t{}\t\t{}\t{}"       , float.min     , float.max     , float.sizeof  , float.alignof);
    Stdout.formatln("ifloat\t{}\t\t\t{}\t\t{}\t{}"      , ifloat.min    , ifloat.max    , ifloat.sizeof , ifloat.alignof);
    Stdout.formatln("double\t{}\t\t\t{}\t\t{}\t{}"      , double.min    , double.max    , double.sizeof , double.alignof);
    Stdout.formatln("idouble\t{}\t\t\t{}\t\t{}\t{}"     , idouble.min   , idouble.max   , idouble.sizeof, idouble.alignof);
    Stdout.formatln("real\t{}\t\t\t{}\t\t{}\t{}"        , real.min      , real.max      , real.sizeof   , real.alignof);
    Stdout.formatln("ireal\t{}\t\t\t{}\t\t{}\t{}"       , ireal.min     , real.max      , ireal.sizeof  , ireal.alignof);
    Stdout.formatln("cfloat\t{}\t\t{}\t\t\t{}\t{}"      , cfloat.min    , real.max      , cfloat.sizeof , cfloat.alignof);
    Stdout.formatln("cdouble\t{}\t\t{}\t\t{}\t{}"       , cdouble.min   , real.max      , cdouble.sizeof, cdouble.alignof);
    Stdout.formatln("creal\t{}\t{}\t\t{}\t{}"           , creal.min     , real.max      , creal.sizeof  , creal.alignof);
}

pour le compiler simplement :

$ ldc test.d

puis ensuite lancez-le :

$ ./test.d

Fichier pour ajouter le support D dans Emacs

Fichier d-mode.el:

;;; d-mode.el --- D Programming Language mode for (X)Emacs
;;;               Requires a cc-mode of version 5.30 or greater

;; Author:     2007 William Baxter
;; Contributors: Andrei Alexandrescu
;; Maintainer: William Baxter
;; Created:    March 2007
;; Version:    2.0.4 (February 2008)
;; Keywords:   D programming language emacs cc-mode

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2 of the License, or
;; (at your option) any later version.
;; 
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.
;; 
;; You should have received a copy of the GNU General Public License
;; along with this program; see the file COPYING.  If not, write to
;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.

;; Usage: 
;; Put these lines in your .emacs startup file.
;;   (autoload 'd-mode "d-mode" "Major mode for editing D code." t)
;;   (add-to-list 'auto-mode-alist '("\\.d[i]?\\'" . d-mode))
;;
;; cc-mode version 5.30 or greater is required.
;; You can check your cc-mode with the command M-x c-version.
;; You can get the latest version of cc-mode at http://cc-mode.sourceforge.net
;;
;; Commentary:
;;   This mode supports most of D's syntax, including nested /+ +/
;;   comments and backquote `string literals`.
;;   
;;   This mode has been dubbed "2.0" because it is a complete rewrite
;;   from scratch.  The previous d-mode was based on cc-mode 5.28 or
;;   so.  This version is based on the cc-mode 5.30 derived mode
;;   example by Martin Stjernholm, 2002.
;;
;;
;; TODO:
;;   * I tried making "with" "version" and "extern" be their own
;;     c-other-block-decl-kwds.  Which is supposed to mean that you
;;     can control the indentation on the block following them
;;     individually.  It didn't seem to work right though.
;;
;; History:
;;   * 2008 February - 2.0.4 - fixed "else static if" indentation problem, 
;;      and also a problem with "debug if()" indentation.
;;      Some D2 additions (invariant as type modifier etc).
;;   * 2007 April - 2.0.3 - new 'ref' and 'macro' keywords.
;;   * 2007 March 3 - Verision 2.0.1 - bugfixes for emacs 21 and
;;      user-installed cc-mode.  Byte compilation was failing.
;;   * 2007 March 3 - Release of 2.0.0 version

;;----------------------------------------------------------------------------
;; Code:

(require 'cc-mode)

;; These are only required at compile time to get the sources for the
;; language constants.  (The cc-fonts require and the font-lock
;; related constants could additionally be put inside an
;; (eval-after-load "font-lock" …) but then some trickery is
;; necessary to get them compiled.)
;; Coment out 'when-compile part for debugging
(eval-when-compile
  (require 'cc-langs)
  (require 'cc-fonts)
)

(eval-and-compile
  ;; Make our mode known to the language constant system.  Use Java
  ;; mode as the fallback for the constants we don't change here.
  ;; This needs to be done also at compile time since the language
  ;; constants are evaluated then.
  (c-add-language 'd-mode 'java-mode))

(c-lang-defconst c-identifier-ops
  ;; For recognizing "~this", ".foo", and "foo.bar.baz" as identifiers
  d '((prefix "~")(prefix ".")(left-assoc ".")))

(c-lang-defconst c-after-id-concat-ops
  ;; Also for handling ~this
  d '("~"))

(c-lang-defconst c-string-escaped-newlines
  ;; Set to true to indicate the D handles backslash escaped newlines in strings
  d t)

(c-lang-defconst c-multiline-string-start-char
  ;; Set to true to indicate that D doesn't mind raw embedded newlines in strings
  d t)

(c-lang-defconst c-opt-cpp-prefix
  ;; Preprocssor directive recognizer.  D doesn't have cpp, but it has #line
  d "\\s *#\\s *")

(c-lang-defconst c-cpp-message-directives d nil)
(c-lang-defconst c-cpp-include-directives d nil)
(c-lang-defconst c-opt-cpp-macro-define d nil)
(c-lang-defconst c-cpp-expr-directives d nil)
(c-lang-defconst c-cpp-expr-functions d nil)

(c-lang-defconst c-assignment-operators
  ;; List of all assignment operators.
  d  '("=" "*=" "/=" "%=" "+=" "-=" ">>=" "<<=" ">>>=" "&=" "^=" "|=" "~="))

(c-lang-defconst c-other-op-syntax-tokens
  "List of the tokens made up of characters in the punctuation or
parenthesis syntax classes that have uses other than as expression
operators."
  d '("/+" "+/" "…" ".." "!" "*" "&"))
  
(c-lang-defconst c-block-comment-starter d "/*")
(c-lang-defconst c-block-comment-ender   d "*/")

(c-lang-defconst c-comment-start-regexp  d "/[*+/]")
(c-lang-defconst c-block-comment-start-regexp d "/[*+]")
(c-lang-defconst c-literal-start-regexp 
  ;; Regexp to match the start of comments and string literals.
  d "/[*+/]\\|\"\\|`")
;;(c-lang-defconst c-comment-prefix-regexp d "//+\\|\\**")

(c-lang-defconst c-doc-comment-start-regexp
 ;; doc comments for D use "///",  "/**" or doxygen's "/*!" "//!"
 d "/\\*[*!]\\|//[/!]")

;;----------------------------------------------------------------------------

;; Built-in basic types
(c-lang-defconst c-primitive-type-kwds
  d '("bit" "byte" "ubyte" "char" "delegate" "double" "float" "function" 
      "int" "long" "ubyte" "short" "uint" "ulong" "ushort" "cent" "ucent" 
      "real" "ireal" "ifloat" "creal" "cfloat" "cdouble"
      "wchar" "dchar" "void"))

;; Keywords that can prefix normal declarations of identifiers
(c-lang-defconst c-modifier-kwds
  d '("auto" "abstract" "const" "deprecated" "extern" 
      "final" "lazy" "private" "protected" "public"
      "scope" "static" "synchronized" "volatile" "mixin"))

(c-lang-defconst c-class-decl-kwds
  ;; Keywords introducing declarations where the following block (if any)
  ;; contains another declaration level that should be considered a class.
  d '("class" "struct" "union" "interface" "template"))

;; (c-lang-defconst c-brace-list-decl-kwds
;;   d '("enum"))

(c-lang-defconst c-type-modifier-kwds
  d '("const" "lazy" "volatile" "invariant" "enum")
)
(c-lang-defconst c-type-prefix-kwds
  ;; Keywords where the following name - if any - is a type name, and
  ;; where the keyword together with the symbol works as a type in
  ;; declarations.  In this case, like "mixin foo!(x) bar;"
  d    '("mixin" "align"))

;;(c-lang-defconst c-other-block-decl-kwds
;;  ;; Keywords where the following block (if any) contains another
;;  ;; declaration level that should not be considered a class.
;;  ;; Each of these has associated offsets e.g. 
;;  ;;   'with-open', 'with-close' and 'inwith' 
;;  ;; that can be customized individually
;;  ;;   TODO: maybe also do this for 'static if' ?  in/out?
;;  ;;   TODO: figure out how to make this work properly
;;  d '("with" "version" "extern"))

(c-lang-defconst c-typedef-decl-kwds
  d '("typedef" "alias"))

(c-lang-defconst c-decl-hangon-kwds
  d '("export"))

(c-lang-defconst c-protection-kwds
  ;; Access protection label keywords in classes.
  d '("export" "private" "package" "protected" "public"))

;;(c-lang-defconst c-postfix-decl-spec-kwds
;;  ;Keywords introducing extra declaration specifiers in the region
;;  ;between the header and the body (i.e. the "K&R-region") in
;;  ;declarations.
;;; This doesn't seem to have any effect.  They aren't exactly "K&R-regions".
;;  d '("in" "out" "body"))

(c-lang-defconst c-type-list-kwds
  d '("import"))

(c-lang-defconst c-ref-list-kwds
  d '("module"))

(c-lang-defconst c-colon-type-list-kwds
  ;; Keywords that may be followed (not necessarily directly) by a colon
  ;; and then a comma separated list of type identifiers.
  d  '("class" "enum"))

(c-lang-defconst c-paren-nontype-kwds
  ;;Keywords that may be followed by a parenthesis expression that doesn't
  ;; contain type identifiers.
  d '("version" "extern" "macro" "mixin"))

(c-lang-defconst c-paren-type-kwds
  ;; Keywords that may be followed by a parenthesis expression containing
  ;; type identifiers separated by arbitrary tokens.
  d  '("throw"))

(c-lang-defconst c-block-stmt-1-kwds
  ;; Statement keywords followed directly by a substatement.
  d '("do" "else" "finally" "try" "in" "out" "body"))

(c-lang-defconst c-block-stmt-2-kwds
  ;; Statement keywords followed by a paren sexp and then by a substatement.
  d '("for" "if" "switch" "while" "catch" "synchronized" "scope"
      "foreach" "foreach_reverse" "with" "unittest" 
      "else static if" "else"))

(c-lang-defconst c-simple-stmt-kwds
  ;; Statement keywords followed by an expression or nothing.
  d '("break" "continue" "goto" "return" "throw"))

(c-lang-defconst c-paren-stmt-kwds
  ;; Statement keywords followed by a parenthesis expression that
  ;; nevertheless contains a list separated with ';' and not ','."
  d '("for" "foreach" "foreach_reverse"))

(c-lang-defconst c-asm-stmt-kwds
  ;; Statement keywords followed by an assembler expression.
  d '("asm"))

(c-lang-defconst c-label-kwds
  ;; Keywords introducing colon terminated labels in blocks.
  d '("case" "default"))

(c-lang-defconst c-before-label-kwds
  ;; Keywords that might be followed by a label identifier.
  d    '("goto" "break" "continue"))

(c-lang-defconst c-constant-kwds
  ;; Keywords for constants.
  d '("null" "true" "false"))

(c-lang-defconst c-primary-expr-kwds
  ;; Keywords besides constants and operators that start primary expressions.
  d '("this" "super"))

(c-lang-defconst c-inexpr-class-kwds
  ;; Keywords that can start classes inside expressions.
  d    nil)

(c-lang-defconst c-inexpr-brace-list-kwds
  ;; Keywords that can start brace list blocks inside expressions.
  d    nil)

(c-lang-defconst c-other-decl-kwds
  d '("module" "import"))

(c-lang-defconst c-other-kwds
  ;; Keywords not accounted for by any other `*-kwds' language constant.
  d '("assert"))


(defcustom d-font-lock-extra-types nil
  "*List of extra types (aside from the type keywords) to recognize in D mode.
   Each list item should be a regexp matching a single identifier.")

(defconst d-font-lock-keywords-1 (c-lang-const c-matchers-1 d)
  "Minimal highlighting for D mode.")

(defconst d-font-lock-keywords-2 (c-lang-const c-matchers-2 d)
  "Fast normal highlighting for D mode.")

(defconst d-font-lock-keywords-3 (c-lang-const c-matchers-3 d)
  "Accurate normal highlighting for D mode.")

(defvar d-font-lock-keywords d-font-lock-keywords-3
  "Default expressions to highlight in D mode.")

(defvar d-mode-syntax-table nil
  "Syntax table used in d-mode buffers.")
(or d-mode-syntax-table
    (setq d-mode-syntax-table
	 (let ((table (funcall (c-lang-const c-make-mode-syntax-table d))))
	   ;; Make it recognize D `backquote strings`
	   (modify-syntax-entry ?` "\"" table)

	   ;; Make it recognize D's nested /+ +/ comments 
	   (modify-syntax-entry ?+  ". 23n"   table)
	   table)))

(defvar d-mode-abbrev-table nil
  "Abbreviation table used in d-mode buffers.")
(c-define-abbrev-table 'd-mode-abbrev-table
  ;; Use the abbrevs table to trigger indentation actions 
  ;; on keywords that, if they occur first on a line, might alter the
  ;; syntactic context.
  ;; Syntax for abbrevs is:
  ;; ( pattern replacement command initial-count)
  '(("else" "else" c-electric-continued-statement 0)
    ("while" "while" c-electric-continued-statement 0)
    ("catch" "catch" c-electric-continued-statement 0)
    ("finally" "finally" c-electric-continued-statement 0)))

(defvar d-mode-map ()
  "Keymap used in d-mode buffers.")
(if d-mode-map
    nil
  (setq d-mode-map (c-make-inherited-keymap))
  ;; Add bindings which are only useful for D
  ;; (define-key d-mode-map "\C-c\C-e"  'd-cool-function)
  )

(c-lang-defconst c-mode-menu
  ;; The definition for the mode menu.  The menu title is prepended to
  ;; this before it's fed to `easy-menu-define'.
  t `(["Comment Out Region"     comment-region
       (c-fn-region-is-active-p)]
      ["Uncomment Region"       (comment-region (region-beginning)
						(region-end) '(4))
       (c-fn-region-is-active-p)]
      ["Indent Expression"      c-indent-exp
       (memq (char-after) '(?\( ?\[ ?\{))]
      ["Indent Line or Region"  c-indent-line-or-region t]
      ["Fill Comment Paragraph" c-fill-paragraph t]
      "----"
      ["Backward Statement"     c-beginning-of-statement t]
      ["Forward Statement"      c-end-of-statement t]
      "----"
      ("Toggle…"
       ["Syntactic indentation" c-toggle-syntactic-indentation
	:style toggle :selected c-syntactic-indentation]
       ["Electric mode"         c-toggle-electric-state
	:style toggle :selected c-electric-flag]
       ["Auto newline"          c-toggle-auto-newline
	:style toggle :selected c-auto-newline]
       ["Hungry delete"         c-toggle-hungry-state
	:style toggle :selected c-hungry-delete-key]
       ["Subword mode"          c-subword-mode
	:style toggle :selected (and (boundp 'c-subword-mode)
                                     c-subword-mode)])))

(easy-menu-define d-menu d-mode-map "D Mode Commands"
  (cons "D" (c-lang-const c-mode-menu d)))

;;----------------------------------------------------------------------------
;;;###autoload (add-to-list 'auto-mode-alist '("\\.d[i]?\\'" . d-mode))

;;;###autoload
(defun d-mode ()
  "Major mode for editing code written in the D Programming Language.
See http://www.digitalmars.com/d for more information about the D language.
The hook `c-mode-common-hook' is run with no args at mode
initialization, then `d-mode-hook'.

Key bindings:
\\{d-mode-map}"
  (interactive)
  (kill-all-local-variables)
  (c-initialize-cc-mode t)
  (set-syntax-table d-mode-syntax-table)
  (setq major-mode 'd-mode
	mode-name "D"
	local-abbrev-table d-mode-abbrev-table
	abbrev-mode t)
  (use-local-map d-mode-map)
  (c-init-language-vars d-mode)
  (c-common-init 'd-mode)
  (easy-menu-add d-menu)
  (c-run-mode-hooks 'c-mode-common-hook 'd-mode-hook)
  (c-update-modeline))

(provide 'd-mode)

;;; d-mode.el ends here

À voir aussi

  • La documentation de l' API tango.
  • exemple et tutorial autour de la bibliothèque tango.
  • Le site tango : [6].
  • Le livre incontournable à mettre dans toutes les mains: [7].
  • Les ressources de digital mars: [8].
  • Les spécifications du langage D version d1.
  • Une documentation en français du langage D en pdf et en html.
  • Suivez les avancées du livre et de la documentation autour du D sur le wikibook que j'ai moi-même rédigé.
  • Présent également sur irc serveur freenode :
    • le chan #d
    • le chan #d.tango
    • le chan #ldc

Remerciements

Je remercie tous les membres de l'équipe du magazine Muffin pour leur aide et l'intégration de cet article dans le magazine. Mais également :

  • misc pour m'avoir fait découvrir gobby et m'avoir aidé à la relecture.
  • trashy pour sa participation à la relecture.
  • paulk pour ses remarques avisées et sa contribution à la relecture.
  • djezael pour son sens de l'orthographe et ses tournures de phrases.
  • Eyyub d'avoir testé tous les exemples et d'avoir comblé certains points noirs.