Le macro possono essere sovraccaricate dal numero di argomenti?

Come funziona? Come può essere implementata una macro variadica C99 / C ++ 11 per espandersi a cose diverse sulla sola base di quanti argomenti vengono dati ad esso?

(Modifica: vedere la fine per una soluzione pronta).

Per ottenere una macro sovraccaricata, prima abbiamo bisogno di una macro che selezioni tra diverse implementazioni. Questa parte non usa una macro variadica. Quindi una macro variadica che conta genericamente i suoi argomenti produce un selettore. Il collegamento del conteggio degli argomenti a un dispatcher produce una macro sovraccaricata.

Avvertenza: questo sistema non può dire la differenza tra zero e uno degli argomenti perché non c’è differenza tra nessun argomento e un singolo argomento vuoto. Sembrano entrambi MACRO() .


Per selezionare tra le implementazioni, utilizzare l’operatore di catenation delle macro con una serie di macro simili alle funzioni.

 #define select( selector, ... ) impl ## _ ## selector( __VA_ARGS__ ) #define impl_1() meh #define impl_2( abc, xyz ) # abc "wizza" xyz() //etc // usage: select( 1 ) => impl_1() => meh // select( 2, huz, bar ) => impl_2( huzza, bar ) => "huz" "wizza" bar() 

Poiché l’operatore ## sopprime l’espansione della macro dei suoi argomenti, è meglio racchiuderlo in un’altra macro.

 #define CAT( A, B ) A ## B #define SELECT( NAME, NUM ) CAT( NAME ## _, NUM ) 

Per contare gli argomenti, usa __VA_ARGS__ per spostare gli argomenti in questo modo (questa è la parte più intelligente):

 #define GET_COUNT( _1, _2, _3, _4, _5, _6 /* ad nauseam */, COUNT, ... ) COUNT #define VA_SIZE( ... ) GET_COUNT( __VA_ARGS__, 6, 5, 4, 3, 2, 1 ) 

Codice libreria:

 #define CAT( A, B ) A ## B #define SELECT( NAME, NUM ) CAT( NAME ## _, NUM ) #define GET_COUNT( _1, _2, _3, _4, _5, _6 /* ad nauseam */, COUNT, ... ) COUNT #define VA_SIZE( ... ) GET_COUNT( __VA_ARGS__, 6, 5, 4, 3, 2, 1 ) #define VA_SELECT( NAME, ... ) SELECT( NAME, VA_SIZE(__VA_ARGS__) )(__VA_ARGS__) 

Uso:

 #define MY_OVERLOADED( ... ) VA_SELECT( MY_OVERLOADED, __VA_ARGS__ ) #define MY_OVERLOADED_1( X ) foo< X > #define MY_OVERLOADED_2( X, Y ) bar< X >( Y ) #define MY_OVERLOADED_3( X, Y, Z ) bang_ ## X< Y >.Z() 

Vorrei postare questo come commento al post di Potatoswatter, ma è troppo lungo e richiede un elenco di codici.

Ecco un po ‘di codice perl per generare un set di macro che sono pensate per essere macro sovraccaricate.

 $ perl -le 'map{ $arity = $_; map { $ar = 2 + $arity + $_; $arm = $ar - 1; $arlist = join("", map{"A$_, "} 1..$arity); $warlist = "WHAT, $arlist"; @li = map {"_$_"} 0..$_; $lis = join(", ", @li); $lim = pop @li; $lims = join(", ", @li); print "#define FEI_${arity}A_$ar($warlist$lis) FEI_${arity}A_$arm($warlist$lims) WHAT($_, $arlist$lim)" } 1..3; print "" } 0..4' 

Ecco l’output della sceneggiatura:

 #define FEI_0A_3(WHAT, _0, _1) FEI_0A_2(WHAT, _0) WHAT(1, _1) #define FEI_0A_4(WHAT, _0, _1, _2) FEI_0A_3(WHAT, _0, _1) WHAT(2, _2) #define FEI_0A_5(WHAT, _0, _1, _2, _3) FEI_0A_4(WHAT, _0, _1, _2) WHAT(3, _3) #define FEI_1A_4(WHAT, A1, _0, _1) FEI_1A_3(WHAT, A1, _0) WHAT(1, A1, _1) #define FEI_1A_5(WHAT, A1, _0, _1, _2) FEI_1A_4(WHAT, A1, _0, _1) WHAT(2, A1, _2) #define FEI_1A_6(WHAT, A1, _0, _1, _2, _3) FEI_1A_5(WHAT, A1, _0, _1, _2) WHAT(3, A1, _3) #define FEI_2A_5(WHAT, A1, A2, _0, _1) FEI_2A_4(WHAT, A1, A2, _0) WHAT(1, A1, A2, _1) #define FEI_2A_6(WHAT, A1, A2, _0, _1, _2) FEI_2A_5(WHAT, A1, A2, _0, _1) WHAT(2, A1, A2, _2) #define FEI_2A_7(WHAT, A1, A2, _0, _1, _2, _3) FEI_2A_6(WHAT, A1, A2, _0, _1, _2) WHAT(3, A1, A2, _3) #define FEI_3A_6(WHAT, A1, A2, A3, _0, _1) FEI_3A_5(WHAT, A1, A2, A3, _0) WHAT(1, A1, A2, A3, _1) #define FEI_3A_7(WHAT, A1, A2, A3, _0, _1, _2) FEI_3A_6(WHAT, A1, A2, A3, _0, _1) WHAT(2, A1, A2, A3, _2) #define FEI_3A_8(WHAT, A1, A2, A3, _0, _1, _2, _3) FEI_3A_7(WHAT, A1, A2, A3, _0, _1, _2) WHAT(3, A1, A2, A3, _3) #define FEI_4A_7(WHAT, A1, A2, A3, A4, _0, _1) FEI_4A_6(WHAT, A1, A2, A3, A4, _0) WHAT(1, A1, A2, A3, A4, _1) #define FEI_4A_8(WHAT, A1, A2, A3, A4, _0, _1, _2) FEI_4A_7(WHAT, A1, A2, A3, A4, _0, _1) WHAT(2, A1, A2, A3, A4, _2) #define FEI_4A_9(WHAT, A1, A2, A3, A4, _0, _1, _2, _3) FEI_4A_8(WHAT, A1, A2, A3, A4, _0, _1, _2) WHAT(3, A1, A2, A3, A4, _3) 

Queste sono le sezioni (regolarmente strutturate) di macro overload che vengono utilizzate per generare FOR_EACH (aka FE ) che possono inviare una macro WHAT facoltativamente con un numero arbitrario di argomenti costanti ( A1 , A2 …) oltre a un numero arbitrario di argomenti in una lista, insieme a un indice nell’ordinamento corretto (un’implementazione ingenua senza l’utilizzo di qualcosa come SELECT per il sovraccarico produrrebbe indici invertiti).

Ad esempio, la sezione rimanente (la parte nonregolare “caso base” del secondo blocco) ha il seguente aspetto:

 #define FE_INDEXED_1ARG(...) VA_SELECT(FEI_1A, __VA_ARGS__) #define FEI_1A_3(WHAT, A1, _0) WHAT(0, A1, _0) 

L’utilità di questo può forse essere messa in discussione (l’ho costruita perché ho visto un suo uso …), e nemmeno questo risponde direttamente alla domanda dell’OP (in effetti, fa l’opposto – un costrutto foreach fa la stessa cosa con tutti gli argomenti variadici …), ma ho solo pensato che la tecnica sia piuttosto interessante (oltre che orribilmente in qualche modo) e che permetta un po ‘di potenza espressiva usando il preprocessore e sarà ansible generare codice macchina molto efficiente in questo modo. Penso che serva anche come un esempio significativo del perché personalmente ritengo che il preprocessore C abbia ancora margini di miglioramento.

Con ciò intendo che il preprocessore C è un abominio assoluto e dovremmo probabilmente eliminarlo e ricominciare da capo 🙂

Segue un miglioramento rispetto alla risposta di Potatoswatter , che può differenziare tra zero e un argomento.

In poche parole, quando __VA_ARGS__ è vuoto, EXPAND __VA_ARGS__ () all’interno VA_SIZE macro VA_SIZE diventa EXPAND () e viene sostituito con 6 virgole. Quindi, VA_SIZE... diventa COMPOSE( GET_COUNT, (,,,,,, , 0, 6, 5, 4, 3, 2, 1) ) e diventa GET_COUNT (,,,,,, , 0, 6, 5, 4, 3, 2, 1) e restituisce 0.

D’altra parte, quando __VA_ARGS__ è eg, int, 5 , EXPAND __VA_ARGS__ () diventa EXPAND int, 5 () . Quindi, VA_SIZE... diventa COMPOSE( GET_COUNT, (EXPAND int, 5 (), 0, 6, 5, 4, 3, 2, 1) ) , che diventa GET_COUNT (EXPAND int, 5 (), 0, 6, 5, 4, 3, 2, 1) e restituisce 2, come descritto nella risposta di Potatoswatter.

Ho avuto l’idea EXPAND dalla risposta di Jason Dang .

Codice libreria:

 #define CAT( A, B ) A ## B #define SELECT( NAME, NUM ) CAT( NAME ## _, NUM ) #define COMPOSE( NAME, ARGS ) NAME ARGS #define GET_COUNT( _0, _1, _2, _3, _4, _5, _6 /* ad nauseam */, COUNT, ... ) COUNT #define EXPAND() ,,,,,, // 6 commas (or 7 empty tokens) #define VA_SIZE( ... ) COMPOSE( GET_COUNT, (EXPAND __VA_ARGS__ (), 0, 6, 5, 4, 3, 2, 1) ) #define VA_SELECT( NAME, ... ) SELECT( NAME, VA_SIZE(__VA_ARGS__) )(__VA_ARGS__) 

Uso:

 #define MY_OVERLOADED( ... ) VA_SELECT( MY_OVERLOADED, __VA_ARGS__ ) #define MY_OVERLOADED_0( ) meh() #define MY_OVERLOADED_1( X ) foo< X > #define MY_OVERLOADED_2( X, Y ) bar< X >( Y ) #define MY_OVERLOADED_3( X, Y, Z ) bang_ ## X< Y >.Z() MY_OVERLOADED() // meh() MY_OVERLOADED(bool) // foo< bool > MY_OVERLOADED(int, 5) // bar< int >( 5 ) MY_OVERLOADED(me, double, now) // bang_me< double >.now() 

Sebbene abbia già risposto, ne ho preparato una versione molto breve. Spero che possa essere d’aiuto.

Implementazione

 // Variable Argument Macro (VA_MACRO) upto 6 arguments #define NUM_ARGS_(_1, _2, _3, _4, _5, _6, TOTAL, ...) TOTAL #define NUM_ARGS(...) NUM_ARGS_(__VA_ARGS__, 6, 5, 4, 3, 2, 1) #define CONCATE_(X, Y) X##Y // Fixed the double '_' from previous code #define CONCATE(MACRO, NUMBER) CONCATE_(MACRO, NUMBER) #define VA_MACRO(MACRO, ...) CONCATE(MACRO, NUM_ARGS(__VA_ARGS__))(__VA_ARGS__) 

Personalizzazione

 // This is how user may define own set of variadic macros #define MY_MACRO(...) VA_MACRO(MY_MACRO, __VA_ARGS__) #define MY_MACRO1(_1) "One" #define MY_MACRO2(_1, _2) "Two" #define MY_MACRO3(_1, _2, _3) "Three" 

uso

 // While using those, user needs to use only the main macro int main () { auto one = MY_MACRO(1); auto two = MY_MACRO(1, 2); auto three = MY_MACRO(1, 2, 3); }