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)

10 réponses

1 2 3 4
Avatar
Gabriel Dos Reis
writes:

| Jean-Marc Bourguet wrote in message
| news:...
| > "Amerio" writes:
|
| > > #define PATH_CONTROLLER_JOYPAD <controller/src/##TARGET##/Joypad.h>
| > > #include PATH_CONTROLLER_JOYPAD
|
| > #define TARGET PS2
| > #define XSTR(x) #x
| > #define STR(x) XSTR(x)
| > #define PLATFILE(base,file) STR(base/TARGET/file)
|
| > #include PLATFILE(controller/src,Joypad.h)
|
| Techniquement, c'est illégale selon la norme. (Mais je doute que tu
| trouves une implémentation où il ne marche pas.) Le problème, c'est que
| l'opérateur # renvoie un seul token, du type « string literal ». Or, un
| « string literal » n'est pas un token légal ici -- il faut trois tokens,
| un « " » (qui n'est un token que dans ce contexte-ci), un
| « q-char-sequence », et un « " ».

Visiblement, ton interprétation est en conflit avec celle des gens qui
ont écrit cette partie de la norme C et C++.

-- Gaby
Avatar
Gabriel Dos Reis
"Amerio" writes:

| 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

C'est que tu dois « stringifier » "/" avant de le concatener avec TARGET.
En fait, tu dois « stringifier » toute la ligne, où ne concatener que
des opérandes « stringifiées ».

-- Gaby
Avatar
Gabriel Dos Reis
writes:

[...]

| Dans la pratique, étant donné le fait que ce que c'est qu'un token
| dépend du contexte, à peu près la seule solution qui tient la route pour
| une implémentation, c'est de traiter faire toute l'expansion des macros
| comme texte, et puis tokeniser le résultat (en contexte). Sa solution
| original est légal

Citations de la norme, please ?

| -- g++ le réfuse parce qu'ils ont fait l'erreur
| d'essayer à tokeniser dans l'expansion des macros, et sans le contexte
| d'utilisation.

Chapitres et versets, please ?

| Qu'il invoque son macro n'importe où que dans une include, leur message
| d'erreur serait correct. Mais non réquis, puisque c'est un comportement
| indéfini. Essayer de donner un message en cas de comportement indéfini,
| c'est bien. Mais si les versions antérieur du compilateur l'ont permis,
| avec un comportement « défini », le moindre respect de l'utilisateur
| exige que le message ne soit qu'un avertissement. Et aussi, évidemment,

Je suggère que tu envoies un message à pour leur
demander de permettre tous les bugs qui « avaient un comportement
"défini" », pour le mondre respect de l'utilisateur.

| il faut être bien sûr que le comportement *est* indéfini ; ici, ce n'est
| pas du tout évident.

-- Gaby
Avatar
Florent 'flure' C.
Le Tue, 22 Jun 2004 11:47:15 +0200, Amerio a écrit :

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)


Peut-être en changeant vos #include et en ajoutant l'option de
compilation qui va bien pour les chercher au bon endroit.
Ainsi vous ne faites plus
#include "controller/src/PS2/Joypad.h"
mais
#include <Joypad.h>
en ayant pris soin de passer l'option -I/chemin vers/controller/src/PS2
(ou XBOX selon l'architecture)

Ainsi, en changeant d'architecture cible, vous ne changez que le Makefile
et non les sources, et basta les #if-#else-#end ...

--
Florent "flure" C.
Décrypter l'@ pour répondre
Coders don't die, they just JMP without RET !

Avatar
Amerio
Florent 'flure' C. wrote:

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)


Peut-être en changeant vos #include et en ajoutant l'option de
compilation qui va bien pour les chercher au bon endroit.
Ainsi vous ne faites plus
#include "controller/src/PS2/Joypad.h"
mais
#include <Joypad.h>
en ayant pris soin de passer l'option -I/chemin vers/controller/src/PS2
(ou XBOX selon l'architecture)

Ainsi, en changeant d'architecture cible, vous ne changez que le Makefile
et non les sources, et basta les #if-#else-#end ...


Helas, cette solution est valable si on a que un ou deux -I a ajouté.
(c'est meme la solution canonique si tout ce qui est plateforme specifique
se trouve dans un seul dossier)
Dans notre cas, on en aurais plus d'une vingtaine, ce qui pose des pb de
maintenance.
(les sources plateformes specifiques sont reparties dans chaque sous dossier
correspondant a chaque sous-partie du code)


Avatar
Florent 'flure' C.
Le Tue, 22 Jun 2004 12:41:49 +0200, Amerio a écrit :

Dans notre cas, on en aurais plus d'une vingtaine, ce qui pose des pb de
maintenance.


Ca ne peut pas être automatisé ?

(les sources plateformes specifiques sont reparties dans chaque sous dossier
correspondant a chaque sous-partie du code)


Dans ce cas, n'y aurait-il pas un problème dans la conception de
l'aborescence ?

En tout cas, la solution de Jean-Marc Bourguet est impressionante de
simplicité et d'élégance !

--
Florent "flure" C.
Décrypter l'@ pour répondre
Coders don't die, they just JMP without RET !

Avatar
Jean-Marc Bourguet
"Amerio" writes:

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


#define TARGET PS2
#define XSTR(x) #x
#define STR(x) XSTR(x)
#define PLATFILE(base,file) STR(base/TARGET/file)

#include PLATFILE(controller/src,Joypad.h)

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
Amerio
Jean-Marc Bourguet wrote:
"Amerio" writes:

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


#define TARGET PS2
#define XSTR(x) #x
#define STR(x) XSTR(x)
#define PLATFILE(base,file) STR(base/TARGET/file)

#include PLATFILE(controller/src,Joypad.h)



Impeccable ! Avec cela, ca marche ! Merci !


Avatar
kanze
"Amerio" wrote in message
news:<40d80024$0$279$...

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)


La norme n'est pas aussi claire qu'elle pourrait l'être. Elle dit bien
qu'il faut que le résultat de ## soit un token légal. Malheureusement,
en C et en C++, ce que c'est qu'un token dépend du contexte. Dans le
contexte ici, par exemple, quelque chose comme » /XBOX « est un token
légal (un h-char-sequence) ; g++ a tort. D'autant plus que ce que tu
fais là, c'est une pratique assez répandue dans la communité C.

Mais j'ai en fait l'impression que les auteurs de g++ prend un véritable
plaisir à casser du code existant. Tant que cette attitude existe, il
faut rester avec g++ 2.95.2, malgré son âge et ses faiblesses.

--
James Kanze GABI Software
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
Jean-Marc Bourguet wrote in message
news:...
"Amerio" writes:

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


#define TARGET PS2
#define XSTR(x) #x
#define STR(x) XSTR(x)
#define PLATFILE(base,file) STR(base/TARGET/file)

#include PLATFILE(controller/src,Joypad.h)


Techniquement, c'est illégale selon la norme. (Mais je doute que tu
trouves une implémentation où il ne marche pas.) Le problème, c'est que
l'opérateur # renvoie un seul token, du type « string literal ». Or, un
« string literal » n'est pas un token légal ici -- il faut trois tokens,
un « " » (qui n'est un token que dans ce contexte-ci), un
« q-char-sequence », et un « " ».

Dans la pratique, étant donné le fait que ce que c'est qu'un token
dépend du contexte, à peu près la seule solution qui tient la route pour
une implémentation, c'est de traiter faire toute l'expansion des macros
comme texte, et puis tokeniser le résultat (en contexte). Sa solution
original est légal -- g++ le réfuse parce qu'ils ont fait l'erreur
d'essayer à tokeniser dans l'expansion des macros, et sans le contexte
d'utilisation.

Qu'il invoque son macro n'importe où que dans une include, leur message
d'erreur serait correct. Mais non réquis, puisque c'est un comportement
indéfini. Essayer de donner un message en cas de comportement indéfini,
c'est bien. Mais si les versions antérieur du compilateur l'ont permis,
avec un comportement « défini », le moindre respect de l'utilisateur
exige que le message ne soit qu'un avertissement. Et aussi, évidemment,
il faut être bien sûr que le comportement *est* indéfini ; ici, ce n'est
pas du tout évident.

--
James Kanze GABI Software
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