__VA_ARGS__相關【轉】
阿新 • • 發佈:2019-02-08
自定義除錯資訊的輸出
除錯資訊的輸出方法有很多種, 例如直接用printf, 或者出錯時使用perror, fprintf等將資訊直接列印到終端上, 在Qt上面一般使用qDebug,而守護程序則一般是使用syslog將除錯資訊輸出到日誌檔案中等等...
使用標準的方法列印除錯資訊有時候不是很方便, 例如Qt程式設計, 在除錯已有的程式碼時, 我想在列印除錯資訊的地方, 把程式碼位置也打印出來以方便定位錯誤, 或者需要在除錯資訊前面加一個前輟, 好方便在除錯資訊太多的時候可以用grep過濾一下, 僅顯示本模組的除錯資訊, 這時就需要一個一個地修改已有的qDebug, 使其成為以下形式:
qDebug( "[模組名稱] 除錯資訊 File:%s, Line:%d", __FILE__, __LINE__ );
這樣的修改比較煩人, 而且一不小心會遺漏某個沒改的... 為了能方便地管理除錯資訊的輸出,一個比較簡單的方法就是自已定義一個列印除錯資訊的巨集, 然後替換原來的,廢話就不多說了,直接給出一個現成的,下面是一個例子, 我用WiFi表示當前程式碼的模組名稱,我要求在模組中的所有除錯資訊前面均帶有[WiFi]前輟,這樣我就能方便地只需使用命令列 | grep "\[WiFi\]"來過濾掉來自其它模組的除錯資訊了: #define qWiFiDebug(format, ...) qDebug("[WiFi] "format" File:%s, Line:%d, Function:%s", ##__VA_ARGS__, __FILE__, __LINE__ , __FUNCTION__); 上面的巨集是使用qDebug輸出除錯資訊,在非Qt的程式中也可以改為printf,守護程序則可以改為syslog等等... 其中,決竅其實就是這幾個巨集 ##__VA_ARGS__, __FILE__, __LINE__ 和__FUNCTION__,下面介紹一下這幾個巨集: 1) __VA_ARGS__ 是一個可變引數的巨集,很少人知道這個巨集,這個可變引數的巨集是新的C99規範中新增的,目前似乎只有gcc支援(VC6.0的編譯器不支援)。巨集前面加上##的作用在於,當可變引數的個數為0時,這裡的##起到把前面多餘的","去掉的作用,否則會編譯出錯, 你可以試試。
2) __FILE__ 巨集在預編譯時會替換成當前的原始檔名
3) __LINE__巨集在預編譯時會替換成當前的行號
4) __FUNCTION__巨集在預編譯時會替換成當前的函式名稱 有了以上這幾個巨集,特別是有了__VA_ARGS__ ,除錯資訊的輸出就變得靈活多了。
有時,我們想把除錯資訊輸出到螢幕上,而有時則又想把它輸出到一個檔案中,可參考下面的例子: //debug.c #include <stdio.h>
#include <string.h> //開啟下面的巨集表示程式執行在除錯版本, 否則為發行版本, 這裡假設只有除錯版本才輸出除錯資訊
#define _DEBUG #ifdef _DEBUG
//開啟下面的巨集就把除錯資訊輸出到檔案,註釋即輸出到終端
#define DEBUG_TO_FILE
#ifdef DEBUG_TO_FILE
//除錯資訊輸出到以下檔案
#define DEBUG_FILE "/tmp/debugmsg"
//除錯資訊的緩衝長度
#define DEBUG_BUFFER_MAX 4096
//將除錯資訊輸出到檔案中
#define printDebugMsg(moduleName, format, ...) {\
char buffer[DEBUG_BUFFER_MAX+1]={0};\
snprintf( buffer, DEBUG_BUFFER_MAX \
, "[%s] "format" File:%s, Line:%d\n", moduleName, ##__VA_ARGS__, __FILE__, __LINE__ );\
FILE* fd = fopen(DEBUG_FILE, "a");\
if ( fd != NULL ) {\
fwrite( buffer, strlen(buffer), 1, fd );\
fflush( fd );\
fclose( fd );\
}\
}
#else
//將除錯資訊輸出到終端
#define printDebugMsg(moduleName, format, ...) \
printf( "[%s] "format" File:%s, Line:%d\n", moduleName, ##__VA_ARGS__, __FILE__, __LINE__ );
#endif //end for #ifdef DEBUG_TO_FILE
#else
//發行版本,什麼也不做
#define printDebugMsg(moduleName, format, ...)
#endif //end for #ifdef _DEBUG int main(int argc, char** argv)
{
int data = 999;
printDebugMsg( "TestProgram", "data = %d", data );
return 0;
}
上面也說了,只有支援C99規範的gcc編譯器才有__VA_ARGS__這個巨集,如果不是gcc編譯器,或者所用的gcc編譯器版本不支援__VA_ARGS__巨集怎麼辦? 可參考下面的程式碼片段,我們換一種做法,可先將可變引數轉換成字串後,再進行輸出即可: void printDebugMsg( const char* format, ...)
{
char buffer[DEBUG_BUFFER_MAX_LENGTH + 1]={0}; va_list arg;
va_start (arg, format);
vsnprintf(buffer, DEBUG_BUFFER_MAX_LENGTH, format, arg);
va_end (arg); printf( "%s", buffer );
}
qDebug( "[模組名稱] 除錯資訊 File:%s, Line:%d", __FILE__, __LINE__ );
這樣的修改比較煩人, 而且一不小心會遺漏某個沒改的... 為了能方便地管理除錯資訊的輸出,一個比較簡單的方法就是自已定義一個列印除錯資訊的巨集, 然後替換原來的,廢話就不多說了,直接給出一個現成的,下面是一個例子, 我用WiFi表示當前程式碼的模組名稱,我要求在模組中的所有除錯資訊前面均帶有[WiFi]前輟,這樣我就能方便地只需使用命令列 | grep "\[WiFi\]"來過濾掉來自其它模組的除錯資訊了: #define qWiFiDebug(format, ...) qDebug("[WiFi] "format" File:%s, Line:%d, Function:%s", ##__VA_ARGS__, __FILE__, __LINE__ , __FUNCTION__); 上面的巨集是使用qDebug輸出除錯資訊,在非Qt的程式中也可以改為printf,守護程序則可以改為syslog等等... 其中,決竅其實就是這幾個巨集 ##__VA_ARGS__, __FILE__, __LINE__ 和__FUNCTION__,下面介紹一下這幾個巨集: 1) __VA_ARGS__ 是一個可變引數的巨集,很少人知道這個巨集,這個可變引數的巨集是新的C99規範中新增的,目前似乎只有gcc支援(VC6.0的編譯器不支援)。巨集前面加上##的作用在於,當可變引數的個數為0時,這裡的##起到把前面多餘的","去掉的作用,否則會編譯出錯, 你可以試試。
2) __FILE__ 巨集在預編譯時會替換成當前的原始檔名
3) __LINE__巨集在預編譯時會替換成當前的行號
4) __FUNCTION__巨集在預編譯時會替換成當前的函式名稱 有了以上這幾個巨集,特別是有了__VA_ARGS__ ,除錯資訊的輸出就變得靈活多了。
有時,我們想把除錯資訊輸出到螢幕上,而有時則又想把它輸出到一個檔案中,可參考下面的例子: //debug.c #include <stdio.h>
#include <string.h> //開啟下面的巨集表示程式執行在除錯版本, 否則為發行版本, 這裡假設只有除錯版本才輸出除錯資訊
#define _DEBUG #ifdef _DEBUG
//開啟下面的巨集就把除錯資訊輸出到檔案,註釋即輸出到終端
#define DEBUG_TO_FILE
#ifdef DEBUG_TO_FILE
//除錯資訊輸出到以下檔案
#define DEBUG_FILE "/tmp/debugmsg"
//除錯資訊的緩衝長度
#define DEBUG_BUFFER_MAX 4096
//將除錯資訊輸出到檔案中
#define printDebugMsg(moduleName, format, ...) {\
char buffer[DEBUG_BUFFER_MAX+1]={0};\
snprintf( buffer, DEBUG_BUFFER_MAX \
, "[%s] "format" File:%s, Line:%d\n", moduleName, ##__VA_ARGS__, __FILE__, __LINE__ );\
FILE* fd = fopen(DEBUG_FILE, "a");\
if ( fd != NULL ) {\
fwrite( buffer, strlen(buffer), 1, fd );\
fflush( fd );\
fclose( fd );\
}\
}
#else
//將除錯資訊輸出到終端
#define printDebugMsg(moduleName, format, ...) \
printf( "[%s] "format" File:%s, Line:%d\n", moduleName, ##__VA_ARGS__, __FILE__, __LINE__ );
#endif //end for #ifdef DEBUG_TO_FILE
#else
//發行版本,什麼也不做
#define printDebugMsg(moduleName, format, ...)
#endif //end for #ifdef _DEBUG int main(int argc, char** argv)
{
int data = 999;
printDebugMsg( "TestProgram", "data = %d", data );
return 0;
}
上面也說了,只有支援C99規範的gcc編譯器才有__VA_ARGS__這個巨集,如果不是gcc編譯器,或者所用的gcc編譯器版本不支援__VA_ARGS__巨集怎麼辦? 可參考下面的程式碼片段,我們換一種做法,可先將可變引數轉換成字串後,再進行輸出即可: void printDebugMsg( const char* format, ...)
{
char buffer[DEBUG_BUFFER_MAX_LENGTH + 1]={0}; va_list arg;
va_start (arg, format);
vsnprintf(buffer, DEBUG_BUFFER_MAX_LENGTH, format, arg);
va_end (arg); printf( "%s", buffer );
}