首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C 語言變參處理進階:以函數型宏實現類型安全、時間戳、線程安全的統一格式化庫

     編輯:關於C語言

痛點:va_list 傳遞的繁瑣與不安全

標準 printf 族函數內部處理變參簡單,但封裝二次開發時極其麻煩:

void Func(char* szFormat, ...) {
    char szBuf[256];
    // 錯誤!... 不能直接轉發
    SafePrintf(szBuf, 256, ...);  // 編譯失敗
}

ANSI C99 要求用 va_copy 複製 va_list,再 va_end 釋放,代碼冗長且易出錯。舊庫只能在每個變參函數開頭複製粘貼處理邏輯,違反 **「同樣邏輯只寫一次」** 原則。

破局:函數型宏 TONY_FORMAT

利用宏的文本替換特性,將變參展開、格式化、邊界檢查、結束符填充**固化為單一調用點**,調用者像調用普通函數一樣傳入格式串:

// 核心宏:4 參數 —— 輸出長度引用、緩衝區、緩衝區大小、格式串
#define TONY_FORMAT(nPrintLength, szBuf, nBufferSize, szFormat) \
{ \
    va_list pArgList; \
    va_start(pArgList, szFormat); \
    nPrintLength += Linux_Win_vsnprintf(szBuf + nPrintLength, \
        nBufferSize - nPrintLength, szFormat, pArgList); \
    va_end(pArgList); \
    if (nPrintLength > (nBufferSize - 1)) nPrintLength = nBufferSize - 1; \
    *(szBuf + nPrintLength) = '\0'; \
}

// 基礎安全格式化函數
inline int SafePrintf(char* szBuf, int nBufSize, char* szFormat, ...) {
    if (!szBuf || !nBufSize || !szFormat) return 0;
    int nRet = 0;
    TONY_FORMAT(nRet, szBuf, nBufSize, szFormat);
    return nRet;
}

關鍵優勢:

  • 零重複:所有變參函數複用同一宏,絕無筆誤風險
  • 邊界保護:強制 nBufferSize 參數,杜絕緩衝區溢出
  • 跨平台:Linux_Win_vsnprintf 宏映射 _vsnprintf/vsnprintf
  • 內聯展開:零函數調用開銷,適合高頻日誌路徑

進階:自動時間戳版本

#define TONY_FORMAT_WITH_TIMESTAMP(nPrintLength, szBuf, nBufferSize, szFormat) \
{ \
    time_t t; struct tm *pTM = NULL; \
    time(&t); pTM = localtime(&t); \
    nPrintLength += SafePrintf(szBuf, nBufferSize, "[%s", asctime(pTM)); \
    szBuf[nPrintLength - 1] = '\0'; nPrintLength--;  // 吃掉 asctime 自帶換行 \
    nPrintLength += SafePrintf(szBuf + nPrintLength, nBufferSize - nPrintLength, "] "); \
    TONY_FORMAT(nPrintLength, szBuf, nBufferSize, szFormat); \
}

inline int SafePrintfWithTimestamp(char* szBuf, int nBufSize, char* szFormat, ...) {
    if (!szBuf || !nBufSize || !szFormat) return 0;
    int nRet = 0;
    TONY_FORMAT_WITH_TIMESTAMP(nRet, szBuf, nBufSize, szFormat);
    return nRet;
}

實戰封裝:TonyPrintf/TonyFPrintf

統一入口,支持控制台/文件、帶/不帶時間戳:

#define TONY_LINE_MAX 1024

inline int TonyPrintf(bool bWithTimestamp, char* szFormat, ...) {
    if (!szFormat) return 0;
    char szBuf[TONY_LINE_MAX];
    int nLen = 0;
    if (!bWithTimestamp) { TONY_FORMAT(nLen, szBuf, TONY_LINE_MAX, szFormat); }
    else { TONY_FORMAT_WITH_TIMESTAMP(nLen, szBuf, TONY_LINE_MAX, szFormat); }
    return printf("%s", szBuf);
}

inline int TonyFPrintf(FILE* fp, bool bWithTimestamp, char* szFormat, ...) {
    if (!fp || !szFormat) return 0;
    char szBuf[TONY_LINE_MAX];
    int nLen = 0;
    if (!bWithTimestamp) { TONY_FORMAT(nLen, szBuf, TONY_LINE_MAX, szFormat); }
    else { TONY_FORMAT_WITH_TIMESTAMP(nLen, szBuf, TONY_LINE_MAX, szFormat); }
    return fprintf(fp, "%s", szBuf);
}

測試驗證

// 控制台輸出
[65]: 123.46, A, 0x00AABBCC
...
[Wed May 12 09:17:43 2010] [65]: 123.46, A, 0x00AABBCC
...
// 文件 test.txt 內容一致

多線程環境下,所有函數僅操作棧上私有緩衝區,**天然線程安全**,無需額外鎖。

工程化落地計劃

  1. 內部驗證 1 年以上,確認穩定性
  2. 納入《0bug-C/C++商用工程之道》第二版工程庫
  3. 配套 CTonyMean 增量平均值模組(支持正負數混合、溢出自愈),已用於高精度計時器自適應修正

總結

函數型宏在「內聯優化」與「代碼復用」之間找到平衡點,解決了 C 語言變參二次封裝的長期痛點。配合嚴格邊界檢查、跨平台宏、時間戳擴展、雙輸出通道,構建出一套**工業級、零開銷、極易用**的格式化基礎設施。**把自己站在用戶立場,把 API 調整到自己用起來都「爽」**,這才是好庫的誕生之道。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved