2010-06-23 92 views
10

我想寫一個像printf這樣的例程,而不是功能上明智的,但是我希望例程能夠像printf一樣編譯檢查特性。如何獲得printf風格的編譯時警告或錯誤

例如,如果我有:

{ 
    int i; 
    std::string s; 
    printf("%d %d",i); 
    printf("%d",s.c_str()); 
} 

編譯器會抱怨像這樣:

1 cc1plus: warnings being treated as errors 
2 In function 'int main()': 
3 Line 8: warning: too few arguments for format 
4 Line 9: warning: format '%d' expects type 'int', but argument 2 has type 'const char*' 

code example

是printf和共同的特殊函數,編譯器將不同的或者是有一些訣竅讓這工作在任何用戶定義的功能?我感興趣的具體編譯器是gcc和msvc

回答

17

不同的編譯器可能會以不同的方式實現此功能。在海灣合作委員會它是通過__attribute__說明與format屬性(閱讀關於它here)實施。爲什麼編譯器執行檢查的原因就是,在printf函數聲明與__attribute__((format(printf, 1, 2)))

在完全相同的方式,你可以使用format屬性相同的格式檢查功能擴展到與海灣合作委員會提供的標準頭文件的自己的可變參數函數使用與printf相同的格式說明符。

如果您使用的參數傳遞約定和格式說明符與標準的printfscanf函數所使用的相同,這一切只會起作用。這些檢查被硬編碼到編譯器中。如果您爲可變參數傳遞使用不同的約定,編譯器將無法幫助您檢查它。

+4

它可以做得比printf和scanf多一點;從當前文檔列表是「printf,scanf,strftime,gnu_printf,gnu_scanf,gnu_strftime或strfmon」 – Cascabel 2010-06-23 20:58:09

+2

這很酷。我希望CodeGear/Embarcadero將這個功能放到他們的編譯器中。 – 2010-06-23 21:00:45

3

printf()和朋友不是特殊的,因爲它們接受可變數量的參數:用戶定義的函數也可以接受可變數量的參數。它們是特殊的,因爲它們的行爲是由標準定義的,所以編譯器知道格式字符串和傳遞給函數的參數之間的關聯應該是什麼。

實際上,編譯器知道在調用函數時將多少個參數傳遞給該函數,因此它解析格式字符串並將參數的期望數量和類型與實際傳遞給該函數的參數進行比較,併發出警告如果它們不匹配。

如果您使用C++,我會避免編寫自己的可變參數函數;在大多數項目中使用它們的原因很少。例如,如果您正在格式化,請使用流或庫,如Boost Format。任何可以使用可變參數函數解決的問題都可以使用非可變參數函數來解決,並且在幾乎所有情況下,結果都更加優雅,習慣和類型安全。

+4

實際上有很多很好的理由來避免流和Boost,當用C++進行編碼時,但所有這些都是特定於域的。我們大多數人都在做一些特定領域的工作,所以我們應該儘量不要忽視這樣一個事實,即有相當多的人對於許多C++功能來說太昂貴了。 – 2010-06-23 20:38:14

+1

@ dash-tom-bang:是的,你說得對:它們確實有用的場景。 – 2010-06-23 20:58:50

1

實際上printf根本沒有任何固有的編譯時安全性。只是發生了一些更新的編譯器已經實現了特殊檢查,因爲他們確切知道格式字符串在附加參數方面意味着什麼。當您使用...作爲參數時,您表示要接受任意參數並接受確保其正確的全部責任。編譯器無法檢查它們的計數/類型安全性。

而不是試圖讓編譯器以這種方式來幫助你,嘗試使用標準流使用的方法:使用(可能是模板)函數或運算符返回引用this允許鏈接。然後,當參數與期望/支持的內容不匹配時,編譯器將能夠立即告訴您。

1

後來有人發佈了mpl :: string到boost組。我認爲它實際上可能已經進入圖書館。如果是這樣的話,你可以通過提供你的模板字符串作爲模板參數(一個mpl :: string),然後使用一些相當深刻的元編程技巧來解析其中的格式化位來實現。然後,您將使用此信息來選擇具有適當參數數量和類型的實現。

不,我不打算爲你做:P這將是非常困難的。但是,我確實相信這是可能的。