Guide du C++ pour .NET Programmation

Guide de Programmation en C++ pour .NET
Chapitre 2 : Programmation en C++ pour .NET
<< Chapitre 1 : Configuration de Visual Studio .NET retour au sommaire >>


L'absence des char*

modifier

Les chaînes de caractères sont très utilisées en C++, mais inexistantes en. NET. Il existe pour cela la classe String, qui doit remplacer l'utilisation des char*. Des méthodes de conversion de l'un à l'autre sont donc disponibles, notamment grâce à la classe Marshal.

Ces méthodes étant statiques, leur appel est assez pénible à taper et surtout peu lisible, surtout si la tâche est très répétitive. Les paragraphes suivants proposeront donc de créer des MACROS pour rendre votre code bien plus lisible.

On notera que cela n'est faisable qu'en C++ pour. NET, et pas en C# car ce dernier langage est dépourvu de macro (en C# la classe Marshal est bien sûr toujours utilisable).

Conversion des char* en String*

modifier

La méthode statique PtrToStringAnsi de la classe Marshal va réaliser la conversion d'un char * en String *. Le code suivant en est un exemple, (la valeur 256 correspond à la taille maximale de votre chaîne initiale, vous pouvez modifier ce paramètre):

using namespace System::Runtime::InteropServices;

char* ch_1 = "Hello World !";

String* ch_2 = Marshal::PtrToStringAnsi(IntPtr((void*) ch_1 ) , 256);

Il est fortement conseillé de créer une Macro dans un fichier header :

#define CONVERT2STRING(x) Marshal::PtrToStringAnsi(IntPtr((void*) x ),256)

Ainsi l'utilisation de la classe Marshal est simplifiée

using namespace System::Runtime::InteropServices;

char* ch_1 = "Hello World !";

String* ch_2 = CONVERT2STRING(ch_1);

Conversion des String* en char*

modifier

De même, La méthode statique StringToHGlobalAnsi de la classe Marshal va réaliser la conversion d'un String * en char *. Cet appel nécessite une libération en utilisant la méthode FreeHGlobal.

using namespace System::Runtime::InteropServices;

String* s = new String("hello, World");

const char* temp = (const  char*)(Marshal::StringToHGlobalAnsi(s).ToPointer());
...
Marshal::FreeHGlobal(IntPtr((void*)temp));


De la même façon que précédemment, on pourra créer des macros :

// creation
#define CONVERT2CHAR(x) (char*)(Marshal::StringToHGlobalAnsi(x).ToPointer())

// libération
#define FREECHAR(x) Marshal::FreeHGlobal(IntPtr((void*) x ));

et les utiliser ainsi :

using namespace System::Runtime::InterropServices;

String* ch_1;

// allocation en mémoire
char* ch_2 = CONVERT2CHAR(ch_1);
...

// libération de la mémoire
FREECHAR(ch_2);

Les structures de données .NET ou non

modifier

Les structures « non gérées » (ou en franglais « non managées ») sont du domaine du C++ classique, donc sans plate-forme .NET, et donc inaccessibles à partir de classes C#.

Les structures « gérées » (ou en franglais « managées »), quant à elles, sont des éléments qui pourront être accessibles.

Le C++ pour .NET permet une utilisation des deux types de structures de données. Il est donc nécessaire dans ce langage de déclarer explicitement qui est géré et qui ne l'est pas, grâce aux mots-clé __gc et __nogc.

Déclarer une classe __gc ou __nogc

modifier

Une classe « gérée » sera déclarée ainsi :

public __gc class MaClasseGeree {
	...
}

De même pour une classe « non gérée » :

public __nogc class MaClasseNonGeree {
	...
}


Déclarer un enum __gc ou ___nogc

modifier

De la même manière on déclare un type énuméré avec __gc pour « géré » et __nogc pour « non géré »:

public __gc enum Couleur = { ROUGE, ORANGE, VERT };


Déclarer un tableau __gc ou __nogc

modifier

De la même manière on déclare un tableau avec __gc pour « géré » et __nogc pour « non géré »:

public String __gc[3] CouleursList = { "Rouge", "Orange", "Vert" };

Les bibliothèques .NET ou non

modifier

L'ordre d'inclusion des bibliothèques (libraries)

modifier

Quelques ambiguïtés malheureuses peuvent apparaître si le principe suivant n'est pas respecté :

l'inclusion des fichiers header « non gérés » est PRIORITAIRE

Sinon vous risquez de voir apparaître des erreurs au milieu de fichiers sources inconnus, dont la signification est incohérente.

Par exemple, un cas récurrent comme l'inclusion de "window.h" peut poser ce genre de problème.

Pour éclaircir votre code et éviter cette erreur, il est conseillé de créer un fichier header uniquement dédié à réaliser les inclusions dans le bon ordre.

Par exemple :

// inclusion_propre.h
#pragma once

// inclusions non gérées
#include <cstring>
#include "la_vieille_bibliothèque.h"

// inclusions d'espaces de nom .NET
using System::Text;
using System::Runtime::InteopServices;

// inclusion de fichier headers gérés

#include "mes_types_geres.h"
#include "mes_constantes.h"

Ainsi chaque fichier .cpp pourra inclure uniquement ce dernier fichier header, à condition que celui-ci soit toujours mis à jour.

Exemple :

// .cpp

#include "inclusion_propre.h"

MaClasseGeree::FonctionTresUtile() {
...
}

Pour plus d'information à propos de cette bizarrerie, voir le document correspondant dans la MSDN