Parâmetros opcionais com macros C ++

105

Existe alguma maneira de obter parâmetros opcionais com macros C ++? Algum tipo de sobrecarga também seria bom.

Cenoc
fonte
1
O mesmo para C: stackoverflow.com/questions/11761703/… Deve ser o mesmo, pois os pré-processadores são basicamente os mesmos: stackoverflow.com/questions/5085533/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
Talvez sobrecargas de função, parâmetros padrão, modelos de variáveis ​​ou possivelmente o idioma do parâmetro nomeado sejam o que você está procurando
smoothware de
Por favor, atualize sua resposta selecionada para as mais votadas com soluções reais, não aquela que diz respeito às votações humildesNo you can't
Albert Renshaw

Respostas:

156

Aqui está uma maneira de fazer isso. Ele usa a lista de argumentos duas vezes, primeiro para formar o nome da macro auxiliar e, em seguida, para passar os argumentos para essa macro auxiliar. Ele usa um truque padrão para contar o número de argumentos para uma 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;
}

Isso torna mais fácil para o chamador da macro, mas não para o gravador.

Derek Ledbetter
fonte
1
Isso é muito legal, mas não acho que funcionaria se eu apenas fizesse PRINT_STRING. Nesse caso, não haveria uma impressão padrão (e esse é realmente o caso que desejo utilizar). Ainda +1 para muito legal.
Cenoc
2
funciona para mim no gcc (e é muito inteligente!) :-) mas não funciona para mim no Visual Studio :-(
Tim Gradwell
3
@TimGradwell - é devido a um bug no compilador MSVC que eles reconheceram, mas não corrigiram em quase uma década. No entanto, soluções alternativas estão disponíveis .
BeeOnRope de
Inteligente, mas não funciona para argumentos macro variáveis ​​opcionais por causa da coisa de 'empurrar' que você está fazendo em `GET_4th_ARG '.
searchengine27
isso é PRINT_STRING_MACRO_CHOOSERmesmo necessário? Posso substituir seu corpo interno diretamente e chamar essa coisa toda de (__VA_ARGS__)?
Herrgott
85

Com grande respeito a Derek Ledbetter por sua resposta - e com desculpas por reavivar uma velha questão.

Compreender o que estava fazendo e pegar em outro lugar a capacidade de preceder o __VA_ARGS__com ##me permitiu chegar a uma variação ...

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

Para não especialistas como eu, que encontram a resposta, mas não conseguem ver como funciona, examinarei o processamento real, começando com o código a seguir ...

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 

Torna-se...

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

Que se torna apenas o sexto argumento ...

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

PS: Remova o #define para XXX_0 para obter um erro de compilação [ie: se uma opção sem argumento não for permitida].

PPS: Seria bom que as situações inválidas (por exemplo: 5) fossem algo que fornecesse um erro de compilação mais claro para o programador!

PPPS: Não sou especialista, por isso fico muito feliz em ouvir comentários (bons, ruins ou outros)!

David Sorkovsky
fonte
3
Você poderia obter um erro de compilação claro se convertesse o argumento selecionado, que deveria ser um nome MACRO, em string usando # (o sinal de libra) e comparasse seus primeiros n caracteres com o prefixo esperado e, se não houver correspondência, imprima um informativo erro.
AturSams
1
Nossa, não sei se isso funciona, mas é pelo menos muito criativo!
Expiação limitada de
4
por que o primeiro argumento está sempre vazio? por que não podemos simplesmente omiti-lo: XXX_X(,##__VA_ARGS__,` ... XXX_X (, XXX_4 (), XXX_3 (), XXX_2 (), XXX_1 (), XXX_0 ()); `
rahman
2
O primeiro argumento vazio (vírgula) é importante. ## __ VA_ARGS__ se for precedido por uma vírgula - remove a vírgula se ## __ VA_ARGS__ se expandir para nada. Você pode ver isso no exemplo "Torna-se ..." já que a primeira linha (sem argumentos) tem apenas 6 parâmetros, mas o resto obtém 7. Este truque garante que a situação sem argumentos funcione
David Sorkovsky
@Eric - é devido a um bug nos compiladores da Microsoft, mas você pode ver esta questão para soluções alternativas.
BeeOnRope de
31

As macros C ++ não mudaram de C. Uma vez que C não tinha sobrecarga e argumentos padrão para funções, certamente não os tinha para macros. Portanto, para responder à sua pergunta: não, esses recursos não existem para macros. Sua única opção é definir várias macros com nomes diferentes (ou não usar macros).

Como nota: em C ++, geralmente é considerado uma boa prática afastar-se das macros o máximo possível. Se você precisa de recursos como este, há uma boa chance de estar usando macros em excesso.

sepp2k
fonte
4
Observe que a razão pela qual é impossível "sobrecarregar" macros é porque elas não têm nenhum tipo inerente. As macros são simplesmente expandidas.
mk12 de
2
Embora eu use macros o mínimo possível, descobri que a depuração via saída de rastreamento fica um pouco mais fácil com coisas como __FILE__e __LINE__e tal ...
Christian Severin
não é uma boa resposta. esta é uma boa resposta: stackoverflow.com/q/27049491/893406
v.oddou
Compilação condicional e depuração / registro é o domínio onde as macros são realmente úteis e legítimas. Todo programador sério sabe disso. O que é uma boa prática é deixar de usar macros para definir constantes e fazer algumas coisas malucas de codificação de nível C para criar modelos de contêiner. Eu gostaria que C ++ adicionasse mais recursos às macros também. Eles são ortogonais aos modelos. O melhor, é claro, seriam os codelets que me permitem adicionar geradores ao compilador para linguagem específica de domínio (aspectos).
Lothar
1
Também acho que essa não é uma boa resposta, porque uma macro é algo completamente diferente de qualquer opção de linguagem C ++, porque ela será tratada ANTES do compilador. Portanto, você pode fazer outras coisas, e nenhum compilador ou vinculador deve otimizar o código, porque talvez não seja para otimizar.
alabamajack
26

Com o maior respeito a Derek Ledbetter , David Sorkovsky , Syphorlate por suas respostas, junto com o método engenhoso para detectar macro argumentos vazios por Jens Gustedt em

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

finalmente, saio com algo que incorpora todos os truques, para que a solução

  1. Usa apenas macros C99 padrão para atingir sobrecarga de função, nenhuma extensão GCC / CLANG / MSVC envolvida (ou seja, deglutição de vírgula pela expressão específica , ##__VA_ARGS__para GCC / CLANG e deglutição implícita por ##__VA_ARGS__para MSVC). Então fique à vontade para passar pelo que falta--std=c99 para o seu compilador se desejar =)
  2. Funciona com nenhum argumento , bem como um número ilimitado de argumentos , se você expandir ainda mais para atender às suas necessidades
  3. Funciona razoavelmente em várias plataformas , pelo menos testado para

    • GNU / Linux + GCC (GCC 4.9.2 no CentOS 7.0 x86_64)
    • GNU / Linux + CLANG / LLVM , (CLANG / LLVM 3.5.0 no CentOS 7.0 x86_64)
    • OS X + Xcode , (XCode 6.1.1 no OS X Yosemite 10.10.1)
    • Windows + Visual Studio , (Visual Studio 2013 Atualização 4 no Windows 7 SP1 de 64 bits)

Para os preguiçosos, basta pular para o último deste post para copiar a fonte. Abaixo está a explicação detalhada, o que esperançosamente ajuda e inspira todas as pessoas que procuram o__VA_ARGS__ soluções como eu. =)

É assim que funciona. Primeiro defina a "função" visível ao usuário sobrecarregado, I nomeou-o create, e a definição da função reais relacionados realCreate, e as definições de macros com diferente número de argumentos CREATE_2, CREATE_1, CREATE_0, como mostrado abaixo:

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

A MACRO_CHOOSER(__VA_ARGS__)parte em última análise resolve para os nomes de definição de macro e a segunda (__VA_ARGS__)parte compreende suas listas de parâmetros. Assim, a chamada de um usuário para create(10)resolve para CREATE_1(10), a CREATE_1parte vem MACRO_CHOOSER(__VA_ARGS__)e o(10) parte vem do segundo (__VA_ARGS__).

O MACRO_CHOOSERusa o truque de que, se __VA_ARGS__estiver vazio, a seguinte expressão será concatenada em uma chamada de macro válida pelo pré-processador:

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

Engenhosamente, podemos definir esta chamada de macro resultante como

#define NO_ARG_EXPANDER() ,,CREATE_0

Observe as duas vírgulas, elas serão explicadas em breve. A próxima macro útil é

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

então as chamadas de

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

são realmente expandidos para

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

Como o nome da macro sugere, devemos contar o número de argumentos posteriormente. Aí vem outro truque: o pré-processador só faz uma simples substituição de texto. Ele infere o número de argumentos de uma chamada de macro apenas a partir do número de vírgulas que vê entre parênteses. Os "argumentos" reais separados por vírgulas não precisam ter sintaxe válida. Eles podem ser qualquer texto. Ou seja, no exemplo acima, NO_ARG_EXPANDER 10 ()é contado como 1 argumento para a chamada do meio. NO_ARG_EXPANDER 20e 20 ()são contados como 2 argumentos para a chamada inferior, respectivamente.

Se usarmos as seguintes macros auxiliares para expandi-los ainda mais

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

A fuga ,depois CREATE_1é uma forma de contornar GCC / CLANG, suprimindo um erro (falsos positivos) dizendo que ISO C99 requires rest arguments to be usedquando passar -pedanticpara o seu compilador. O FUNC_RECOMPOSERé uma solução alternativa para o MSVC ou não pode contar o número de argumentos (ou seja, vírgulas) entre parênteses de chamadas de macro corretamente. Os resultados são posteriormente resolvidos para

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

Como você deve ter percebido, a última e única etapa de que precisamos é empregar um truque de contagem de argumentos padrão para finalmente escolher os nomes de versão de macro desejados:

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

que resolve os resultados para

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

e certamente nos dá as chamadas de função reais desejadas:

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

Juntando tudo, com algum rearranjo de declarações para melhor legibilidade, toda a fonte do exemplo de 2 argumentos está aqui:

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

Embora complicado, feio, sobrecarregando o desenvolvedor de API, surge uma solução para sobrecarregar e definir parâmetros opcionais de funções C / C ++ para nós, loucos. O uso das APIs sobrecarregadas que estão chegando se tornou muito agradável e agradável. =)

Se houver qualquer outra simplificação possível dessa abordagem, por favor, me avise em

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

Mais uma vez, um agradecimento especial a todas as pessoas brilhantes que me inspiraram e levaram a concretizar este trabalho! =)

Jason Deng
fonte
3
Como expandir isso para 3 ou 4 funções?
Phylliida
@Phylliida ideone.com/jD0Hm5 - de zero a cinco argumentos suportados.
xx
9

Para qualquer pessoa que pesquise com dificuldade alguma solução VA_NARGS que funcione com Visual C ++. A macro a seguir funcionou perfeitamente para mim (também com parâmetros zero!) No 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 você quiser uma macro com parâmetros opcionais, pode fazer:

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

Isso funcionou para mim também em vc. Mas não funciona para parâmetros zero.

int x=99;
x=mymacro(2);//2
x=mymacro(2,2);//4
x=mymacro(2,2,2);//8
Syforlate
fonte
Estou recebendounresolved external symbol _bool referenced in function _main
Avidan Borisov
sim, isso pode acontecer em alguns casos. você precisa estar ciente de que bool (#__ VA_ARGS__)? é diferente das outras macros, pois está sendo avaliada em tempo de execução. dependendo do seu caso, você pode omitir essa parte do código.
Syphorlate
2
Na verdade, acabei com pastebin.com/H3T75dcn, que funciona perfeitamente (0 argumentos também).
Avidan Borisov
Obrigado pelo link, e sim você pode fazer isso usando sizeof também, mas para mim isso não funcionou em alguns casos, mas o princípio é o mesmo (avaliação booleana).
Syphorlate
Você poderia dar alguns exemplos onde ele falha?
Avidan Borisov
7

gcc/ g++suporta macros varargs, mas não acho que seja padrão, então use-o por sua própria conta e risco.

Paul R
fonte
4
Eles são padrão no C99 e estão sendo adicionados ao C ++ 0x também.
greyfade de
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 */
}

AVISO LEGAL: Principalmente inofensivo.

Joe D
fonte
há um erro no seu código. por favor faça :%s/MY_MACRO_/THINK_/g:)
João Portela
também não funcionou com nenhum argumento usando g ++i686-apple-darwin10-g++-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5664)
João Portela
1
Argumentos zero não existem para macros variadiac, uma vez que o token vazio é um marcador válido.
Paul Fultz II
3

Não é exatamente para isso que o pré-processador foi projetado.

Dito isso, se você quiser entrar na área de um desafio sério de programação de macro com um mínimo de legibilidade, você deve dar uma olhada na biblioteca de pré-processador Boost . Afinal, não seria C ++ se não houvesse três níveis de programação totalmente compatíveis com Turing (pré-processador, metaprogramação de modelo e nível básico C ++)!

Pontus Gagge
fonte
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)

Você sabe no momento da chamada quantos args vai passar, então não há realmente necessidade de sobrecarga.

Edward Strange
fonte
2
Na verdade, eu estava perguntando sobre a existência do recurso.
Cenoc
3

Versão mais concisa do código de 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;
}
Megamozg
fonte
3

Como um grande fã de monstros macro horríveis, eu queria expandir a resposta de Jason Deng e torná-la realmente utilizável. (Para o bem ou para o mal.) O original não é muito agradável de usar porque você precisa modificar a grande sopa de letrinhas toda vez que quiser fazer uma nova macro e é ainda pior se você precisar de diferentes quantidades de argumentos.

Então fiz uma versão com estes recursos:

  • 0 caso de argumento funciona
  • 1 a 16 argumentos sem nenhuma modificação na parte confusa
  • Fácil de escrever mais funções macro
  • Testado em gcc 10, clang 9, Visual Studio 2017

Atualmente, acabei de criar no máximo 16 argumentos, mas se precisar de mais (realmente agora? Você está ficando bobo ...), pode editar FUNC_CHOOSER e CHOOSE_FROM_ARG_COUNT e adicionar algumas vírgulas a NO_ARG_EXPANDER.

Consulte a excelente resposta de Jason Deng para obter mais detalhes sobre a implementação, mas apenas colocarei o código aqui:

#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;
}
Kuukunen
fonte
2

Você pode usar BOOST_PP_OVERLOADde umboost biblioteca.

Exemplo do documento de reforço oficial :

#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
desativar 13
fonte
0

Dependendo do que você precisa, você pode fazer isso com var args com macros. Agora, parâmetros opcionais ou sobrecarga de macro, não existe tal coisa.

Gianni
fonte
-1

Nenhum dos exemplos acima (de Derek Ledbetter, David Sorkovsky e Joe D) para contar argumentos com macros funcionou para mim usando o Microsoft VCC 10. O __VA_ARGS__argumento é sempre considerado como um único argumento (token-izing com ##ou não), então a mudança de argumento na qual esses exemplos se baseiam não funciona.

Portanto, uma resposta curta, como afirmado por muitos outros acima: não, você não pode sobrecarregar macros ou usar argumentos opcionais nelas.

TheProgammerd
fonte
1
Você pode, mas apenas em C99 ou C ++ 11 (devido a ter __VA_ARGS__). VC2010 é C89 / C ++ 03 (com alguns bits de C ++ 11 começando a aparecer, mas não aquele ainda).
puetzk