macro définition

J’ai essayé de définir une macro fonctionnant comme ci-dessous. L’appel 1 n’a pas de problème, mais l’appel 2 a provoqué une erreur du compilateur car le 3ème argument n’est pas disponible. Comment définir une macro prenant en charge à la fois les appels 1 et 2?

#define RDF_LOG(dbglevel, fmt, ...) (rdfDBG(dbglevel, " " fmt, __VA_ARGS__)) void rdfDBG(int dbglevel, const char *fmt, ...) { /* printf debug message */ } RDF_LOG(kERROR, "Fail to open file %s\n", pinfile); /* Call 1 */ RDF_LOG(kERROR, "Insufficient Memory\n"); /* call 2 , comstackr -> error: expected expression before ')' token */ 

Vous obtenez une virgule supplémentaire dans la deuxième extension de macro, car vous avez une virgule inconditionnelle après fmt dans la définition de macro.

Supprimer le paramètre fmt de la définition de la macro semble résoudre le problème. la chaîne de formatage fait alors partie de __VA_ARGS__ :

 #define RDF_LOG(dbglevel, ...) (rdfDBG(dbglevel, " " __VA_ARGS__)) void rdfDBG(int dbglevel, const char *fmt, ...) { /* printf debug message */ } RDF_LOG(kERROR, "Fail to open file %s\n", pinfile); /* Call 1 */ RDF_LOG(kERROR, "Insufficient Memory\n"); 

Cela s’étend à:

 void rdfDBG(int dbglevel, const char *fmt, ...) { } (rdfDBG(kERROR, " " "Fail to open file %s\n", pinfile)); (rdfDBG(kERROR, " " "Insufficient Memory\n")); 

Incidemment, il semble que le " " est destiné à exiger que le format soit un littéral de chaîne (et ma version modifiée le préserve). Êtes-vous sûr de vouloir faire ça? Bien que ce soit rare, il peut être utile d’avoir une chaîne de format non littérale.

Extensions GCC

GCC a une extension pour gérer ça (notez la virgule manquante avant le ... ):

Incorrect (références __VA_ARGS__ non autorisées dans l’extension GCC):

 #define RDF_LOG(dbglevel, fmt ...) (rdfDBG(dbglevel, " " fmt, __VA_ARGS__)) 

Correct (ne faisant pas référence à __VA_ARGS__ ):

 #define RDF_LOG(dbglevel, fmt...) (rdfDBG(dbglevel, " " fmt)) void rdfDBG(int dbglevel, const char *fmt, ...) { /* printf debug message */ } enum { kERROR }; void x(const char *pinfile); void x(const char *pinfile) { RDF_LOG(kERROR, "Fail to open file %s\n", pinfile); RDF_LOG(kERROR, "Insufficient Memory\n"); } 

Vous pouvez dire que je n’utilise pas l’extension GCC – parce que j’utilise des compilateurs autres que GCC.

Il y a aussi le deuxième mécanisme (spécifique à GCC) mentionné par Adam dans son commentaire:

 #define RDF_LOG(dbglevel, fmt, ...) (rdfDBG(dbglevel, " " fmt, ## __VA_ARGS__)) void rdfDBG(int dbglevel, const char *fmt, ...) { /* printf debug message */ } enum { kERROR }; void x(const char *pinfile); void x(const char *pinfile) { RDF_LOG(kERROR, "Fail to open file %s\n", pinfile); RDF_LOG(kERROR, "Insufficient Memory\n"); } 

Standard C99

A défaut, vous devez utiliser le mécanisme standard C99:

 #define RDF_LOG(dbglevel, ...) (rdfDBG(dbglevel, " " __VA_ARGS__)) void rdfDBG(int dbglevel, const char *fmt, ...) { /* printf debug message */ } RDF_LOG(kERROR, "Fail to open file %s\n", pinfile); /* Call 1 */ RDF_LOG(kERROR, "Insufficient Memory\n"); 

Cela, fondamentalement, trompe ou contourne le problème dans ce contexte. Dans le cas général, C99 nécessite une virgule et au moins un argument.