not - Parámetros opcionales con macros de C++
ejemplos de macros en c (13)
¿Hay alguna forma de obtener parámetros opcionales con C ++ Macros? Algún tipo de sobrecarga sería bueno también.
Aquí hay una manera 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 la cantidad de argumentos en 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 hace que sea más fácil para quien llama de la macro, pero no para el escritor.
Con el mayor respeto a Derek Ledbetter , David Sorkovsky , Syphorlate por sus respuestas, junto con el ingenioso método para detectar macroargumentos vacíos por Jens Gustedt en
https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/
finalmente saco algo que incorpora todos los trucos, para que la solución
- Utiliza solo macros estándar C99 para lograr la sobrecarga de funciones, sin implicar la extensión GCC / CLANG / MSVC (es decir, comas que tragan la expresión específica
, ##__VA_ARGS__
para GCC / CLANG, y la deglución implícita de##__VA_ARGS__
para##__VA_ARGS__
). Así que no dude en pasar la falta--std=c99
a su compilador si lo desea =) - Funciona con cero argumentos , así como un número ilimitado de argumentos , si lo amplías aún más para satisfacer tus necesidades
Funciona de forma razonablemente multiplataforma , al menos probado
- 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 Actualización 4 en Windows 7 SP1 64 bits)
Para los lazy, simplemente salte a la última de esta publicación para copiar la fuente. A continuación se encuentra la explicación detallada, que con suerte ayuda e inspira a todas las personas que buscan las soluciones generales __VA_ARGS__
como yo. =)
Así es como va. Primero defina la "función" sobrecargada visible para el usuario, la llamé 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)
La parte MACRO_CHOOSER(__VA_ARGS__)
finalmente resuelve los nombres de definición de macro, y la segunda parte (__VA_ARGS__)
comprende sus listas de parámetros. Entonces, la llamada de un usuario para create(10)
resuelve en CREATE_1(10)
, la parte CREATE_1
viene de MACRO_CHOOSER(__VA_ARGS__)
y la (10)
parte del segundo (__VA_ARGS__)
.
El MACRO_CHOOSER
usa el truco de que, si __VA_ARGS__
está vacío, el preprocesador concatena la siguiente expresión en una macro llamada válida:
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, que se explican pronto. La próxima 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 ampliaron 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 del macro, debemos contar la cantidad de argumentos más adelante. Aquí viene otro truco: el preprocesador solo reemplaza texto simple. Infiere el número de argumentos de una macro llamada simplemente a partir del número de comas que ve dentro de los paréntesis. Los "argumentos" reales separados por comas no necesitan ser de 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 utilizamos las siguientes macros de ayuda 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 final ,
después de CREATE_1
es una CREATE_1
para GCC / CLANG, que suprime un error (falso positivo) que dice que ISO C99 requires rest arguments to be used
al pasar -pedantic
a su compilador. El FUNC_RECOMPOSER
es una FUNC_RECOMPOSER
para MSVC, o no puede contar el número de argumentos (es decir, comas) dentro del paréntesis de las llamadas de macro correctamente. Los resultados se resuelven 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 los ojos de águila que ha visto, el último paso que necesitamos es emplear un truco de conteo de argumentos estándar para finalmente elegir los nombres de las versiones de macro deseadas:
#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 cierta reorganizació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 es complicado y feo, es una carga para el desarrollador de API, existe una solución para sobrecargar y establecer parámetros opcionales de funciones C / C ++ para nosotros, los locos. El uso de las API vencidas y sobrecargadas se vuelve muy agradable y placentero. =)
Si hay alguna otra simplificación posible de este enfoque, házmelo saber a
https://github.com/jason-deng/C99FunctionOverload
Nuevamente, ¡un agradecimiento especial a todas las personas brillantes que me inspiraron y me llevaron a lograr este trabajo! =)
Con gran respeto a Derek Ledbetter por su respuesta, y con mis disculpas por haber resucitado una vieja pregunta.
Obtener una comprensión de lo que estaba haciendo y elegir en otro lugar la capacidad de preceder a __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 tropiezan con la respuesta, pero no pueden ver cómo funciona, paso por el proceso actual, 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 convierte en ...
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) );
Lo cual 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 argumento].
PPS: Sería bueno tener las situaciones no válidas (por ejemplo: 5) ser algo que le da un error de compilación más clara para el programador!
PPPS: no soy un experto, así que estoy muy contento de escuchar comentarios (buenos, malos u otros).
Dependiendo de lo que necesite, puede hacerlo con var args con macros. Ahora, parámetros opcionales o sobrecarga macro, no existe tal cosa.
Las macros de C ++ no han cambiado desde C. Como C no tenía sobrecarga y argumentos predeterminados para las funciones, ciertamente no las tenía para las macros. Entonces, para responder a su pregunta: no, esas características 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, existe una buena posibilidad de que exagere el uso de macros.
Ninguno de los ejemplos anteriores (de Derek Ledbetter, David Sorkovsky y Joe D) para contar argumentos con macros trabajó para mí usando Microsoft VCC 10. El argumento __VA_ARGS__
siempre se considera como un único argumento (token-izing con ##
o no ), por lo que el argumento de cambio en el que se basan esos ejemplos no funciona.
Por lo tanto, respuesta breve, como lo afirman muchos otros más arriba: no, no puede sobrecargar macros o usar argumentos opcionales en ellas.
Para cualquiera que busque con dificultad alguna solución VA_NARGS que funcione con Visual C ++. El siguiente macro funcionó perfectamente para mí (¡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 lo siguiente:
//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
Para eso no está diseñado el preprocesador.
Dicho esto, si desea ingresar al área de la macroprogramación seriamente desafiante con un mínimo de legibilidad, debe echarle un vistazo a la biblioteca del preprocesador de 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 base C ++)!
Puede usar BOOST_PP_OVERLOAD
desde una biblioteca de boost
.
Ejemplo del documento oficial de impulso :
#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
Una 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;
}
gcc
/ g++
admite macros varargs, pero no creo que esto sea estándar, así que úsala bajo tu propio riesgo.
#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)
Ya sabe en el momento de la llamada cuántos argumentos va a pasar, por lo que realmente no es necesario sobrecargarlo.
#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.