OVH Cloud OVH Cloud

1/2 newbie en c++ : formatage des reels

26 réponses
Avatar
Bonjour,

je suis en train d'apprendre la stl, le bouquin de jossutis est presque
parfait mais certains points restent obscurs.

Par exemple :

1)
en pascal write(123.456) renvoie 123.456.
et write(123.456:7:4") renvoie 123.4560

en c++ "cout << 123.456" renvoie 123.456000
cout << setprecision(0) << 123.456 renverra 123
... etc

donc on ne peut pas avoir un nombre de décimales flottant avec ostream
c'est bien ça ?

et il n'y a pas d'équivalent de sprintf en c++ à part ostringstream

j'ai tout compris ou je me plante ?

merci d'avance

Alain

10 réponses

1 2 3
Avatar
Samuel Krempp
le Tuesday 07 September 2004 20:09, "@ écrivit :

parce que :
snprintf(dest, 20, "%-3d %4st%-10.2f", entier, chaine, double);


pas forcément, si tu utilises Format de James, ou boost::format

string dest = str( boost::format("%-3d %4st%-10.2f")
% entier % chaine % doubl);

--
Sam

Avatar
Alain Cabiran
class Budget
{
public:
const string &category;
const string &name;
const Currency &value;




Pour commencer, c'est quoi, c'est membre référence ? Les membres
références sont normalement rarissime. (De même que les membres donnés
publiques, d'ailleurs, dans une class qui a aussi des membres privés.)


je ne me rappelle plus dans quel bouquin j'ai lu ça.

d'après ce que j'ai compris on peut faire :

class A
{
public:
int valeur();
void setValeur(int nlleValeur);
....
private:
int pValeur;
};

ou alors :

class A
{
public:
const int &valeur;
void setValeur(int nlleValeur);
...
private:
pValeur;
};

j'ai trouvé la solution de constante référence plus jolie et qui évite
une fonction et ainsi des bug en cas de printf à l'arrache pour debugger
vite fait donc j'ai gardé cette habitude. (mauvaise ou bonne je ne sais
pas, j'apprend !)

Budget(char * c, char *n, Currency v);
private:
string categorie;
string nom;
Currency valeur;
}




char * est indispensable pour des problèmes de portabilité, je
converti mon




AMA plutôt 'char const*' ? Sinon tu ne pourras pas passer des chaînes
constantes...



oui, je n'ai pas retranscrit les const, c'est pas du copier/coller, le
source (le début, j'avance pas depuis que j'essaye d'éviter Qt) est sous
linux, pas sous Windows et j'étais sous Windows.

.......
Apparemment tu cherches
: ..., categorie(c, std::min(std::strlen(c), std::size_t(80)))
Voire simplement categorie( s ).



du tout, enfin presque :

Budget est une pseudo-struct totalement en lecture seule.
contenant categorie, nom, valeur et d'autres champs. Elle est modifiable
uniquement via son "Id" via la classe contenant la collection
des budgets

begin{HorsSujet}
pour la petite histoire :

TCollection en BP7 sous dos lors de la première implémentation en 1993
TList sous Delphi en 1996
???? en Qt 2 (mon disque a explosé et j'ai tout perdu je me souviens
plus de de l'année)
QPtrList en Qt 3 depuis 2001

et sera list<Budget> en STL je penses... si j'y arrive !
end{HorsSujet}

Ce que je cherchais à faire, c'est tout simplement :

lors de la lecture du fichier contenant le budget, ou de l'import d'un
budget fabriqué à la main par l'utilisateur dans un fichier texte, le
constructeur se débrouille tout seul pour dégager les erreurs
genre nom trop longs ou si le fichier est corrompu ou si un rigolo
(moi par exemple :-)) lui balance un fichier totalement foireux comme
une image jpeg trafiquée à l'import, le système de vérification soit au
plus profond possible du coeur du système de gestion. Plus les objets
gèrent les erreurs, moins on a besoin de le gérer en amont.
je me trompe ?

Ton 'c' vient d'où alors ? L'intérêt des std::string est justement de
ne pas être obligé de couper les chaînes à cause d'utilisation des
buffeurs de taille fixe ! Par contre, si 'c' est un pointeur invalide
(NULL, non initialisé ou pointant sur une zone de mémoire libérée),
effectivement ça se passera mal...



comme dans le cas d'un fichier foireux genre disquette hs, je l'avais
oublié celui là. le prog que j'utilise pour faire mes comptes marche pas
trop mal et j'ai toujours mis un point d'honneur à ce que la place tenue
par les fichiers de données et les temps de traitements soit minimaux.

begin{HorsSujet}
en delphi, 10 ans de compte tenaient largement sur une disquette 360Ko.
Actuellement avec Qt, j'ai 4 ans de compte sur 80ko (pas de compression) :-)
end{HorsSujet}

C'est vrai que quelques coups de string(c) avec un c où strlen(c) vaut
35478945231, ça pourrait poser des problèmes sur beaucoup de systèmes.
Du genre bad_alloc. D'ailleurs, l'existance même d'une chaîne (C ou
std::string) de cette taille est impossible sur pas mal des machines.

Si le poster a réelement à gerer des chaînes de cette longueur, il se
peut bien qu'il a besoin des moyens spéciaux.


j'ai volontairement exagéré la taille pour montrer le problème :
impossible de contrôler la quantité d'octets copiés à la source
enfin presque, je vais pas vous/me casser plus la tête :
#include <cstring>
strncpy
comme ça le problème est réglé

j'espère que c'est plus clair : je ne veux pas copier des données
inutiles en mémoire pour rien, ne copier que le nécessaire et pas plus.

string ne sait pas faire, cstring oui, tant pis j'utiliserai
strncpy. C'est pas beau mais au moins ça fait ce que je voulais sans
exceptions, sans copie inutile, simplement :

class MyString
{
MyString(char *source, long maxLen) {
while (source[i] && (i < MaxLen)) do {
data.append(source[i]);
++i;
}
}
}

(c'est du géronimo :-))


On aimerait d'abord comprendre le problème.

Tu peux simplement appeler std::random_shuffle(it_begin, in_end) mais
ni le générateur de nombres aléatoires ni la façon d'initialiser son
seed sont spécifiés dans la Norme (§ 25.2.11). Si le résultat n'est
pas assez aléatoire pour toi, tu peux utiliser ton propre générateur
qui gère son seed. Exemple (il y en a d'autres sur le Web) :
....

Chez Boost, par exemple, où à ma site.
....


pas assez aléatoire qu'il disait :-)

quand je suis tombé dessus, j'ai crié eurêka ! mes collègues en train de
manger à côté m'ont pris pour un dingue ! Le soir, j'ai implémenté
un mélangeur de nom de fichiers (genre celui qui permet d'avoir des
fonds d'écrans aléatoires sous Linux) en quelques lignes de code seulement.

Puissant le C++ me suis-je dit... avant d'exécuter le programme
plusieurs fois de suite et de voir que random ne randome rien du tout,
la séquence étant toujours la même, tant pis. Il semble que
"fonctionnal" ai quelque chose là dessus mais j'ai pas encore lu.

Encore merci à tous pour vos lumières et votre patience, je vais
continuer de découvrir la STL et implémenter un première version du
nouveau core de mon prog pour gérer mes comptes. Je le mettrai en ligne
à ce moment là (le core devrait être en C++/STL et me permettre de créer
des gui avec BC6 et Qt et pourquoi pas, curses.

a+

Alain

ps: au fait, pourquoi la faq de fr.comp.lang.c++ ne propose que des
ouvrage en anglais et uniquement chez Addison Wesley ? :-)



Avatar
kanze
Samuel Krempp wrote in message
news:<413f4a01$0$7592$...
le Wednesday 08 September 2004 10:26, écrivit :

C'est vrai que quelques coups de string(c) avec un c où strlen(c)
vaut 35478945231, ça pourrait poser des problèmes sur beaucoup de
systèmes. Du genre bad_alloc. D'ailleurs, l'existance même d'une
chaîne (C ou std::string) de cette taille est impossible sur pas mal
des machines.


oui, et je trouve que "string(c)" est la bonne chose à faire.


Tout à fait. Ce que je soupçonne, c'est que la longueur qu'il impose,
c'est parce que dans la version C, il avait des char[] de longueur fixe,
et que ce ne fait pas partie de la spécification externe. Donc, c'est
string(c), et on se libère du contraint de la longueur. Si la longueur
fasse réelement partie des contraints externes (parfois le cas dans les
protocols de transmission, par exemple), c'est assez facile à tronquer
par la suite.

[...]
Dans les milieux internationaux, 42 est la valeur préférée, mais les
allemands semblent préférer 4711.


ça m'intrigue, autant je connais 42, autant je ne vois pas du tout
d'où vient 4711...
Qu'est ce que c'est que ce nombre ??


Une marque de l'eau de cologne très connue. Qui sert en effet très
souvent en Allemagne quand on a besoin d'un nombre quelconque.

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
kanze
Alain Cabiran wrote in message
news:<413f5e94$0$316$...
class Budget
{
public:
const string &category;
const string &name;
const Currency &value;



Pour commencer, c'est quoi, c'est membre référence ? Les membres
références sont normalement rarissime. (De même que les membres
donnés publiques, d'ailleurs, dans une class qui a aussi des membres
privés.)


je ne me rappelle plus dans quel bouquin j'ai lu ça.

d'après ce que j'ai compris on peut faire :

class A
{
public:
int valeur();


int valeur() const ;

void setValeur(int nlleValeur);
....
private:
int pValeur;
};

ou alors :

class A
{
public:
const int &valeur;
void setValeur(int nlleValeur);
...
private:
pValeur;
};

j'ai trouvé la solution de constante référence plus jolie et qui évite
une fonction et ainsi des bug en cas de printf à l'arrache pour
debugger vite fait donc j'ai gardé cette habitude. (mauvaise ou bonne
je ne sais pas, j'apprend !)


Je ne l'ai jamais vu avant. De tête, comme ça, en revanche, je vois deux
choses qui pourraient être des désavantages dans certains cas : avec
toutes les implémentations que je connais, on augmente la taille de
l'objet -- quand les types référencés sont simple, comme int, on peut
facilement la doubler, et on interdit l'affectation. Selon les
compilateurs, c'est aussi fort possible que l'accès en soit moins rapide
qu'avec une fonction inline.

Toutefois est-il que la fonction inline est l'idiome consacré. Si on
utilise quelque chose d'autre, il faut être prêt à le justifier, et
défendre l'écart de la norme. L'idiome consacré varie en ce qui
concerne le nom des ces fonctions. En général, on a soit :
int valeur() const { return maValeur ; }
void valeur( int nlleValeur ) { maValeur = nlleValeur ; }
ou :
int getValeur() const { return maValeur ; }
void setValeur( int nlleValeur ) { maValeur = nlleValeur ; }
En revanche, je n'en ai rarement vu un mélange. Il y a aussi discussion
sur le type de retour du setter -- ou bien, void, ou bien, il renvoie
l'ancien valeur, ce qui permet au client de le restaurer, s'il le veut,
ou bien, il renvoie une référence à la classe, ce qui permet
l'enchaînement des fonctions. Et là, en revanche, j'ai bien vu des
melanges, à l'intérieur d'un projet, et même à l'intérieur d'une seule
classe.

Aussi, certains te diras que c'est une mauvaise conception qui expose
les attributes internes au moyen des getter et des setter. Je ne serais
pas si catégorique, mais si tu le fais systèmatiquement, dans toutes tes
classes, je dirais bien qu'il faudrait se poser des questions.

Budget(char * c, char *n, Currency v);
private:
string categorie;
string nom;
Currency valeur;
}

char * est indispensable pour des problèmes de portabilité, je
converti mon


AMA plutôt 'char const*' ? Sinon tu ne pourras pas passer des
chaînes constantes...



oui, je n'ai pas retranscrit les const, c'est pas du copier/coller, le
source (le début, j'avance pas depuis que j'essaye d'éviter Qt) est
sous linux, pas sous Windows et j'étais sous Windows.

.......
Apparemment tu cherches
: ..., categorie(c, std::min(std::strlen(c), std::size_t(80)))
Voire simplement categorie( s ).



du tout, enfin presque :

Budget est une pseudo-struct totalement en lecture seule. contenant
categorie, nom, valeur et d'autres champs. Elle est modifiable
uniquement via son "Id" via la classe contenant la collection des
budgets


Ce n'est pas là la question. La question, c'est est-ce que le cahier des
charges impose une longueur maximal des chaînes (disons à cause d'une
interface avec une base de données où les champs sont déclarés
varchar(80)), ou est-ce que c'est un artifact technique de
l'implémentation précédante (où le programmeur s'est servi des char
xxx[80] ? Dans ce deuxième cas, la meilleur solution est simplement de
laisser tomber la restriction -- un std::string a une longueur dynamique
qu'il gère tout seul, on n'a pas besoin de s'en occuper soi-même.

Donc, on se servira soit de:
..., categorie( c )
tout court, soit de
..., categorie( std::string( c ).substr( 0, 80 ) )
pour tronquer la chaîne une fois qu'on l'a créé.

En ce qui concerne la modifiabilité : quand j'avais un problème
semblable, je me suis servi de l'héritage. L'interface de base ne
supportait que les getter (qui dans mon cas étaient toutes virtuelles --
c'était une véritable interface). La classe d'implémentation de
l'interface contenait elle les variables et les setters. (Dans le
projet, la convention de nommage utilisée setXXX et getXXX. Je n'ai donc
pas eu de problème de cachage des noms.) Tous les objets étaient créés
et gérés par la collection, dont les fonctions d'accès ne renvoiaient
que des pointeurs à l'interface constante.

C'est une conception un peu particulière, qu'il faut justifier. Dans mon
cas, la « collection » en était en fait cinq ou six std::set, triés sur
des champs qu'on modifiait, et dont les objets appartenaient ou non
selon le contenu des champs. C'était donc à la collection de gerer les
modifications. (Chose que j'ai fait au moyen d'un pointeur intelligent
ami de la collection -- le constructeur du pointeur cherchait l'objet à
partir de l'id et enlevait l'objet de tous les std::set ; le destructeur
le remettait dans les set selon les valeurs actuelles des attributes.)

begin{HorsSujet}
pour la petite histoire :

TCollection en BP7 sous dos lors de la première implémentation en 1993
TList sous Delphi en 1996
???? en Qt 2 (mon disque a explosé et j'ai tout perdu je me souviens
plus de de l'année)
QPtrList en Qt 3 depuis 2001

et sera list<Budget> en STL je penses... si j'y arrive !
end{HorsSujet}

Ce que je cherchais à faire, c'est tout simplement :

lors de la lecture du fichier contenant le budget, ou de l'import d'un
budget fabriqué à la main par l'utilisateur dans un fichier texte, le
constructeur se débrouille tout seul pour dégager les erreurs genre
nom trop longs ou si le fichier est corrompu ou si un rigolo (moi par
exemple :-)) lui balance un fichier totalement foireux comme une image
jpeg trafiquée à l'import, le système de vérification soit au plus
profond possible du coeur du système de gestion. Plus les objets
gèrent les erreurs, moins on a besoin de le gérer en amont. je me
trompe ?


Pas du tout. Il faut toujours valider les entrées le plus possibles, et
signaler les erreurs le plus tôt possible.

Ce qui laisse encore ouvert la question : d'où vient la contraite sur la
longueur ?

Aussi, je verrais la validation bien avant la construction de ton
objet. À quelques exceptions près, le constructeur n'est pas un bon
endroit pour parser. La rôle de ton objet, c'est de contenir ou de
rassembler un certain nombre de champs en un enrégistrement, non de
pouvoir interpréter un nombre toujours croissant de formats d'entrée. À
ta place, pour chaque format d'entrée, je créerais une classe parseur
particulière, qui lui sait traiter le format, et renvoie les objets.

Ton 'c' vient d'où alors ? L'intérêt des std::string est justement
de ne pas être obligé de couper les chaînes à cause d'utilisation
des buffeurs de taille fixe ! Par contre, si 'c' est un pointeur
invalide (NULL, non initialisé ou pointant sur une zone de mémoire
libérée), effectivement ça se passera mal...



comme dans le cas d'un fichier foireux genre disquette hs, je l'avais
oublié celui là. le prog que j'utilise pour faire mes comptes marche
pas trop mal et j'ai toujours mis un point d'honneur à ce que la place
tenue par les fichiers de données et les temps de traitements soit
minimaux.

begin{HorsSujet}
en delphi, 10 ans de compte tenaient largement sur une disquette
360Ko. Actuellement avec Qt, j'ai 4 ans de compte sur 80ko (pas de
compression) :-)
end{HorsSujet}

C'est vrai que quelques coups de string(c) avec un c où strlen(c)
vaut 35478945231, ça pourrait poser des problèmes sur beaucoup de
systèmes. Du genre bad_alloc. D'ailleurs, l'existance même d'une
chaîne (C ou std::string) de cette taille est impossible sur pas mal
des machines.

Si le poster a réelement à gerer des chaînes de cette longueur, il
se peut bien qu'il a besoin des moyens spéciaux.


j'ai volontairement exagéré la taille pour montrer le problème :
impossible de contrôler la quantité d'octets copiés à la source


Pourquoi ?

enfin presque, je vais pas vous/me casser plus la tête :
#include <cstring>
strncpy
comme ça le problème est réglé


Sauf qu'il ne l'est pas, parce que tu as toujours le problème de créer
la chaîne que tu vas copier. Si la longueur doit réelement être limitée,
autant la limiter dès l'entrée.

j'espère que c'est plus clair : je ne veux pas copier des données
inutiles en mémoire pour rien, ne copier que le nécessaire et pas
plus.


Le problème reste. Qu'est-ce qui est nécessaire. Pourquoi limiter la
longueur d'un nom, par exemple ?

Si on le limite, évidemment, l'endroit de la limiter, c'est lors des
entrées.

string ne sait pas faire, cstring oui, tant pis j'utiliserai
strncpy. C'est pas beau mais au moins ça fait ce que je voulais sans
exceptions, sans copie inutile, simplement :

class MyString
{
MyString(char *source, long maxLen) {
while (source[i] && (i < MaxLen)) do {
data.append(source[i]);
++i;
}
}
}

(c'est du géronimo :-))


Et ça risque d'être un peu lent.

Je ne vois toujours pas d'où tu as ces char* avec des longueurs
excessives. Si c'était moi, d'abord, il n'y aurait pas de char* du tout
ici ; je lirais directement dans un std::string. Et s'il fallait les
restrictions sur la longueur, je les ferais dès la lecture, pour ne
jamais avoir une chaîne trop longue.

On aimerait d'abord comprendre le problème.

Tu peux simplement appeler std::random_shuffle(it_begin, in_end)
mais ni le générateur de nombres aléatoires ni la façon
d'initialiser son seed sont spécifiés dans la Norme (§ 25.2.11). Si
le résultat n'est pas assez aléatoire pour toi, tu peux utiliser ton
propre générateur qui gère son seed. Exemple (il y en a d'autres sur
le Web) :
....

Chez Boost, par exemple, où à ma site.
....


pas assez aléatoire qu'il disait :-)

quand je suis tombé dessus, j'ai crié eurêka ! mes collègues en train
de manger à côté m'ont pris pour un dingue ! Le soir, j'ai implémenté
un mélangeur de nom de fichiers (genre celui qui permet d'avoir des
fonds d'écrans aléatoires sous Linux) en quelques lignes de code
seulement.

Puissant le C++ me suis-je dit... avant d'exécuter le programme
plusieurs fois de suite et de voir que random ne randome rien du tout,
la séquence étant toujours la même, tant pis. Il semble que
"fonctionnal" ai quelque chose là dessus mais j'ai pas encore lu.


Attention, il y a deux aspects. D'abord, il y a un problème avec la
qualité des générateurs aléatoire dans pas mal de bibliothèques -- les
valeurs de retour de rand() ne sont pas toujours aussi aléatoire qu'on
puisse souhaiter. Mais il y a aussi une question de « seed » -- ce dont
on parle, c'est en fait un générateur pseudo-aléatoire. Et à partir d'un
« seed » identique, il doit générer toujours la même suite de valeurs.
Doit : c'est un feature, non un boggue. Comment veux-tu débogguer la
reste du programme, si tu ne peux pas réproduire ce qui s'est passé deux
fois de suite.

Et avec tous les générateurs (celui de la bibliothèque standard, celui
chez moi, et ceux de Boost), c'est à toi de choisir le seed. Il y a une
valeur par défaut, mais c'est toujours la même valeur. C-à-d que si tu
ne fais rien, à chaque invocation du programme, tu vas avoir la même
suite de valeurs aléatoire. La façon classique portable de choisir un
seed variable, c'est time() -- avec la bibliothèque standard, donc, tu
appelles « srand( time( NULL ) ) » une fois au début du programme.

Sous Linux, il y a aussi une périphérique /dev/random, qui renvoie des
valeurs aléatoire. Réelement aléatoire, je crois.

ps: au fait, pourquoi la faq de fr.comp.lang.c++ ne propose que des
ouvrage en anglais et uniquement chez Addison Wesley ? :-)


En anglais, peut-être parce qu'il n'y en a pas (ou peu) de bon en
français. De Addison Wesley -- c'est vrai qu'ils ont un niveau de
qualité nettement supérieur des autres maisons d'édition. Dans le temps,
Prentice Hall n'était pas mauvais non plus. "The C Programming Lanague",
de Kernighan et Richie, était de Prentice Hall, ainsi que le Robert
Martin. Mais il me semble avoir lu qu'ils ont abandonné la publication
technique. (Et Addison Wesley et Prentice Hall appartienent à la même
maison mère. Qui a sans doute voulu réduire la concurrence entre ces
marques en spécialisant chacun dans un domaine différent.)

En dehors de Addison Wesley, Wiley, Springer et O'Reilly sont souvent
très bien. Mais les deux premiers sont souvent plus théorique et
abstrait, et O'Reilly a un approche qui est plus proche des manuals, et
n'ont qu'un nombre restreint de livres sur le C++.

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34




Avatar
Alain Cabiran
je strippe parce que ça devient long :-)

en fait, la contrainte, c'est moi qui la fixe puisque c'est mon bébé.
les premières versions utilisaient des string[30] (pascal, delphi).
parce que le stockage sur disque était fixe pour les strings.

j'ai gardé la contrainte en C++ (je n'ai jamsi implementé le proc en C)
et l'ai poussé à 40 pour avoir de la marge au cas où. sachant que Qt
manipule les données pour les rendre portables (j'ai compilé mon soft
sous win et nux et effectivement, ça a marché)

Budget est un élément de plan comptable simplifié, une catégorie ou
domaine et un nom dans cette catégorie. Deux niveaux c'est bien
suffisant pour un budget familial.

Maintenant la contrainte actuelle autrement dit out Qt et bonjour C++
avec STL (juste STL et les entêtes C si nécessaires): pour le stockage
en brut, ok, j'ai compris c'est texte ou texte sinon adieu la
portabilité entre win et linux.

maintenant je veux pouvoir importer un budget manuel genre
maison téléphone -100
maison électricité -100
revenus salaires 100.50
revenus indemnité 101.64

pas de pb de contrainte jusque là mais ça ne DOIT PAS planter si je lui
balance :
fdsqgre85gre456gre4z56ht'a786gfd486tz768egrz45z8htehgt87z

ça fois 1000

donc pas le choix faut tronquer. J'avais dans l'idée que la classe
Budget pouvait gérer ça elle même. Or, ce n'est pas le cas, je l'ai
compris (je suis un peu long à la détente :-))

donc je vais les conseils de James, je vais verrouiller dans les parsers
d'importation. ça m'oblige à réimplementer encore plus complètement
les objets mais c'est pas grave, au point où j'en suis, c'est une
refonte complète du programme que je suis en train de faire :-)

merci pour l'idée, je voyais pas comment avancer avec mes contraintes
maintenant je ne suis plus bloqué par ça (mais par autre chose, voir mon
autre post si ça vous intéresse)

Alain


Samuel Krempp wrote in message
news:<413f4a01$0$7592$...

le Wednesday 08 September 2004 10:26, écrivit :



oui, et je trouve que "string(c)" est la bonne chose à faire.



Tout à fait. Ce que je soupçonne, c'est que la longueur qu'il impose,
c'est parce que dans la version C, il avait des char[] de longueur fixe,
et que ce ne fait pas partie de la spécification externe. Donc, c'est
string(c), et on se libère du contraint de la longueur. Si la longueur
fasse réelement partie des contraints externes (parfois le cas dans les
protocols de transmission, par exemple), c'est assez facile à tronquer
par la suite.

[...]

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34



Avatar
Alain Cabiran
toujours sur la conception de la classe Budget ...

voilà mon nouveau problème, j'espère ne pas me planter en tapant,
j'ai pas gardé le code de test (oui je sais... :-))

// class collection;
/* classe contenant des A et fournissant des services
* genre des parsers d'importation ;-) et contenant
* une list<A>
*/

// A ne doit être accessible qu'en lecture seule en direct
// et en lecture/écriture par collection.
// (pour les champs d'une listview qt par exemple)

les champs imortant sont 2 string (categorie et nom) qui servent
pour les comparaisons. (et uniquement ça, le stockage sur disque
se fait par l'id du budget, beauuuuucoup plus petit)

je veux définir les operateur <, = et ==.
pour < et =, pas de problème par contre pour == c'est une autre histoire
:

// deux choix, choix n° 1

class A {
// friend class collections;
friend operator==(const A&, const A&);
public:
A(nv) : pv(v) { } ;
int v() { return pv; };
private:
int pv;
};

bool operator ==(const A&a1, const A&a2)
{
return (a1.pv == a2.pv)
};

// et choix n°2

class A {
// friend class collection;
public:
const int &v;
A(int nv);
private:
int pv;
}

bool operator ==(const A&a1, const A& a2)
{
return (a1.v == a2.v)
}

voilà pourquoi j'avais pris l'habitude d'utiliser les réf constantes,
pour virer les "friend operator" que j'avais pas 1/2 tonnes sur d'autres
classes (les opérateur + - / = < > >= <= == != <<)

y'a t-il une implémentation qui permette d'utiliser les fonction inline
mais sans les friend operator à chaque fois ??

j'avais bêtement essayé :

a1.v() == a2.v() mais les compilos me le refusent parce que
a1 et a2 sont constants ...

là, je vois pas (je devrais peut-être acheter ze C++ bouquin chez
a. wesley ????)

merci d'avance pour vos réponses

très cordialement,

Alain


....
Je ne l'ai jamais vu avant. De tête, comme ça, en revanche, je vois deux
choses qui pourraient être des désavantages dans certains cas : avec
toutes les implémentations que je connais, on augmente la taille de
l'objet -- quand les types référencés sont simple, comme int, on peut
facilement la doubler, et on interdit l'affectation. Selon les
compilateurs, c'est aussi fort possible que l'accès en soit moins rapide
qu'avec une fonction inline.
.....

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
kanze
Alain Cabiran wrote in message
news:<4145f0ce$0$313$...
toujours sur la conception de la classe Budget ...

voilà mon nouveau problème, j'espère ne pas me planter en tapant, j'ai
pas gardé le code de test (oui je sais... :-))

// class collection;
/* classe contenant des A et fournissant des services
* genre des parsers d'importation ;-) et contenant
* une list<A>
*/

// A ne doit être accessible qu'en lecture seule en direct
// et en lecture/écriture par collection.
// (pour les champs d'une listview qt par exemple)

les champs imortant sont 2 string (categorie et nom) qui servent pour
les comparaisons. (et uniquement ça, le stockage sur disque se fait
par l'id du budget, beauuuuucoup plus petit)

je veux définir les operateur <, = et ==. pour < et =, pas de problème
par contre pour == c'est une autre histoire :

// deux choix, choix n° 1

class A {
// friend class collections;
friend operator==(const A&, const A&);
public:
A(nv) : pv(v) { } ;
int v() { return pv; };
private:
int pv;
};

bool operator ==(const A&a1, const A&a2)
{
return (a1.pv == a2.pv)
};


Et pourquoi pas simplement :

bool
operator==( A const& lhs, A const& rhs )
{
return lhs.v() == rhs.v() ;
}

Pas besoin de friend ici.

// et choix n°2

class A {
// friend class collection;
public:
const int &v;
A(int nv);
private:
int pv;
}

bool operator ==(const A&a1, const A& a2)
{
return (a1.v == a2.v)
}

voilà pourquoi j'avais pris l'habitude d'utiliser les réf constantes,
pour virer les "friend operator" que j'avais pas 1/2 tonnes sur
d'autres classes (les opérateur + - / = < > >= <= == != <<)


Dans ton cas précis, tel que tu le présentes ici, il n'y a pas besoin de
friend. Plus généralement, je crois qu'il faut poser des questions si ta
rélation d'ordre dépend des « attributes » invisible à l'utilisateur. Je
dirais pareil en ce qui concerne l'opérateur d'insertion (<<), à
l'exception près que s'il ne sert qu'aux fins de débogguage.

Ceci dit, étant donné les rapports entre les différents opérateurs de
comparaison, il m'arrive souvent d'implémenter une fonction :

int
A::compare( A const& other ) const
{
// renvoie -1, 0 ou 1, selon que *this est
// <, == ou > à other.
}

C'est une fonction membre, donc avec accès à toutes les attributes.

Ensuite, j'ai simplement :

inline bool
operator==( A const& lhs, A const& rhs )
{
return lhs.compare( rhs ) == 0 ;
}

inline bool
operator<( A const& lhs, A cosnt& rhs )
{
return lhs.compare( rhs ) < 0 ;
}

// ...

À vrai dire, je fais ça tellement souvent, je me démande si ça ne vaut
pas la peine d'en définir des macros :

#define declareOneCmpOp( Type, op )
inline bool
operator op ( Type const& lhs, Type const& rhs )
{
return lhs.compare( rhs ) op 0 ;
}

#define declareCmpOps( Type )
declareOneCmpOp( Type, == )
declareOneCmpOp( Type, != )
declareOneCmpOp( Type, < )
declareOneCmpOp( Type, <= )
declareOneCmpOp( Type, > )
declareOneCmpOp( Type, >= )

Enfin, pour les opérateurs binaires classiques, la solution canonique,
c'est d'implémenter d'abord les formes avec affectation (e.g. +=, -=,
etc.). En tant que membres, évidemment. Ensuite, on implémente les
opérateurs binaires pûrs en termes des opérateurs avec affectation, du
genre :

A
operator+( A const& rhs, A const& lhs )
{
A result( rhs ) ;
result += lhs ;
return result ;
}

Là aussi, on pouvait penser à un macro, mais dans mon travail, le cas
des opérateurs arithmétiques est plutôt rare.

y'a t-il une implémentation qui permette d'utiliser les fonction
inline mais sans les friend operator à chaque fois ??


Voir ci-dessus.

j'avais bêtement essayé :

a1.v() == a2.v() mais les compilos me le refusent parce que
a1 et a2 sont constants ...


J'ai raté ça. Évidemment, la fonction v() doit être constante. C'est le
b a ba de la « const correctness ». Donc :

inline int A::v() const { return pv; };

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
Michel Michaud
Dans news:,
Ceci dit, étant donné les rapports entre les différents
opérateurs de comparaison, il m'arrive souvent d'implémenter
une fonction :

int
A::compare( A const& other ) const
{
// renvoie -1, 0 ou 1, selon que *this est
// <, == ou > à other.
}

C'est une fonction membre, donc avec accès à toutes les
attributes.

Ensuite, j'ai simplement :

inline bool
operator==( A const& lhs, A const& rhs )
{
return lhs.compare( rhs ) == 0 ;
}

[...]

À vrai dire, je fais ça tellement souvent, je me démande si ça
ne vaut pas la peine d'en définir des macros :

#define declareOneCmpOp( Type, op )
inline bool
operator op ( Type const& lhs, Type const& rhs )
{
return lhs.compare( rhs ) op 0 ;
}


Toi qui est amateur de Barton et Nackman, je suis surpris que tu
penses à une macro et que tu ne penses pas plutôt à faire ceci :

class A : public Comparaisons<A> // CRTP ou B&N trick
{
public :
int Compare(const A& p_v) const
{
return ...;
}

//...
};

A a1, a2, a3;

if (a1 < a2 || a2 == a3) ...

Il faut simplement avoir ceci quelque part :

template <typename T>
class Comparaisons
{
public :
friend bool operator==(const T& p_gauche, const T& p_droit)
{ return p_gauche.Compare(p_droit) == 0; }

friend bool operator!=(const T& p_gauche, const T& p_droit)
{ return p_gauche.Compare(p_droit) != 0; }

friend bool operator<=(const T& p_gauche, const T& p_droit)
{ return p_gauche.Compare(p_droit) <= 0; }

friend bool operator>=(const T& p_gauche, const T& p_droit)
{ return p_gauche.Compare(p_droit) >= 0; }

friend bool operator<(const T& p_gauche, const T& p_droit)
{ return p_gauche.Compare(p_droit) < 0; }

friend bool operator>(const T& p_gauche, const T& p_droit)
{ return p_gauche.Compare(p_droit) > 0; }
};

Le CRTP, il ne faut pas l'oublier :-) En fait, je me demande
pourquoi ce n'est pas ce qu'on a dans la norme, au lieu de
rel_ops...

--
Michel Michaud
http://www.gdzid.com
FAQ de fr.comp.lang.c++ :
http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ/

Avatar
Jean-Marc Bourguet
"Michel Michaud" writes:

class A : public Comparaisons<A> // CRTP ou B&N trick
{
public :
int Compare(const A& p_v) const
{
return ...;
}

//...
};

A a1, a2, a3;

if (a1 < a2 || a2 == a3) ...

Il faut simplement avoir ceci quelque part :

template <typename T>
class Comparaisons
{
public :
friend bool operator==(const T& p_gauche, const T& p_droit)
{ return p_gauche.Compare(p_droit) == 0; }

friend bool operator!=(const T& p_gauche, const T& p_droit)
{ return p_gauche.Compare(p_droit) != 0; }

friend bool operator<=(const T& p_gauche, const T& p_droit)
{ return p_gauche.Compare(p_droit) <= 0; }

friend bool operator>=(const T& p_gauche, const T& p_droit)
{ return p_gauche.Compare(p_droit) >= 0; }

friend bool operator<(const T& p_gauche, const T& p_droit)
{ return p_gauche.Compare(p_droit) < 0; }

friend bool operator>(const T& p_gauche, const T& p_droit)
{ return p_gauche.Compare(p_droit) > 0; }
};


Un petit probleme, ca ne trouve pas les operateurs quand il y a des
conversions sur les deux membres puisqu'ils ne sont trouve que par
ADL.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
kanze
"Michel Michaud" wrote in message
news:<d6N1d.12263$...
Dans news:,
Ceci dit, étant donné les rapports entre les différents
opérateurs de comparaison, il m'arrive souvent d'implémenter
une fonction :

int
A::compare( A const& other ) const
{
// renvoie -1, 0 ou 1, selon que *this est
// <, == ou > à other.
}

C'est une fonction membre, donc avec accès à toutes les
attributes.

Ensuite, j'ai simplement :

inline bool
operator==( A const& lhs, A const& rhs )
{
return lhs.compare( rhs ) == 0 ;
}


[...]
À vrai dire, je fais ça tellement souvent, je me démande si ça
ne vaut pas la peine d'en définir des macros :

#define declareOneCmpOp( Type, op )
inline bool
operator op ( Type const& lhs, Type const& rhs )
{
return lhs.compare( rhs ) op 0 ;
}


Toi qui est amateur de Barton et Nackman, je suis surpris que tu
penses à une macro et que tu ne penses pas plutôt à faire ceci :

class A : public Comparaisons<A> // CRTP ou B&N trick


Bien vu. C'est effectivement mieux que les macros. À ma décharge, je
dirais que j'ai commencé à me servir de cette technique avant d'avoir lu
Barton et Nackman, et même avant d'avoir des compilateurs qui
supportaient les templates. Et puisqu'elle marchait, je ne me suis
jamais posé de question depuis s'il y avait une meilleur solution.

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


1 2 3