2010-02-22 9 views
21

¿Es posible escribir una macro C que devuelve el número de sus argumentos?¿Macro que devuelve el número de argumentos que se da en C?

Quiero algo que hace:

foo(1) -> 1 
foo(cat, dog) -> 2 
foo(red, green, blue) -> 3 

Aún mejor si esta macro se puede definir de una manera tal que se trabaja con ## para que

foo(1) -> bar1(1) 
foo(cat, dog) -> bar2(cat, dog) 
foo(red, green, blue) -> car3(red, green, blue) 

Gracias!

EDITAR: Realmente quiero un macro, no una función. Las sugerencias para usar funciones serán downvoted.

+2

¿Por qué no usar simplemente una función con una lista de argumentos de longitud variable? –

+1

Si pudieras hacerlo de una manera significativa, todos estaríamos haciéndolo ahora y lo sabrías. – jbcreix

+0

@jbcreix No se puede inventar nada nuevo con este enfoque :) Es posible - ver mi respuesta a continuación. – qrdl

Respuesta

65

Se puede hacer - el mecanismo fue explicado en el grupo de noticias comp.std.c en enero de 2006. Hubo otra pregunta sobre esto recientemente en SO 2124339.

guardé el código de distancia, por si acaso ...

#ifndef JLSS_ID_NARG_H 
#define JLSS_ID_NARG_H 

/* 
** http://groups.google.com/group/comp.std.c/browse_thread/thread/77ee8c8f92e4a3fb/346fc464319b1ee5?pli=1 
** 
** Newsgroups: comp.std.c 
** From: Laurent Deniau <[email protected]> 
** Date: Mon, 16 Jan 2006 18:43:40 +0100 
** Subject: __VA_NARG__ 
** 
** A year ago, I was asking here for an equivalent of __VA_NARG__ which 
** would return the number of arguments contained in __VA_ARGS__ before its 
** expansion. In fact my problem at that time (detecting for a third 
** argument) was solved by the solution of P. Mensonides. But I was still 
** thinking that the standard should have provided such a facilities rather 
** easy to compute for cpp. 
** 
** This morning I had to face again the same problem, that is knowing the 
** number of arguments contained in __VA_ARGS__ before its expansion (after 
** its expansion can always be achieved if you can do it before). I found a 
** simple non-iterative solution which may be of interest here as an answer 
** to who will ask in the future for a kind of __VA_NARG__ in the standard 
** and I post it for archiving. May be some more elegant-efficient solution 
** exists? 
** 
** Returns NARG, the number of arguments contained in __VA_ARGS__ before 
** expansion as far as NARG is >0 and <64 (cpp limits): 
** 
** #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,[..],_61,_62,_63,N,...) N 
** #define PP_RSEQ_N() 63,62,61,60,[..],9,8,7,6,5,4,3,2,1,0 
** 
** [..] stands for the continuation of the sequence omitted here for 
** lisibility. 
** 
** PP_NARG(A) -> 1 
** PP_NARG(A,B) -> 2 
** PP_NARG(A,B,C) -> 3 
** PP_NARG(A,B,C,D) -> 4 
** PP_NARG(A,B,C,D,E) -> 5 
** PP_NARG(A1,A2,[..],A62,A63) -> 63 
** 
** ====== 
** 
** Newsgroups: comp.std.c 
** From: Roland Illig <[email protected]> 
** Date: Fri, 20 Jan 2006 12:58:41 +0100 
** Subject: Re: __VA_NARG__ 
** 
** Laurent Deniau wrote: 
** > This morning I had to face again the same problem, that is knowing the 
** > number of arguments contained in __VA_ARGS__ before its expansion (after 
** > its expansion can always be achieved if you can do it before). I found a 
** > simple non-iterative solution which may be of interest here as an answer 
** > to who will ask in the future for a kind of __VA_NARG__ in the standard 
** > and I post it for archiving. May be some more elegant-efficient solution 
** > exists? 
** 
** Thanks for this idea. I really like it. 
** 
** For those that only want to copy and paste it, here is the expanded version: 
** 
** // Some test cases 
** PP_NARG(A) -> 1 
** PP_NARG(A,B) -> 2 
** PP_NARG(A,B,C) -> 3 
** PP_NARG(A,B,C,D) -> 4 
** PP_NARG(A,B,C,D,E) -> 5 
** PP_NARG(1,2,3,4,5,6,7,8,9,0, // 1..10 
**   1,2,3,4,5,6,7,8,9,0, // 11..20 
**   1,2,3,4,5,6,7,8,9,0, // 21..30 
**   1,2,3,4,5,6,7,8,9,0, // 31..40 
**   1,2,3,4,5,6,7,8,9,0, // 41..50 
**   1,2,3,4,5,6,7,8,9,0, // 51..60 
**   1,2,3) -> 63 
** 
**Note: using PP_NARG() without arguments would violate 6.10.3p4 of ISO C99. 
*/ 

/* The PP_NARG macro returns the number of arguments that have been 
** passed to it. 
*/ 

#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 

#endif /* JLSS_ID_NARG_H */ 

Funciona bien siempre y cuando no hay más de 64 argumentos. Aquí está el código de prueba utilicé:

#include "narg.h" 
#include <stdio.h> 

#define PRINT(pp_narg)  printf("%2d = %s\n", pp_narg, # pp_narg) 

#ifndef lint 
/* Prevent over-aggressive optimizers from eliminating ID string */ 
extern const char jlss_id_narg_c[]; 
const char jlss_id_narg_c[] = "@(#)$Id: narg.c,v 1.2 2010/01/24 18:12:05 jleffler Exp $"; 
#endif /* lint */ 

int 
main(void) 
{ 
    PRINT(PP_NARG(A)); 
    PRINT(PP_NARG(A, B)); 
    PRINT(PP_NARG(A, B, C)); 
    PRINT(PP_NARG(A, B, C, D)); 
    PRINT(PP_NARG(A, B, C, D, E)); 

    PRINT(PP_NARG(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, // 1..10 
        1, 2, 3, 4, 5, 6, 7, 8, 9, 0, // 11..20 
        1, 2, 3, 4, 5, 6, 7, 8, 9, 0, // 21..30 
        1, 2, 3, 4, 5, 6, 7, 8, 9, 0, // 31..40 
        1, 2, 3, 4, 5, 6, 7, 8, 9, 0, // 41..50 
        1, 2, 3, 4, 5, 6, 7, 8, 9, 0, // 51..60 
        1, 2, 3)); 

    /** 
    ** If the number of arguments to PP_NARG() is greater than 63, the 
    ** 64th argument is returned. This is well-defined behaviour, but 
    ** not exactly what was intended. 
    */ 
    PRINT(PP_NARG(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, // 1..10 
        1, 2, 3, 4, 5, 6, 7, 8, 9, 0, // 11..20 
        1, 2, 3, 4, 5, 6, 7, 8, 9, 0, // 21..30 
        1, 2, 3, 4, 5, 6, 7, 8, 9, 0, // 31..40 
        1, 2, 3, 4, 5, 6, 7, 8, 9, 0, // 41..50 
        1, 2, 3, 4, 5, 6, 7, 8, 9, 0, // 51..60 
        1, 2, 3, -123456789)); 

    PRINT(PP_NARG(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, // 1..10 
        1, 2, 3, 4, 5, 6, 7, 8, 9, 0, // 11..20 
        1, 2, 3, 4, 5, 6, 7, 8, 9, 0, // 21..30 
        1, 2, 3, 4, 5, 6, 7, 8, 9, 0, // 31..40 
        1, 2, 3, 4, 5, 6, 7, 8, 9, 0, // 41..50 
        1, 2, 3, 4, 5, 6, 7, 8, 9, 0, // 51..60 
        1, 2, 3, -123456789, -987654321)); 

    return(0); 
} 
+2

Inteligente ... desafortunadamente (para mí de todos modos), requiere compatibilidad con el estilo C99 '__VA_ARGS__'. Aún así, muy inteligente. –

+4

Esto es ingenioso. Se merece más votos por votos. – anon

+4

@MichaelBurr Creo que pedir soporte para un estándar de hace más de 10 años es razonable. –

1

utilizo macro siguiente:

#define NUMARGS(...) (sizeof((int[]){__VA_ARGS__})/sizeof(int)) 

Tenga en cuenta que sólo funciona para C99, porque macros variadic no fueron apoyadas en C89. Aunque no funciona para cero argumentos.

Pero si está utilizando GCC, puede utilizar macro ligeramente modificado:

#define NUMARGS(...) (sizeof((int[]){0, ##__VA_ARGS__})/sizeof(int)-1) 

Funciona correctamente incluso de cero argumentos, porque preprocesador de GCC elimina coma adicional cuando se está pegando vacío __VA_ARGS__.

+3

Desafortunadamente, esto no funciona si los argumentos a NUMARGS() no están definidos o son cadenas, como en 'int main() { return NUMARGS (" a ", b, c); } ' – 18446744073709551615

3

Me doy cuenta de que esta es una pregunta muy antigua, pero como nadie respondió el 'bonus' para agregar el número a los nombres de las funciones, aquí hay un ejemplo de la respuesta de Jonathan reducida a 9 args por brevedad. Asume que ha predefinido las funciones numeradas o las usará como base para definirlas.

#define MKFN(fn,...) MKFN_N(fn,##__VA_ARGS__,9,8,7,6,5,4,3,2,1,0)(__VA_ARGS__) 
#define MKFN_N(fn,n0,n1,n2,n3,n4,n5,n6,n7,n8,n,...) fn##n 
#define myfunc(...) MKFN(myfunc,##__VA_ARGS__) 
myfunc(); 
myfunc(a,b,c,d,e,f); 

//gcc -E this.c 
//myfunc0(); 
//myfunc6(a,b,c,d,e,f); 

un lugar donde he visto este tipo de funciones se encontraba en llamadas al sistema del kernel de Linux, pero los números sería fuera por uno porque el primer argumento es el número de llamada al sistema (generalmente definida como __NR_something), asi que aquí hay un ejemplo que explica eso.

#define MKFN(fn,...) MKFN_N(fn,##__VA_ARGS__,9,8,7,6,5,4,3,2,1,0)(__VA_ARGS__) 
#define MKFN_N(fn,NR,n0,n1,n2,n3,n4,n5,n6,n7,n8,n,...) fn##n 
#define syscall(...) MKFN(syscall,##__VA_ARGS__) 
syscall(__NR_fork); 
syscall(77,a,b,c,d,e,f);//fake example 
Cuestiones relacionadas