Pregunta

Me he encontrado con un pequeño problema teórico. En un fragmento de código que estoy manteniendo hay un conjunto de macros como

#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

Lo que me gustaría hacer es reemplazarlos con algo como esto:

/* 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__))

... que, por supuesto, no es un código de preprocesador válido.

Si ignora que este caso en particular probablemente debería resolverse usando una función en lugar de una macro preprocesadora , ¿es posible definir una macro variable MAX_OF_N ()?

Para mayor claridad, el resultado final debe ser una macro única que tome un número arbitrario de parámetros y evalúe al más grande de ellos. Tengo la extraña sensación de que esto debería ser posible, pero no veo cómo.

¿Fue útil?

Solución

No, porque el preprocesador solo toma un " deslizar " en el archivo. No hay forma de definir macros de forma recursiva.

El único código que he visto hacer algo como esto era no variadic, pero usaba valores predeterminados que el usuario tenía que pasar:

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

asumiendo que todos los valores no son negativos.

Las funciones en línea deberían darte lo mismo para C ++ al menos. Como indicas, es mejor dejar una función con argumentos variables similares a printf () .

Otros consejos

Es posible escribir una macro que evalúe la cantidad de argumentos con los que se llama. (No pude encontrar un enlace al lugar donde lo vi por primera vez). Entonces, podría escribir MAX_OF_N () que funcionaría como quisiera, pero aún necesitaría todas las macros numeradas hasta algún límite:

#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__)

Ahora MAX_OF_N (a, b, c, d, e) evaluará a max (a, max (b, max (c, max (d, e)))) . (He probado en gcc 4.2.1.)

Tenga en cuenta que es fundamental que el caso base ( MAX_OF_2 ) no repita sus argumentos más de una vez en la expansión (por lo que puse max en este ejemplo) ). De lo contrario, estaría duplicando la duración de la expansión para cada nivel, por lo que puede imaginar lo que sucederá con 64 argumentos :)

Puede considerar este engaño, ya que no es recursivo y no hace el trabajo en el preprocesador. Y utiliza una extensión GCC. Y solo funciona para un tipo. Es, sin embargo, una variable variable MAX_OF_N macro:

#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, sí, y debido a la expresión de variable potencial en la lista de inicializadores, no creo que un equivalente de esto (usar su propia función para evitar std :: max_element) funcione en C89. Pero tampoco estoy seguro de que las macros variables estén en C89.

Aquí hay algo que creo que se relaciona con el " solo un tipo " restricción. Sin embargo, se está poniendo un poco peludo:

#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);
}

Creo que, incluso si pudiera expandir macros recursivamente, habría un pequeño problema con su enfoque en términos de eficiencia ... cuando las macros se expanden, si el MAX_OF_ [N-1] es mayor, entonces debes evaluarlo nuevamente desde cero.

Aquí hay una respuesta tonta y estúpida que probablemente a nadie le gustará xD

archivo " source.c "

#include "my_macros.h"
...

archivo " 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

archivo " 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))

entonces tendrás esas macros bonitas definidas:

#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))))

y lo mejor de todo es que ... funciona ^ _ ^

Si vas por este camino en C ++, echa un vistazo a plantilla de metaprogramación . No es bonito, y puede que no resuelva el problema exacto, pero manejará la recursión.

Primero, las macros no se expanden recusrsivamente. Aunque, las macros pueden volver a entrar creando una macro para cada nivel de recursión y luego deduciendo el nivel de recursión. Sin embargo, toda esta repetición y deducción de la recursión se realiza mediante Boost.Preprocessor biblioteca. Por lo tanto, puede utilizar la macro de plegado de orden superior para calcular el máximo:

#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

Ahora, esto entenderá los números literales entre 0-256. No funcionará en las variables o expresiones de C ++, porque el preprocesador de C no entiende C ++. Es sólo un reemplazo de texto puro. Pero C ++ proporciona una función llamada función " " eso funcionará en las expresiones de C ++, y puede usarlo para calcular el valor máximo.

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...));
}

Ahora, el código anterior requiere un compilador de C ++ 11. Si está utilizando C ++ 03, puede crear múltiples sobrecargas de la función para simular parámetros variadic. Además, podemos usar el preprocesador para generar este código repetitivo para nosotros (eso es para lo que está ahí). Así que en C ++ 03, puedes escribir esto:

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, ~) 
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top