FR/Documentation/LanguageCpp
Le but de ce chapitre est d'expliquer les particularités du langage C++ dans un environnement UNO mais pas d'expliquer en détail ce qu'est le C++.
Contents
Le langage C++ et UNO
Le but de ce chapitre est d'expliquer les particularités du langage C++ dans l'environnement UNO et non de fournir des connaissances en C++ traditionnel. Pour dire les choses autrement je veux donner les fondement de C++ et UNO pour nous permettre de commencer.
L'apprentissage de C++ peut se faire à l'aide de livres électroniques malheureusement en anglais ici.
Notre exemple de départ : un binaire exécutable
Nous allons partir d'un exemple du SDK. L'objectif de l'exemple présenté est de créer un exécutable qui interragit avec OpenOffice.org. On peut imaginer deux types d'interactions : une interaction directe avec OpenOffice.org ou une interaction avec les librairies partagées d'OpenOffice. Nous nous intéresserons au second cas pour lequel le fichier makefile est plus simple. La librairie dynamique partagée est cppuhelper.uno.so (cppuhelper.uno.dll sous Windows). Le premier cas sera examiné plus tard.
Je suppose (je ne connais personne de l'équipe de développement du SDK) que cet exemple est fourni pour montrer le programme le plus simple qui peut être réalisé avec le SDK. C'est l'exemple Lifetime : voir dans le répertoire “<OpenOffice.org1.1_SDK>/examples/DevelopersGuide/ProfUNO/Lifetime”
Avant de lancer l'exemple, il vous faudra positionner votre environnement pour pouvoir créer un programme UNO. Ce qui est requis comme information dépend de la plateforme que vous utilisez. Nous montrons ci-dessous comment les choses se passent sous LINUX. Pour tester cet exemple nous avons simplement à lancer le makefile : Template:Documentation/Linux
La dernière ligne de commande lance seulement le binaire exécutable “ProfUnoLifetime” qui interagit avec cpphelper.uno.so (cppuhelper.uno.dll sous Windows) même si OpenOffice.org n'est pas lancé. Cet exemple crée et dispose un objet UNO sans plus avec des constructeurs et destructeurs qui affichent un message correspondant. Sa taille étant réduite, nous donnons son code source maintenant :
// Listing 1 Premier Exemple (du SDK) // C++ #include <stdio.h> #include <cppuhelper/weak.hxx> class MyOWeakObject : public ::cppu::OWeakObject { public: MyOWeakObject() { fprintf( stdout, "constructed\n" ); } ~MyOWeakObject() { fprintf( stdout, "destructed\n" ); } }; void simple_object_creation_and_destruction() { // create the UNO object com::sun::star::uno::XInterface * p = new MyOWeakObject(); // acquire it, refcount becomes one p->acquire(); fprintf( stdout, "before release\n" ); // release it, refcount drops to zero p->release(); fprintf( stdout, "after release\n" ); } int main( char * argv[] ) { simple_object_creation_and_destruction(); return 0; }
Les deux méthodes acquire et release seront rencontrées et détaillées plus tard. Ce qu'elles font exactement n'est pas important non plus pour le moment. Cet exemple nous rappelle aussi comment on écrit des classes qui h"ritent d'autres classes et comment instancier la classe.
Tous les listings de ce chapitre pourront être réalisés (et compilés) uniquement en changeant le code source de cet exemple sans en changer de nom, ce qui permet de garder le même makefile.
Types
Les types UNO sont répertoriés dans le tableau ci-dessous :
- Types UNO
UNO | Type description | Java | C++ | Basic |
char | 16-bit unicode character type | char | sal_Unicode | - |
boolean | boolean type; true and false | boolean | sal_Bool | Boolean |
byte | 8-bit ordinal type | byte | sal_Int8 | Integer |
short | signed 16-bit ordinal type | short | sal_Int16 | Integer |
unsigned short | unsigned 16-bit ordinal type | - | sal_uInt16 | - |
long | signed 32-bit ordinal type | int | sal_Int32 | Long |
unsigned long | unsigned 32-bit type | - | sal_uInt32 | - |
hyper | signed 64-bit ordinal type | long | sal_Int64 | - |
unsigned hyper | unsigned 64-bit ordinal type | - | sal_uInt64 | - |
float | processor dependent float | float | float (IEEE float) | Single |
double | processor dependent double | double | double (IEEE double) | Double |
Les colonnes UNO représentent tous les types que nous pouvons trouver dans les fichiers IDL. Nous décrirons les fichiers IDL plus tard (voir chapitre 10). Les colonnes C++ sont celles qui nous intéressent le plus, quand nous programmons en C++. Si vous voulez tester les différents programmes donnés vous pouvez aller dans le répertoire exemple du SDK : <OpenOffice.org1.1_SDK>/examples/DevelopersGuide/ProfUNO/CppBinding et remplacer alors complètement le fichier initial ”office_connect.cxx” par le programme ci dessous :
//Listing 2 Petit programme d'exemple // C++ #include <stdio.h> #include <cppuhelper/bootstrap.hxx> main( ) { sal_Int32 var1; var1 = 34; printf("The var1 value is : %d\n",var1); return 0; }
ensuite lancer un make :
make ALL make office_connect.run
et cela fonctionne. Ne pas oublier: ./setsdkenv_unix sur Linux.
Sequences
Une séquence est une vue abstraite de la notion d'ensemble. Comme exemple, nous donnons
//Listing 3 Utilisation des séquences // C++ #include <stdio.h> #include <cppuhelper/bootstrap.hxx> #include <com/sun/star/uno/Sequence.hxx> using namespace com::sun::star::uno; main( ) { Sequence < sal_Int32 > SetOfInt(5); //var1 = 34; SetOfInt[2]=44; printf("The value is : %d\n",SetOfInt[2]); return 0; }
Nous pouvons déduire 5 règles d'utilisation des séquences :
- le type de chaque élément est défini entre : “<“ et “>”
- la déclaration de séquence est faite comme d'habitude avec un constructeur nécessitant un paramètre optionnel entre parenthèses qui spécifie la taille de la séquence
- l'accès à chaque éléments de la séquence est faite comme avec un tableau à l'aide de crochets et un index entre ceux-ci
- Une directive d'inclusion est nécessaire ; rien ne se fera sans Sequence.hxx. Prenez bien note que pour cette fois le fichier correspondant est fourni avec le SDK mais comme j'ai déjà eu l'occasion de l'écrire, ce ne sera pas toujours le cas.
- Il faut gérer l'espace de nommage correspondant : “using namespace ...” Si vous ne le faites pas il vous faudra écrire com::sun::star::uno::Sequence au lieu de Sequence.
Nous donnons maintenant un exemple sur une séquence d'entiers.
//Listing 4 Un autre exemple de séquence // C++ #include <stdio.h> #include <cppuhelper/bootstrap.hxx> #include <com/sun/star/uno/Sequence.hxx> using namespace com::sun::star::uno; main( ) { Sequence < sal_Int32 > SetOfInt(5); sal_Int32 *table; SetOfInt[2]=44; printf("The value is : %d\n",SetOfInt[2]); // Tests whether the sequence has elements, i.e. elements count is greater than zero if (SetOfInt.hasElements()) printf("Set not empty\n"); // Gets a pointer to elements array for reading and writing. If the sequence // has a length of 0, then the returned pointer is undefined table = SetOfInt.getArray(); table[4]=78; printf("The value is : %d\n",SetOfInt[4]); // print out 78 SetOfInt.realloc(7); printf("New length : %d\n",SetOfInt.getLength()); // print out 7 return 0; }
Les commentaires affichés expliquent ce qui résulte du lancement du programme. La méthode realloc est particulièrement importante : elle permet de changer la taille de l'ensemble en cours de route.
Portons notre attention maintenant sur l'utilisation des séquences comme paramètre de retour d'une fonction ou comme paramètre. Un exemple est encore plus parlant qu'un long discours :
// Listing 5 Sequence et Parametres // C++ // function Sequence < sal_Int32 > initSequence(){ sal_Int32 sourceArray[5]={1,2,3,4,5}; Sequence < sal_Int32 > SeqOfInt(sourceArray,5); return SeqOfInt; } // reference parameter void initSequence2(Sequence < sal_Int32 > &SeqOfInt ) { sal_Int32 sourceArray[4]={1,2,3,4}; Sequence < sal_Int32 > SeqOfInt2(sourceArray,4); SeqOfInt=SeqOfInt2; }
Un appel trivial pourrait être :
// Listing 6 Sequence et Parametres (suite) // C++ SetOfInt = initSequence(); printf("New length : %d\n",SetOfInt.getLength()); // prints out 5 initSequence2(SetOfInt); printf("New length : %d\n",SetOfInt.getLength()); // prints out 4
On ne peut pas quitter cette section sans ajouter que les opérateurs == et != sont disponibles avec les séquences. A noter aussi que « toUnoSequence », « getCppuSequenceType » et « getCharSequenceCppuType() » sont des fonctions membres de la classe séquence.
Passer une séquence dans une fonction comme paramètre par valeur est, comme un vecteur (de la librairie standard C++ STL) quelquechose d'inefficace parcequ'il faut recopier toute la séquence dans la pile. Il est donc largement préférable d'utiliser une référence constante comme montré ci-dessous :
// Listing 7 La méthode de référence constante //C++ #include <iostream> #include <cppuhelper/bootstrap.hxx> #include <com/sun/star/uno/Sequence.hxx> using namespace com::sun::star::uno; using namespace std; // Passer une Sequence par valeur en utilisant la référence constante void avoidCopy(const Sequence< double > &ConstRef){ // ConstRef[2]=6.7; would fail here for(int i=0;i<ConstRef.getLength();i++) cout<<ConstRef[i]<<" "; cout<<endl; } main( ) { Sequence < double > demo(5); demo[0]=1.1; demo[1]=2.1; demo[2]=3.1; demo[3]=4.1; demo[4]=5.1; avoidCopy(demo); return 0; }
Pour aller plus loin aller consulter le Developer's Guide (UNO C++ Binding).
Chaîne de caractères
Nous redonnons la représentation des différentes chaînes de caractères.
- Le type STRING et UNO
UNO | description | Java | C++ | Basic |
string | String of 16-bit unicode characters | Java.lang.string | ::rtl::OUString | String |
OUString et OString
UNO fournit deux classes pour la manipulations des chaînes de caractères OUString et OString. Pour simplifier disons que la première permet de manipuler les chaînes UNICode tandis que la deuxième permet de manipuler les chaînes ASCII. Seuls les OString peuvent donc être utilisé avec un printf.
Toutes les chaînes de caractères provenant d'UNO API sont des OUString. Cela signifie, que si vous voulez voir quelque-chose dans la console, vous devez convertir les OUString en OString. Si vous demandez quelque-chose à l'utilisateur et que vous voulez le donnez à UNO vous devez convertir les OString en OUString. Ensuite si vous voulez utiliser la console ( en général pour des tests) vous devez apprendre les deux types de langages.
Conversion OUString en OString
Si e.Message est une OUString, nous donnons un exemple de conversion:
//Listing 8 // C++ OString o = OUStringToOString( e.Message, RTL_TEXTENCODING_ASCII_US ); printf( "Error: No such element ;%s\n", o.pData->buffer );
Conversion OString en OUString
La conversion inverse est aussi facile :
//Listing 9 // C++ OUString foo; foo = OUString::createFromAscii("Hi every body");
Pour compléter cet exemple de conversion, donnons maintenant un programme complet :
//Listing 10 // C++ #include <stdio.h> #include <cppuhelper/bootstrap.hxx> using namespace rtl; main( ) { OUString OUStr; OUStr = OUString::createFromAscii("Hi every body"); OString OStr = OUStringToOString( OUStr, RTL_TEXTENCODING_ASCII_US ); printf( "OUStr was : %s\n", OStr.pData->buffer ); return 0; }
Nous voyons qu'un “ using namespace rtl;” est nécessaire. Les méthodes des classes OUString et OString sont similaires : l'exemple complet ci-dessous en montre quelques-unes :
//Listing 11 // C++ #include <stdio.h> #include <cppuhelper/bootstrap.hxx> using rtl::OUString; using rtl::OString; main( ) { OUString OUStr1,OUStr2; OString OStr1, OStr2; OStr1="Hi every body"; OStr2="every body"; OUStr1 = OUString::createFromAscii("Hi every body"); OUStr2 = OUString::createFromAscii("Hi every body"); // Returns the length of this string printf("Length : %d\n",OUStr1.getLength()); // prints out 13 // Compares two strings printf("CompareTo : %d\n",OUStr1.compareTo(OUStr2)); // prints out 0 // Compares two strings printf("equals : %d\n",OUStr1.equals(OUStr2)); // prints out 1 if (OUStr1.equals(OUStr2)) printf("OK\n");// prints out OK // Compares two strings printf("CompareToAscii : %d\n",OUStr1.compareToAscii(OStr1)); // prints out 0 // Compares two strings with a maximum count of characters printf("CompareToAscii : %d\n",OUStr1.compareToAscii(OStr1),10); // prints out 0 // Perform a comparison of a substring in this string // is "every body" in position 3 of OUStr1 ? if (OUStr1.match(OUString::createFromAscii("every body"),3)) printf("match : OK\n"); //prints out match : OK // Perform a comparison of a substring in this string if (OUStr1.matchAsciiL(OStr2,OStr2.getLength(),3)) printf("matchAsciiL: OK\n"); // Returns the float value from this string OUStr2 = OUString::createFromAscii("1.675"); printf("toFloat : %f\n",OUStr2.toFloat()); // prints out 1.675000 return 0; }
Remarque : J'ai rencontré un problème avec la méthode “concat”.
Chemin d'accès
Les chemins d'accès aux fichiers dépendent du système d'exploitation. Pour supprimer cette dépendance, UNO utilise URL et un moyen de transformer un chemin d'accès en un URL. Exemple:
//Listing 12 // Don't forget #include <osl/file.hxx> OUString sDocUrl; osl::FileBase::getFileURLFromSystemPath( OUString::createFromAscii("/home/smoutou/edt.sxd"),sDocUrl); // Windows C++ to check // osl::FileBase::getFileURLFromSystemPath( // OUString::createFromAscii("C:\My Documents\tata.sxc"),sDocUrl);
Un exemple complet est donné ci-dessous :
//Listing 13 // C++ #include <stdio.h> #include <cppuhelper/bootstrap.hxx> #include <osl/file.hxx> using rtl::OUString; using rtl::OString; using namespace osl; main( ) { OUString sDocUrl; OString toPrintOut; osl::FileBase::getFileURLFromSystemPath( OUString::createFromAscii("/home/smoutou/edt.sxd"),sDocUrl); toPrintOut = OUStringToOString( sDocUrl, RTL_TEXTENCODING_ASCII_US ); printf( "URL is : %s\n", toPrintOut.pData->buffer ); // prints out URL is : file:///home/smoutou/edt.sxd return 0; }
lequel montre comment utiliser la conversion. Rappelez-vous que vous travaillez avec UNO et qu'ensuite les URL sont des OUString.
Exemple SDK
Voici un peu de code fourni par le SDK :
//Listing 14 // C++ #include <stdio.h> #include <rtl/ustrbuf.hxx> #include <rtl/string.hxx> using rtl::OUString; using rtl::OUStringBuffer; using rtl::OString; int main( int argc, char * argv [] ) { // string concatination sal_Int32 n = 42; double pi = 3.14159; // give it an initial size, should be a good guess. // stringbuffer extends if necessary OUStringBuffer buf( 128 ); // append an ascii string buf.appendAscii( "pi ( here " ); // numbers can be simply appended buf.append( pi ); // lets the compiler count the stringlength, so this is more efficient than // the above appendAscii call, where length of the string must be calculated at // runtime buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(" ) multiplied with " ) ); buf.append( n ); buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(" gives ") ); buf.append( (double)( n * pi ) ); buf.appendAscii( RTL_CONSTASCII_STRINGPARAM( "." ) ); // now transfer the buffer into the string. // afterwards buffer is empty and may be reused again ! OUString string = buf.makeStringAndClear(); // I could of course also used the OStringBuffer directly OString oString = rtl::OUStringToOString( string , RTL_TEXTENCODING_ASCII_US ); // just to print something printf( "%s\n" ,oString.getStr() ); return 0; }
Cet exemple montre une autre manière d'utiliser le OUString, l'OString et le buffer.
Sequence de chaînes
Les Sequences opnt été abordées dans la section précédente section. Si vous voulez construire des séquences de chaînes (utiles dans les list box ...) regardez avec attention ce code :
// Listing 15 An Example of Sequence of Strings // C++ // Sequence of string // Don't forget to add #include <com/sun/star/uno/Sequence.hxx> // Don't forget to add using namespace com::sun::star::uno; Sequence<OUString>seqStrlistItems(2); seqStrlistItems[0]=OUString::createFromAscii("Item1"); seqStrlistItems[1]=OUString::createFromAscii("Item2"); // retrieve them and prints out for (int i=0;i<seqStrlistItems.getLength();i++){ OString toPrintOut = OUStringToOString(seqStrlistItems[i],RTL_TEXTENCODING_ASCII_US); printf("-- %s\n",toPrintOut.pData->buffer); }
Ce code affiche dans un terminal :
-- Item1 -- Item2 [smoutou@p3 Lifetime]$
Il est facile à comprendre et ne nécessite donc pas d'explications ultérieures.