C macro magic - PP_NARG

I've just spent two days delivering my C Foundation course to the massively talented people at Cisco (nee Tandberg nee Codian) in Langley. One of the guys on the course called Alex showed me an amazing C99 macro that counts the number of parameters. The capacity of C to surprise me with something new never ceases to amaze. Here it is (cut down to handle just 6 parameters to save space):
#include <stdio.h>

#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, N, ...)   (N)

#define PP_RSEQ_N() \
    6,5,4,3,2,1,0

int main(void)
{
    printf("%d\n", PP_NARG(a,b+c+d)); // 2
    printf("%d\n", PP_NARG(a,b,c,d,e+f)); // 5
    return 0;
}
Alex mentioned it doesn't work for zero parameters - it incorrectly returns one. Of course I can't resist seeing if I can fix that. After playing around a bit I've come up with this which seems to work.
#define PP_NARG(...) \
    PP_NARG_(sizeof #__VA_ARGS__, __VA_ARGS__, PP_RSEQ_N())

#define PP_NARG_(delta, ...) \
    PP_ARG_N(delta, __VA_ARGS__)

#define PP_ARG_N( \
    delta, _1, _2, _3, _4, _5, _6, N, ...)   (N - ((delta)==1))

#define PP_RSEQ_N() \
    6,5,4,3,2,1,0
Alex joked that at parties he's tried to impress girls with this macro. Of course he increases the number of parameters to 128. Size matters after all. And naturally he boasts not only of the macro's size but also of it capacity for expansion!

4 comments:

  1. Lars Petter5:09 pm

    Hi Jon!

    Yes, we like Macros! In C, clever usage of Macros can be extremely useful.

    My question is whether it is possible to automagically rename a function name depending on the dimension of a fixed size matrix argument using a single macro?

    void mul2(int a[2][2])
    void mul4(int a[4][4])
    void mul8(int a[8][8])

    So basically the name of the function should be mulN if the matrix has dimensions [N][N]. I know howto do this manually, but i would like to do it automagically using a single macro.

    ReplyDelete
  2. Hi Lars,
    I'm not quite sure what you mean by "rename". You can do this:

    #define DECLARE_MUL(N) \
    void mul ## N (int a[N][N] )

    DECLARE_MUL(2);
    DECLARE_MUL(4);
    DECLARE_MUL(8);

    Which pre-processes to this:

    void mul2(int a[2][2]);
    void mul4(int a[4][4]);
    void mul8(int a[8][8]);

    Is that any use?

    ReplyDelete
  3. Lars Petter6:53 pm

    Yes, thanks Jon, that's exactly what I needed! Most useful actually, this will lead to less code and more software!

    ReplyDelete