OVH Cloud OVH Cloud

empêcher la compilation de 'x'+"yz" ?

50 réponses
Avatar
Philippe Guglielmetti
J'ai perdu des heures à chasser un bug stupide du style
std::string bad='x'+"yz"; // donne tout sauf "xyz"
("yz" était en fait le résultat d'une conversion genre class::operator
char*() ...)

Existe-t-il une combine pour empêcher C++ de compiler des bêtises à la C ?

J'ai essayé de définir un operator+(const char lhs, const char* rhs) qui
râlerait, mais VC 7.1 dit qu'une des deux opérandes doit être une classe...
--
Philippe Guglielmetti - www.dynabits.com

10 réponses

1 2 3 4 5
Avatar
Samuel Krempp
le Monday 23 August 2004 23:43, écrivit :

Samuel Krempp writes:

le Sunday 22 August 2004 19:35, écrivit :

Utiliser le C++, c'est hyper dangéreux. Je ne vois pas pourquoi quelque
chose du genre « table[ ch - CHAR_MIN ] » serait plus dangéreux que
d'autre chose.


Quand on soustrait 2 chars, on obtient un char, non ?
Alors j'imagine que ce CHAR_MIN est de type int


Pourquoi ?

Il me semble que CHAR_MIN est bien de type « char ». Non ?


en fait la soustraction de 2 char donne un int, donc ok, CHAR_MIN peut être
un char sans que la soustraction puisse déborder.

il reste que c'est un entier qui est utiliser comme indice.

--
Sam



Avatar
kanze
Samuel Krempp wrote in message
news:<4129cf09$0$22039$...
le Sunday 22 August 2004 19:35, écrivit :

"Philippe Guglielmetti" writes:

|> a écrit:
|> > Il faut bien, comme minimum, que je puisse utiliser des char
|> > comme index dans un tableau.

|> Pourquoi faire?

Implémenter <ctype.h> pour commencer. Ou quelque chose de semblable,
avec ses propres classifications.

|> c'est hyper dangereux!

Utiliser le C++, c'est hyper dangéreux. Je ne vois pas pourquoi
quelque chose du genre « table[ ch - CHAR_MIN ] » serait plus
dangéreux que d'autre chose.


Quand on soustrait 2 chars, on obtient un char, non ?


Peut-être si on soustrayait deux char, on obtiendrait un char. Je ne
sais pas ; la soustraction n'est pas défini sur des char en C++.

En fait, les promotions integrales font qu'on ne soustrait jamais
quelque chose de plus petit qu'un int.

Alors j'imagine que ce CHAR_MIN est de type int (ou en tout cas qque
chose qui a au moins autant de valeurs positives que char n'a de
valeurs différentes en tout).

Au début je pensais que tu voulais dire utiliser un char *directement*
comme indice dans un tableau, alors que visiblement tu veux juste
pouvoir obtenir un indice à partir d'un char. Et ça je peux comprendre
que ça puisse servir.


En fait, ce que je veux, c'est l'équivalent de hash_map< char, ... >.
Seulement, ...[], indicé par un char, c'est autrement efficace. Et dans
les endroits où je m'en suis servi, l'efficacité était importante.

J'ai l'impression qu'on a besoin de rien de plus que de pouvoir faire
un entier en soustrayant 2 chars (un peu comme pour des pointeurs) et
de connaître "le premier char".

Si on avait ça et un type char qui n'est pas intrinsèquement un type
intégral, je pense que ça éviterait pas mal de pièges possibles sans
empêcher quoi que ce soit d'utile.


Dans les langages où il y a réelement un type caractère (et non
simplement un petit entier qui s'appelle char), on a prèsque toujours
une fonction « integrée » pour on obtenir un int, du genre ORD en
Pascal. (Mais le Pascal permet la déclaration des tableaux indicés par
des caractères, sans passer par ORD.) Le besoin y est.

AMHA, on n'a pas (encore) assez de savoir en ce qui concerne ce qui est
réelement un caractère pour pouvoir oser en integrer l'abstraction dans
le langage. Alors, l'utilisation d'un petit entier me semble convenir
parfaitement. (Mais je serais d'accord de ne pas supporter des
conversions implicites entre ce petit entier et les autres types
numériques:-). Au moins dans la mésure que je peux le spécifier comme
était le type de l'indice de mon tableau, et dans ce cas-là, il serait
illégal d'indicer le tableau autrement qu'avec un caractère.)

--
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
drkm wrote in message
news:...
Samuel Krempp writes:

le Sunday 22 August 2004 19:35, écrivit :

Utiliser le C++, c'est hyper dangéreux. Je ne vois pas pourquoi
quelque chose du genre « table[ ch - CHAR_MIN ] » serait plus
dangéreux que d'autre chose.


Quand on soustrait 2 chars, on obtient un char, non ? Alors
j'imagine que ce CHAR_MIN est de type int


Pourquoi ?

Il me semble que CHAR_MIN est bien de type « char ». Non ?


Selon la norme C (§5.2.4.2.1 -- je cite du C99, mais je crois que
c'était pareil en C90, et donc en C++): « [...] except for CHAR_BIT and
MB_LEN_MAX, the following shall be replaced by expressions that have the
same type as would an expression that is an object of the corresponding
type converted according to the integer promotions. » Donc, dans la
pratique, on peut partir du principe que CHAR_MIN a type
int. (L'exception serait où sizeof(int)==1 et les char ordinaire sont
non signé. Mais si sizeof(int)==1, je doute que j'utiliserais un tableau
comme ci-dessus:-).)

Mais j'avoue que je ne vois pas d'où peut venir le problème. Les
promotions integrales jouent leur rôle, et le résultat est bien un int
avec la valeur attendue.

--
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
"Philippe Guglielmetti" wrote in message
news:<412ae0e7$0$11774$...
drkm wrote in message

Il me semble que CHAR_MIN est bien de type « char ». Non ?


CHAR_MIN est une macro (C...), donc sa valeur dépend du contexte ou tu
l'utilise


Pas si vite...

D'abord, j'espère que tu voulais dire son type, et non sa valeur. Je ne
l'aimerais pas du tout si sa valeur variait. Mais ensuite, il n'existe
pas de valeur sans type fixe en C++. Macro ou non, CHAR_MIN a bien un
type, et un seul.

Selon la norme C, il doit avoir le type int, SAUF si sizeof(int)==1 et
les char sont non-signé -- dans ce cas-là, il doit avoir type unsigned
int. (En revanche, dans ce cas-là, il doit aussi avoir la valeur 0, et
l'expression que j'utilise marche correctement, même si l'arithmétique
qu'elle contient se fait en non-signé, plutôt que signé.)

#define CHAR_MIN 0


Donc, CHAR_MIN a type int.

char m=CHAR_MIN; // marche


Tout à fait, parce qu'il y a une conversion implicite de int en char.

long* m=CHAR_MIN; // marche aussi


Parce que CHAR_MIN est une expression entière constante dont la
valeur est 0. Et aussi, il a un type entier (ce qui n'est pas toujours
le cas des expressions entière constante) et c'est un rvalue.

en C++ on devrait utiliser std::numeric_limits<char>::min() qui
renvoie un char,


Personellement, je préfère l'int.

Aussi, est-ce que tu as essayé de compiler ça avec g++ 2.95.2 ?

Et même avec un compilateur moderne, la déclaration du tableau est :

int characteristiques[ CHAR_MAX - CHAR_MIN + 1 ] ;

Essaie de faire ça avec std::numeric_limits<char>.

(En l'occurance, j'ai écris le code où je me suis réelement servi de
cette expression en 1987 environ. Alors, std::numeric_limits<char>...)

ce qui va faire:

char m=std::numeric_limits<char>::min(); // marche

long* m=std::numeric_limits<char>::min() ; // warning bienvenu


Complètement illégal. std::numeric_limits<char>::min() n'est pas une
constante. Tu n'as donc pas une constante de pointeur nul, mais plutôt
une expression de type char quelconque. Et il n'y a pas de conversion
implicite d'un type entier en pointeur, sauf s'il s'agit d'une constante
de pointeur nul.

--
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
drkm
"Philippe Guglielmetti" writes:

CHAR_MIN est une macro (C...), donc sa valeur dépend du contexte ou
tu l'utilise


Non. Ni son type, d'ailleurs.

--drkm, en recherche d'un stage : http://www.fgeorges.org/ipl/stage.html

Avatar
Matthieu Moy
James Kanze writes:

[...]

Dans ces cas-là, on utilise ou bien les « signed char » ou bien les
«@unsigned char », pas les char tout court. Mais il existe bien des
cas où ce qu'on veut, c'est que l'indice soit un char -- un espèce
de std::map< char, quelqueChose >, pour ainsi dire. Et imposer un
std::map quand un simple tableau ferait l'affaire... (Sans parler du
fait que std::map, aussi impose un ordre.)


Scoop no 1 : Il y a des langages ou il n'y a pas d'arithmetique sur
les char et ou on peut pourtant indicer un tableau sur les char. (Qui
a dit "Ada" ?)

Un autre cas, en passant... Comment est-ce que tu écriras une fonction
d'hachage sans l'arithmétique sur des char's ?


Scoop no 2 : Il y a des langages ou il n'y a pas d'arithmetique sur
les char et ou on peut pourtant faire des fonctions de hashage. (Qui a
dit "Le meme que ci-dessus" ?)

Bref, si si, on peut vivre sans l'arithmetique sur les char, il suffit
d'avoir des fonctions pour faire la conversion char <-> petit entier.
Ca ne change rien au code genere, mais c'est un typage plus fort,
c'est tout. (Le typage fort n'etant pas la preoccupation principale
des gens qui ont invente le C, si on veut du typage fort, faut soit
utiliser un autre langage, soit faire avec ;-)

--
Matthieu

Avatar
kanze
"Philippe Guglielmetti" wrote in message
news:<412aded2$0$11775$...
Michel Michaud wrote in message

iM8Wc.6741$

Dans news:,



Un autre cas, en passant... Comment est-ce que tu écriras une

fonction d'hachage sans l'arithmétique sur des char's ?


je pourrai même choisir la valeur numérique associée à chaque

lettre c et obtenue par MES_LETTRES.find(c) !


houla... j'ai des craintes pour la performance...


N'est-ce pas:-).

je maintiens que considérer char comme un entier est un héritage
malencontrueux du C.


La définition exacte de char en C++, c'est bien un héritage de C. Un
héritage non sans problèmes, dans la mésure où le petit entier peu être
signé, et avoir des valeurs négatives. Mais il n'y a pas que C où les
caractères sont des petits entiers. Et quand je régarde les langages qui
ont un vrai type de caractère, je constate que ça ne marche pas aussi
bien que ça non plus, au moins quand on considère
l'internationalisation.

Le problème, à mon avis, c'est qu'on ne sait pas exactement que doit
être les sémantiques d'un « caractère ». J'ai déjà signalé des cas où
c'est intéressant de le traiter comme un petit entier : dans le cas des
langages comme Pascal, où il y a un type caractère, on a par exemple une
fonction de conversion dans les deux sens.

Ceci dit, je ne serais pas contre un typage plus fort, qu'on soit obligé
à préciser la conversion en entier quand on le veut. Je voulais
simplement indiquer que la question n'est pas si tranchée qu'on semblait
penser. Et je pose encore la question : quels doivent être les
opérations permis sur un caractère ? (Et en passant, quel doit être sa
taille ?)

Considère aussi l'idiome consacré de C et de C++, où on renvoie le
caractère sur un type entier plus grand, afin d'aussi pouvoir indiquer
la fin de fichier. C'est un idiome qui marche extrèmement bien, et
qu'AMHA, il faudrait supporter aussi si on avait un type caractère.

Les différences d'implémentation rendent ceci difficile à maintenir,
voire dangereux (James, ton hachage devra être testé sur différents
OS, en Unicode etc).


Je n'ai jamais eu de moindre problème de portabilité. Il faut dire que
dans la pratique, je ne rencontre qu'un seul encodage, ou deux très
proche (8859-1 et 8859-15). Quant à l'Unicode, les char sur mes machines
n'ont que 8 bits. L'Unicode donc signifie UTF-8, avec les problèmes des
caractères composés que ça implique. Alors, dans la mésure où je n'en ai
pas besoin...

En C++, il existe static_cast et reinterpret_cast ou le bon vieux cast
a parenthèses du C qui permettrait de considérer char (et bool) comme
un type non-entier, voire une classe, et d'accéder à leur
représentation si besoin. Mais plus de manière transparente, c'est
trop dangereux.


On dit souvent que c'est dangéreux, mais jusqu'ici, je n'ai jamais vu
une erreur de programmation où il en était la cause.

Le code est plus lisible quand on a un type de caractère. Là, je suis
d'accord. Et les conventions de codage que j'utilise précise qu'on
utilise « char » comme type de caractère. Avec quelques exceptions dues
aux traditions -- quand j'affecte le int renvoyé par fgetc ou par
streambuf::sgetc à un char (après avoir tester pour EOF), je ne me sers
pas d'un cast. (Et en passant, note bien que la norme ne garantit pas
cette conversion -- elle peut dans certains cas provoquer un crash du
programme. Mais la tradition est tellement forte qu'aucun compilateur
n'oserait ne pas le supporter.)

Entre parenthèses, depuis que les int ont 32 bits comme les long (qui
devraient passer à 64, logiquement...), que les shorts en ont 16, et
que wchar_t grignote le bon vieux char à coups de macros cryptiques
pour la "portabilité",


Depuis quand est-ce que les int ont 32 bits ? Il existe encore des
processeurs où ils ont 36 bits, par exemple, en plus de ceux qui n'ont
encore que 16 bits.

il nous faut un vrai "byte" pour la table de hachage de James.


Non. Il faut que ce qu'on hache puisse être converti en unsigned. Il
faut aussi que le type qui sert de caractère ne donne que des valeurs
positives quand on le convertit -- c-à-d que 'à' ait toujours la valeur
192. Parce que les char peuvent être signé, ça veut dire qu'il faut
d'abord convertir explicitement en unsigned char, pour éviter que la
converision implicite en unsigned donne quelque chose comme 4294967232.
(En fait, ce n'est réelement un problème que si le hachage doit servir
en dehors du programme, disons dans un fichier.)

Les JAVAistes doivent bien se foutre de notre gueule s'ils lisent
ceci.


Pourquoi ? Chex eux, c'est pire. Leur type char, c'est bien un petit
entier, exactement comme en C/C++. Seulement, ils n'en ont qu'un, et ils
ont assez d'autres contraits pour qu'il soit assez difficile, sinon
impossible, d'utiliser un autre type entier, dans le cas où le UTF-16 ne
convient pas. (Et dans la pratique, UTF-16, c'est probablement le moins
intéressant de tous les alternatifs.) La seule chose bien qu'ils ont
fait, par rapport à C++, c'est d'exiger que char soit non signé.

Si je verrais une évolution en C++ vers un type propre de caractère,
j'imaginerais quelque chose du genre « character< IntType > ». Parce que
c'est vraiment le cas que différentes applications ont besoin de
différents types de caractère. (Mais je vois mal comment l'implémenter
comme un template, étant donné que la sémantique n'est pas toujours la
même. À la rigueur, il faudrait deux templates, un pour les encodages où
les caractères peuvent être composés, et un pour les autres. Sauf qu'il
faut aussi distinguer entre les encodages où la valeur EOF exige un type
plus grand, et ceux où il passe dans le type de caractère même.)

--
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
James Kanze
Pierre Maurette writes:


[...]
|> Je précise bien que je ne me base que sur un seul environnement. Et
|> en fait, sur un assembleur, MASM (ou clône), qui permet de bien
|> appréhender la tuyauterie. Mais c'est tout à fait portable sur un
|> compilateur C++ du même environnement.

|> MASM est un vieux truc, qui réussit l'exploit d'utiliser le même mot
|> "segment" pour désigner des zones définies par le programmeur
|> (essentiellement par leurs attributs) et les segments physiques,
|> eux-mêmes réels ou virtuels, en simplifiant beaucoup.

Je le connais. Je l'ai même enseigné, aux alentours de 1980.

|> Sous Windows, la notion de segment existe quand même au minimum en
|> termes de droits, entre le code, les données et la pile.

Attention : le mot segment a une signification en dehors de Intel. Les
compilateurs et les éditeurs de Unix ont toujours geré les segments :
text, data, bss et stack. Je crois que l'utilisation du mot en Windows
moderne s'inspire plus de Unix que de Intel.

|> J'ai eu à expliquer que l'adresse d'une variable est une constante
|> symbolique connue à la compilation, ce n'est pas absolument
|> immédiat. En d'autres termes que dans:
|> mov bx, @data
|> mov cx, offset VALEUR
|> "@data" et "offset VALEUR" SONT (et non pas ressemblent à) des valeurs
|> immédiates.

Tout à fait d'accord. Mais toutes les valeurs immédiates ne sont pas
connues du compilateur. Même en assembleur, @data, par exemple, pourrait
n'être fourni qu'à l'édition de liens.

--
James Kanze
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
Pierre Maurette
James Kanze a écrit:
[...]
Attention : le mot segment a une signification en dehors de Intel. Les
compilateurs et les éditeurs de Unix ont toujours geré les segments :
text, data, bss et stack. Je crois que l'utilisation du mot en Windows
moderne s'inspire plus de Unix que de Intel.
Oui, en fait, que ce soit sous Unix ou sous Windows, un segment est

surtout défini par ses attributs, ses droits d'accès. Ce que vous
appelez "Intel" n'est que la tuyauterie permettant d'arriver au
résultat. La tuyauterie Intel est de plus "variable", depuis
l'inénarrable 16 bits + 16 bits = 20 bits, jusqu'à pouvoir définir
aujourd'hui des segments (descripteurs, mais en fait très proches de
ce que veut l'OS) dans un espace adressable linéaire (flat), voire
virtuel.
Il me semble qu'il existe des versions de Windows NT non Intel
(Alpha?). Ce qui expliquerait (?) l'absence de flottants 80 bits en
VC++ .

|> J'ai eu à expliquer que l'adresse d'une variable est une constante
|> symbolique connue à la compilation, ce n'est pas absolument
|> immédiat. En d'autres termes que dans:
|> mov bx, @data
|> mov cx, offset VALEUR
|> "@data" et "offset VALEUR" SONT (et non pas ressemblent à) des valeurs
|> immédiates.

Tout à fait d'accord. Mais toutes les valeurs immédiates ne sont pas
connues du compilateur. Même en assembleur, @data, par exemple, pourrait
n'être fourni qu'à l'édition de liens.
Le problème, une fois que c'est pigé, est de l'expliquer. A un

programmeur C++, ça ne poserait pas de problème, de plus il s'en
branlerait. A un programmeur C, ce serait un peu plus chaud. Mais à un
aficionado de l'assembleur, il faut expliquer comment ça marche...
--
Pierre

Avatar
James Kanze
Pierre Maurette writes:

|> James Kanze a écrit:
|> [...]
|> >Attention : le mot segment a une signification en dehors de Intel.
|> >Les compilateurs et les éditeurs de Unix ont toujours geré les
|> >segments : text, data, bss et stack. Je crois que l'utilisation du
|> >mot en Windows moderne s'inspire plus de Unix que de Intel.

|> Oui, en fait, que ce soit sous Unix ou sous Windows, un segment est
|> surtout défini par ses attributs, ses droits d'accès.

Plus ou moins. Dans les systèms modernes, on peut bien avoir plusieurs
segments avec les mêmes droits.

Une autre attribute des segments logiques, c'est la façon qu'ils se
combinent.

|> Ce que vous appelez "Intel" n'est que la tuyauterie permettant
|> d'arriver au résultat.

Ce qui est particulière chez Intel, c'est qu'ils appellent cette
tuyauterie aussi des segments:-). Sérieusement, tels qu'ils sont gerés
sous Windows ou sous Linux, les « segments » Intel ne diffèrent pas de
ce qu'on trouve sur la plupart des machines modernes. Du côté hardware,
ils offrent bien des possibilités supérieur, mais ni Windows ni Linux ne
s'en servent.

|> La tuyauterie Intel est de plus "variable", depuis l'inénarrable 16
|> bits + 16 bits = 20 bits, jusqu'à pouvoir définir aujourd'hui des
|> segments (descripteurs, mais en fait très proches de ce que veut
|> l'OS) dans un espace adressable linéaire (flat), voire virtuel.

Tout à fait. Au niveau hardware, en fait, on a la possibilité d'une part
d'adresser bien plus que 2^32 octets de mémoire, et de l'autre, de
réelement « segmenter » le programme en blocs isolés, avec une interface
rigide enforcée par le hardware. On pourrait, par exemple, implémenter
de vrais objets const, y compris quand ils ont un constructeur
non-trivial. Mais ni Windows ni Linux ne veut « libérer »
l'architecture.

(Quand on régarde l'architecture moderne Intel, il ne faut pas
méséstîmer l'influence de l'Intel 432 dedans. Machine dont le langage
machine était basé sur l'Ada.)

|> Il me semble qu'il existe des versions de Windows NT non Intel
|> (Alpha?).

Il en a existé. Ils n'ont pas eu un succes notable, et je ne crois pas
qu'ils soient encore maintenues.

|> Ce qui expliquerait (?) l'absence de flottants 80 bits en VC++ .

Aucun rapport. L'absence des flottants 80 bits, c'est une particularité
du compilateur, non du système.

|> >|> J'ai eu à expliquer que l'adresse d'une variable est une
|> >|> constante symbolique connue à la compilation, ce n'est pas
|> >|> absolument immédiat. En d'autres termes que dans: mov bx, @data
|> >|> mov cx, offset VALEUR "@data" et "offset VALEUR" SONT (et non
|> >|> pas ressemblent à) des valeurs immédiates.

|> >Tout à fait d'accord. Mais toutes les valeurs immédiates ne sont
|> >pas connues du compilateur. Même en assembleur, @data, par exemple,
|> >pourrait n'être fourni qu'à l'édition de liens.

|> Le problème, une fois que c'est pigé, est de l'expliquer.

Le problème là, c'est que ça ne s'explique pas à quelqu'un qui ne
comprend rien dans les architectures hardware ou comment fonctionne un
compilateur et un éditeur de liens.

--
James Kanze
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 4 5