¿Hay alguna forma de obtener parámetros opcionales con las macros de C ++? Algún tipo de sobrecarga también estaría bien.
No you can't
¿Hay alguna forma de obtener parámetros opcionales con las macros de C ++? Algún tipo de sobrecarga también estaría bien.
No you can't
Respuestas:
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.
PRINT_STRING_MACRO_CHOOSER
siquiera necesario? ¿Puedo reemplazarlo con su cuerpo interno directamente y llamar a todo esto con (__VA_ARGS__)
?
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).
XXX_X(,##__VA_ARGS__,` ...
XXX_X (, XXX_4 (), XXX_3 (), XXX_2 (), XXX_1 (), XXX_0 ()); `
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.
__FILE__
y __LINE__
y tal ...
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
, ##__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=c99
a su compilador si lo desea =)Funciona razonablemente multiplataforma , al menos probado para
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_1
parte proviene de MACRO_CHOOSER(__VA_ARGS__)
y la (10)
parte proviene del segundo (__VA_ARGS__)
.
El MACRO_CHOOSER
utiliza 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 20
y 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_1
es una solución alternativa para GCC / CLANG, suprimiendo un error (falso positivo) que dice eso ISO C99 requires rest arguments to be used
cuando se pasa -pedantic
a su compilador. El FUNC_RECOMPOSER
es 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! =)
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
unresolved external symbol _bool referenced in function _main
gcc
/ g++
admite macros varargs, pero no creo que esto sea estándar, así que úselo bajo su propio riesgo.
#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.
:%s/MY_MACRO_/THINK_/g
:)
i686-apple-darwin10-g++-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5664)
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 ++)!
#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.
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;
}
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:
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;
}
Puede utilizar BOOST_PP_OVERLOAD
desde 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
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.