cmacrosc-preprocessordefault-valuevariadic-macros

Default arguments to C macros


Suppose I have function bshow() with signature

void bshow(int arg0, int arg1, int arg2);

but for arbitrary reasons I want to implement it as a macro.

Furthermore, I want the function have default arguments

int arg0=0x10;
int arg1=0x11;
int arg2=0x12;

I've already done this for the case that bshow() is a function, using the standard tricks.

But how can I do it as a macro?

Eg. suppose I have a macro nargs() that uses the C Preprocessor to count the number of arguments. Eg.

nargs()     // get replaced by 0 by the preprocessor
nargs(a)    // get replaced by 1 by the preprocessor
nargs(a,b)  // get replaced by 2 by the preprocessor

I'd like to do something like (which doesn't work):

#define arg0_get(a0,...) a0
#define arg1_get(a0,a1,...) a1
#define arg2_get(a0,a1,a2,...) a2

#define bshow(...)  do{  \
  int arg0=0x10;  if(0<nargs(__VA_ARGS__)) arg0 = arg0_get(__VA_ARGS__);  \
  int arg1=0x11;  if(1<nargs(__VA_ARGS__)) arg1 = arg1_get(__VA_ARGS__);  \
  int arg2=0x12;  if(2<nargs(__VA_ARGS__)) arg2 = arg2_get(__VA_ARGS__);  \
  /* do stuff here */ \
}while(0)

Actually I've already implemented the bshow() function as a macro, as follows (here it has the actual number of arguments):

#define __bshow(bdim,data, nbits,ncols,base)({  \
  bdim,data, nbits,ncols,base; \
  putchar(0x0a);  \
  printf("nbits %d\n",nbits);  \
  printf("ncols %d\n",ncols);  \
  printf("base  %d\n",base);   \
})

#define _bshow(bdim,data, nbits,ncols,base, ...) __bshow(bdim,data, nbits,ncols,base)
#define bshow(...)  \
  if(     2==nargs(__VA_ARGS__))  _bshow(__VA_ARGS__, 32,24,16,0,__VA_ARGS__);  \
  else if(3==nargs(__VA_ARGS__))  _bshow(__VA_ARGS__, 24,16,0,__VA_ARGS__);  \
  else if(4==nargs(__VA_ARGS__))  _bshow(__VA_ARGS__, 16,0,__VA_ARGS__);  \
  else if(5==nargs(__VA_ARGS__))  _bshow(__VA_ARGS__, 0,__VA_ARGS__);  \

// test
bshow(0,1);
bshow(0,1, 10);
bshow(0,1, 10,11);
bshow(0,1, 10,11,12);

EDIT:

The proposed solution doesn't have the intended effect because it seems to "instantiate" all instances of the macro, which in general has unintended consequences.


But I wonder if there's a more elegant way to do it.

It'd also be nice to abstract away the entire construction inside its own macro, so that one can apply it to other functions easily, as opposed to having to write the boilerplate manually for each function/macro.

Also this wasn't too helpful.


Solution

  • I found a nice answer.

    What you do is you call the vfn() macro, which is (I think) a higher-order macro that returns a macro that returns the token concatenated with the number of args (in hex base, no 0-padding) and then evaluates it at the args. Or something.

    Eg. supposed you want to overload a macro called bshow(). You #define the macro bshow() as #define bshow() vfn(bshow,__VA_ARGS__), and you define 1 instance of bshow for each argument count (eg. #define bshow0(...), for 0 arguments, #define bshow1(...) for 1 argument, #define bshow2(...) for 2 arguments, etc.). So now, eg., bshow(0,1) returns bshow2() (because you called it with 2 arguments) evaluated at (0,1), which is _bshow(0,1, 16,32,16), and then _bshow(0,1, 16,32,16) gets evaluated too. You can check the final preprocessor output by running gcc with the -E option, but the intermediate steps are hard to understand (for me).

    You also need to decide on the mandatory args and the optional args.

    That's almost all I (sort of) understand about what's going on, although I did upload a YT tutorial a while ago on how the argument-counting works.

    // ----------------------------------------------------------------
    // library
    #define __nargs100__(a00,a01,a02,a03,a04,a05,a06,a07,a08,a09,a0a,a0b,a0c,a0d,a0e,a0f,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a1a,a1b,a1c,a1d,a1e,a1f,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a2a,a2b,a2c,a2d,a2e,a2f,a30,a31,a32,a33,a34,a35,a36,a37,a38,a39,a3a,a3b,a3c,a3d,a3e,a3f,a40,a41,a42,a43,a44,a45,a46,a47,a48,a49,a4a,a4b,a4c,a4d,a4e,a4f,a50,a51,a52,a53,a54,a55,a56,a57,a58,a59,a5a,a5b,a5c,a5d,a5e,a5f,a60,a61,a62,a63,a64,a65,a66,a67,a68,a69,a6a,a6b,a6c,a6d,a6e,a6f,a70,a71,a72,a73,a74,a75,a76,a77,a78,a79,a7a,a7b,a7c,a7d,a7e,a7f,a80,a81,a82,a83,a84,a85,a86,a87,a88,a89,a8a,a8b,a8c,a8d,a8e,a8f,a90,a91,a92,a93,a94,a95,a96,a97,a98,a99,a9a,a9b,a9c,a9d,a9e,a9f,aa0,aa1,aa2,aa3,aa4,aa5,aa6,aa7,aa8,aa9,aaa,aab,aac,aad,aae,aaf,ab0,ab1,ab2,ab3,ab4,ab5,ab6,ab7,ab8,ab9,aba,abb,abc,abd,abe,abf,ac0,ac1,ac2,ac3,ac4,ac5,ac6,ac7,ac8,ac9,aca,acb,acc,acd,ace,acf,ad0,ad1,ad2,ad3,ad4,ad5,ad6,ad7,ad8,ad9,ada,adb,adc,add,ade,adf,ae0,ae1,ae2,ae3,ae4,ae5,ae6,ae7,ae8,ae9,aea,aeb,aec,aed,aee,aef,af0,af1,af2,af3,af4,af5,af6,af7,af8,af9,afa,afb,afc,afd,afe,aff,a100,...)  a100
    #define __nargs__(...)   __nargs100__(,##__VA_ARGS__, ff,fe,fd,fc,fb,fa,f9,f8,f7,f6,f5,f4,f3,f2,f1,f0,ef,ee,ed,ec,eb,ea,e9,e8,e7,e6,e5,e4,e3,e2,e1,e0,df,de,dd,dc,db,da,d9,d8,d7,d6,d5,d4,d3,d2,d1,d0,cf,ce,cd,cc,cb,ca,c9,c8,c7,c6,c5,c4,c3,c2,c1,c0,bf,be,bd,bc,bb,ba,b9,b8,b7,b6,b5,b4,b3,b2,b1,b0,af,ae,ad,ac,ab,aa,a9,a8,a7,a6,a5,a4,a3,a2,a1,a0,9f,9e,9d,9c,9b,9a,99,98,97,96,95,94,93,92,91,90,8f,8e,8d,8c,8b,8a,89,88,87,86,85,84,83,82,81,80,7f,7e,7d,7c,7b,7a,79,78,77,76,75,74,73,72,71,70,6f,6e,6d,6c,6b,6a,69,68,67,66,65,64,63,62,61,60,5f,5e,5d,5c,5b,5a,59,58,57,56,55,54,53,52,51,50,4f,4e,4d,4c,4b,4a,49,48,47,46,45,44,43,42,41,40,3f,3e,3d,3c,3b,3a,39,38,37,36,35,34,33,32,31,30,2f,2e,2d,2c,2b,2a,29,28,27,26,25,24,23,22,21,20,1f,1e,1d,1c,1b,1a,19,18,17,16,15,14,13,12,11,10,f,e,d,c,b,a,9,8,7,6,5,4,3,2,1,0)
    #define __vfn(name, n)  name##n
    #define _vfn( name, n)  __vfn(name, n)
    #define vfn(  fn, ...)  _vfn(fn, __nargs__(__VA_ARGS__))(__VA_ARGS__)
    
    // ----------------------------------------------------------------
    // example
    
    // backend: actual implementation, 2 mandatory args, 3 optional args
    #define _bshow(bdim,data, ncols,nbits,base)({  \
      /* do stuff here */  \
    })
    
    // "frontend", default arguments get implemented here. the suffix is the number of arguments, in hexadecimal base
    #define bshow2(...)  _bshow(__VA_ARGS__, 16,32,16)
    #define bshow3(...)  _bshow(__VA_ARGS__, 32,16)
    #define bshow4(...)  _bshow(__VA_ARGS__, 16)
    #define bshow5(...)  _bshow(__VA_ARGS__)
    #define bshow(...)  vfn(bshow,__VA_ARGS__)
    
    // test
    bshow(0x100,data0);
    bshow(0x100,data0, 14);
    bshow(0x100,data0, 12,16);
    bshow(0x100,data0, 10, 8,2);