tags:

views:

372

answers:

5

Is it possible to write a C macro that returns the length of its arguments?

I want something that does:

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

Even better if this macro can be defined in such a way that it works with ## so that

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

Thanks!

EDIT: I really want a macro, not a function. Suggestions to use functions will be downvoted.

A: 

How would this be useful? You have to write the function call, which means you know exactly how many arguments you've given it. You can then pass that number as the first argument.

You can even use this to determine which function to call based on the number of arguments using a switch.

David Kanarek
A: 

No such thing I'm afraid, but if you consult the C's FAQ, Look specifically in section 15, Variable Length Argument Lists, notably, question number 8 (15.8) and question number 4 (15.4)..

Hope this helps, Best regards, Tom.

tommieb75
This answer is incorrect.
Jonathan Leffler
@Jonathan: Hmmm...interesting code...but what's worrying me is that the legitimacy of the owner of the code is highlighted in the revision control Id, under your user name..is it open-sourced/public domain or what's the licence?
tommieb75
@tommieb75: the version control information simply shows that I have a copy under RCS control. As the comments in the header plainly show, I collected the code from public postings in the comp.std.c news group. I wrote the test code, but it can be treated as being in the public domain; I have no vested interest in licencing that. As for the actual macros, you have the email address of the author - contact them. I haven't had a use for the code, yet - other than this posting.
Jonathan Leffler
+9  A: 

It can be done - the mechanism was explained in the comp.std.c newsgroup in January 2006. There was another question about this recently on SO 2124339.

I stashed the code away, just in case...

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

It works fine as long as there are no more than 64 arguments. Here's the test code I used:

#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);
}
Jonathan Leffler
Clever... unfortunately (for me anyway), it requires C99-style `__VA_ARGS__` support. Still, very clever.
Michael Burr
This is ingenious. Deserves more upvotes.
anon
A: 

I use following macro:

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

Please note that it works only for C99, because variadic macros were not supported in C89. Although it doesn't work for zero arguments.

But if you are using GCC, you can use slightly modified macro:

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

It works correctly even for zero arguments, because GCC's preprocessor removes extra comma when you are pasting empty __VA_ARGS__.

qrdl
A: 

In VC++ 2008 I get the following:

warning C4003: not enough actual parameters for macro 'PP_ARG_N'

And the result of PP_NARG( ) with whatever arguments ends up expanded to nothing... To see what arguments PP_ARG_N ends up being called with, I commented out the #define PP_ARG_N ..... and the result is as expected.

Here's what I get after the preprocessor if I try PP_ARGN(a, b, c);

GCC:

3;

VC++2008:

;

In either, if I comment out #define PP_ARG_N ..... :

PP_ARG_N(a, b, c, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0);

So why the hell is VC++ telling me there aren't enough parameters??! If I enter the above line directly, it becomes 3; as expected. So what's going on with VC++? Is it doing the argument expansion and rescanning in the wrong order? Most importantly, how do I get around this? How do I change the above set of macros to get the number of items in VAR_ARG in VC++?