ético revista pensamiento para log lecturas horas home gubernamental etica ethos desarrollo certificacion centro cdpe acceso c++ c overloading c-preprocessor variadic-macros

c++ - pensamiento - lecturas revista ethos gubernamental



¿Pueden las macros estar sobrecargadas por el número de argumentos? (4)

¿Cómo funciona this ? ¿Cómo se puede implementar una macro variadic C99 / C ++ 11 para expandir a diferentes cosas sobre la única base de cuántos argumentos se le dan?


(Editar: ver el final para una solución ya hecha).

Para obtener una macro sobrecargada, primero necesitamos una macro que seleccione entre varias implementaciones. Esta parte no utiliza una macro variad. Luego, una macro variada que cuenta genéricamente sus argumentos produce un selector. Al conectar el conteo de argumentos en un despachador se produce una macro sobrecargada.

Advertencia: este sistema no puede distinguir la diferencia entre cero y un argumento porque no hay diferencia entre ningún argumento y un solo argumento vacío. Ambos se ven como MACRO() .

Para seleccionar entre las implementaciones, use el operador de catenación de macros con una serie de macros similares a funciones.

#define select( selector, ... ) impl ## _ ## selector( __VA_ARGS__ ) #define impl_1() meh #define impl_2( abc, xyz ) # abc "wizza" xyz() //etc // usage: select( 1 ) => impl_1() => meh // select( 2, huz, bar ) => impl_2( huzza, bar ) => "huz" "wizza" bar()

Debido a que el operador ## suprime la expansión de macro de sus argumentos, es mejor envolverlo en otra macro.

#define CAT( A, B ) A ## B #define SELECT( NAME, NUM ) CAT( NAME ## _, NUM )

Para contar los argumentos, use __VA_ARGS__ para cambiar los argumentos así (esta es la parte inteligente):

#define GET_COUNT( _1, _2, _3, _4, _5, _6 /* ad nauseam */, COUNT, ... ) COUNT #define VA_SIZE( ... ) GET_COUNT( __VA_ARGS__, 6, 5, 4, 3, 2, 1 )

Código de la biblioteca:

#define CAT( A, B ) A ## B #define SELECT( NAME, NUM ) CAT( NAME ## _, NUM ) #define GET_COUNT( _1, _2, _3, _4, _5, _6 /* ad nauseam */, COUNT, ... ) COUNT #define VA_SIZE( ... ) GET_COUNT( __VA_ARGS__, 6, 5, 4, 3, 2, 1 ) #define VA_SELECT( NAME, ... ) SELECT( NAME, VA_SIZE(__VA_ARGS__) )(__VA_ARGS__)

Uso:

#define MY_OVERLOADED( ... ) VA_SELECT( MY_OVERLOADED, __VA_ARGS__ ) #define MY_OVERLOADED_1( X ) foo< X > #define MY_OVERLOADED_2( X, Y ) bar< X >( Y ) #define MY_OVERLOADED_3( X, Y, Z ) bang_ ## X< Y >.Z()


A continuación hay una mejora sobre la respuesta de Potatoswatter , que puede diferenciar entre cero y un argumento.

En pocas palabras, cuando __VA_ARGS__ está vacío, EXPAND __VA_ARGS__ () dentro de la macro VA_SIZE convierte en EXPAND () y se sustituye por 6 comas. Por lo tanto, VA_SIZE... convierte en COMPOSE( GET_COUNT, (,,,,,, , 0, 6, 5, 4, 3, 2, 1) ) , y eso se convierte en GET_COUNT (,,,,,, , 0, 6, 5, 4, 3, 2, 1) y devuelve 0.

Por otro lado, cuando __VA_ARGS__ es, por ejemplo, int, 5 , EXPAND __VA_ARGS__ () convierte en EXPAND int, 5 () . Por lo tanto, VA_SIZE... convierte en COMPOSE( GET_COUNT, (EXPAND int, 5 (), 0, 6, 5, 4, 3, 2, 1) ) , que se convierte en GET_COUNT (EXPAND int, 5 (), 0, 6, 5, 4, 3, 2, 1) y devuelve 2, como se describe en la respuesta de Potatoswatter.

EXPAND idea de EXPANDIR de la respuesta de Jason Dang .

Código de la biblioteca:

#define CAT( A, B ) A ## B #define SELECT( NAME, NUM ) CAT( NAME ## _, NUM ) #define COMPOSE( NAME, ARGS ) NAME ARGS #define GET_COUNT( _0, _1, _2, _3, _4, _5, _6 /* ad nauseam */, COUNT, ... ) COUNT #define EXPAND() ,,,,,, // 6 commas (or 7 empty tokens) #define VA_SIZE( ... ) COMPOSE( GET_COUNT, (EXPAND __VA_ARGS__ (), 0, 6, 5, 4, 3, 2, 1) ) #define VA_SELECT( NAME, ... ) SELECT( NAME, VA_SIZE(__VA_ARGS__) )(__VA_ARGS__)

Uso:

#define MY_OVERLOADED( ... ) VA_SELECT( MY_OVERLOADED, __VA_ARGS__ ) #define MY_OVERLOADED_0( ) meh() #define MY_OVERLOADED_1( X ) foo< X > #define MY_OVERLOADED_2( X, Y ) bar< X >( Y ) #define MY_OVERLOADED_3( X, Y, Z ) bang_ ## X< Y >.Z() MY_OVERLOADED() // meh() MY_OVERLOADED(bool) // foo< bool > MY_OVERLOADED(int, 5) // bar< int >( 5 ) MY_OVERLOADED(me, double, now) // bang_me< double >.now()


Aunque ya está contestada, he preparado una versión muy corta de la misma. Espero que pueda ayudar.

Implementación

// Variable Argument Macro (VA_MACRO) upto 6 arguments #define NUM_ARGS_(_1, _2, _3, _4, _5, _6, TOTAL, ...) TOTAL #define NUM_ARGS(...) NUM_ARGS_(__VA_ARGS__, 6, 5, 4, 3, 2, 1) #define CONCATE_(X, Y) X##Y // Fixed the double ''_'' from previous code #define CONCATE(MACRO, NUMBER) CONCATE_(MACRO, NUMBER) #define VA_MACRO(MACRO, ...) CONCATE(MACRO, NUM_ARGS(__VA_ARGS__))(__VA_ARGS__)

Personalización

// This is how user may define own set of variadic macros #define MY_MACRO(...) VA_MACRO(MY_MACRO, __VA_ARGS__) #define MY_MACRO1(_1) "One" #define MY_MACRO2(_1, _2) "Two" #define MY_MACRO3(_1, _2, _3) "Three"

Uso

// While using those, user needs to use only the main macro int main () { auto one = MY_MACRO(1); auto two = MY_MACRO(1, 2); auto three = MY_MACRO(1, 2, 3); }


Pondría esto como un comentario en la publicación de Potatoswatter, pero es demasiado largo y requiere una lista de códigos.

Aquí hay un poco de código perl para generar un conjunto de macros que están destinadas a ser macros sobrecargadas.

$ perl -le ''map{ $arity = $_; map { $ar = 2 + $arity + $_; $arm = $ar - 1; $arlist = join("", map{"A$_, "} 1..$arity); $warlist = "WHAT, $arlist"; @li = map {"_$_"} 0..$_; $lis = join(", ", @li); $lim = pop @li; $lims = join(", ", @li); print "#define FEI_${arity}A_$ar($warlist$lis) FEI_${arity}A_$arm($warlist$lims) WHAT($_, $arlist$lim)" } 1..3; print "" } 0..4''

Aquí está la salida del script:

#define FEI_0A_3(WHAT, _0, _1) FEI_0A_2(WHAT, _0) WHAT(1, _1) #define FEI_0A_4(WHAT, _0, _1, _2) FEI_0A_3(WHAT, _0, _1) WHAT(2, _2) #define FEI_0A_5(WHAT, _0, _1, _2, _3) FEI_0A_4(WHAT, _0, _1, _2) WHAT(3, _3) #define FEI_1A_4(WHAT, A1, _0, _1) FEI_1A_3(WHAT, A1, _0) WHAT(1, A1, _1) #define FEI_1A_5(WHAT, A1, _0, _1, _2) FEI_1A_4(WHAT, A1, _0, _1) WHAT(2, A1, _2) #define FEI_1A_6(WHAT, A1, _0, _1, _2, _3) FEI_1A_5(WHAT, A1, _0, _1, _2) WHAT(3, A1, _3) #define FEI_2A_5(WHAT, A1, A2, _0, _1) FEI_2A_4(WHAT, A1, A2, _0) WHAT(1, A1, A2, _1) #define FEI_2A_6(WHAT, A1, A2, _0, _1, _2) FEI_2A_5(WHAT, A1, A2, _0, _1) WHAT(2, A1, A2, _2) #define FEI_2A_7(WHAT, A1, A2, _0, _1, _2, _3) FEI_2A_6(WHAT, A1, A2, _0, _1, _2) WHAT(3, A1, A2, _3) #define FEI_3A_6(WHAT, A1, A2, A3, _0, _1) FEI_3A_5(WHAT, A1, A2, A3, _0) WHAT(1, A1, A2, A3, _1) #define FEI_3A_7(WHAT, A1, A2, A3, _0, _1, _2) FEI_3A_6(WHAT, A1, A2, A3, _0, _1) WHAT(2, A1, A2, A3, _2) #define FEI_3A_8(WHAT, A1, A2, A3, _0, _1, _2, _3) FEI_3A_7(WHAT, A1, A2, A3, _0, _1, _2) WHAT(3, A1, A2, A3, _3) #define FEI_4A_7(WHAT, A1, A2, A3, A4, _0, _1) FEI_4A_6(WHAT, A1, A2, A3, A4, _0) WHAT(1, A1, A2, A3, A4, _1) #define FEI_4A_8(WHAT, A1, A2, A3, A4, _0, _1, _2) FEI_4A_7(WHAT, A1, A2, A3, A4, _0, _1) WHAT(2, A1, A2, A3, A4, _2) #define FEI_4A_9(WHAT, A1, A2, A3, A4, _0, _1, _2, _3) FEI_4A_8(WHAT, A1, A2, A3, A4, _0, _1, _2) WHAT(3, A1, A2, A3, A4, _3)

Estos son los (grupos regularmente estructurados de) grupos de sobrecargas de macro que se usan para generar FOR_EACH (también conocido como FE ) que pueden enviar una macro WHAT opcionalmente con un número arbitrario de argumentos constantes ( A1 , A2 ...) además de un número arbitrario de argumentos en una lista, junto con un índice en el orden correcto (una implementación ingenua sin usar algo como SELECT para la sobrecarga produciría índices invertidos).

A modo de ejemplo, la sección restante (la parte no regular del "caso base" del segundo bloque) se parece a lo siguiente:

#define FE_INDEXED_1ARG(...) VA_SELECT(FEI_1A, __VA_ARGS__) #define FEI_1A_3(WHAT, A1, _0) WHAT(0, A1, _0)

Quizás se pueda cuestionar la utilidad de esto (lo construí porque vi un uso para él ...), y esto tampoco responde directamente a la pregunta del OP (de hecho, de alguna manera hace lo contrario: una construcción foreach) hace lo mismo con todos los argumentos variados ...), pero simplemente pensé que la técnica es bastante interesante (además de absolutamente horrorosa en algunos aspectos) y permite bastante poder expresivo usando el preprocesador y será posible generar Código de máquina muy eficiente de esta manera. Creo que también sirve como un ejemplo conmovedor de por qué personalmente creo que el preprocesador C todavía tiene margen de mejora.

Con esto quiero decir que el preprocesador de C es una abominación absoluta y probablemente deberíamos descartarlo y empezar de cero :)