2014-03-12 82 views
2

我在寫一個Scheme解釋器。對於每個內置類型(整數,字符,字符串等),我希望能有統一進行命名的讀取和打印功能:使用C預處理器宏命名慣用功能?

READ_ERROR Scheme_read_integer(FILE *in, Value *val); 
READ_ERROR Scheme_read_character(FILE *in, Value *val); 

我想,以確保這些函數的命名一致性

#define SCHEME_READ(type_) Scheme_read_##type_ 
#define DEF_READER(type_, in_strm_, val_) READ_ERROR SCHEME_READ(type_)(FILE *in_strm_, Value *val_) 

所以,現在,而不是上面,在代碼中我可以寫

DEF_READER(integer, in, val) 
{ 
// Code here ... 
} 

DEF_READER(character, in, val) 
{ 
// Code here ... 
} 

if (SOME_ERROR != SCHEME_READ(integer)(stdin, my_value)) do_stuff(); // etc. 

現在是否被認爲是預處理器的單一使用?我在不知不覺中在自己的腳下拍攝自己的腳?我是否應該繼續使用這些函數的顯式名稱?

如果不是有這樣的事情做得很好的例子嗎?

回答

2

我已經在一個項目中看到了這種廣泛的做法,並且存在嚴重的步行射擊的危險。

當您嘗試維護代碼時,會發生問題。即使您的宏定義函數定義都很整齊,在封面下您可以得到像Scheme_read_integer這樣的函數名稱。這可能成爲一個問題,當崩潰堆棧上出現類似Scheme_read_integer的東西時。如果有人搜索源包Scheme_read_integer,他們不會找到它。這可能會導致很大的痛苦和咬牙切齒;)

如果您是唯一的開發人員,並且代碼庫不是那麼大,並且您還記得使用這種技術幾年並且/或者它有很好的文檔記錄,你可能沒有問題。在我的情況下,它是一個很大的代碼庫,沒有文檔記錄,沒有任何原始開發人員。其結果是牙齒咬牙切齒。

我會去上肢體和使用C++模板建議,但我猜,因爲你特別提到C.

希望這有助於這不是一個選項。

+0

這幫了很多,謝謝。我可以看到自動生成的名稱如何產生維護噩夢。 – user1569339

+0

只需要注意一點:我不確定這將如何轉換爲C++模板。我會使用靜態成員函數或名稱空間來獲得等價的'Scheme_read_integer(in,val)'=>'Scheme :: Integer :: read(in,val)' – user1569339

+0

它更像'template Scheme_stuff FILE * in,T val)',它將定義一個類,其中'read'成員將類型參數'T'作爲參數。它有點冗長,但它減少了一些未來的維護問題。我不得不多想一想,但靜態函數可以工作,並且仍然是自我註釋。 – Eric

1

我通常是宏的粉絲,但你應該考慮內聯包裝函數。它們會增加可忽略的運行時間開銷,並且會在調試時出現在堆棧回溯等中。