Question

J'ai rencontré un petit problème théorique. Dans un morceau de code que je maintiens, il y a un ensemble de macros comme

#define MAX_OF_2(a, b)       (a) > (b) ? (a) : (b)
#define MAX_OF_3(a, b, c)    MAX_OF_2(MAX_OF_2(a, b), c)
#define MAX_OF_4(a, b, c, d) MAX_OF_2(MAX_OF_3(a, b, c), d)
...etc up to MAX_OF_8

Ce que j'aimerais faire, c'est les remplacer par quelque chose comme ceci:

/* Base case #1, single input */
#define MAX_OF_N(x)      (x)

/* Base case #2, two inputs */
#define MAX_OF_N(x, y)   (x) > (y) ? (x) : (y)

/* Recursive definition, arbitrary number of inputs */
#define MAX_OF_N(x, ...) MAX_OF_N(x, MAX_OF_N(__VA_ARGS__))

... qui, bien sûr, n'est pas un code de préprocesseur valide.

En ignorant que ce cas particulier devrait probablement être résolu en utilisant une fonction plutôt qu'une macro de préprocesseur , est-il possible de définir une macro variadique MAX_OF_N ()?

Juste pour plus de clarté, le résultat final devrait être une macro unique qui prend un nombre arbitraire de paramètres et s’évalue au plus grand d’entre eux. J'ai le sentiment étrange que cela devrait être possible, mais je ne vois pas comment.

Était-ce utile?

La solution

Non, car le préprocesseur ne prend qu'un "balayage". au fichier. Il n'y a aucun moyen de le faire pour définir de manière récursive des macros.

Le seul code que j'ai vu faire quelque chose comme ceci n'était pas variadique, mais utilisait les valeurs par défaut que l'utilisateur devait transmettre:

x = MAX_OF_8 (a, b, -1, -1, -1, -1, -1, -1)

en supposant que toutes les valeurs étaient non négatives.

Les fonctions en ligne devraient vous donner la même chose pour C ++ au moins. Comme vous le dites, il vaut probablement mieux laisser une fonction avec des arguments variables similaires à printf () .

Autres conseils

Il est possible d'écrire une macro qui évalue le nombre d'arguments avec lesquels elle est appelée. (Je ne pouvais pas trouver un lien vers l'endroit où je l'avais vu pour la première fois.) Ainsi, vous pourriez écrire MAX_OF_N () qui fonctionnerait comme vous le souhaitez, mais vous auriez encore besoin de toutes les macros numérotées jusqu'à une limite:

#define MAX_OF_1(a)         (a)         
#define MAX_OF_2(a,b)       max(a, b)

#define MAX_OF_3(a,...)    MAX_OF_2(a,MAX_OF_2(__VA_ARGS__))
#define MAX_OF_4(a,...)    MAX_OF_2(a,MAX_OF_3(__VA_ARGS__))
#define MAX_OF_5(a,...)    MAX_OF_2(a,MAX_OF_4(__VA_ARGS__))
...
#define MAX_OF_64(a,...)   MAX_OF_2(a,MAX_OF_63(__VA_ARGS__))

// NUM_ARGS(...) evaluates to the literal number of the passed-in arguments.
#define _NUM_ARGS2(X,X64,X63,X62,X61,X60,X59,X58,X57,X56,X55,X54,X53,X52,X51,X50,X49,X48,X47,X46,X45,X44,X43,X42,X41,X40,X39,X38,X37,X36,X35,X34,X33,X32,X31,X30,X29,X28,X27,X26,X25,X24,X23,X22,X21,X20,X19,X18,X17,X16,X15,X14,X13,X12,X11,X10,X9,X8,X7,X6,X5,X4,X3,X2,X1,N,...) N
#define NUM_ARGS(...) _NUM_ARGS2(0, __VA_ARGS__ ,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)

#define _MAX_OF_N3(N, ...) MAX_OF_ ## N(__VA_ARGS__)
#define _MAX_OF_N2(N, ...) _MAX_OF_N3(N, __VA_ARGS__)
#define MAX_OF_N(...)      _MAX_OF_N2(NUM_ARGS(__VA_ARGS__), __VA_ARGS__)

Maintenant, MAX_OF_N (a, b, c, d, e) sera évalué à max (a, max (b, max (c, max (d, e)))) . (J'ai testé sur gcc 4.2.1.)

Notez qu'il est essentiel que le cas de base ( MAX_OF_2 ) ne répète pas ses arguments plus d'une fois dans l'extension (c'est pourquoi j'ai mis max dans cet exemple). ). Sinon, vous doubleriez la longueur de l'extension pour chaque niveau, vous pouvez donc imaginer ce qui se passera avec 64 arguments:)

Vous pourriez envisager cette triche, car elle n’est pas récursive et ne fait pas le travail dans le pré-processeur. Et il utilise une extension GCC. Et cela ne fonctionne que pour un type. Il s’agit toutefois d’une macro MAX_OF_N variadique:

#include <iostream>
#include <algorithm>

#define MAX_OF_N(...) ({\
        int ra[] = { __VA_ARGS__ }; \
        *std::max_element(&ra[0], &ra[sizeof(ra)/sizeof(int)]); \
    })

int main() {
    int i = 12;
    std::cout << MAX_OF_N(1,3,i,6);
}

Oh oui, et à cause de l'expression de variable potentielle dans la liste des initialiseurs, je ne pense pas qu'un équivalent à cela (en utilisant sa propre fonction pour éviter std :: max_element) fonctionnerait en C89. Mais je ne suis pas sûr que les macros variadiques soient dans C89 non plus.

Voici quelque chose qui, selon moi, contourne le "seul type". restriction. Cela devient un peu poilu, cependant:

#include <iostream>
#include <algorithm>

#define MAX_OF_N(x, ...) ({\
        typeof(x) ra[] = { (x), __VA_ARGS__ }; \
        *std::max_element(&ra[0], &ra[sizeof(ra)/sizeof(ra[0])]); \
    })

int main() {
    int i = 12;
    std::cout << MAX_OF_N(i+1,1,3,6,i);
}

Je pense que même si vous pouviez développer des macros de manière récursive, votre approche présenterait un petit problème d'efficacité: lorsque les macros sont développées, si le MAX_OF_ [N-1] est supérieur, vous devez l’évaluer à nouveau.

Voici une réponse stupide et stupide selon laquelle personne ne voudra probablement xD

fichier "source.c"

#include "my_macros.h"
...

fichier "Makefile"

myprogram: source.c my_macros.h
 gcc source.c -o myprogram

my_macros.h: make_macros.py
 python make_macros.py > my_macros.h

fichier "make_macros.py"

def split(l):
    n = len(l)
    return l[:n/2], l[n/2:]

def gen_param_seq(n):
    return [chr(i + ord("A")) for i in range(n)]

def make_max(a, b):
    if len(a) == 1:
        parta = "("+a[0]+")"
    else:
        parta = make_max(*split(a))

    if len(b) == 1:
        partb = "("+b[0]+")"
    else:
        partb = make_max(*split(b))

    return "("+parta +">"+partb+"?"+parta+":"+partb+")"

for i in range(2, 9):
    p = gen_param_seq(i)
    print "#define MAX_"+str(i)+"("+", ".join(p)+") "+make_max(*split(p))

alors vous aurez ces jolies macros définies:

#define MAX_2(A, B) ((A)>(B)?(A):(B))
#define MAX_3(A, B, C) ((A)>((B)>(C)?(B):(C))?(A):((B)>(C)?(B):(C)))
#define MAX_4(A, B, C, D) (((A)>(B)?(A):(B))>((C)>(D)?(C):(D))?((A)>(B)?(A):(B)):((C)>(D)?(C):(D)))
#define MAX_5(A, B, C, D, E) (((A)>(B)?(A):(B))>((C)>((D)>(E)?(D):(E))?(C):((D)>(E)?(D):(E)))?((A)>(B)?(A):(B)):((C)>((D)>(E)?(D):(E))?(C):((D)>(E)?(D):(E))))
#define MAX_6(A, B, C, D, E, F) (((A)>((B)>(C)?(B):(C))?(A):((B)>(C)?(B):(C)))>((D)>((E)>(F)?(E):(F))?(D):((E)>(F)?(E):(F)))?((A)>((B)>(C)?(B):(C))?(A):((B)>(C)?(B):(C))):((D)>((E)>(F)?(E):(F))?(D):((E)>(F)?(E):(F))))
#define MAX_7(A, B, C, D, E, F, G) (((A)>((B)>(C)?(B):(C))?(A):((B)>(C)?(B):(C)))>(((D)>(E)?(D):(E))>((F)>(G)?(F):(G))?((D)>(E)?(D):(E)):((F)>(G)?(F):(G)))?((A)>((B)>(C)?(B):(C))?(A):((B)>(C)?(B):(C))):(((D)>(E)?(D):(E))>((F)>(G)?(F):(G))?((D)>(E)?(D):(E)):((F)>(G)?(F):(G))))
#define MAX_8(A, B, C, D, E, F, G, H) ((((A)>(B)?(A):(B))>((C)>(D)?(C):(D))?((A)>(B)?(A):(B)):((C)>(D)?(C):(D)))>(((E)>(F)?(E):(F))>((G)>(H)?(G):(H))?((E)>(F)?(E):(F)):((G)>(H)?(G):(H)))?(((A)>(B)?(A):(B))>((C)>(D)?(C):(D))?((A)>(B)?(A):(B)):((C)>(D)?(C):(D))):(((E)>(F)?(E):(F))>((G)>(H)?(G):(H))?((E)>(F)?(E):(F)):((G)>(H)?(G):(H))))

et la meilleure chose à ce sujet est que ... ça marche ^ _ ^

Si vous suivez cette voie en C ++, consultez métaprogrammation des modèles . Ce n'est pas joli et cela ne résoudra peut-être pas votre problème, mais il gérera la récursivité.

Premièrement, les macros ne se développent pas de manière récurrente. Cependant, les macros peuvent avoir une réentrance en créant une macro pour chaque niveau de récursivité, puis en déduisant le niveau de récursivité. Cependant, toute cette répétition et déduction de la récursion est prise en charge par le Boost.Preprocessor bibliothèque. Vous pouvez donc utiliser la macro de rang supérieur pour calculer le max:

#define MAX_EACH(s, x, y) BOOST_PP_IF(BOOST_PP_GREATER_EQUAL(x, y), x, y)
#define MAX(...) BOOST_PP_SEQ_FOLD_LEFT(MAX_EACH, 0, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) 

MAX(3, 6, 8) //Outputs 8
MAX(4, 5, 9, 2) //Outputs 9

Maintenant, cela comprendra les nombres littéraux compris entre 0 et 256. Cela ne fonctionnera pas avec les variables ou expressions C ++, car le préprocesseur C ne comprend pas le C ++. C'est juste du remplacement de texte pur. Mais C ++ fournit une fonctionnalité appelée "fonction". cela fonctionnera sur les expressions C ++ et vous pourrez l’utiliser pour calculer la valeur maximale.

template<class T>
T max(T x, T y)
{
    return x > y ? x : y;
}

template<class X, class... T>
auto max(X x, T ... args) -> decltype(max(x, max(args...)))
{
    return max(x, max(args...));
}

Maintenant, le code ci-dessus nécessite un compilateur C ++ 11. Si vous utilisez C ++ 03, vous pouvez créer plusieurs surcharges de la fonction afin de simuler des paramètres variadiques. De plus, nous pouvons utiliser le préprocesseur pour générer ce code répétitif (c'est pour ça qu'il est là). Donc, en C ++ 03, vous pouvez écrire ceci:

template<class T>
T max(T x, T y)
{
    return x > y ? x : y;
}

#define MAX_FUNCTION(z, n, data) \
template<class T> \
T max(T x, BOOST_PP_ENUM_PARAMS(n, T x)) \
{ \
    return max(x, max(BOOST_PP_ENUM_PARAMS(n, x)));\
}

BOOST_PP_REPEAT_FROM_TO(2, 64, MAX_FUNCTION, ~) 
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top