0

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.

étale-cohomology
  • 2,098
  • 2
  • 28
  • 33
  • The reason why this isn't possible in any elegant way is because the original specification doesn't make sense - you shouldn't _want_ to have a variable number of arguments to a function, because that in itself doesn't solve any actual real-world problem. Simply make a function that takes an array and a size. – Lundin Jun 07 '22 at 14:38
  • I'm really confused, you asked a question, then answered it with a video you did about the question a year ago? Do you need any additional help? Because I'm not sure if your edit is about your answer. – camel-cdr Jun 07 '22 at 20:13
  • étale-cohomology, _standard tricks_ fail when 0 is a specified argument. – chux - Reinstate Monica Jul 17 '22 at 03:58

1 Answers1

0

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);
halfer
  • 19,824
  • 17
  • 99
  • 186
étale-cohomology
  • 2,098
  • 2
  • 28
  • 33
  • 4
    I have rolled this back as a non-mod in order to help you avoid a suspension. Please consider that this is not a battle that can be won, or that it is a battle that is worth winning. If you are an evangelist then knocking on an extra door will achieve far more than adding off-topic material here. The key issue is not that Stack Overflow editors are anti-religion, it's that we are pro-technical writing. In the same way as one cannot litter documentation with politics, religion, and conversation, one does not do it here either. I urge you to resist the temptation to rollback again. – halfer Aug 29 '22 at 09:19