2009-12-10 7 views
9

ce que je voudrais faire (à des fins d'exploitation forestière) est quelque chose comme ceci:CPP: éviter l'expansion macro d'un paramètre de la fonction macro

Ce code a été écrit pour montrer mon problème, code réel est complexe et oui , j'ai de bonnes raisons d'utiliser des macros même sur C++ =)

# define LIB_SOME 1 
# define LIB_OTHER 2 

# define WHERE "at file #a, line #l, function #f: " 
// (look for syntax hightlighting error at SO xd) 
# define LOG_ERROR_SIMPLE(ptr, lib, str) ptr->log ("ERROR " str \ 
                " at library " #lib); 
# define LOG_ERROR(ptr, lib, str) LOG_ERROR_SIMPLE(ptr, lib, WHERE str) 

LOG_ERROR_SIMPLE (this, LIB_SOME, "doing something") 
LOG_ERROR (this, LIB_OTHER, "doing something else") 

LOG_ERROR_SIMPLE() écrit le mise en chaîne du paramètre lib (un nom macro entouré de " «)

mais LOG_ERROR écrit la mise en chaîne de la macro déjà expander d ("2"). ceci est attendu, puisque lib a obtenu son expansion avant d'étendre et d'appeler LOG_ERROR_SIMPLE. mais ce n'est pas ce dont j'ai besoin.

Fondamentalement, ma question est la suivante: comment éviter la macro-expansion d'un paramètre de fonction macro lors de l'appel d'une autre fonction macro?

Il y a une astuce que j'utilise qui évite l'expansion macro:

LOG_ERROR(ptr, lib, str, x) LOG_ERROR_SIMPLE(ptr, x##lib, WHERE str) 

    LOG_ERROR(this, LIB_OTHER, "some error",) 

(coller x et lib produit LIB_OTHER et cette valeur est utilisée pour appeler LOG_ERROR_SIMPLE, son pas macro étendu avant cet appel)

Il existe un moyen d'obtenir ce même comportement sans utiliser un truc?

Répondre

7

que je fais:

#include <cstdio> 

#define FOO 1 
#define BAR 2 

#define LOG_SIMPLE(ptr, lib, str) printf("%s\n", #lib); 
#define LOG(ptr, lib, str) LOG_SIMPLE(ptr, ##lib, str) 

int main() 
{ 
    LOG_SIMPLE(0, FOO, "some error"); 
    LOG(0, BAR, "some other error"); 
} 

qui imprime:

FOO 
BAR 

Fonctionne avec MSVC2005 mais pas avec gcc/g ++.


EDIT: pour le faire fonctionner avec gcc/g ++ vous pouvez abuser des macros variadique:

#include <stdio.h> 

#define FOO 1 
#define BAR 2 

#define LOG_SIMPLE(ptr, str, lib) printf("%s\n", #lib); 
#define LOG(ptr, str, lib, ...) LOG_SIMPLE(ptr, str, lib##__VA_ARGS__) 

int main() 
{ 
    LOG_SIMPLE(0, "some error", FOO); 
    LOG(0, "some other error", BAR); 
    LOG(0, "some other error", FOO, BAR); 
} 

Cependant, il est votre discipline de ne pas utiliser la macro avec trop de paramètres. MSVC2005 imprime

FOO 
BAR 
FOO2 

tandis que des impressions gcc sur

FOO 
BAR 
FOOBAR 
+0

Lorsque vous utilisez le préprocesseur de GCC (en supprimant le '# include 'qui n'existe pas), j'obtiens le bon programme sur stdout, mais un message d'erreur sur stderr. t.c: 11: 1: erreur: coller "," et "BAR" ne donne pas un jeton de pré-traitement valide –

+0

même ici, ne fonctionne pas avec gcc/g ++ –

+0

Eh bien, "ne fonctionne pas" est un peu fort. J'ai eu un programme compilable sur stdout quand j'ai lancé 'gcc -E t.c'. –

0

Je ne pense pas que vous le pouvez. Qu'est-ce que vous pourriez faire, cependant, est d'ajouter une couche de macro pour qu'elle unpeel à sa place:

#define WRAP(x) x 
#define LOG_ERROR(ptr, lib, str) LOG_ERROR_SIMPLE(ptr, lib, WHERE WRAP(str)) 
+1

BOOST_PP_IDENTITY (X) – KitsuneYMG

+0

@kts Ooh, whaddya sait. Je pense à quelque chose, et les amis de Boost sont en avance sur moi. –

+0

ni WRAP ni _IDENTITY ne fonctionne .. car (au moins dans CPP) il appelle LOG_ERROR_SIMPLE avec WRAP () comme paramètre, et non comme "ce qui est à l'intérieur du nom mais pas développé" – conejoroy

0

Vous l'aviez presque.Utilisez

#define LOG_ERROR(ptr, lib, str) LOG_ERROR_SIMPLE(ptr, ##lib, WHERE str) 

Sur gcc
LOG_ERROR(this, LIB_OTHER, "some error")
donne
this->log ("ERROR " "at file #a, line #l, function #f: " "some error" " at library " "LIB_OTHER");

Je voudrais aussi enlever la fuite ';' de votre macro afin que votre code ressemblerait à ceci:
LOG_ERROR(this, LIB_OTHER, "some error");

+0

Je pense que l'OP veut éviter la citation de l'argument d'où la question. Si "LIB_OTHER" lui convenait, je pense qu'il n'aurait pas demandé en premier lieu –

+0

coller une lib avec une virgule donne cette erreur: coller "," et "LIB_OTHER" ne donne pas un jeton de pré-traitement valide. Y at-il un caractère "neutre" ou un paramètre vide caché que je pourrais utiliser pour coller lib avec "quelque chose de vide" et sans erreurs? – conejoroy

+0

Désolé. J'ai mal compris vos besoins. BOOST_PP_EMPTY est une macro qui se développe à rien. Vous pouvez également essayer/**/(c'est un commentaire de style c vide au cas où le démarquage le tue) – KitsuneYMG

4

Si vous ne avez pas besoin des alias lib élargis (ie « 1 » & « 2 ») dans vos macros cpp, vous pouvez aussi utiliser un ENUM au lieu de valeurs définies.

+2

Bien que j'aurais aimé trouver la solution à mon problème de macro-expansion, vous m'avez fait repenser l'un de ses éléments. J'ai changé mes constantes macro en enums, leur nom ne se développe pas macro (puisqu'ils ne sont pas une macro) et maintenant le problème n'est pas résolu, mais son gone =) – conejoroy

+0

Maintenant je veux toujours le résoudre, mais En ce qui concerne mon programme, grâce à votre suggestion, je n'ai pas besoin de .. – conejoroy