86Fermer88
PpHdLe 12/11/2014 à 20:44
Folco (./86) :
J'ai meme essayé de les comprendre, c'est certainement très puissant, mais les sources sont quand même vachement capillo-tractées, et j'ai pas compris comment ça marchait, donc j'utilise pas. On a ses principes, non mais. embarrassed.gif?14


Ah mais la méta programmation en préprocesseur C n'est pas compliqué. C'est juste des couches, empilées dans des couches, empilées dans des couches, pour faire les substitutions que l'on veut.
Tout repose sur P99_HAS_COMMA qui retourne 0 ou 1 si l'argument qu'on lui donne en paramètre à une virgule.
Pour ce faire, on définit la macro comme étant: #define P99_HAS_COMMA(...) P00_ARG(__VA_ARGS__, \ 1, 1, 1, 1, 1, 1, 1, \ ... 1, 1, 1, 1, 1, 1, 1, 1, \ 1, 1, 1, 1, 1, 1, 0, 0)
P00_ARG retourne son 159ième argument.
ie. on utilise l'argument VA_ARGS qui va décaler ou pas les arguments donnés en paramètre à P00_ARG qui va retourner 1 ou 0 selon que l'on a une virgule ou pas.

Avec cette macro, on peut construire P99_IS_EMPTY: #define P99_IS_EMPTY(...) \ P00_ISEMPTY( \ /* test if there is just one argument, that might be empty */ \ P99_HAS_COMMA(__VA_ARGS__), \ /* test if P99_IS__EQ__ together with the argument \ adds a comma */ \ P99_HAS_COMMA(P00_IS__EQ__ __VA_ARGS__), \ /* test if the argument together with a parenthesis \ adds a comma */ \ P99_HAS_COMMA(__VA_ARGS__ (/*empty*/)), \ /* test if placing it between P99_IS__EQ__ and the \ parenthesis adds a comma */ \ P99_HAS_COMMA(P00_IS__EQ__ __VA_ARGS__ (/*empty*/)) \ )
Le point clé est le dernier argument : si __VA_ARGS__ est vide, on obtient : P00_IS__EQ__ (/*empty*/), qui reconnu comme un appel de macro, qui va injecter une virgule !
Donc l'argument est vide lorsque seul le dernier élément (P99_HAS_COMMA(P00_IS__EQ__ __VA_ARGS__ (/*empty*/)) ) est vrai et pas les autres !
Donc on évalue tous les arguments, qui ne valent que 1 ou 0. On concatène le tout (P00_ISEMPTY) et via une autre rangé de macro, on dit si oui ou non on a gagné: #define P00_IS_EMPTY_CASE_0000 P00_IS_EMPTY_CASE_0000 #define P00_IS_EMPTY_CASE_0001 , #define P00_IS_EMPTY_CASE_0010 P00_IS_EMPTY_CASE_0010 #define P00_IS_EMPTY_CASE_0011 P00_IS_EMPTY_CASE_0011 ....
Là, il réutilise sa macro IS_COMMA pour dire si oui ou non, c'est vide. J'aime beaucoup love

A partir de là tu as 0 ou 1 selon que c'est vide ou pas.
Tu redéfinis des macros #define MACHIN_TO_KEEP_IF_1(...) #define MACHIN_TO_KEEP_IF_0(...) __VA_ARGS__
tu concatènes ta sortie de ta macro précédente avec MACHIN_TO_KEEP (avec le bon niveau de substitution) et si c'est pas vide, tu expanses ton argument.
En faisant simple, ca donne
#define KEEP_IF_NOT_EMPTY(...) MACHIN_TO_KEEP_ ## IS_EMPTY(__VA_ARGS__) (func(__VA_ARGS__))
(sauf que ca ne va pas marcher parque les substitutions ne se passeront pas au bon moment (## est prioritaire sur l'appel d'une macro !) Mais le principe est là.