OVH Cloud OVH Cloud

include avec define ?

34 réponses
Avatar
Amerio
Bonjour,

J'ai un code dont l'interface est commune a toutes mes plateformes, mais
dont l'implémentation est fortement spécifique.
Ex : controller/Joypad.h et controller/src/PS2/Joypad.h et
controller/src/XBOX/Joypad.h
(oui ce sont des .h, inline oblige)

Dans le code source (controller/src/Joypad.cpp), on doit inclure le bon
src/???/Joypad.h
Actuellement, on fait cela, sans pb (target défini a PS2 ou XBOX, etc..):
#define PATH_CONTROLLER_JOYPAD <controller/src/##TARGET##/Joypad.h>
#include PATH_CONTROLLER_JOYPAD

Mais cette technique ne marche plus avec le dernier (?) gcc (au passage :
impossible de changer de version de gcc, cette version est imposée : 3.3.3
custom). Il nous jete avec le msg :
pasting "/" and "TARGET" does not give a valid preprocessing token

Ma question : cette facon de faire (define puis include) me semblait la plus
simple. Mais est elle standard ? Y-a-t-il un autre moyen ? (sans faire 36
#if (TARGET==XXX) etc)

4 réponses

1 2 3 4
Avatar
kanze
Falk Tannhäuser wrote in message
news:...
wrote:
Ou est-ce que l'opérateur de stringification met des "..." ?
L'opérateur de stringification génère un token de type string
literal. L'autre façon d'obtenir un string literal, évidemment,
c'est de mettre le texte entre des "...". Sauf que dans le contexte
d'un include, mettre le texte entre des "..." ne fait pas un string
literal.

C'est une distinction très importante. Par exemple :

#include "a-b" // défini par l'implémentation,
// éventuellement illégal

char const a[] = "a-b" ; // défini par la norme.

La distinction est même très réele, dans des implémentations
existantes : sous Windows, "n" est bien une séquence de deux
caractères dans un q-char-sequence, mais non dans un string literal.


Effectivement. Les linges suivantes compilent chez moi avec gcc 3.3.1
CygWin, à condition que le fichier "c:tbn.h" existe :

#define STR(x) #x
#define X STR(c:tbn.h)
#include X
char const c[] = X;

(sizeof c == 9)

Je ne suis pas sûr que cela soit le comportement mandaté par 16.3.2/2
et 16.2/4+3


Ce n'est certainement pas mandaté -- l'interprátation d'un « nom de
ficher » (q-char-sequence ou h-char-sequence) dans un include dépend de
l'implémnetation, et l'implémentation n'est obligée à accepter que les
alphanumérique et un seul point:-). Mais évidemment, aucune
implémentation n'est aussi stupide que de nous limiter ainsi quand le
système sous-jacent permet plus.

Plus généralement, je ne sais pas si le séquence « c:tbn.h » soit
légal en dehors d'une chaîne de caractères. Je ne crois pas, puisque
n'est pas un token du préprocesseur (et dans la description de
l'opérateur #, la norme dit « [...] replaced by a single character
string literal preprocessing token that contains the spelling of the
PREPROCESSING TOKEN SEQUENCE for the corresponding argument. [emphasis
ajouté] » C'est clair qu'il faut que le paramètre qu'on va stringiser
soit une suite légale de tokens, ce qui n'est pas le cas ici.

Et deuxième, indépendamment de la question que Gabi et moi disputons, le
résultat d'un opérateur # est un « string literal ». Or, dans un string
literal, le a bel et bien une signification spéciale. Qui n'est pas
celle de séparateur d'élément dans un chemin.

Enfin, en passant, que donne quelque chose comme :

#include "diru0041"
#include "citu00e9"

?

Dans le premier cas, la norme est claire : le programme est mal formé.
C-à-d que le compilateur est obligé a émettre un diagnostique. Même s'il
y a bien un fichier nommé u0041 dans un répertoire nommé dir chez toi.
(Selon la norme, l'interprétation des noms universels de caractères a
lieu avant même la découpe en tokens, et « [...], or if the universal
character name designates a character in the basic source character set,
the program is ill-formed ».) Dans le deuxième cas, une implémentation
peut bien chercher un fichier du nom « cité ». En fait, le mapping nom
donné -> nom de fichier dépend de l'implémentation ; l'implémentation
pourrait bien définir le mapping tel que le nom « cité » se mappe à un
fichier du nom 00e9 dans le répertoire cit. Mais d'une part, il faut que
ce mapping soit documenté, et de l'autre, il faut alors que
l'implémentation fasse le même mapping pour « cité » (à condition
d'accepter un é comme un caractère légal en entrée).

Mais ici, je crois que le problème dans la norme est claire. Ainsi que
la raison : les noms universels de caractères ont été ajouté à la norme
(C++ d'abord) bien après l'écriture de la chapitre 16 (que le C++
réprend prèsque mot-à-mot de C90), et personne n'a pensé aux rétouches
nécessaire dans ce chapitre.

--
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
Gabriel Dos Reis wrote in message
news:...
writes:

[...]

| > | Ce qui ne va pas sans poser des problèmes dans la définition de
| > | l'opérateur # -- on est amené à parler de l'« orthographe
| > | original », par exemple.

| > | Je sais qu'à l'époque où la norme C était tout neuf, j'ai eu des
| > | discussions avec certains membres du comité -- je ne me rappelle
| > | plus qui, et que ce sont eux qui m'ont dit que la forme proposée
| > | par Jean-Marc était illégal, et que la bonne forme était celui
| > | du premier posting.

| > Peux-tu articuler clairement pourquoi cette solution du premier
| > posting est valide ?

| Je suis tempté à dire que c'est pour les mêmes raisons que la
| solution que tu préconsises est valide.

Reprenons le code de départ.

#define PATH_CONTROLLER_JOYPAD <controller/src/##TARGET##/Joypad.h>
#include PATH_CONTROLLER_JOYPAD

La première ligne est une suite de pp-tokens comme suit :

{#}{define} {PATH_CONTROLLER_JOYPAD} {<}{controller}{/}{src}{/}{##}{TARGET}{##}{/}{Joypad}{.}{h}{>}

Le traitement de cette directive tombe sous le coup de 16.3.3/3 :


Seulement lors de l'expansion.

For both object-like and function-like macro invocations, before the
replacement list is reexamined for more macro names to replace, each
instance of a ## preprocessing token in the replacement list (not
from an argument) is deleted and the preceding preprocessing token
is concatenated with the following preprocessing token. If the
result is not a valid preprocessing token, the behavior is
undefined. The resulting token is available for further macro
replacement. The order of evaluation of ## operators is
unspecified.

Donc, avant même que la subtitution se produise (qui donnerait TARGET
-> PS2), la norme demande que {/} et {TARGET} soit concatenés, ce qui
donnerait {/TARGET} qui ne peut être un pp-otken valide.


Même pas un h-char-sequence ? (Dans le contexte où l'expansion du macro
a lieu, c'est bien le seul token permis.)

| En fait, il n'y a rien dans la norme qui l'interdit.

Voir ci-dessus.

| Mais je crois que toi aussi, il y a des passages que tu as sauté
| dans ce que j'ai écrit. Dans la contexte de l'expansion d'un
| include, si une suite de caractères comme « x/y » est un token valid
| ou non dépend de l'implémentation -- mais sous Unix, tous les
| compilateurs que je connais (y compris g++ 3.4.0 dans le cas
| général) l'accepte comme h-char-sequence (et dans la contexte d'une
| include, après un <, le *seul* token légal du préprocesseur, c'est
| un h-char-sequence).

Je crois que tu confonds deux choses distinctes :

(1) ce que fait l'opérateur ## ; et
(2) et une suite de pp-tokens, comme {x}{/}{y}, dans un context
comme j'ai cité dans le précédent mail.

La norme demande explicitement que

#include <x/y>

soit pp-tokeniser comme

{#}{include} {<x/y>}

alors que

#define PATH <x/y>

sera pp-tokeniser comme

{#}{define} {PATH} {<}{x}{/}{y}{>}


La norme dit clairement (§2.1/1, point 3) que la tokenisation dépend de
la contexte. Il cite même comme exemple (non-normatif, je sais) le cas
d'include. Alors, l'exigeance est que ## donne un token du
préprocesseur. Or, ou bien, un h-char-sequence est un token du
préprocesseur (mais seulement dans ce contexte), ou bien, en quoi
consiste-t-il ?

| Note bien §2.1/3 : « The process of dividing a source file's
| characters into preprocessing tokens is context-dependent. » On
| n'est pas n'importe où, on est dans une contexte où le seul token du
| préprocesseur permis est un h-char-sequence. Alors, si le résultat
| de ## n'est pas une h-char-sequence valide, le code est illégal --
| s'il l'est, le code est légal. Évidemment, ce que c'est qu'un
| h-char-sequence dépend de l'implémentation. Mais il doit être
| documenté, et il doit être pareil partout. Si on accepte « #include
| <x/y> », on est obligé dans cette contexte d'accepter x / y comme
| résultat de ##.

Je crois que tu vas trop vite dans ton raisonnement et que tu tombes
dan un trap. Par exemple, la ligne

#include <x/y>

est composée de

{#}{include} {<x/y>}


Je dirais plutôt de :

{#}{include}{<}{x/y}{>}

(ou {x/y} est un token de type h-char-sequence). Mais je ne sais pas si
la distinction est vraiment significative de quelque chose.

alors quelque chose comme

#define PATH <x/y>

donne

{#}{define} {PATH} {<}{x}{/}{y}{>}


Tout à fait d'accord. Enfin, pour ce que dit la norme -- je crois que
cette interprétation mène à quelques problèmes aussi. D'après ce qu'a
posté Falk Tannhäuser, ce n'est pas ce que fait g++ sous Windows non
plus. Mais je préfère ce que fait g++ sous windows, qu'une
interprétation trop rigueureuse de la norme.

c'est-à-dire le context de #include demane que si on voit '<' ou '"',
on prenne la plus longue chaîne possible pour faire un header-name.
Alors que dans le cas de #define, on tokenise et si on écrit quelque
chose

#include PATH

il y a expansion en

{#}{include} {<}{x}{/}{y}{>}

Note bien qu'ici, '<' apparaît comme un pp-token en soi -- et non
comme partie de chaîne de caractères qui ferait un header-name -- et
c'est seulement après cela que la norme demande que la suite de
pp-tokens entre les tokens {<} et {>} soit collectée pour former un
header-name (la méthode, i.e. comment c'est fabriqué dépend de
l'implémentation).


C'est là où je décroche. Je suis prêt à accepter que c'est une
interprétation possible. Je ne suis pas convaincu que c'est la meilleur
interprétation, ni celle voulue par les auteurs. Ne serait-ce que parce
que cette interprétation rend toute construction des noms de fichiers
pratiquement impossible, ou au moins très limitée.

Note bien, qu'il y a d'abord substitutions de *pp-tokens* et après
collection. Pendant cette substitution, il n'y a pas de notion de
h-char.


C'est là où je ne suis pas d'accord. Le contexte, c'est bien celui d'un
#include.

| Il y a une deuxième chose qui me chagrine un peu, si on accepte ton
| interprétation. Prenons l'exemple dans la norme, qui génère « #include
| "vers2.h" ». Si j'ai bien compris ton argumentation, l'exemple serait
| illégal si la version était « 2.2 », plutôt que « 2 », parce que le
| résultat de ## dans l'expansion de INCFILE n'est pas un token légal du
| préprocesseur (et la stringisation ne s'appliquera que plus tard).

Reproduisons l'exemple de la norme pour voir de quoi on parle.
16.3.5/6

#define str(s) # s
#define xstr(s) str(s)
#define debug(s, t) printf("x" # s "= %d, x" # t "= %s",
x ## s, x ## t)
#define INCFILE(n) vers ## n /* from previous #include example */
#define glue(a, b) a ## b
#define xglue(a, b) glue(a, b)
#define HIGHLOW "hello"
#define LOW LOW ", world"

debug(1, 2);
fputs(str(strncmp("abcd", "abc", 4 ) /* this goes away */
== 0) str(: @n), s);
#include xstr(INCFILE(2).h)
glue(HIGH, LOW);
xglue(HIGH, LOW)

Si tu écris INCFILE(2.2) au lieu de INCFILE(2), tu tombes sous le coup
de 16.3.3/2

If, in the replacement list, a parameter is immediately preceded
or followed by a ## preprocessing token, the parameter is replaced
by the corresponding argument s preprocessing token sequence.

Donc, l'expansion de la macro INCFILE donne

{vers}{##}{2.2}

ce qui après concaténation ressemble à {vers2.2} qui n'est pas un
pp-token valide.


Ce qui veut dire que j'ai réelement compris ce que tu essaies de
dire:-). C'est déjà quelque chose.

Je comprends ton argument. Je ne l'accepte pas. C'est trop contraignant,
et je ne crois pas que l'intention des auteurs de la norme a été d'être
aussi contraignant.

Note bien que l'exemple de la norme n'est pas

#include xstr(INCFILE(2.h))

qui produirait un pp-token invalde lors de la concaténation, mais bien

#include xstr(INCFILE(2).h)

de sorte la concatération se passe bien et la stringification fasse
proprement ce qu'il doit faire.

| D'abord, est-ce que j'ai bien compris ton argumentation, et que
| c'est juste.

Oui.

| Et deuxièmement, indépendamment de ce que dit la norme, est-ce que
| c'est une contrainte raisonable ?

De mon point ce vue, cela facilite le traitement -- si on se base sur
une subtsitution de tokens. (Si on fait une substituion sur du texte,
cela n'est pas nécessaire).


Je ne dis pas que ça ne rend pas plus facile la vie de celui qui
implémente le compilateur. Ce que je dis c'est que c'est une restriction
qui n'existait en aucun compilateur jusqu'il y a très peu, et que c'est
une restriction artificielle (de point de vue de l'utilisateur),
extrèmement contraignante, et rend la facilité de composition à peu près
inutilisable, parce que qui peut dire que si tu as une version 2
aujourd'hui, tu n'en aurais pas un 2.1 demain (ou dans le cas qui nous
intéresse, qu'il n'aurait jamais un cible dont le nom n'est pas un token
valide).

Concrêtement, l'idée est qu'on prend deux tokens et on les remplace
par un autre. On peut imaginer une routine dont le propos est de
valider une suite de caractères comme un pp-token ou non. Donc on
prend les deux tokens, on assemble leurs orthographes et on demande à
la routine si c'est un pp-token ou non. C'est simple, stupide et
efficace. L'autre alternative serait de redemander un redécoupage en
suite de tokens -- c'est le procédé traditionnel, par exemple gcc
-traditional.


Je te comprends. Je sais que c'est l'idée. Malheureusement, cette idée
s'adapte mal dans le cas des #include ; plus généralement, cette idée ne
peut pleinement fonctionner que dans le cas où la partie lexique du
langage n'avait aucune dépendence sur le contexte.

| En autres mots, est-ce que la solution proposée par Jean-Marc doit
| échouer si le nom du cible (TARGET) était PS-2, à la place de PS2 ?

Non, la solution proposée par Jean-Marc marche même si TARGET était
PS-2 à la place de PS2. La raison est qu'il n'utilise pas l'opérateur
## qui pose de problème dans le cas de INCFILE(2.2).


Entendu.

En revanche, il y aurait un problème s'il avait en nom comme (en
supposant que le système permet des @ dans les noms de fichiers), ou
s'il précisait le chemin avec des comme séparateur de répertoire
(alors que c'est ce qu'il y a de plus naturel sous Windows). (Mais le
introduit un pléthorie d'autres problèmes. Admettons qu'il existe un
système où le séparateur des répertoires est $, si tu veux, pour les
éviter.)

| (Et en passant, je crois que la version « correcte » de ce qu'il
| voulait faire serait :

| #define PATH_CONTROLLER_JOYPAD <controller##/##src##/##TARGET##/##Joypad.h>

Non, ce n'est pas correcte pour la même raison qu'avant : quand tu
concatènes {controller} et {/}, tu n'obtiens pas un pp-token valide.


Sauf dans le contexte d'un h-char-sequence:-).

Je suis content que tu m'as expliqué ton interprétation en détail. Je le
comprends, et je suis obligé à avouer que c'est une interprétation
possible, aussi valable que la mienne. J'ai mes doutes que ce soit
réelement l'intention des auteurs originaux, étant donné le contexte
historique, et ce que faisait traditionnellement les compilateurs. Mais
c'est difficile à dire -- les opérateurs # et ## sont des innovations du
comité C, introduites parce qu'il n'y avait pas de solution existante
universellement présente. Et il faut toujours se méfier des
« intentions » quand on en parle quinze ans plus tard.

En attendant : je trouve dommage que g++ ait décidé à casser du code qui
marchait dans leurs versions précédantes, sur la base d'une
interprétation de la norme qui est ouverte à discussion, et qui ne
correspond pas à ce que les compilateurs font en général. Mais dans la
mésure qu'il y a une alternative, qui est de toute fois (à mon avis)
supérieur, parce qu'il utilise la forme "..." plutôt que <...>, ce n'est
pas la fin du monde. (Même si je suis convaincu que la forme de
Jean-Marc n'est pas légal, pour les raisons que j'ai cité, je ne crois
pas qu'un compilateur ôserait jamais la réfuser.) J'espère seulement
pour mes amis qui travaille avec g++ sous Windows qu'il ne soit pas trop
cohérent, et qu'il continue à laisser passer des comme séparateur de
répertoire, même s'il ne sont pas des tokens du préprocesseur (et bien
que ça pose un réel problème si le caractère qui suit est un u ou un U).

--
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
Gabriel Dos Reis
writes:

| Gabriel Dos Reis wrote in message
| news:...
| > writes:
|
| > [...]
|
| > | > | Ce qui ne va pas sans poser des problèmes dans la définition de
| > | > | l'opérateur # -- on est amené à parler de l'« orthographe
| > | > | original », par exemple.
|
| > | > | Je sais qu'à l'époque où la norme C était tout neuf, j'ai eu des
| > | > | discussions avec certains membres du comité -- je ne me rappelle
| > | > | plus qui, et que ce sont eux qui m'ont dit que la forme proposée
| > | > | par Jean-Marc était illégal, et que la bonne forme était celui
| > | > | du premier posting.
|
| > | > Peux-tu articuler clairement pourquoi cette solution du premier
| > | > posting est valide ?
|
| > | Je suis tempté à dire que c'est pour les mêmes raisons que la
| > | solution que tu préconsises est valide.
|
| > Reprenons le code de départ.
|
| > #define PATH_CONTROLLER_JOYPAD <controller/src/##TARGET##/Joypad.h>
| > #include PATH_CONTROLLER_JOYPAD
|
| > La première ligne est une suite de pp-tokens comme suit :
|
| > {#}{define} {PATH_CONTROLLER_JOYPAD} {<}{controller}{/}{src}{/}{##}{TARGET}{##}{/}{Joypad}{.}{h}{>}
|
| > Le traitement de cette directive tombe sous le coup de 16.3.3/3 :
|
| Seulement lors de l'expansion.
|
| > For both object-like and function-like macro invocations, before the
| > replacement list is reexamined for more macro names to replace, each
| > instance of a ## preprocessing token in the replacement list (not
| > from an argument) is deleted and the preceding preprocessing token
| > is concatenated with the following preprocessing token. If the
| > result is not a valid preprocessing token, the behavior is
| > undefined. The resulting token is available for further macro
| > replacement. The order of evaluation of ## operators is
| > unspecified.
|
| > Donc, avant même que la subtitution se produise (qui donnerait TARGET
| > -> PS2), la norme demande que {/} et {TARGET} soit concatenés, ce qui
| > donnerait {/TARGET} qui ne peut être un pp-otken valide.
|
| Même pas un h-char-sequence ?

Puisqu'on travaille avec des pp-otkens, il n'y a plus de notions de
h-char-sequence.

| (Dans le contexte où l'expansion du macro
| a lieu, c'est bien le seul token permis.)

Un h-char-sequence n'est pas un token, James.

| > | En fait, il n'y a rien dans la norme qui l'interdit.
|
| > Voir ci-dessus.
|
| > | Mais je crois que toi aussi, il y a des passages que tu as sauté
| > | dans ce que j'ai écrit. Dans la contexte de l'expansion d'un
| > | include, si une suite de caractères comme « x/y » est un token valid
| > | ou non dépend de l'implémentation -- mais sous Unix, tous les
| > | compilateurs que je connais (y compris g++ 3.4.0 dans le cas
| > | général) l'accepte comme h-char-sequence (et dans la contexte d'une
| > | include, après un <, le *seul* token légal du préprocesseur, c'est
| > | un h-char-sequence).
|
| > Je crois que tu confonds deux choses distinctes :
|
| > (1) ce que fait l'opérateur ## ; et
| > (2) et une suite de pp-tokens, comme {x}{/}{y}, dans un context
| > comme j'ai cité dans le précédent mail.
|
| > La norme demande explicitement que
|
| > #include <x/y>
|
| > soit pp-tokeniser comme
|
| > {#}{include} {<x/y>}
|
| > alors que
|
| > #define PATH <x/y>
|
| > sera pp-tokeniser comme
|
| > {#}{define} {PATH} {<}{x}{/}{y}{>}
|
| La norme dit clairement (§2.1/1, point 3) que la tokenisation dépend de
| la contexte.

Justement. Dans le contexte de define, c'est ce que tu obtiens. Relis
bien ta norme que tu cites. Au moment de l'expansion de macros, la
ligne a déjà été découpée en suite de pp-tokens. Et une expansion de
macro substitue des suites de pp-tokens.

| Il cite même comme exemple (non-normatif, je sais) le cas
| d'include.

Oui, dans le contexte d'include. La pp-tokenisation est différente
dans le contexte de define.

| Alors, l'exigeance est que ## donne un token du
| préprocesseur.

Oui, et {/TARGET} n'est pas un.

| Or, ou bien, un h-char-sequence est un token du
| préprocesseur (mais seulement dans ce contexte), ou bien, en quoi
| consiste-t-il ?

Ce n'est pas un pp-token.

| > | Note bien §2.1/3 : « The process of dividing a source file's
| > | characters into preprocessing tokens is context-dependent. » On
| > | n'est pas n'importe où, on est dans une contexte où le seul token du
| > | préprocesseur permis est un h-char-sequence. Alors, si le résultat
| > | de ## n'est pas une h-char-sequence valide, le code est illégal --
| > | s'il l'est, le code est légal. Évidemment, ce que c'est qu'un
| > | h-char-sequence dépend de l'implémentation. Mais il doit être
| > | documenté, et il doit être pareil partout. Si on accepte « #include
| > | <x/y> », on est obligé dans cette contexte d'accepter x / y comme
| > | résultat de ##.
|
| > Je crois que tu vas trop vite dans ton raisonnement et que tu tombes
| > dan un trap. Par exemple, la ligne
|
| > #include <x/y>
|
| > est composée de
|
| > {#}{include} {<x/y>}
|
| Je dirais plutôt de :
|
| {#}{include}{<}{x/y}{>}

Bah non. Un header-name est un pp-token à lui tout seul, ce n'est pas
une suite de pp-tokens. Regarde en §2.8 la définition d'un header-name.

Pendant la pp-tokenisation, le pp-token header-name n'est reconnu que
lorsque le précédent pp-token est {include}. Entre autres, c'est ce
que cela veut dire que la pp-tokenisation dépend du contexte.

| (ou {x/y} est un token de type h-char-sequence). Mais je ne sais pas si

Tu viens d'inventer un pp-token.

| la distinction est vraiment significative de quelque chose.

Elle l'est. Tu donnais des leçons dans un précédent message sur la
différence entre "xxxx" dans le contexte la directive include et dans
le corps du programme. Je croyais que c'était parce que tu avais
compris ce point de pp-tokenisation dépendant du contexte. Visiblement
non :-(.

| > alors quelque chose comme
|
| > #define PATH <x/y>
|
| > donne
|
| > {#}{define} {PATH} {<}{x}{/}{y}{>}
|
| Tout à fait d'accord. Enfin, pour ce que dit la norme -- je crois que
| cette interprétation mène à quelques problèmes aussi. D'après ce qu'a
| posté Falk Tannhäuser, ce n'est pas ce que fait g++ sous Windows non
| plus.

Là, je te prierais d'expliquer un peu plus ce que tu veux dire.

[...]

| > c'est-à-dire le context de #include demane que si on voit '<' ou '"',
| > on prenne la plus longue chaîne possible pour faire un header-name.
| > Alors que dans le cas de #define, on tokenise et si on écrit quelque
| > chose
|
| > #include PATH
|
| > il y a expansion en
|
| > {#}{include} {<}{x}{/}{y}{>}
|
| > Note bien qu'ici, '<' apparaît comme un pp-token en soi -- et non
| > comme partie de chaîne de caractères qui ferait un header-name -- et
| > c'est seulement après cela que la norme demande que la suite de
| > pp-tokens entre les tokens {<} et {>} soit collectée pour former un
| > header-name (la méthode, i.e. comment c'est fabriqué dépend de
| > l'implémentation).
|
| C'est là où je décroche. Je suis prêt à accepter que c'est une
| interprétation possible.

Il n'y en a pas d'autre.

| Je ne suis pas convaincu que c'est la meilleur
| interprétation, ni celle voulue par les auteurs.

Il n'y en a pas d'autre. À moins que tu commences à inventer des
pp-tokens comme h-char-sequence ou h-fu-bhar.

La norme C++ (basée sur C90) donne moins d'exemples que C99.

Probablement, quelqu'un ne comprennait pas bien les règles, pourtant pas
excessivement compliquées, de la pp-tokenization. Alors C99 a mis des
exemples. Je me permets de le reproduire ici, juste pour contraster
avec ta prière « interprétation, ni celle voulue par les auteurs ».

[#4] EXAMPLE The following sequence of characters:

0x3<1/a.h>1e2
#include <1/a.h>
#define const.member@$

forms the following sequence of preprocessing tokens (with
each individual preprocessing token delimited by a { on the
left and a } on the right).

{0x3}{<}{1}{/}{a}{.}{h}{>}{1e2}
{#}{include} {<1/a.h>}
{#}{define} {const}{.}{member}{@}{$}


| Ne serait-ce que parce
| que cette interprétation rend toute construction des noms de fichiers
| pratiquement impossible, ou au moins très limitée.

Ce qu'il nous faut c'est des exemples, pas des affirmations
péremptoires de cet ordre -- surtout vu la discussion.

| > Note bien, qu'il y a d'abord substitutions de *pp-tokens* et après
| > collection. Pendant cette substitution, il n'y a pas de notion de
| > h-char.
|
| C'est là où je ne suis pas d'accord.

Cela a peu d'importance :-).

| Le contexte, c'est bien celui d'un #include.

Oui et alors ? Le '<' ne provient pas d'une pp-tokenization directe,
après celle de {include}. Il provient d'une expansion de macro, et
dans la définition de la macro, il était un pp-token à lui tout-seul.

(Tu me donnes l'impression que pour toi, une définiion de macro n'est
pas pp-tokenisée, ce qui serait en flagrante contradiction avec la
notion même telle qu'expliquée par les normes C90, C++ et C99).

| > | Il y a une deuxième chose qui me chagrine un peu, si on accepte ton
| > | interprétation. Prenons l'exemple dans la norme, qui génère « #include
| > | "vers2.h" ». Si j'ai bien compris ton argumentation, l'exemple serait
| > | illégal si la version était « 2.2 », plutôt que « 2 », parce que le
| > | résultat de ## dans l'expansion de INCFILE n'est pas un token légal du
| > | préprocesseur (et la stringisation ne s'appliquera que plus tard).
|
| > Reproduisons l'exemple de la norme pour voir de quoi on parle.
| > 16.3.5/6
|
| > #define str(s) # s
| > #define xstr(s) str(s)
| > #define debug(s, t) printf("x" # s "= %d, x" # t "= %s",
| > x ## s, x ## t)
| > #define INCFILE(n) vers ## n /* from previous #include example */
| > #define glue(a, b) a ## b
| > #define xglue(a, b) glue(a, b)
| > #define HIGHLOW "hello"
| > #define LOW LOW ", world"
|
| > debug(1, 2);
| > fputs(str(strncmp("abcd", "abc", 4 ) /* this goes away */
| > == 0) str(: @n), s);
| > #include xstr(INCFILE(2).h)
| > glue(HIGH, LOW);
| > xglue(HIGH, LOW)
|
| > Si tu écris INCFILE(2.2) au lieu de INCFILE(2), tu tombes sous le coup
| > de 16.3.3/2
|
| > If, in the replacement list, a parameter is immediately preceded
| > or followed by a ## preprocessing token, the parameter is replaced
| > by the corresponding argument s preprocessing token sequence.
|
| > Donc, l'expansion de la macro INCFILE donne
|
| > {vers}{##}{2.2}
|
| > ce qui après concaténation ressemble à {vers2.2} qui n'est pas un
| > pp-token valide.
|
| Ce qui veut dire que j'ai réelement compris ce que tu essaies de
| dire:-). C'est déjà quelque chose.
|
| Je comprends ton argument. Je ne l'accepte pas.

Ce n'est pas mon argument. C'est ce que demande la norme. Si t'es pas
d'accord avec la norme, tu sais ce qu'il te reste à faire.

| C'est trop contraignant,
| et je ne crois pas que l'intention des auteurs de la norme a été d'être
| aussi contraignant.

Oui mais dans cette discussion, jusqu'à présent, tu n'as pas brillé
par le fait que tes croyances -- de ce que l'intention des auteurs de
la norme seraient -- avaient l'ombre d'un début de vraissemblance
avec ce que ces auteurs écrivent et dans la partie normative et dans
les exemples. Jusqu'à présent, leurs exemples sont en flagrante
contradiction avec ce que tu as pu dire (voir les exemples que j'ai
reproduit ici, ainsi que les parties normatives que j'ai citées).

Tu regardes trop « ça se discute ». Tu devrais éteindre la télé
maintenant. Nous avons un sérieux sujet à traiter.

| > Note bien que l'exemple de la norme n'est pas
|
| > #include xstr(INCFILE(2.h))
|
| > qui produirait un pp-token invalde lors de la concaténation, mais bien
|
| > #include xstr(INCFILE(2).h)
|
| > de sorte la concatération se passe bien et la stringification fasse
| > proprement ce qu'il doit faire.
|
| > | D'abord, est-ce que j'ai bien compris ton argumentation, et que
| > | c'est juste.
|
| > Oui.
|
| > | Et deuxièmement, indépendamment de ce que dit la norme, est-ce que
| > | c'est une contrainte raisonable ?
|
| > De mon point ce vue, cela facilite le traitement -- si on se base sur
| > une subtsitution de tokens. (Si on fait une substituion sur du texte,
| > cela n'est pas nécessaire).
|
| Je ne dis pas que ça ne rend pas plus facile la vie de celui qui
| implémente le compilateur. Ce que je dis c'est que c'est une restriction
| qui n'existait en aucun compilateur jusqu'il y a très peu, et que c'est
| une restriction artificielle (de point de vue de l'utilisateur),
| extrèmement contraignante, et rend la facilité de composition à peu près
| inutilisable, parce que qui peut dire que si tu as une version 2
| aujourd'hui, tu n'en aurais pas un 2.1 demain (ou dans le cas qui nous
| intéresse, qu'il n'aurait jamais un cible dont le nom n'est pas un token
| valide).

Bon, je suggère tu crées un nouveau fil de discussion dédié uniquement
à ce nouveau troll. Nous avons un sujet sérieux å traiter ; tu as créé
de la confusion avec tes allégations infondées sur ce que serait
l'intention des auteurs de la partie CPP ; j'ai envie d'éclaircir ce
point ici et maintenant. Je n'ai pas envie que tu l'embrouilles avec
ce nouveau départ de troll. Je te promets d'y répondre, si tu le
fais dans un fil séparé.

| > Concrêtement, l'idée est qu'on prend deux tokens et on les remplace
| > par un autre. On peut imaginer une routine dont le propos est de
| > valider une suite de caractères comme un pp-token ou non. Donc on
| > prend les deux tokens, on assemble leurs orthographes et on demande à
| > la routine si c'est un pp-token ou non. C'est simple, stupide et
| > efficace. L'autre alternative serait de redemander un redécoupage en
| > suite de tokens -- c'est le procédé traditionnel, par exemple gcc
| > -traditional.
|
| Je te comprends. Je sais que c'est l'idée. Malheureusement, cette idée
| s'adapte mal dans le cas des #include ;

Bah non. Elle marche bien. Super bien même.

| plus généralement, cette idée ne
| peut pleinement fonctionner que dans le cas où la partie lexique du
| langage n'avait aucune dépendence sur le contexte.

Et alors ? Les pp-token sont convertis plus tard en tokens (pour
la partie lexicale du langage). Et cela marche bien.

[...]

| > | (Et en passant, je crois que la version « correcte » de ce qu'il
| > | voulait faire serait :
|
| > | #define PATH_CONTROLLER_JOYPAD <controller##/##src##/##TARGET##/##Joypad.h>
|
| > Non, ce n'est pas correcte pour la même raison qu'avant : quand tu
| > concatènes {controller} et {/}, tu n'obtiens pas un pp-token valide.
|
| Sauf dans le contexte d'un h-char-sequence:-).

James, si on vient de pp-tokeniser {define}, on n'est pas dans le
contexte d'un h-char-sequence ou d'un h-bhar-fu.

| Je suis content que tu m'as expliqué ton interprétation en détail. Je le
| comprends, et je suis obligé à avouer que c'est une interprétation
| possible, aussi valable que la mienne.

Bah non. La tienne n'est pas valable -- elle viole les règles de la
norme et est contraire aux exemples.

| J'ai mes doutes que ce soit réelement l'intention des auteurs originaux,

C'est ta version de « je suis donc je doute » ?

| étant donné le contexte historique, et ce que faisait
| traditionnellement les compilateurs.

Tu as eu largement l'occasion d'instruire le groupe sur la tradition,
l'histoire et la révolution. Et tu as même insisté sur le fait que la
révolution était voulue. On peut passer au corps du sujet maintenant ?

| Mais
| c'est difficile à dire -- les opérateurs # et ## sont des innovations du
| comité C, introduites parce qu'il n'y avait pas de solution existante
| universellement présente. Et il faut toujours se méfier des
| « intentions » quand on en parle quinze ans plus tard.

Et tu as certainement remarqué que je m'en suis tenu aux écrits
(parties normatives et exemples). Tu as pris tout seul l'initiative de
faire une expérimentation hasardeuse sur le terrain de l'intention.

| En attendant : je trouve dommage que g++ ait décidé à casser du code qui

G++ n'a pas décidé de casser du code. G++ a corrigé des bugs dans son
implémentation. Si tu en trouves de vrais bugs, mais vraiement de vrais,
tu sais que je serais plus qu'heureux que tu le rapportes.

| marchait dans leurs versions précédantes, sur la base d'une
| interprétation de la norme qui est ouverte à discussion, et qui ne

Il n'y a que toi qui refuses ce qui est écrit dans la norme. Ce n'est
pas la même chose que « ouverte à discussion ».

| correspond pas à ce que les compilateurs font en général. Mais dans la
| mésure qu'il y a une alternative, qui est de toute fois (à mon avis)
| supérieur, parce qu'il utilise la forme "..." plutôt que <...>, ce n'est
| pas la fin du monde. (Même si je suis convaincu que la forme de
| Jean-Marc n'est pas légal, pour les raisons que j'ai cité, je ne crois

Ah OK, ce n'est plus raison là, c'est la conviction, i.e. la foi.
Mauvaie foi, change de foi.

Pour l'assistance, je rappelle que la forme de Jean-Marc est celle
basée directement sur les exemples de la norme.

-- Gaby
Avatar
kanze
Gabriel Dos Reis wrote in message
news:...
writes:

| Gabriel Dos Reis wrote in message
| news:...
| > writes:


J'ai enfin trouvé un peu de temps pour étudier la question plus en
détail, y compris une lecture de la rationnale de C90. Plus j'y
reflechis, plus je trouve que ton interprétation est raisonable. Ce qui
ne veut pas dire qu'elle me plaît, ni même que je le crois l'intention
de tous les auteurs de la norme C.

En fait, il y avait deux choses qui me manquait : la définition d'un
header-name preprocessor token en §2.8, et le fait que « each
non-white-space character that cannot be one of the above » (en §2.4)
est aussi une pp-token.

Reste que j'ai toujours un problème avec la solution de Jean-Marc et
l'exemple dans la norme. On est bien d'accord que le résultat d'un
opérateur #, c'est un token de type « string-literal ». Or, la dernière
phrase de §16.2/4 parle de la « sequence de preprocessing tokens between
a < and a > preprocessing token pair or a pair of " characters ».
Seulement, dans les solutions proposés, il n'y a pas de « pair of "
characters » ; il n'y a qu'un « string-literal », qui est un token en
soi. Et il n'y a certainement pas de « sequence of preprocessing tokens
*between* » quoique ce soit -- ça suppose au moins trois éléments
distincts : un " (dont la norme ne dit pas d'où il vient), un pp-token
(mais il peut y en avoir plus) et encore un ".

Quoiqu'en disent les exemples (non-normatifs), ici, je ne vois pas
comment ça peut marcher. En se basant uniquement sur le texte normatif.

[...]

| En attendant : je trouve dommage que g++ ait décidé à casser du code qui

G++ n'a pas décidé de casser du code.


Chaque fois que tu changes un comportement, tu casses du code. Même si
ce comportement était une erreur. Changer un comportement sans offrir
une option de compatibilité, est irresponsable, au moins dans la mésure
que certains programmes auraient pu compter sur ce comportement. Changer
un comportement qui était généralement accepté et utilisé, c'est pire.

G++ a corrigé des bugs dans son implémentation. Si tu en trouves de
vrais bugs, mais vraiement de vrais, tu sais que je serais plus
qu'heureux que tu le rapportes.


On a peut-être une définition différente de bug. Donner un comportement
défini à quelque chose qui n'est pas défini par la norme, ce n'est pas
un bug. Surtout quand c'est un comportement qui correspond à ce que font
la plupart des compilateurs.

Changer un comportement défini par le compilateur, sans même avertir les
utilisateurs, ce n'est pas vraiment montrer beaucoup de respect pour les
utilisateurs.

[...]
Pour l'assistance, je rappelle que la forme de Jean-Marc est celle
basée directement sur les exemples de la norme.


Exemples non normatifs. Et les mots précis de la norme, pris
littéralement, l'interdit. Si j'ai fini par accepter que ton
interprétation de la norme vaut pour le token-pasting, je ne vois
toujours pas comment l'utilisation d'un opérateur # peut générer un "
suivi d'une séquence de pp-tokens suivi d'un ". Il y a contradiction
entre le texte normatif et les exemples.

Et je suis tout à fait contre l'idée de générer une erreur chaque fois
qu'il y a un comportement indéfini selon la norme. Quand le comportement
indéfini correspond à un comportement qui varie réelement entre des
compilateurs, voire à l'intérieur d'un seul compilateur, selon les
options de compilation, d'accord. Mais quand il s'agit d'un comportement
qui a toujours été défini par le compilateur dans la passée, je ne suis
pas d'accord.

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