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

a écrit:

Il faut bien, comme minimum,
que je puisse utiliser des char comme index dans un tableau.


Pourquoi faire? c'est hyper dangereux!
Tu penses comme un C-iste pour qui il y a un "typedef unsigned short int
char;" quelque part...


Crois-tu vraiment que James pense cela ?

AMHA (à mon humble avis...), tableau['x'] devrait être interdit en C++,
surtout depuis que 'é'
n'a pas la même valeur selon les OS, et encore plus depuis Unicode.
Le bidouilleur nostalgique peut toujours faire tableau[(unsigned short)'x']


Un char est un entier. C'est comme ça. Que dire de :

char i = getIndex() ;
maChaine[ i ] = '' ;

par exemple ? Il peut réellement exister des contraintes poussant à
utiliser des char comme entiers.

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


Avatar
drkm
drkm writes:

"Philippe Guglielmetti" writes:

AMHA (à mon humble avis...), tableau['x'] devrait être interdit en C++,
surtout depuis que 'é'
n'a pas la même valeur selon les OS, et encore plus depuis Unicode.
Le bidouilleur nostalgique peut toujours faire tableau[(unsigned short)'x']


Un char est un entier. C'est comme ça.


Mais je suis d'accord pour avertir sur l'indiçage au moyen d'un
litéral caractère. Ne serait-ce que pour le problème signé ou non.
Tiens, au fait, que se passe-t-il dans l'indiçage d'un tableau au
moyen d'un entier négatif ? Comportement indéfini, j'imagine ; je
suppose qu'il s'agit du cas général de sortie des bornes du tableau.

À propos de « Le bidouilleur nostalgique peut toujours faire ... »,
je ne vois pas ce qu'il y a ou non de nostalgique là-dedans. Cela me
semble une mauvaise pratique, tout simplement.

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


Avatar
Loïc Joly
drkm wrote:
drkm writes:


"Philippe Guglielmetti" writes:



AMHA (à mon humble avis...), tableau['x'] devrait être interdit en C++,
surtout depuis que 'é'
n'a pas la même valeur selon les OS, et encore plus depuis Unicode.
Le bidouilleur nostalgique peut toujours faire tableau[(unsigned short)'x']




Un char est un entier. C'est comme ça.



Dans l'idéal, je dirais que signed char ou unsigned char sont des
entiers, et char est un caractère. Mais on n'est pas dans un monde idéal.


Mais je suis d'accord pour avertir sur l'indiçage au moyen d'un
litéral caractère. Ne serait-ce que pour le problème signé ou non.
Tiens, au fait, que se passe-t-il dans l'indiçage d'un tableau au
moyen d'un entier négatif ? Comportement indéfini, j'imagine ; je
suppose qu'il s'agit du cas général de sortie des bornes du tableau.


Pas forcément :

int main()
{
char toto[] = "Hello world";
char *world = toto+6;
cout << world[-2] << "=='o'" << endl;
}

--
Loïc



Avatar
drkm
Loïc Joly writes:

drkm wrote:

drkm writes:

Un char est un entier. C'est comme ça.



Dans l'idéal, je dirais que signed char ou unsigned char sont des
entiers, et char est un caractère. Mais on n'est pas dans un monde
idéal.


Certes non ;-)

Dans un monde idéal, je dirais que signed char et unsigned char
n'existeraient pas, et char serait un caractère, pas un entier.

Mais dans la norme, il est bien défini comme un type entier, non ?

Mais je suis d'accord pour avertir sur l'indiçage au moyen d'un
litéral caractère. Ne serait-ce que pour le problème signé ou non.
Tiens, au fait, que se passe-t-il dans l'indiçage d'un tableau au
moyen d'un entier négatif ? Comportement indéfini, j'imagine ; je
suppose qu'il s'agit du cas général de sortie des bornes du tableau.


Pas forcément :

int main()
{
char toto[] = "Hello world";
char *world = toto+6;
cout << world[-2] << "=='o'" << endl;
}


Ok, je n'avais pas pensé à ce cas. Donc cela reste un calcul
classique d'après l'adresse du premier élément, au moyen de
l'arithmétique des pointeurs. Merci.

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



Avatar
James Kanze
"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.

|> Tu penses comme un C-iste pour qui il y a un "typedef unsigned short
|> int char;" quelque part...

Non.

|> AMHA (à mon humble avis...), tableau['x'] devrait être interdit en
|> C++, surtout depuis que 'é' n'a pas la même valeur selon les OS, et
|> encore plus depuis Unicode.

Certes, l'initialisation du tableau dépend de l'encodage utilisé. En
général, j'ai une liste des caractères pour chaque classification
quelque part, qui me sert à initialiser le tableau.

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

|> Un char est un entier. C'est comme ça. Que dire de :

|> char i = getIndex() ;
|> maChaine[ i ] = '' ;

|> par exemple ? Il peut réellement exister des contraintes poussant à
|> utiliser des char comme entiers.

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.)

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

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

|> drkm writes:

|> > "Philippe Guglielmetti" writes:

|> >> AMHA (à mon humble avis...), tableau['x'] devrait être interdit
|> >> en C++, surtout depuis que 'é' n'a pas la même valeur selon les
|> >> OS, et encore plus depuis Unicode. Le bidouilleur nostalgique
|> >> peut toujours faire tableau[(unsigned short)'x']

|> > Un char est un entier. C'est comme ça.

|> Mais je suis d'accord pour avertir sur l'indiçage au moyen d'un
|> litéral caractère. Ne serait-ce que pour le problème signé ou non.
|> Tiens, au fait, que se passe-t-il dans l'indiçage d'un tableau au
|> moyen d'un entier négatif ? Comportement indéfini, j'imagine ; je
|> suppose qu'il s'agit du cas général de sortie des bornes du tableau.

Dis donc. Tu fréquentes ce groupe assez longtemps pour me connaître
mieux que ça. L'indice réele du tableau, c'est toujours ch - CHAR_MIN.

--
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
Michel Michaud
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 ?


Manques-tu d'imagination à ce point ? Avec quelque chose du
genre de

const string MES_LETTRES= "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcd ... etc. "
"ÀÈÌÒÙàèìòùÂÊÎÔÛâê ... etc."
" ... etc. ";

je pourrai même choisir la valeur numérique associée à chaque
lettre c et obtenue par MES_LETTRES.find(c) !

Tu peux faire la même chose pour l'ensemble des caractères
au besoin...

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

Avatar
drkm
James Kanze writes:

drkm writes:

|> drkm writes:

|> > "Philippe Guglielmetti" writes:

|> >> AMHA (à mon humble avis...), tableau['x'] devrait être interdit
|> >> en C++, surtout depuis que 'é' n'a pas la même valeur selon les
|> >> OS, et encore plus depuis Unicode. Le bidouilleur nostalgique
|> >> peut toujours faire tableau[(unsigned short)'x']

|> > Un char est un entier. C'est comme ça.

|> Mais je suis d'accord pour avertir sur l'indiçage au moyen d'un
|> litéral caractère. Ne serait-ce que pour le problème signé ou non.
|> Tiens, au fait, que se passe-t-il dans l'indiçage d'un tableau au
|> moyen d'un entier négatif ? Comportement indéfini, j'imagine ; je
|> suppose qu'il s'agit du cas général de sortie des bornes du tableau.

Dis donc. Tu fréquentes ce groupe assez longtemps pour me connaître
mieux que ça. L'indice réele du tableau, c'est toujours ch - CHAR_MIN.


Le fil de la discussion m'a fait me poser cette question, tout
simplement. Pour voir si dans ce cas (indice négatif), on utilisait
toujours le calcul d'adresse depuis l'adresse du premier élément du
tableau (donc en descendant vers des adresses plus petites).

Mais je ne vois pas le rapport avec toi ...

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

Avatar
kanze
Pierre Maurette wrote in message
news:...
a écrit:

Falk Tannhäuser wrote in message
news:<cg2lso$tee$...

Fabien LE LEZ wrote:

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


A priori, non. Mais il n'est pas impossible que certains compilos
génèrent un warning (modulo l'option qui va bien).


Avec 'gcc -Wall', j'ai bien un warning "array subscript has type `char'"
quand je fais
std::string bad = &'x'["yz"];
(qui est fonctionellement équivalent à
std::string bad = 'x' + "yz";
mais il me ne semble pas avoir de moyen pour faire générer un
warning dans ce cas)


On pourrait générer un avertissement chaque fois que le programmeur
essaie d'utiliser un char comme un petit entier:-). Le problème alors
sera, comment trouver les avertissements « intéressants » parmi
l'inondation.
1: const char offset_nom = sizeof("Pierre");// / sizeof(char);

2: std::string pluriel_debile = "Automobile" + 's';
3: std::string nom_prenom = "Pierre Maurette";
4: std::string mon_nom0 = offset_nom + "Pierre Maurette";
5: std::string mon_nom1 = offset_nom + nom_prenom.c_str();

Tout ça sans ouharning (gcc 3.3.1, -Wall, et VC7.1).


À part 2, je ne vois pas ce que tu pourrais vouloir comme avertissement.
Les autres correspond bien à des choses que je vois quotidiennement en
C++.

Si l'on considère que tous mes compilos me préviennent quand je fais:
if(ptr = fonction_qui_renvoie_un_pointeur()){...}
et logiquement se taisent avec:
if((ptr = fonction_qui_renvoie_un_pointeur()) != NULL){...}
le ligne 2 sent également la faute de frappe ou l'erreur de débutant à
plein nez. Elle mériterait un warning.


Tout à fait d'accord. Sauf que je me démande si ce genre d'erreur est
réelement assez fréquente pour que les implémenteurs de compilateur
investissent l'effort.

Aussi, tu n'as pas dit pourquoi tu veux l'avertissement : parce que
l'entier provient d'une constante de caractère, ou parce que le résultat
de l'addition déborde.

En fait, je préfèrerais une classe string qui refuse la ligne 2 et qui
accepte:

NouvelleString pluriel_debile = "Automobile" + "s";


Le problème, c'est que la classe string n'y est pour rien. Le problème,
c'est que l'expression à droit de l'= est évaluée avant l'appel au
constructeur de string, et que cette expression ne contient rien qui n'a
le type string.

Il y a néaumoins une idée intéressante là-dedans. Actuellement,
l'addition de deux pointeurs n'est pas permise. On pourrait donc définir
l'addition de deux char const* en précisant que les char const* sont
converti en std::string.

C'est une idée. J'avoue que je n'en suis pas convaincu moi-même. Ça pue
trop le cas spécial. Mais peut-être une extension qui permet le
surcharge des opérateurs même quand ils ne comportent pas de types
définis par l'utilisateur, dans certains cas bien définis. Puis, un
surcharge dans la bibliothèque, et le tour est joué.

Je ne sais pas non plus si ce sera réelement si utile. Dans la pratique,
on concatène des chaînes constantes sans opérateur aujourd'hui -- tu
n'écriras jamais en fait « "Automobile" + "s", mais plutôt quelque chose
comme getObjectType() + "s", où getObjectType() renvoie un char const*.
Seulement, on peut imaginer qu'à la longue, les fonctions qui renvoient
des char const*, plutôt que des std::string, disparaissent.

La ligne 3 me convient parfaitement. Alors que je me demandais si elle
était légale ;-)

La ligne 4 est à mon sens débile. Il faudrait au moins avertir (même
dans ce cas, où le compilo peut savoir que rien de fâcheux ne va se
passer). Si l'on veut faire du C, les classes string ont prévu ce
qu'il faut (ligne 5).


La ligne 4 n'est qu'un cas particulier de ce que tu fais dans la ligne
5. Je ne vois pas comment on pourrait l'interdire ; même générer un
avertissement me paraît problèmatique.

Et ce qui me déçoit, c'est que l'AnsiString de la VCL Borland a le
même comportement que std::string sur ce point. En revanche, Delphi et
la même VCL autorisent logiquement:


La différence est simple, et n'a rien à voir avec la bibliothèque. En
C++, pour des raisons historiques, si rien d'autre, le type d'une
constante de chaîne est char const[], qui se convertit très vite en char
const*. Dans les autres langages, le type d'une chaîne constante est
leur équivalent de std::string.

Le problème, c'est au niveau du langage, non au niveau de la
bibliothèque. Et c'est quelque chose de tellement fondamental dans le
langage que je ne le vois pas changer, jamais.

var
Test: AnsiString;
begin
Test := 'Pierre '+ 'Maurette';

La différence est facilement explicable, mais c'est chiant.


Est-ce que c'est réelement chiant ? Combien de fois est-ce que tu
concatènes réelement deux constantes de chaîne ? Sans que tu saches
qu'il s'agit des constantes, parce qu'en C++, tu peux bien écrire :

std::string test = "Pierre " "Maurette" ;

(Ce n'est pas très intéressant comme ça, mais quand une des chaînes est
en fait un macro, ça peut être intéressant.)

Je crois en fait que le problème, c'est qu'on veut concaténer des
chaînes renvoyées par des fonctions existantes, voire des fonctions
extern "C", qui elles renvoient des char const*. C-à-d que c'est un
problème qui va disparaître avec le temps, au fur et à mésure que les
interfaces plus modernes se développent.

Dans ce cas particulier, l'arithmétique ne comporte que des
constantes (un pointeur constant et un entier constant). Or, tous les
compilateurs que je connais font du « constant folding », c-à-d
qu'ils évaluent l'expression lors de la compilation, et non à
l'exécution. C'est un peu plus complex quand il y a un pointeur,
évidemment, parce que constante ou non, l'adresse exacte finale n'est
pas connue du compilateur.


Oui. Je pense que le compilateur connaît toutes les adresses des
données initialisées (ou même non initialisées) au pire à une
constante près, déterminée par le lieur puis le chargeur.


Faute d'en connaître l'adresse, il en connaît la longueur. Il sait donc
qu'ajouter tant à l'adresse déborde.

On peut donc affirmer qu'il a tous les éléments nécessaires et le
programmeur peut considérer ces adresses comme des constantes connues
à la compilation.

int* ptr = &MaVar;
génère un truc du genre de ce qu'on peut faire directement en
assembleur:
mov eax, offset MaVar
offset MaVar est une constante immédiate, en tous cas se retrouvera
sous cette forme dans l'executable en mémoire (pas nécessairement dans
le point exe sur le disque).
Et il me semble que sur les archis que je connais (en fait, une seule,
ou à peu près :-() les choses sont encore plus simples, le travail du
lieur et du chargeur se réduit à positionner la valeur de segments ou
leurs descripteurs. Ainsi, le compilateur connait les valeurs
numériques des offset, c'est à dire des adresses.


Pas forcement. En fait, plutôt rarement. D'abord, évidemment, parce
qu'il y a beaucoup de systèmes sans ségments. Puis, aujourd'hui, la
plupart des systèmes sur Intel (Windows, Unix) ont choisi d'ignorer les
segments, en limitant l'adressage aux 32 bits de l'offset. Et enfin,
même avant, il était courant de mettre des variables de plusieurs unités
de compilation différentes dans le même segment.

Mais le compilateur n'a pas besoin de connaître l'adresse exacte. Il
sait que des adresses dans l'intervale [adresse,adresse+n[ sont valide,
et que seulement ces adresses sont valide. Si dans le code, je fais
adresse+i, avec i>n, il sait que l'expression n'est pas valide.

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