1. 程式人生 > 實用技巧 >日誌輸出可變引數巨集的使用記錄

日誌輸出可變引數巨集的使用記錄

#define COLOR_RED "\033[1, 31m"
#define COLOR_NONE "\033[0m"
#if DEBUG #define DBG_PRT(fmt, args...)\ do\ {\ printf(COLOR_RED"file:%s line:%s", __FILE__, __LINE__);\ printf(fmt,##args);\ }while(0)
#else
#define DGB_PRT(fmt, args...)
#endif

上述程式碼是在嵌入式日誌列印中常用的巨集函式,這個巨集列印函式可以顯示列印資訊所在的檔案、行數、函式名等一些你需要的資訊。另外需要注意的是,在巨集定義中儘量使用do while(0)結構來包含內容,如果用花括號{}來包含往往會出現一些錯誤,比如我將上述程式碼改成小面用花括號包含的形式:

#define DBG_PRT(fmt, args...)\
{\
    printf(COLOR_RED"file:%s line:%s", __FILE__, __LINE__);\
    printf(fmt, ##args);\
}
if (1)
  DGB_PRT("hello world"); 
展開後為:
if (1)
{
  printf(COLOR_RED"file:%s line:%s", __FILE__, __LINE__);printf(args);
};-->這裡會有一個;號,會有語法錯誤

上述DGB_PRT(fmt, args...)定義中, “...”符號表示的是可變引數,args...的寫法表示在後續的使用中
可以用args識別符號表示可變引數列表,然後在後面的巨集定義中,直接使用 args 代表變參列表就可以了。
我們在識別符號args 前面加上巨集連線符 ##,這樣做的好處是,當變參列表非空時,## 的作用是連線 fmt,和變參列表,
各個引數之間用逗號隔開,巨集可以正常使用;當變參列表為空時,## 還有一個特殊的用處,它會將固定引數 fmt 後面的逗號刪除掉,
這樣巨集也就可以正常使用了。

可變引數巨集的另外兩種寫法

第一種,使用__VA_ARGS__表示可變引數列表

#define DBG_PRT(fmt, ...)\
do\
{\ printf(COLOR_RED
"file:%s line:%s", __FILE__, __LINE__);\ printf(fmt, ##__VA_ARGS__);\ }while(0)

第二種寫法比較簡潔,直接將可變引數列表...和fmt識別符號連在一起,這樣後續使用的時候直接使用fmt識別符號,這個識別符號就同時包含了格式化字串和可變引數列表,同時可以應對引數為空的情形,因為可變引數列表和fmt是連線在一起的,可變引數列表為空時,最後fmt表示的內容就只有格式化字串了。

#define DBG_PRT(fmt...)\
do\
{\
    printf(COLOR_RED"file:%s line:%s", __FILE__, __LINE__);\
    printf(fmt);\
}while(0)

另外記錄一下#和##的使用方法(參考https://blog.csdn.net/baidu_33850454/article/details/79363033)

使用#把巨集引數變為一個字串——字串化操作符

用##把兩個巨集引數貼合在一起——字元連線操作符

一般用法

#include<cstdio>
#include<climits>
using namespace std;
#define STR(s)     #s
#define CONS(a,b)  int(a##e##b)
int main()
{
   printf(STR(vck));           // 輸出字串"vck"
   printf("%d\n", CONS(2,3));  // 2e3 輸出:2000
   return 0;
}

注意: 當巨集引數是另一個巨集的時候,需要注意的是凡巨集定義裡有用’#’或’##’的地方巨集引數是不會再展開.
即, 只有當前巨集生效, 引數裡的巨集!不!會!生!效 !!!!

#define A          (2)
#define STR(s)     #s
#define CONS(a,b)  int(a##e##b)
printf("int max: %s\n",  STR(INT_MAX));    // INT_MAX #
printf("%s\n", CONS(A, A));                // compile error --- int(AeA)

展開後:

printf("int max: %s\n","INT_MAX");
printf("%s\n", int(AeA));

由於A和INT_MAX均是巨集,且作為巨集CONS和STR的引數,並且巨集CONS和STR中均含有#或者##符號,所以A和INT_MAX均不能被解引用。導致不符合預期的情況出現。

解決這個問題的方法很簡單. 加多一層中間轉換巨集. 加這層巨集的用意是把所有巨集的引數在這層裡全部展開,那麼在轉換巨集裡的那一個巨集(_STR)就能得到正確的巨集引數.

#define A           (2)
#define _STR(s)     #s
#define STR(s)      _STR(s)          // 轉換巨集
#define _CONS(a,b)  int(a##e##b)
#define CONS(a,b)   _CONS(a,b)       // 轉換巨集

printf("int max: %s\n",STR(INT_MAX));
//輸出為: int max:0x7fffffff
//STR(INT_MAX) --> _STR(0x7fffffff)
然後再轉換成字串; printf("%d\n", CONS(A, A));
//輸出為:200
//CONS(A, A) --> _CONS((2), (2)) --> int((2)e(2))