C Preprocessor tricks
In this blog, I will show some examples about advanced C Preprocessor examples. First of all, I would say CPP macros are ugly, hard to debug and error prone.
define a list of items
I would like to use an example to illustrate the idea behind it. For example, I would like to implementat a set of unix command as below
#define CMD(XX) XX(ls) XX(cp) XX(rm) XX(echo)
Then I could be able to write a common template function as below.
#define DEFINE_COMMON_TEMPLATE(name)            \
void name()                                     \
{                                               \
   printf(#name " is not implemented\n");       \
}
CMD(DEFINE_COMMON_TEMPLATE)
It will expanded to
void ls ()
{
  printf ("ls" " is not implemented\n");
}
void cp ()
{
  printf ("cp" " is not implemented\n");
} // ...
In the main function, we can use the similiar trick, as below
#define COMMAND_SWITCH(name)                    \
    if (strcmp(argv[1], #name) == 0){           \
        name();                                 \
    } else
    CMD(COMMAND_SWITCH) {
        printf("unknown commmand %s\n", argv[1]);
    }
And it will expanded into
  if (strcmp (argv[1], "ls") == 0)
    {
      ls ();
    }
  else if (strcmp (argv[1], "cp") == 0)
    {
      cp ();
    }
  else if (strcmp (argv[1], "rm") == 0)
    {
      rm ();
    }
  else if (strcmp (argv[1], "echo") == 0)
    {
      echo ();
    }
  else
    {
      printf ("unknown commmand %s\n", argv[1]);
    }
The whole program is listed here.
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#define CMD(XX) XX(ls) XX(cp) XX(rm) XX(echo)
#define DEFINE_COMMON_TEMPLATE(name)            \
void name()                                     \
{                                               \
   printf(#name " is not implemented\n");       \
}
CMD(DEFINE_COMMON_TEMPLATE)
int main(int argc, char *argv[])
{
#define COMMAND_SWITCH(name)                    \
    if (strcmp(argv[1], #name) == 0){           \
        name();                                 \
    } else
    CMD(COMMAND_SWITCH) {
        printf("unknown commmand %s\n", argv[1]);
    }
    return 0;
}
get the number of varadic argument
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#define _GET_N_OF_ARGS(_0,_1,_2,_3,_4,N,...) N
#define N_OF_ARGS(...) _GET_N_OF_ARGS(__VA_ARGS__,5,4,3,2,1,0)
int main(int argc, char *argv[])
{
    printf("%d\n", N_OF_ARGS(a));
    printf("%d\n",N_OF_ARGS(a,b));
    printf("%d\n",N_OF_ARGS(a,b,c));
    printf("%d\n",N_OF_ARGS(a,b,c,d));
    printf("%d\n",N_OF_ARGS(a,b,c,d,e));
    return 0;
}
N_OF_ARGS can handle at most 5 arguments and at least 1, otherwise,
it is unpredictable error.
int main(int argc, char *argv[])
{
    printf("%d\n",1);
    printf("%d\n",2);
    printf("%d\n",3);
    printf("%d\n",4);
    printf("%d\n",5);
    return 0;
}
But
N_OF_ARGS()
// => _GET_N_OF_ARGS(,5,4,3,2,1)
// => 1
printf("%d\n",N_OF_ARGS(a,b,c,d,e,f));
// => _GET_N_OF_ARGS(a,b,c,d,e,f)
// => f