2012-09-24 39 views
8

我有一個C++類,它是日誌系統的前端。它的日誌功能使用C++ 11的可變參數模板來實現:如何在C++ 11可變參數模板中使用GCC的printf格式屬性?

template <typename... Args> 
void Frontend::log(const char *fmt, Args&&... args) { 
    backend->true_log(fmt, std::forward<Args>(args)...); 
} 

每個日誌後端實現自己的true_log版本,即,除其他事項外,採用轉發參數來調用vsnprintf。例如:

void Backend::true_log(const char *fmt, ...) { 
    // other stuff.. 
    va_list ap; 
    va_start(ap, fmt); 
    vsnprintf(buffer, buffer_length, fmt, ap); 
    va_end(ap); 
    // other stuff.. 
} 

一切都很好,我很高興。

現在,我想爲log()參數添加一個靜態檢查:具體來說,我想使用GCC的printf格式屬性。

我開始用__attribute__ ((format (printf, 2, 3)))標記log()函數(因爲this是第一個「隱藏」參數,我需要將參數索引移動一個)。這是不行的,如果失敗,編譯錯誤,因爲:

error: args to be formatted is not ‘...’ 

然後,我想同樣的屬性添加到true_log()功能。它編譯,但沒有實際執行錯誤檢查:我試圖傳遞給log()一些無效的格式/變量組合,並且沒有發出警告。也許這種檢查「太遲了」,換句話說,有關變量的信息在調用鏈中已經丟失了?

作爲最後的手段,如果我註釋log()__attribute__ ((format (printf, 2, 0))),我會收到有關錯誤格式字符串的警告,但不會針對無效的格式/變量組合發出診斷。

總結問題:如果我使用C++ 11的可變參數模板,如何從GCC進行全格式檢查?

+0

由於vsnprintf()不能處理比​​什麼老學校...可以做什麼更多,爲什麼要首先打擾可變參數模板呢? –

+1

爲什麼當你丟棄類型信息時使用可變參數模板?只要讓'true_log()'成爲你真正的日誌記錄功能。 –

+0

或者讓'Frontend :: log'帶一個變量參數... –

回答

2

我不相信你可以。我敢打賭,GCC只驗證格式字符串,如果它是文字。這就是爲什麼將format屬性放在true_log上不起作用的原因 - 該函數的調用方式與運行時確定的字符串相似(語法上)。直接將它放在log上就可以規避這種情況,但是需要format屬性才能支持variadic模板,而您證明它沒有。

我建議你看看更多的C++ - ish方法來做格式化輸出。例如,boost::format其工作方式與printf類似,但動態驗證參數類型的數量和類型與格式字符串匹配。不過,它並沒有使用可變參數模板,而是逐個消耗了輸入給它的參數(通過運算符%)。

1

爲了記錄,我最終刪除了C++ 11 variadic模板,並使用傳統的va_list

__attribute__((format(printf, 2, 3))) 
void Frontend::log(const char *fmt, ...) { 
    va_list ap; 
    va_start(ap, fmt); 
    backend->true_log(fmt, ap); 
    va_end(ap); 
} 

void Backend::true_log(const char *fmt, va_list ap) { 
    // log the message somehow 
} 
相關問題