Makro, das die Anzahl der Argumente zurückgibt, die in C angegeben sind? [duplicate]

Lesezeit: 7 Minuten

Benutzer-Avatar
anon

Ist es möglich, ein C-Makro zu schreiben, das die Anzahl seiner Argumente zurückgibt?

Ich möchte etwas, das Folgendes tut:

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

Noch besser, wenn dieses Makro so definiert werden kann, dass es mit ## so funktioniert

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

Vielen Dank!

EDIT: Ich möchte wirklich ein Makro, keine Funktion. Vorschläge zur Verwendung von Funktionen werden abgelehnt.

  • Warum nicht einfach eine Funktion mit einer Argumentliste variabler Länge verwenden?

    – Anonym.

    22. Februar 2010 um 1:14 Uhr

  • Wenn Sie es auf sinnvolle Weise tun könnten, würden wir es jetzt alle tun, und Sie wüssten es.

    Benutzer14554

    22. Februar 2010 um 1:14 Uhr

  • @jbcreix Mit diesem Ansatz können Sie nichts Neues erfinden 🙂 Es ist möglich – siehe meine Antwort unten.

    – qrdl

    22. Februar 2010 um 6:17 Uhr

  • @Anon, in C in einer Argumentliste mit variabler Länge müssen Sie die Anzahl der Argumente auf die eine oder andere Weise kennen. ZB kann printf abstürzen, wenn Ihre Anzahl an Argumenten nicht der im Formatstring erwarteten Anzahl entspricht (eigentlich: nur wenn nicht genügend Argumente angegeben werden und dann kommt es auch noch auf die Typen an).

    – Patrick

    22. Februar 2010 um 6:26 Uhr

Benutzer-Avatar
Jonathan Leffler

Es ist machbar – der Mechanismus wurde in der Newsgroup comp.std.c im Januar 2006 erklärt. Dazu gab es kürzlich eine weitere Frage in SO 2124339.

Ich habe den Code versteckt, nur für den Fall…

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

Es funktioniert gut, solange es nicht mehr als 64 Argumente gibt. Hier ist der Testcode, den ich verwendet habe:

#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);
}

  • Clever … leider (für mich jedenfalls) erfordert es C99-Stil __VA_ARGS__ Unterstützung. Trotzdem sehr clever.

    – Michael Burr

    22. Februar 2010 um 5:37 Uhr

  • Das ist genial. Verdient mehr Upvotes.

    – gleich

    22. Februar 2010 um 8:47 Uhr

  • @MichaelBurr Ich denke, es ist vernünftig, um Unterstützung für einen Standard von vor mehr als 10 Jahren zu bitten.

    – Matti Virkkunen

    6. Oktober 2013 um 1:22 Uhr

  • Wenngleich using PP_NARG() without arguments would violate 6.10.3p4 of ISO C99, es gibt eine Möglichkeit, es zum Laufen zu bringen, zumindest mit gcc. Definieren PP_NARG so was: #define PP_NARG(...) PP_NARG_(_,##__VA_ARGS__,PP_RSEQ_N()) und PP_NARG_so was: #define PP_NARG_(_, ...) PP_ARG_N(__VA_ARGS__). Beachten Sie die hinzugefügte Fälschung _ erster Parameter und die Verwendung von ## um leere Varargs zu eliminieren.

    – Fabio A.

    20. März 2018 um 14:24 Uhr


Mir ist klar, dass dies eine ziemlich alte Frage ist, aber da noch nie jemand auf den „Bonus“ geantwortet hat, um die Zahl zu den Funktionsnamen hinzuzufügen, ist hier ein Beispiel für diesen Teil, bei dem Jonathans Antwort der Kürze halber auf 9 Argumente reduziert ist. Es geht davon aus, dass Sie die nummerierten Funktionen vordefiniert haben oder diese als Grundlage verwenden, um sie zu definieren.

#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);

Ein Ort, an dem ich diese Art von Funktionen gesehen habe, war in den Syscalls des Linux-Kernels, aber die Zahlen waren um eins falsch, weil das erste Argument die Syscall-Nummer ist (normalerweise als __NR_irgendwas definiert), also ist hier ein Beispiel, das dies berücksichtigt.

#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

Benutzer-Avatar
qrdl

Ich verwende folgendes Makro:

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

Bitte beachten Sie, dass es nur für C99 funktioniert, da variadische Makros in C89 nicht unterstützt wurden. Obwohl es für null Argumente nicht funktioniert.

Wenn Sie jedoch GCC verwenden, können Sie ein leicht modifiziertes Makro verwenden:

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

Es funktioniert auch für Nullargumente korrekt, da der Präprozessor von GCC zusätzliche Kommas entfernt, wenn Sie leer einfügen __VA_ARGS__.

  • Leider funktioniert dies nicht, wenn die Argumente für NUMARGS() undefiniert oder Strings sind, wie in int main() { return NUMARGS("a",b,c); }

    – 18446744073709551615

    15. Mai 2014 um 7:58 Uhr

1361980cookie-checkMakro, das die Anzahl der Argumente zurückgibt, die in C angegeben sind? [duplicate]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy