Parametri opzionali con macro C ++


105

C'è un modo per ottenere parametri opzionali con le macro C ++? Anche una sorta di sovraccarico sarebbe bello.


1
Lo stesso vale per C: stackoverflow.com/questions/11761703/... dovrebbe essere lo stesso dal momento che i preprocessori sono fondamentalmente gli stessi: stackoverflow.com/questions/5085533/...
Ciro Santilli郝海东冠状病六四事件法轮功

Forse i sovraccarichi di funzioni, i parametri predefiniti, i modelli variadici o forse l'idioma del parametro denominato sono ciò che stai cercando
smoothware

Si prega di aggiornare la risposta selezionata a quelle altamente votate con soluzioni effettive, non quella umile che diceNo you can't
Albert Renshaw

Risposte:


156

Ecco un modo per farlo. Utilizza l'elenco di argomenti due volte, prima per formare il nome della macro di supporto e quindi per passare gli argomenti a quella macro di supporto. Utilizza un trucco standard per contare il numero di argomenti in una macro.

enum
{
    plain = 0,
    bold = 1,
    italic = 2
};

void PrintString(const char* message, int size, int style)
{
}

#define PRINT_STRING_1_ARGS(message)              PrintString(message, 0, 0)
#define PRINT_STRING_2_ARGS(message, size)        PrintString(message, size, 0)
#define PRINT_STRING_3_ARGS(message, size, style) PrintString(message, size, style)

#define GET_4TH_ARG(arg1, arg2, arg3, arg4, ...) arg4
#define PRINT_STRING_MACRO_CHOOSER(...) \
    GET_4TH_ARG(__VA_ARGS__, PRINT_STRING_3_ARGS, \
                PRINT_STRING_2_ARGS, PRINT_STRING_1_ARGS, )

#define PRINT_STRING(...) PRINT_STRING_MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)

int main(int argc, char * const argv[])
{
    PRINT_STRING("Hello, World!");
    PRINT_STRING("Hello, World!", 18);
    PRINT_STRING("Hello, World!", 18, bold);

    return 0;
}

Ciò semplifica il chiamante della macro, ma non l'autore.


1
Questo è abbastanza interessante, ma non credo che funzionerebbe se facessi PRINT_STRING. In tal caso non ci sarebbe una stampa predefinita (e questo è effettivamente il caso che voglio utilizzare). Ancora +1 per davvero fantastico.
Cenoc

2
funziona per me in gcc (ed è molto intelligente!) :-) ma non funziona per me in Visual Studio :-(
Tim Gradwell

3
@TimGradwell - è dovuto a un bug nel compilatore MSVC che hanno riconosciuto ma non risolto da quasi un decennio. Tuttavia, sono disponibili soluzioni alternative .
BeeOnRope

Intelligente, ma non funziona per gli argomenti macro variadici opzionali a causa del "push out" che stai facendo in `GET_4th_ARG '.
searchengine27

è PRINT_STRING_MACRO_CHOOSERanche necessario? Posso sostituire direttamente con il suo corpo interno e chiamare tutta questa cosa con (__VA_ARGS__)?
Herrgott

85

Con grande rispetto a Derek Ledbetter per la sua risposta e con scuse per aver ripreso una vecchia domanda.

Comprendere ciò che stava facendo e cogliere altrove la capacità di precedere il __VA_ARGS__con ##mi ha permesso di trovare una variazione ...

// The multiple macros that you would need anyway [as per: Crazy Eddie]
#define XXX_0()                     <code for no arguments> 
#define XXX_1(A)                    <code for one argument> 
#define XXX_2(A,B)                  <code for two arguments> 
#define XXX_3(A,B,C)                <code for three arguments> 
#define XXX_4(A,B,C,D)              <code for four arguments>  

// The interim macro that simply strips the excess and ends up with the required macro
#define XXX_X(x,A,B,C,D,FUNC, ...)  FUNC  

// The macro that the programmer uses 
#define XXX(...)                    XXX_X(,##__VA_ARGS__,\
                                          XXX_4(__VA_ARGS__),\
                                          XXX_3(__VA_ARGS__),\
                                          XXX_2(__VA_ARGS__),\
                                          XXX_1(__VA_ARGS__),\
                                          XXX_0(__VA_ARGS__)\
                                         ) 

Per i non esperti come me che si imbattono nella risposta, ma non riescono a vedere come funziona, passerò attraverso l'effettiva elaborazione, a partire dal seguente codice ...

XXX();
XXX(1); 
XXX(1,2); 
XXX(1,2,3); 
XXX(1,2,3,4); 
XXX(1,2,3,4,5);      // Not actually valid, but included to show the process 

Diventa ...

XXX_X(, XXX_4(), XXX_3(),  XXX_2(),    XXX_1(),      XXX_0()         );
XXX_X(, 1,       XXX_4(1), XXX_3(1),   XXX_2(1),     XXX_1(1),       XXX_0(1)          );
XXX_X(, 1,       2,        XXX_4(1,2), XXX_3(1,2),   XXX_2(1,2),     XXX_1(1,2),       XXX_0(1,2)        );
XXX_X(, 1,       2,        3,          XXX_4(1,2,3), XXX_3(1,2,3),   XXX_2(1,2,3),     XXX_1(1,2,3),     XXX_0(1,2,3)      );
XXX_X(, 1,       2,        3,          4,            XXX_4(1,2,3,4), XXX_3(1,2,3,4),   XXX_2(1,2,3,4),   XXX_1(1,2,3,4),   XXX_0(1,2,3,4)    );
XXX_X(, 1,       2,        3,          4,            5,              XXX_4(1,2,3,4,5), XXX_3(1,2,3,4,5), XXX_2(1,2,3,4,5), XXX_1(1,2,3,4,5), XXX_0(1,2,3,4,5) );

Che diventa solo il sesto argomento ...

XXX_0(); 
XXX_1(1); 
XXX_2(1,2); 
XXX_3(1,2,3); 
XXX_4(1,2,3,4); 
5; 

PS: rimuovi #define per XXX_0 per ottenere un errore di compilazione [es .: se un'opzione senza argomenti non è consentita].

PPS: Sarebbe bello che le situazioni non valide (es .: 5) fossero qualcosa che fornisse un errore di compilazione più chiaro al programmatore!

PPPS: non sono un esperto, quindi sono molto felice di sentire commenti (buoni, cattivi o altro)!


3
Potresti ottenere un chiaro errore di compilazione se hai convertito l'argomento selezionato che dovrebbe essere un nome MACRO in una stringa usando # (il cancelletto) e hai confrontato i suoi primi n caratteri con il prefisso previsto e se non c'è corrispondenza, hai stampato un'informativa errore.
AturSams

1
Wow, non so se funziona, ma almeno è molto creativo!
Espiazione limitata,

4
perché il primo argomento è sempre vuoto? perché non possiamo semplicemente ometterlo: XXX_X(,##__VA_ARGS__,` ... XXX_X (, XXX_4 (), XXX_3 (), XXX_2 (), XXX_1 (), XXX_0 ()); `
rahman

2
Il primo argomento vuoto (virgola) è importante. ## __ VA_ARGS__ se è preceduto da una virgola - rimuove la virgola se ## __ VA_ARGS__ non si espande in nulla. Puoi vederlo nell'esempio "Diventa ..." poiché la prima riga (senza argomenti) ha solo 6 parametri ma il resto ne ottiene 7. Questo trucco garantisce che la situazione senza argomenti
funzioni

@Eric - è dovuto a un bug nei compilatori microsoft, ma puoi vedere questa domanda per soluzioni alternative.
BeeOnRope

31

Le macro C ++ non sono cambiate da C. Dal momento che C non aveva argomenti di sovraccarico e predefiniti per le funzioni, certamente non li aveva per le macro. Quindi, per rispondere alla tua domanda: no, queste funzionalità non esistono per le macro. L'unica opzione è definire più macro con nomi diversi (o non utilizzare affatto le macro).

Come nota a margine: in C ++ è generalmente considerata una buona pratica allontanarsi il più possibile dalle macro. Se hai bisogno di funzionalità come questa, ci sono buone probabilità che stai facendo un uso eccessivo delle macro.


4
Si noti che il motivo per cui è impossibile "sovraccaricare" le macro è perché non hanno alcun tipo intrinseco. Le macro vengono semplicemente espanse.
mk12

2
Anche se io uso le macro il meno possibile, ho scoperto che il debug tramite output di traccia diventa un po 'più facile con le cose come __FILE__e __LINE__e tali ...
Christian Severin

non è una buona risposta. questa è una buona risposta: stackoverflow.com/q/27049491/893406
v.oddou

Compilazione condizionale e debug / registrazione è il dominio in cui le macro sono davvero utili e legittime. Ogni programmatore serio lo sa. La buona pratica è evitare di usare le macro per definire le costanti e fare alcune cose folli di codifica di livello C per creare modelli di contenitori. Vorrei che anche il C ++ aggiungesse più funzionalità alle macro. Sono ortogonali ai modelli. I migliori, naturalmente, sarebbero i codelets che mi consentono di aggiungere generatori nel compilatore per linguaggi (aspetti) specifici del dominio.
Lothar

1
Penso anche che questa non sia una buona risposta, perché una macro è qualcosa di completamente diverso da qualsiasi opzione del linguaggio C ++, perché verrà gestita PRIMA del compilatore. Quindi puoi fare altre cose e nessun compilatore o linker deve ottimizzare il codice, perché forse non è da ottimizzare.
alabamajack

26

Con il massimo rispetto a Derek Ledbetter , David Sorkovsky , Syphorlate per le loro risposte, insieme al metodo ingegnoso per rilevare macro argomenti vuoti di Jens Gustedt a

https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/

finalmente esco con qualcosa che incorpora tutti i trucchi, in modo che la soluzione

  1. Utilizza solo macro C99 standard per ottenere il sovraccarico delle funzioni, nessuna estensione GCC / CLANG / MSVC coinvolta (ad esempio, deglutizione virgola dall'espressione specifica , ##__VA_ARGS__per GCC / CLANG e deglutizione implicita da ##__VA_ARGS__per MSVC). Quindi sentiti libero di passare ciò che manca --std=c99al tuo compilatore se lo desideri =)
  2. Funziona per zero argomenti , così come per un numero illimitato di argomenti , se lo espandi ulteriormente per soddisfare le tue esigenze
  3. Funziona ragionevolmente multipiattaforma , almeno testato per

    • GNU / Linux + GCC (GCC 4.9.2 su CentOS 7.0 x86_64)
    • GNU / Linux + CLANG / LLVM , (CLANG / LLVM 3.5.0 su CentOS 7.0 x86_64)
    • OS X + Xcode , (XCode 6.1.1 su OS X Yosemite 10.10.1)
    • Windows + Visual Studio , (Visual Studio 2013 Update 4 su Windows 7 SP1 64 bit)

Per i più pigri, vai all'ultimo di questo post per copiare la fonte. Di seguito è riportata la spiegazione dettagliata, che si spera aiuti e ispira tutte le persone che cercano le __VA_ARGS__soluzioni generali come me. =)

Ecco come va. Prima definire la "funzione" user-visibile sovraccarico, ho chiamata create, e la relativa definizione di funzione effettiva realCreate, e le definizioni di macro con diverso numero di argomenti CREATE_2, CREATE_1, CREATE_0, come mostrato di seguito:

#define create(...) MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)

void realCreate(int x, int y)
{
  printf("(%d, %d)\n", x, y);
}

#define CREATE_2(x, y) realCreate(x, y)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_0() CREATE_1(0)

La MACRO_CHOOSER(__VA_ARGS__)parte alla fine si risolve nei nomi delle definizioni delle macro e la seconda (__VA_ARGS__)parte comprende i loro elenchi di parametri. Quindi la chiamata di un utente a si create(10)risolve in CREATE_1(10), la CREATE_1parte proviene da MACRO_CHOOSER(__VA_ARGS__)e la (10)parte proviene dalla seconda(__VA_ARGS__) .

Il MACRO_CHOOSERutilizza il trucco che, se __VA_ARGS__è vuota, la seguente espressione viene concatenato in una chiamata macro valida dal preprocessore:

NO_ARG_EXPANDER __VA_ARGS__ ()  // simply shrinks to NO_ARG_EXPANDER()

Ingegnosamente, possiamo definire questa chiamata macro risultante come

#define NO_ARG_EXPANDER() ,,CREATE_0

Notare le due virgole, verranno presto spiegate. La prossima macro utile è

#define MACRO_CHOOSER(...) CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER __VA_ARGS__ ())

così le chiamate di

create();
create(10);
create(20, 20);

sono effettivamente espansi a

CHOOSE_FROM_ARG_COUNT(,,CREATE_0)();
CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER 10 ())(10);
CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER 20, 20 ())(20, 20);

Come suggerisce il nome della macro, dobbiamo contare il numero di argomenti in seguito. Ecco un altro trucco: il preprocessore fa solo una semplice sostituzione del testo. Deduce il numero di argomenti di una chiamata macro semplicemente dal numero di virgole che vede tra parentesi. Gli "argomenti" effettivi separati da virgole non devono necessariamente essere di sintassi valida. Possono essere qualsiasi testo. Vale a dire, nell'esempio sopra, NO_ARG_EXPANDER 10 ()viene conteggiato come 1 argomento per la chiamata centrale. NO_ARG_EXPANDER 20e20 () sono contati rispettivamente come 2 argomenti per l'ultima chiamata.

Se usiamo le seguenti macro di supporto per espanderle ulteriormente

##define CHOOSE_FROM_ARG_COUNT(...) \
  FUNC_RECOMPOSER((__VA_ARGS__, CREATE_2, CREATE_1, ))
#define FUNC_RECOMPOSER(argsWithParentheses) \
  FUNC_CHOOSER argsWithParentheses

Il trailing ,after CREATE_1è una soluzione per GCC / CLANG, sopprimendo un errore (falso positivo) che lo dice ISO C99 requires rest arguments to be usedquando si passa -pedantical compilatore. La FUNC_RECOMPOSERè un modo per aggirare le MSVC, o non correttamente può contare il numero di argomenti (cioè virgole) all'interno delle parentesi chiamate macro. I risultati sono ulteriormente risolti

FUNC_CHOOSER (,,CREATE_0, CREATE_2, CREATE_1, )();
FUNC_CHOOSER (NO_ARG_EXPANDER 10 (), CREATE_2, CREATE_1, )(10);
FUNC_CHOOSER (NO_ARG_EXPANDER 20, 20 (), CREATE_2, CREATE_1, )(20, 20);

Come gli occhi di aquila che potresti aver visto, l'ultimo unico passaggio di cui abbiamo bisogno è utilizzare un trucco standard per il conteggio degli argomenti per scegliere finalmente i nomi delle versioni macro desiderate:

#define FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3

che risolve i risultati in

CREATE_0();
CREATE_1(10);
CREATE_2(20, 20);

e certamente ci dà le chiamate di funzione effettive desiderate:

realCreate(0, 0);
realCreate(10, 10);
realCreate(20, 20);

Mettendo tutto insieme, con qualche riorganizzazione delle affermazioni per una migliore leggibilità, l' intera fonte dell'esempio a 2 argomenti è qui:

#include <stdio.h>

void realCreate(int x, int y)
{
  printf("(%d, %d)\n", x, y);
}

#define CREATE_2(x, y) realCreate(x, y)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_0() CREATE_1(0)

#define FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3
#define FUNC_RECOMPOSER(argsWithParentheses) FUNC_CHOOSER argsWithParentheses
#define CHOOSE_FROM_ARG_COUNT(...) FUNC_RECOMPOSER((__VA_ARGS__, CREATE_2, CREATE_1, ))
#define NO_ARG_EXPANDER() ,,CREATE_0
#define MACRO_CHOOSER(...) CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER __VA_ARGS__ ())
#define create(...) MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)

int main()
{
  create();
  create(10);
  create(20, 20);
  //create(30, 30, 30);  // Compilation error
  return 0;
}

Sebbene complicato, brutto, che appesantisca lo sviluppatore API, arriva una soluzione per sovraccaricare e impostare parametri opzionali delle funzioni C / C ++ per noi pazzi. L'utilizzo delle API sovraccariche in uscita diventa molto piacevole e piacevole. =)

Se è possibile semplificare ulteriormente questo approccio, fatemelo sapere all'indirizzo

https://github.com/jason-deng/C99FunctionOverload

Ancora un ringraziamento speciale a tutte le persone brillanti che mi hanno ispirato e portato a realizzare questo lavoro! =)


3
Come si espande questo a 3 o 4 funzioni?
Phylliida

@Phylliida ideone.com/jD0Hm5 - da zero a cinque argomenti supportati.
xx

9

Per chiunque cerchi dolorosamente qualche soluzione VA_NARGS che funzioni con Visual C ++. La seguente macro ha funzionato perfettamente (anche con zero parametri!) In visual c ++ express 2010:

#define VA_NUM_ARGS_IMPL(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,N,...) N
#define VA_NUM_ARGS_IMPL_(tuple) VA_NUM_ARGS_IMPL tuple
#define VA_NARGS(...)  bool(#__VA_ARGS__) ? (VA_NUM_ARGS_IMPL_((__VA_ARGS__, 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

Se vuoi una macro con parametri opzionali puoi fare:

//macro selection(vc++)
#define SELMACRO_IMPL(_1,_2,_3, N,...) N
#define SELMACRO_IMPL_(tuple) SELMACRO_IMPL tuple
#define mymacro1(var1) var1
#define mymacro2(var1,var2) var2*var1
#define mymacro3(var1,var2,var3) var1*var2*var3
#define mymacro(...) SELMACRO_IMPL_((__VA_ARGS__, mymacro3(__VA_ARGS__), mymacro2(__VA_ARGS__), mymacro1(__VA_ARGS__))) 

Questo ha funzionato anche per me in vc. Ma non funziona per zero parametri.

int x=99;
x=mymacro(2);//2
x=mymacro(2,2);//4
x=mymacro(2,2,2);//8

Sto ottenendounresolved external symbol _bool referenced in function _main
Avidan Borisov

sì, in alcuni casi può succedere. devi essere consapevole che bool (#__ VA_ARGS__)? è diverso dalle altre macro poiché viene valutato in fase di esecuzione. a seconda del tuo caso potresti però omettere quella parte del codice.
Syphorlate

2
In realtà sono finito con pastebin.com/H3T75dcn che funziona perfettamente (anche 0 argomenti).
Avidan Borisov

Grazie per il collegamento, e sì, puoi farlo anche usando sizeof, ma per me in alcuni casi non ha funzionato ma il principio è lo stesso (valutazione booleana).
Syphorlate

Potresti fornire alcuni esempi in cui fallisce?
Avidan Borisov


5
#include <stdio.h>

#define PP_NARG(...) \
    PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
#define PP_NARG_(...) \
    PP_ARG_N(__VA_ARGS__)
#define PP_ARG_N( \
    _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \ 
    _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
    _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
    _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
    _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
    _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
    _61,_62,_63,N,...) N
#define PP_RSEQ_N() \
    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 PP_CONCAT(a,b) PP_CONCAT_(a,b)
#define PP_CONCAT_(a,b) a ## b

#define THINK(...) PP_CONCAT(THINK_, PP_NARG(__VA_ARGS__))(__VA_ARGS__)
#define THINK_0() THINK_1("sector zz9 plural z alpha")
#define THINK_1(location) THINK_2(location, 42)
#define THINK_2(location,answer) THINK_3(location, answer, "deep thought")
#define THINK_3(location,answer,computer) \
  printf ("The answer is %d. This was calculated by %s, and a computer to figure out what this"
          " actually means will be build in %s\n", (answer), (computer), (location))

int
main (int argc, char *argv[])
{
  THINK (); /* On compilers other than GCC you have to call with least one non-default argument */
}

DISCLAIMER: Per lo più innocuo.


c'è un errore nel codice. per favore :%s/MY_MACRO_/THINK_/g:)
João Portela

inoltre, non ha funzionato con zero argomenti utilizzando g ++i686-apple-darwin10-g++-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5664)
João Portela

1
Gli argomenti zero non esistono per le macro variadiac, poiché il token vuoto è un segnaposto valido.
Paul Fultz II

3

Non è proprio ciò per cui è progettato il preprocessore.

Detto questo, se vuoi entrare nell'area della programmazione macro seriamente impegnativa con un minimo di leggibilità, dovresti dare un'occhiata alla libreria del preprocessore Boost . Dopotutto, non sarebbe C ++ se non ci fossero tre livelli di programmazione completamente compatibili con Turing (preprocessore, metaprogrammazione di template e C ++ di livello base)!


3
#define MY_MACRO_3(X,Y,Z) ...
#define MY_MACRO_2(X,Y) MY_MACRO(X,Y,5)
#define MY_MACRO_1(X) MY_MACRO(X,42,5)

Sai al momento della chiamata quanti argomenti passerai, quindi non c'è davvero bisogno di sovraccarico.


2
In realtà stavo chiedendo dell'esistenza della funzione.
Cenoc

3

Versione più concisa del codice di Derek Ledbetter:

enum
{
    plain = 0,
    bold = 1,
    italic = 2
};


void PrintString(const char* message = NULL, int size = 0, int style = 0)
{
}


#define PRINT_STRING(...) PrintString(__VA_ARGS__)


int main(int argc, char * const argv[])
{ 
    PRINT_STRING("Hello, World!");
    PRINT_STRING("Hello, World!", 18);
    PRINT_STRING("Hello, World!", 18, bold);

    return 0;
}

3

Essendo un grande fan degli orribili macro mostri, volevo espandere la risposta di Jason Deng e renderla effettivamente utilizzabile. (Nel bene o nel male.) L'originale non è molto bello da usare perché devi modificare la grande zuppa alfabetica ogni volta che vuoi creare una nuova macro ed è anche peggio se hai bisogno di una quantità diversa di argomenti.

Quindi ho realizzato una versione con queste caratteristiche:

  • Il caso di 0 argomenti funziona
  • Da 1 a 16 argomenti senza alcuna modifica alla parte disordinata
  • Facile scrivere più funzioni macro
  • Testato in gcc 10, clang 9, Visual Studio 2017

Attualmente ho fatto solo 16 argomenti al massimo, ma se ne hai bisogno di più (davvero ora? Stai diventando sciocco ...) puoi modificare FUNC_CHOOSER e CHOOSE_FROM_ARG_COUNT, quindi aggiungere alcune virgole a NO_ARG_EXPANDER.

Si prega di consultare l'eccellente risposta di Jason Deng per maggiori dettagli sull'implementazione, ma inserirò il codice qui:

#include <stdio.h>

void realCreate(int x, int y)
{
    printf("(%d, %d)\n", x, y);
}

// This part you put in some library header:
#define FUNC_CHOOSER(_f0, _f1, _f2, _f3, _f4, _f5, _f6, _f7, _f8, _f9, _f10, _f11, _f12, _f13, _f14, _f15, _f16, ...) _f16
#define FUNC_RECOMPOSER(argsWithParentheses) FUNC_CHOOSER argsWithParentheses
#define CHOOSE_FROM_ARG_COUNT(F, ...) FUNC_RECOMPOSER((__VA_ARGS__, \
            F##_16, F##_15, F##_14, F##_13, F##_12, F##_11, F##_10, F##_9, F##_8,\
            F##_7, F##_6, F##_5, F##_4, F##_3, F##_2, F##_1, ))
#define NO_ARG_EXPANDER(FUNC) ,,,,,,,,,,,,,,,,FUNC ## _0
#define MACRO_CHOOSER(FUNC, ...) CHOOSE_FROM_ARG_COUNT(FUNC, NO_ARG_EXPANDER __VA_ARGS__ (FUNC))
#define MULTI_MACRO(FUNC, ...) MACRO_CHOOSER(FUNC, __VA_ARGS__)(__VA_ARGS__)

// When you need to make a macro with default arguments, use this:
#define create(...) MULTI_MACRO(CREATE, __VA_ARGS__)
#define CREATE_0() CREATE_1(0)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_2(x, y) \
    do { \
        /* put whatever code you want in the last macro */ \
        realCreate(x, y); \
    } while(0)


int main()
{
    create();
    create(10);
    create(20, 20);
    //create(30, 30, 30);  // Compilation error
    return 0;
}

2

Puoi usare BOOST_PP_OVERLOADda una boostlibreria.

Esempio dal documento ufficiale di boost :

#include <boost/preprocessor/facilities/overload.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/facilities/empty.hpp>
#include <boost/preprocessor/arithmetic/add.hpp>

#define MACRO_1(number) MACRO_2(number,10)
#define MACRO_2(number1,number2) BOOST_PP_ADD(number1,number2)

#if !BOOST_PP_VARIADICS_MSVC

#define MACRO_ADD_NUMBERS(...) BOOST_PP_OVERLOAD(MACRO_,__VA_ARGS__)(__VA_ARGS__)

#else

// or for Visual C++

#define MACRO_ADD_NUMBERS(...) \
  BOOST_PP_CAT(BOOST_PP_OVERLOAD(MACRO_,__VA_ARGS__)(__VA_ARGS__),BOOST_PP_EMPTY())

#endif

MACRO_ADD_NUMBERS(5) // output is 15
MACRO_ADD_NUMBERS(3,6) // output is 9

0

A seconda di ciò di cui hai bisogno, potresti farlo con var args con macro. Ora, parametri opzionali o sovraccarico di macro, non esiste nulla di simile.


-1

Nessuno degli esempi precedenti (da Derek Ledbetter, David Sorkovsky e Joe D) per contare gli argomenti con le macro ha funzionato per me usando Microsoft VCC 10. L' __VA_ARGS__argomento è sempre considerato come un singolo argomento (token-zzandolo con ##o no), quindi lo spostamento dell'argomento su cui si basano questi esempi non funziona.

Quindi, risposta breve, come affermato da molti altri sopra: no, non puoi sovraccaricare le macro o utilizzare argomenti opzionali su di esse.


1
Puoi, ma solo in C99 o C ++ 11 (a causa di __VA_ARGS__). VC2010 è C89 / C ++ 03 (con alcuni bit di C ++ 11 che iniziano a comparire, ma non ancora quello).
puetzk
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.