2010-02-22 8 views
21

È possibile scrivere una macro C che restituisca il numero dei suoi argomenti?La macro restituisce il numero di argomenti che viene fornito in C?

Voglio qualcosa che fa:

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

Ancora meglio se questa macro può essere definito in modo tale che funziona con ## in modo che

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

Grazie!

MODIFICA: Voglio davvero una macro, non una funzione. I suggerimenti per utilizzare le funzioni saranno downvoted.

+2

Perché non utilizzare solo una funzione con un elenco di argomenti a lunghezza variabile? –

+1

Se potessi farlo in un modo significativo, lo faremo tutti ora e tu lo sapresti. – jbcreix

+0

@jbcreix Non puoi inventare nulla di nuovo con questo approccio :) È possibile - vedi la mia risposta qui sotto. – qrdl

risposta

65

Può essere fatto - il meccanismo è stato spiegato nel newsgroup comp.std.c nel gennaio 2006. C'era un'altra domanda su questo recentemente su SO 2124339.

Ho conservato il codice via, nel caso in cui ...

#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 */ 

Funziona bene fino a quando non ci sono più di 64 argomenti. Ecco il codice di prova che ho usato:

#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

Intelligente ... sfortunatamente (per me comunque), richiede il supporto in stile C99 -__VA_ARGS__'. Comunque, molto intelligente. –

+4

Questo è geniale. Merita più upvotes. – anon

+4

@MichaelBurr Penso che chiedere il supporto per uno standard di più di 10 anni fa sia ragionevole. –

1

Io uso seguente macro:

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

prega di notare che funziona solo per C99, perché le macro variadic non erano supportate in C89. Anche se non funziona per argomenti zero.

Ma se si sta utilizzando GCC, è possibile utilizzare macro leggermente modificata:

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

Funziona correttamente anche per zero argomenti, perché preprocessore di GCC rimuove virgola in più quando si incolla vuoto __VA_ARGS__.

+3

Sfortunatamente, questo non funziona se gli argomenti di NUMARGS() non sono definiti o sono stringhe, come in 'int main() { restituisce NUMARGS (" a ", b, c); } ' – 18446744073709551615

3

Mi rendo conto che questa è una domanda piuttosto vecchia, ma poiché nessuno ha mai risposto al "bonus" per aggiungere il numero ai nomi delle funzioni, ecco un esempio di quella parte con la risposta di Jonathan ridotta a 9 arg per brevità. Presuppone che tu abbia predefinito le funzioni numerate, o lo userai come base per definirle.

#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 posto in cui ho visto questo tipo di funzioni è stato in chiamate di sistema del kernel di Linux, ma i numeri sarebbe fuori da uno perché il primo argomento è il numero syscall (di solito definita come __NR_something), ecco un esempio che spiega quello.

#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 
Problemi correlati