Parámetros opcionales con macros de C ++

105

¿Hay alguna forma de obtener parámetros opcionales con las macros de C ++? Algún tipo de sobrecarga también estaría bien.

Cenoc
fuente
1
Lo mismo para C: stackoverflow.com/questions/11761703/… Debería ser el mismo ya que los preprocesadores son básicamente los mismos: stackoverflow.com/questions/5085533/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
Tal vez lo que está buscando sea sobrecargas de funciones, parámetros predeterminados, plantillas variadas o posiblemente el idioma del parámetro nombrado
smoothware
Actualice su respuesta seleccionada a las que han votado mucho a favor con soluciones reales, no a las que han votado menos a favor que diceNo you can't
Albert Renshaw

Respuestas:

156

He aquí una forma de hacerlo. Utiliza la lista de argumentos dos veces, primero para formar el nombre de la macro auxiliar y luego para pasar los argumentos a esa macro auxiliar. Utiliza un truco estándar para contar el número de argumentos de 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;
}

Esto lo hace más fácil para quien llama a la macro, pero no para quien escribe.

Derek Ledbetter
fuente
1
Esto es genial, pero no creo que funcione si solo hiciera PRINT_STRING. En ese caso, no habría una impresión predeterminada (y ese es el caso que quiero utilizar). Todavía +1 por realmente genial.
Cenoc
2
funciona para mí en gcc (¡y es muy inteligente!) :-) pero no funciona para mí en Visual Studio :-(
Tim Gradwell
3
@TimGradwell: se debe a un error en el compilador de MSVC que han reconocido pero que no han solucionado en casi una década. Sin embargo, existen soluciones alternativas .
BeeOnRope
Inteligente, pero no funciona para argumentos de macro variadic opcionales debido a la cosa de 'expulsar' que está sucediendo en 'GET_4th_ARG'.
motor de búsqueda27 de
¿Es eso PRINT_STRING_MACRO_CHOOSERsiquiera necesario? ¿Puedo reemplazarlo con su cuerpo interno directamente y llamar a todo esto con (__VA_ARGS__)?
Herrgott
85

Con gran respeto a Derek Ledbetter por su respuesta, y con disculpas por revivir una vieja pregunta.

Obtener una comprensión de lo que estaba haciendo y retomar en otro lugar la capacidad de preceder al __VA_ARGS__con ##me permitió encontrar una variación ...

// 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 los no expertos como yo que se topan con la respuesta, pero no pueden ver cómo funciona, pasaré por el procesamiento real, comenzando con el siguiente código ...

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 

Se vuelve ...

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 convierte en el sexto argumento ...

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

PD: Elimine #define para XXX_0 para obtener un error de compilación [es decir, si no se permite una opción sin argumentos].

PPS: ¡Sería bueno que las situaciones inválidas (por ejemplo: 5) fueran algo que ofreciera un error de compilación más claro al programador!

PPPS: No soy un experto, así que estoy muy feliz de escuchar comentarios (buenos, malos u otros).

David Sorkovsky
fuente
3
Puede obtener un error de compilación claro si convierte el argumento seleccionado, que se supone que es un nombre MACRO, en una cadena usando # (el signo de almohadilla) y compara sus primeros n caracteres con el prefijo esperado y, si no hay coincidencia, imprime un informativo error.
AturSams
1
Vaya, no sé si esto funciona, ¡pero al menos es muy creativo!
Expiación limitada
4
¿Por qué el primer argumento siempre está vacío? por qué no podemos simplemente omitirlo: XXX_X(,##__VA_ARGS__,` ... XXX_X (, XXX_4 (), XXX_3 (), XXX_2 (), XXX_1 (), XXX_0 ()); `
rahman
2
El primer argumento vacío (coma) es importante. ## __ VA_ARGS__ si está precedido por una coma, elimina la coma si ## __ VA_ARGS__ se expande a nada. Puede verlo en el ejemplo "Se convierte en ...", ya que la primera línea (sin argumentos) tiene solo 6 parámetros, pero el resto obtiene 7. Este truco asegura que la situación sin argumentos funciona
David Sorkovsky
@Eric: se debe a un error en los compiladores de microsoft, pero puede ver esta pregunta para encontrar soluciones.
BeeOnRope
31

Las macros de C ++ no han cambiado de C. Dado que C no tenía sobrecarga ni argumentos predeterminados para las funciones, ciertamente no los tenía para las macros. Entonces, para responder a su pregunta: no, esas funciones no existen para las macros. Su única opción es definir múltiples macros con diferentes nombres (o no usar macros en absoluto).

Como nota al margen: en C ++ generalmente se considera una buena práctica alejarse de las macros tanto como sea posible. Si necesita funciones como esta, es muy probable que esté abusando de las macros.

sepp2k
fuente
4
Tenga en cuenta que la razón por la que es imposible "sobrecargar" macros es porque no tienen ningún tipo inherente. Las macros simplemente se expanden.
mk12
2
Aunque yo uso macros lo menos posible, he encontrado que la depuración a través de la salida de rastreo se pone un poco más fácil con cosas como __FILE__y __LINE__y tal ...
Cristiano Severin
no es una buena respuesta. esta es una buena respuesta: stackoverflow.com/q/27049491/893406
v.oddou
La compilación condicional y la depuración / registro es el dominio donde las macros son realmente útiles y legítimas. Todo programador serio lo sabe. Lo que es una buena práctica es evitar el uso de macros para definir constantes y hacer algunas cosas locas de codificación de nivel C para crear plantillas de contenedor. Me gustaría que C ++ también agregara más funciones a las macros. Son ortogonales a las plantillas. Lo mejor, por supuesto, serían los codelets que me permitan agregar generadores al compilador para el lenguaje específico del dominio (aspectos).
Lothar
1
También creo que esta no es una buena respuesta, porque una macro es algo completamente diferente a cualquier opción del lenguaje C ++, porque se manejará ANTES del compilador. Entonces puede hacer otras cosas, y ningún compilador o enlazador debe optimizar el código, porque tal vez no sea para optimizar.
alabamajack
26

Con el mayor respeto a Derek Ledbetter , David Sorkovsky , Syphorlate por sus respuestas, junto con el ingenioso método para detectar macro argumentos vacíos de Jens Gustedt en

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

finalmente salgo con algo que incorpora todos los trucos, para que la solución

  1. Utiliza solo macros C99 estándar para lograr la sobrecarga de funciones, sin extensión GCC / CLANG / MSVC involucrada (es decir, deglución de coma por la expresión específica , ##__VA_ARGS__para GCC / CLANG, y deglución implícita por ##__VA_ARGS__para MSVC). Así que siéntase libre de pasar lo que falta --std=c99a su compilador si lo desea =)
  2. Funciona para cero argumentos , así como para un número ilimitado de argumentos , si lo expande aún más para satisfacer sus necesidades
  3. Funciona razonablemente multiplataforma , al menos probado para

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

Para los perezosos, simplemente salte al último de esta publicación para copiar la fuente. A continuación se muestra la explicación detallada, que con suerte ayuda e inspira a todas las personas que buscan __VA_ARGS__soluciones generales como yo. =)

Así es como funciona. En primer lugar definir la "función" visible para el usuario sobrecargado, lo nombré create, y la definición de función real relacionada realCreate, y las definiciones de macro con diferente número de argumentos CREATE_2, CREATE_1, CREATE_0, como se muestra a continuación:

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

En MACRO_CHOOSER(__VA_ARGS__)última instancia, la parte se resuelve en los nombres de las definiciones de macro y la segunda (__VA_ARGS__)parte comprende sus listas de parámetros. Entonces, la llamada de un usuario a se create(10)resuelve en CREATE_1(10), la CREATE_1parte proviene de MACRO_CHOOSER(__VA_ARGS__)y la (10)parte proviene del segundo (__VA_ARGS__).

El MACRO_CHOOSERutiliza el truco de que, si __VA_ARGS__está vacía, la siguiente expresión se concatena en una llamada a la macro válida por el preprocesador:

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

Ingeniosamente, podemos definir esta macro llamada resultante como

#define NO_ARG_EXPANDER() ,,CREATE_0

Tenga en cuenta las dos comas, se explicarán pronto. La siguiente macro útil es

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

entonces las llamadas de

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

en realidad se expanden 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);

Como sugiere el nombre de la macro, más adelante contaremos el número de argumentos. Aquí viene otro truco: el preprocesador solo realiza un simple reemplazo de texto. Infiere el número de argumentos de una llamada de macro simplemente del número de comas que ve entre paréntesis. Los "argumentos" reales separados por comas no necesitan tener una sintaxis válida. Pueden ser cualquier texto. Es decir, en el ejemplo anterior, NO_ARG_EXPANDER 10 ()se cuenta como 1 argumento para la llamada intermedia. NO_ARG_EXPANDER 20y 20 ()se cuentan como 2 argumentos para la última llamada respectivamente.

Si usamos las siguientes macros auxiliares para expandirlas aún más

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

El seguimiento ,posterior CREATE_1es una solución alternativa para GCC / CLANG, suprimiendo un error (falso positivo) que dice eso ISO C99 requires rest arguments to be usedcuando se pasa -pedantica su compilador. El FUNC_RECOMPOSERes un trabajo en torno a MSVC, o que no se puede contar el número de argumentos (es decir, comas) dentro del paréntesis de las llamadas macro correctamente. Los resultados se resuelven además a

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 el ojo de águila que puede haber visto, el último único paso que necesitamos es emplear un truco de conteo de argumentos estándar para finalmente elegir los nombres de la versión de macro deseados:

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

que resuelve los resultados a

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

y ciertamente nos da las llamadas de función reales deseadas:

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

Poniendo todo junto, con una reordenación de las declaraciones para una mejor legibilidad, la fuente completa del ejemplo de 2 argumentos está aquí:

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

Aunque complicado, feo, agobiante al desarrollador de API, llega una solución para sobrecargar y configurar parámetros opcionales de funciones C / C ++ para nosotros los locos. El uso de las API sobrecargadas resultantes se vuelve muy agradable y agradable. =)

Si existe alguna posible simplificación adicional de este enfoque, hágamelo saber en

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

¡De nuevo un agradecimiento especial a todas las personas brillantes que me inspiraron y me llevaron a lograr este trabajo! =)

Jason Deng
fuente
3
¿Cómo se amplía esto a 3 o 4 funciones?
Phylliida
@Phylliida ideone.com/jD0Hm5 : se admiten de cero a cinco argumentos.
xx
9

Para cualquiera que busque dolorosamente alguna solución VA_NARGS que funcione con Visual C ++. La siguiente macro funcionó para mí perfectamente (¡también con cero parámetros!) En 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

Si desea una macro con parámetros opcionales, puede hacer:

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

Eso funcionó para mí también en vc. Pero no funciona para cero parámetros.

int x=99;
x=mymacro(2);//2
x=mymacro(2,2);//4
x=mymacro(2,2,2);//8
Sforlato
fuente
Estoy recibiendounresolved external symbol _bool referenced in function _main
Avidan Borisov
sí, eso puede suceder en algunos casos. debe tener en cuenta que bool (#__ VA_ARGS__)? es diferente a las otras macros ya que se está evaluando en tiempo de ejecución. Sin embargo, dependiendo de su caso, puede omitir esa parte del código.
Syphorlate
2
De hecho, terminé con pastebin.com/H3T75dcn, que funciona perfectamente (0 argumentos también).
Avidan Borisov
Gracias por el enlace, y sí, también puede hacerlo usando sizeof, pero para mí eso no funcionó en algunos casos, pero el principio es el mismo (evaluación booleana).
Syphorlate
¿Podría dar algunos ejemplos en los que falla?
Avidan Borisov
7

gcc/ g++admite macros varargs, pero no creo que esto sea estándar, así que úselo bajo su propio riesgo.

Paul R
fuente
4
Son estándar en C99 y también se están agregando a C ++ 0x.
greyfade
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 */
}

DESCARGO DE RESPONSABILIDAD: Mayormente inofensivo.

Joe D
fuente
hay un error en su código. por favor haz :%s/MY_MACRO_/THINK_/g:)
João Portela
Además, no funcionó con cero argumentos usando g ++i686-apple-darwin10-g++-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5664)
João Portela
1
No existen argumentos cero para las macros de variadiac, ya que el token vacío es un marcador de posición válido.
Paul Fultz II
3

Eso no es realmente para lo que está diseñado el preprocesador.

Dicho esto, si desea entrar en el área de la programación de macros que desafía seriamente con un mínimo de legibilidad, debería echar un vistazo a la biblioteca del preprocesador Boost . Después de todo, ¡no sería C ++ si no hubiera tres niveles de programación completamente compatibles con Turing (preprocesador, metaprogramación de plantillas y nivel básico C ++)!

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

En el momento de la llamada, sabe cuántos argumentos va a pasar, por lo que realmente no hay necesidad de sobrecargar.

Edward extraño
fuente
2
De hecho, estaba preguntando sobre la existencia de la función.
Cenoc
3

Versión más concisa del 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
fuente
3

Como gran fanático de los horribles monstruos macro, quería ampliar la respuesta de Jason Deng y hacerla realmente utilizable. (Para bien o para mal). El original no es muy agradable de usar porque necesita modificar la gran sopa de letras cada vez que desea crear una nueva macro y es aún peor si necesita una cantidad diferente de argumentos.

Así que hice una versión con estas características:

  • 0 caso de argumento funciona
  • 1 a 16 argumentos sin modificaciones a la parte desordenada
  • Fácil de escribir más funciones macro
  • Probado en gcc 10, clang 9, Visual Studio 2017

Actualmente acabo de hacer un máximo de 16 argumentos, pero si necesita más (¿de verdad ahora? Se está volviendo tonto ...) puede editar FUNC_CHOOSER y CHOOSE_FROM_ARG_COUNT, luego agregar algunas comas a NO_ARG_EXPANDER.

Consulte la excelente respuesta de Jason Deng para obtener más detalles sobre la implementación, pero solo pondré el código aquí:

#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
fuente
2

Puede utilizar BOOST_PP_OVERLOADdesde unboost biblioteca.

Ejemplo del documento oficial de 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
desactivar13
fuente
0

Dependiendo de lo que necesite, puede hacerlo con var args con macros. Ahora, parámetros opcionales o sobrecarga de macros, no existe tal cosa.

Gianni
fuente
-1

Ninguno de los ejemplos anteriores (de Derek Ledbetter, David Sorkovsky y Joe D) para contar argumentos con macros funcionó para mí usando Microsoft VCC 10. El __VA_ARGS__argumento siempre se considera como un solo argumento (tokenizándolo con ##o no), por lo que el cambio de argumento en el que se basan esos ejemplos no funciona.

Entonces, respuesta corta, como lo indicaron muchos otros anteriormente: no, no puede sobrecargar macros o usar argumentos opcionales en ellas.

TheProgammerd
fuente
1
Puede, pero solo en C99 o C ++ 11 (debido a que tiene __VA_ARGS__). VC2010 es C89 / C ++ 03 (con algunos bits de C ++ 11 comenzando a aparecer, pero todavía no).
puetzk