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*
modifierLes 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*
modifierLa 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*
modifierDe 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
modifierLes 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
modifierUne 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
modifierDe 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
modifierDe 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
modifierL'ordre d'inclusion des bibliothèques (libraries)
modifierQuelques 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