Domanda

Ho riscontrato un piccolo problema teorico. In un pezzo di codice che sto mantenendo c'è un insieme di macro come

#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

Quello che mi piacerebbe fare è sostituirli con qualcosa del genere:

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

... che, ovviamente, non è un codice preprocessore valido.

Ignorando che questo caso particolare dovrebbe probabilmente essere risolto usando una funzione piuttosto che una macro preprocessore , è possibile definire una macro MAX_OF_N () variadica?

Solo per chiarezza, il risultato finale dovrebbe essere una singola macro che accetta un numero arbitrario di parametri e valuta il più grande di essi. Ho la strana sensazione che ciò dovrebbe essere possibile, ma non vedo come.

È stato utile?

Soluzione

No, perché il preprocessore richiede solo uno "scorrimento". al file. Non c'è modo di convincerlo a definire ricorsivamente le macro.

L'unico codice che ho visto fare qualcosa del genere era non variadic, ma utilizzava valori predefiniti che l'utente doveva superare:

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

supponendo che tutti i valori fossero non negativi.

Le funzioni in linea dovrebbero darti almeno lo stesso per C ++. Come dici tu, probabilmente è meglio lasciare una funzione con argomenti variabili simili a printf () .

Altri suggerimenti

È possibile scrivere una macro che valuti il ??numero di argomenti con cui viene chiamata. (Non sono riuscito a trovare un link al luogo in cui l'ho visto per la prima volta.) Quindi potresti scrivere MAX_OF_N () che funzionerebbe come desideri, ma avresti comunque bisogno di tutte le macro numerate fino a un certo 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__)

Ora MAX_OF_N (a, b, c, d, e) valuterà max (a, max (b, max (c, max (d, e)))) . (Ho testato su gcc 4.2.1.)

Nota che è fondamentale che il caso base ( MAX_OF_2 ) non ripeta i suoi argomenti più di una volta nell'espansione (motivo per cui ho inserito max in questo esempio ). Altrimenti, raddoppieresti la lunghezza dell'espansione per ogni livello, quindi puoi immaginare cosa accadrà con 64 argomenti :)

Potresti considerare questo imbroglio, poiché non è ricorsivo e non fa il lavoro nel preprocessore. E utilizza un'estensione GCC. E funziona solo per un tipo. È, tuttavia, una macro MAX_OF_N variadica:

#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ì, e a causa della potenziale espressione di variabili nell'elenco di inizializzatori, non penso che un equivalente di questo (usando la propria funzione per evitare std :: max_element) funzionerebbe in C89. Ma non sono sicuro che le macro variadiche siano anche in C89.

Ecco qualcosa che penso aggira il " solo un tipo " restrizione. Sta diventando un po 'peloso, però:

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

Penso che, anche se potessi espandere le macro in modo ricorsivo, ci sarebbe un piccolo problema con il tuo approccio in termini di efficienza ... quando le macro vengono espanse, se MAX_OF_ [N-1] è maggiore, quindi è necessario valutarlo di nuovo da zero.

Ecco una risposta stupida e stupida che probabilmente a nessuno piacerà xD

file " source.c "

#include "my_macros.h"
...

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

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

allora avrai quelle belle macro definite:

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

e la cosa migliore è che ... funziona ^ _ ^

Se stai percorrendo questa strada in C ++, dai un'occhiata a metaprogrammazione dei modelli . Non è carino e potrebbe non risolvere il problema esatto, ma gestirà la ricorsione.

Innanzitutto, le macro non si espandono in modo ricusivo. Tuttavia, le macro possono rientrare creando una macro per ciascun livello di ricorsione e quindi deducendo il livello di ricorsione. Tuttavia, tutta questa ripetizione e deducendo la ricorsione, è gestita da Boost.Preprocessor . È quindi possibile utilizzare la macro di piega di ordine superiore per calcolare il massimo:

#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

Ora, questo comprenderà i numeri letterali tra 0-256. Non funzionerà su variabili o espressioni C ++, perché il preprocessore C non capisce C ++. È solo pura sostituzione del testo. Ma C ++ fornisce una funzione chiamata "funzione" che funzionerà su espressioni C ++ e puoi usarlo per calcolare il valore massimo.

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

Ora, il codice sopra richiede un compilatore C ++ 11. Se si utilizza C ++ 03, è possibile creare più sovraccarichi della funzione al fine di simulare i parametri variadici. Inoltre, possiamo usare il preprocessore per generare questo codice ripetitivo per noi (ecco a cosa serve). Quindi in C ++ 03, puoi scrivere questo:

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, ~) 
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top