2017年10月26日 星期四

[C,GCC,CPP] 巨集與不定變數 (## __VA_ARGS__)

為了debug方便,我們常會利用marcro將客製化printf其中牽扯了不定變數巨集的問題,如下說明:

#include <stdio.h>

#if VERSION==0
#define PRINTF(fmt,...)
#elif VERSION==1
#define PRINTF(fmt,...) printf(fmt,__VA_ARGS__) //C99 standard version. fmt is replaced by strings.
#else
#define PRINTF(fmt,...) printf(fmt,##__VA_ARGS__) //Use ## to solve the comma problem.
#endif
int main()
{
        PRINTF("Only one argument test\n");
        PRINTF("Two arguments test, and the time is %s\n",__TIME__);
}

VERSION 1會產生compiler error(")"前少了一個描述):

user@user:~/works/test/gnu_lab$ gcc -c -DVERSION=1 test.c -o test.i
test.c: In function ‘main’:
test.c:6:47: error: expected expression before ‘)’ token
 #define PRINTF(fmt,...) printf(fmt,__VA_ARGS__) //C99 standard version
test.c:10:2: note: in expansion of macro ‘PRINTF’
  PRINTF("Only one argument test\n");


利用-E(CPP)來將Macro展開,查看出了什麼問題(當只有一個參數時展開多了一個",")

user@user:~/works/test/gnu_lab$ gcc -E -DVERSION=1 test.c -o test.i
user@user:~/works/test/gnu_lab$ cat test.i
# 8 "test.c"
int main()
{
 printf("Only one argument test\n",);
 printf("Two arguments test, and the time is %s\n","10:57:36");
}

為了避免只有一個參數的錯誤,GNU CPP 使用 "##" 做為標記,當沒有不定變數時(只有一個參數)CPP會將"##"前的","移除

user@user:~/works/test/gnu_lab$ gcc -E -DVERSION=2 test.c -o test.i
int main()
{
 printf("Only one argument test\n");
 printf("Two arguments test, and the time is %s\n","10:57:36");
}

另外,為了增加可讀性,GCC允許在並定變數前增加其使用方式如下:

#define PRINTF(fmt,args...) printf(fmt,args)



Note:
  1. …: variable argument
  2. __VA_ARGS__: this identifier is replaced by variable argument


Reference:

  1. http://stackoverflow.com/questions/5588855/standard-alternative-to-gccs-va-args-trick
  2. https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html

沒有留言:

張貼留言