Une macro C peut-elle contenir des variables temporaires?

J’ai une fonction que je dois macro’ize. La fonction contient des variables temporaires et je ne me souviens pas s’il existe des règles relatives à l’utilisation de variables temporaires dans les substitutions de macros.

long fooAlloc(struct foo *f, long size) { long i1, i2; double *data[7]; /* do something */ return 42; } 

MACRO Forme:

 #define ALLOC_FOO(f, size) \ {\ long i1, i2;\ double *data[7];\ \ /* do something */ \ } 

Est-ce correct? (c.-à-d. aucun effet secondaire désagréable – autre que les effets habituels: pas “type safe”, etc.). BTW, je sais que “les macros sont diaboliques” – je dois simplement l’utiliser dans ce cas – pas beaucoup de choix.

Il n’y a que deux conditions dans lesquelles cela fonctionne de manière “raisonnable”.

  1. La macro n’a pas d’instruction return. Vous pouvez utiliser le truc do while While.

     #define macro(x) do { int y = x; func(&y); } while (0) 
  2. Vous ne ciblez que GCC.

     #define min(x,y) ({ int _x = (x), _y = (y); _x < _y ? _x : _y; }) 

Cela aiderait si vous expliquez pourquoi vous devez utiliser une macro (votre bureau a-t-il des "lundis macro" ou autre chose?). Sinon, nous ne pouvons pas vraiment aider.

Tout d’abord, je recommande fortement les fonctions inline. Très peu de choses que les macros peuvent faire et ne peuvent pas faire, et elles sont beaucoup plus susceptibles de faire ce que vous attendez.

Un piège de macros, que je n’ai pas vu dans d’autres réponses, est l’observation de noms de variables.
Supposons que vous définissiez:

 #define A(x) { int temp = x*2; printf("%d\n", temp); } 

Et quelqu’un l’utilisait de cette façon:

 int temp = 3; A(temp); 

Après le prétraitement, le code est le suivant:

 int temp = 3; { int temp = temp*2; printf("%d\n", temp); } 

Cela ne fonctionne pas, car le temp interne ombrage le externe.
La solution courante consiste à appeler la variable __temp , en supposant que personne ne définira une variable à l’aide de ce nom (ce qui est une hypothèse étrange, étant donné que vous venez de le faire).

Les macros C ne sont que des substitutions textuelles (relativement simples).

La question que vous vous posez peut-être est la suivante: puis-je créer des blocs (également appelés instructions composées) dans une fonction comme dans l’exemple ci-dessous?

 void foo(void) { int a = 42; { int b = 42; { int c = 42; } } } 

Et la réponse est oui.

Maintenant, comme @DiesortingchEpp l’a mentionné dans sa réponse, si la macro est une instruction composée, comme dans votre exemple, il est recommandé de joindre les instructions de macro à do { ... } while (0) plutôt qu’à { ... } . Le lien ci-dessous explique quelle situation la do { ... } while (0) d’une macro tente d’éviter:

http://gcc.gnu.org/onlinedocs/cpp/Swallowing-the-Semicolon.html

De même, lorsque vous écrivez une macro de type fonction, demandez-vous toujours si vous avez un réel avantage, car écrire le plus souvent une fonction est préférable.

Ceci est généralement correct, sauf que les macros sont généralement entourées de do { ... } while(0) (jetez un coup d’œil à cette question pour plus d’explications):

 #define ALLOC_FOO(f, size) \ do { \ long i1, i2;\ double *data[7];\ /* do something */ \ } while(0) 

Aussi, dans la mesure où votre fonction fooAlloc origine retourne long vous devez modifier votre macro pour stocker le résultat d’une autre manière. Ou, si vous utilisez GCC, vous pouvez essayer l’extension d’ instruction composée :

 #define ALLOC_FOO(f, size) \ ({ \ long i1, i2;\ double *data[7];\ /* do something */ \ result; \ }) 

Enfin, vous devez prendre en compte les éventuels effets secondaires de l’argument macro-expansion. Le modèle habituel consiste à définir une variable temporaire pour chaque argument à l’intérieur d’un bloc et à les utiliser à la place:

 #define ALLOC_FOO(f, size) \ ({ \ typeof(f) _f = (f);\ typeof(size) _size = (size);\ long i1, i2;\ double *data[7];\ /* do something */ \ result; \ }) 

La réponse d’Eldar vous montre la plupart des pièges de la programmation macro et une extension utile (mais non standard) de gcc.

Si vous souhaitez vous en tenir à la norme, une combinaison de macros (pour la généricité) et de fonctions en inline (pour les variables locales) peut être utile.

 inline long fooAlloc(void *f, size_t size) { size_t i1, i2; double *data[7]; /* do something */ return 42; } #define ALLOC_FOO(T) fooAlloc(malloc(sizeof(T)), sizeof(T)) 

Dans un tel cas, l’utilisation de sizeof n’évalue l’expression que pour le type au moment de la compilation et non pour sa valeur. Par conséquent, F ne serait pas évalué deux fois.

BTW, “tailles” doivent généralement être tapées avec size_t et non avec long ou similaire.

Edit: En ce qui concerne la question de Jonathan sur inline fonctions en inline , j’ai écrit quelque chose sur le modèle en inline de C99, ici .

Oui, cela devrait fonctionner lorsque vous utilisez une structure de bloc et que les variables temp sont déclarées dans la scope interne de ce bloc.

Notez que le dernier \ après le} est redondant.

Ils peuvent. Ils ne devraient souvent pas.

Pourquoi cette fonction doit-elle être une macro? Pourriez-vous le remplacer?

Si vous utilisez c ++, utilisez inline ou utilisez -o3 avec gcc, toutes les fonctions seront inline. Je ne comprends toujours pas pourquoi vous avez besoin de macro cette fonction.

Une solution pas parfaite: (ne fonctionne pas avec les macros récursives, par exemple plusieurs boucles les unes dans les autres)

 #define JOIN_(X,Y) X##Y #define JOIN(X,Y) JOIN_(X,Y) #define TMP JOIN(tmp,__LINE__) #define switch(x,y) int TMP = x; x=y;y=TMP int main(){ int x = 5,y=6; switch(x,y); switch(x,y); } 

deviendra après l’exécution du préprocesseur:

 int main(){ int x=5,y=6; int tmp9 = x; x=y; y=tmp9; int tmp10 = x; x=y; y=tmp10; }