2009-07-13 84 views
5

我一直在使用一個主要由程序員編寫的大型代碼庫,他們不再在公司工作。其中一名程序員顯然在他的心臟中有一個特別的地方很長長宏。我可以看到使用宏的唯一好處是能夠編寫不需要在所有參數中傳遞的函數(在我閱讀的最佳實踐指南中建議不要這樣做)。除此之外,我認爲沒有內聯函數的好處。大C宏。有什麼好處?

一些宏如此複雜我也很難想象有人甚至寫他們。我試圖以這種精神創造一個,這是一場噩夢。調試非常困難,因爲它在調試器中將N +行代碼放入1中(例如,在這個大塊代碼中存在段錯誤,祝你好運!)。我不得不把宏拉出來,並運行它宏觀大小來調試它。我可以看到寫這些的人的唯一方法是在調試完成後用代碼編寫的代碼自動生成它們(或者比我更聰明,第一次寫得完美,我猜這總是可能的) 。

我錯過了什麼嗎?我瘋了嗎?有沒有我不知道的調試技巧?請填寫我的意見。我真的很想聽聽觀衆中的宏觀愛好者。 :)利益的

回答

1

部分是代碼複製,而不最終維護成本 - 即,而不是複製代碼的其他地方,你從它創建一個宏,並只需要編輯一次......

當然,你也可以創建一個方法來調用,但這是更多的工作......我反對自己的宏觀使用,只是試圖提出一個潛在的基本原理。

8

對我來說,宏的最佳方式是壓縮代碼,減少失誤。其缺點顯然在於調試,所以必須小心使用它們。

我傾向於認爲,如果生成的代碼是不是幅度要小,不易發生錯誤(這意味着宏打理一些簿記細節),那麼它是不值得的訂單。

在C++中,很多這樣的應用可以使用模板來代替,但不是全部。一個很有用的宏的簡單例子是MFC的事件處理程序宏 - 如果沒有它們,創建事件表將會變得很難,你必須編寫(和讀取)的代碼會更加複雜。

+0

由於這些原因,我可以理解一些簡短的宏,但是將理智驗證函數聲明爲宏還是內聯函數會有什麼好處嗎?我在工作中遇到了一些X_VALIDATE宏內的大量失敗,但它們包含多個斷言語句和循環。核心文件不會告訴我哪個斷言失敗(從而使調試更加困難)。在我看來,這可能是一種內聯函數,沒有成本和很多好處。你會同意嗎? – jdizzle 2009-07-13 20:18:38

+2

在宏(而不是內聯)中斷言的主要好處是__FILE__和__LINE__將是正確的。既然你有一個核心,你可以得到整個堆棧,所以對你來說沒什麼大不了的。如果它可以在沒有宏的情況下編寫,請在沒有宏的情況下執行。如果它只能通過一個宏來完成,那麼你就可以得到好處。對我來說,好處需要簡單得多的代碼或更少的錯誤代碼。 – 2009-07-13 20:35:52

+0

__FILE__和__LINE__應該是`__FILE__`和`__LINE__`上面(不知道我需要轉義) – 2009-07-13 20:37:23

4

如果宏是非常長的,他們很可能使代碼簡短而高效。實際上,他可能使用宏來明確內聯代碼或從運行時代碼路徑中刪除決策點。

明白,這可能是重要的,在過去,這樣的優化不是由許多編譯器完成的,有些事情,我們今天理所當然的,如快速函數調用,不是有效的,那麼。

3

對我來說,宏是邪惡的。由於它們有如此多的副作用,並且在C++中可以通過內聯獲得相同的性能提升,所以它們不值得冒險。

例如,看到這短短的宏:

#define max(a, b) ((a)>(b)?(a):(b)) 

那就試試這個電話:

max(i++, j++) 

更多。假設你有

#define PLANETS 8 
#define SOCCER_MIDDLE_RIGHT 8 

如果拋出一個錯誤,它會參考「8」,而不是它的meaninful表示任。

3

我只知道做你描述的兩個原因。

首先是強制內聯函數。這幾乎沒有意義,因爲inline關鍵字通常會做同樣的事情,函數內聯無論如何都是過早的微觀優化。

其次是模擬C或C++中的嵌套函數。這與你的「寫作函數不需要在所有參數中傳遞」有關,但實際上可以比這更強大。 Walter Bright gives嵌套函數在哪裏可以有用。

還有其他原因使用宏,比如在功能和模板不能(在Boost.Preprocessor庫擅長此方法使用特定的預處理器的功能(如包括自動生成的錯誤消息__FILE____LINE__)或減少樣板代碼;例如,請參閱Boost.ScopeExitthis sample enum code),但這些原因似乎不適用於您所描述的內容。

0

我根本不使用宏。內聯函數實現了宏可以執行的每一個有用的目的。宏允許你做非常奇怪和違反直覺的事情,比如分離標識符(然後人們如何搜索標識符?)。

2

非常長的宏會產生性能上的缺陷,比如編譯後的二進制大小增加,當然還有其他一些不使用它們的原因。

對於最有問題的宏,我會考慮通過預處理器運行代碼,並用函數調用(如果可能,內聯)或直線LOC替換宏輸出。如果宏存在與其他體系結構/操作系統的兼容性,則可能會卡住。

0

我也參與過一個產品,其中一位遺留程序員(幸好已經很久沒有去過)也與宏有特殊的關係。他的'自定義'腳本語言是瀟灑的高度。這是因爲他用C語言編寫了他的C++類,這意味着所有的類函數和變量都是公開的。無論如何,他寫了幾乎所有的宏觀和變化函數(另一個醜陋的怪物在世界上強加)。所以不是寫一個適當的模板類,而是使用宏代替!他還使用宏來創建工廠類,而不是普通的代碼......他的代碼幾乎是不可變的。

從我所看到的,宏可以用在它們很小並且被聲明性地使用並且不包含像循環和其他程序流表達式那樣的移動部分。如果這個宏是一個或最多兩行並且它聲明和實例化的東西,那也沒關係。有些東西在運行時不會中斷。另外,宏的不應該包含類定義或函數定義。如果宏包含需要使用調試程序的代碼,則應將該宏刪除並替換爲其他內容。

它們也可用於包裝自定義跟蹤/調試功能。例如,您希望在調試版本中進行自定義跟蹤,但不希望發佈版本。

無論如何,當你在這樣的遺留代碼中工作時,只要確保一次刪除一點宏的混亂。如果你堅持下去,有足夠的時間最終你會將它們全部移除,讓自己的生活變得更輕鬆。我過去做過這個,特別是雜亂的宏。我所做的是打開編譯器開關,讓預處理器生成一個輸出文件。然後我突襲該文件,然後複製代碼,重新縮進它,然​​後用生成的代碼替換宏。感謝那個編譯器功能。

0

我已經使用過的一些代碼非常廣泛地用於代替方法。原因是計算機/操作系統/運行時有一個非常小的堆棧,所以堆棧溢出是一個常見問題。使用宏而不是方法意味着堆棧中的方法更少。

幸運的是,大部分代碼已經過時,所以它現在已經(大部分)消失了。

1

有許多很好的理由寫在宏C.

一些最重要的是使用的X宏創建配置表,爲使功能類似的宏可以接受多個參數類型作爲輸入和將表格從人類可讀/可配置/可理解的值轉換爲計算機使用的值。

我不能真正看到人們編寫非常長的宏的原因,除了歷史的自動函數內聯。

我要說的是調試複雜的宏,當(寫入X宏時等),我傾向於預處理源文件,並替換預處理文件的原件。

這使您能夠看到生成的C代碼,併爲您提供真實的線路以便在調試器中使用。

0

C89沒有內聯函數。如果使用擴展禁用的編譯器(這是出於多種原因需要執行的操作),那麼宏可能是唯一的選擇。

雖然C99在1999年問世,但長期以來一直存在抵觸情緒,商業編譯器供應商並不覺得值得他們的時間去實現C99。有些(如MS)還沒有。因此,對於許多公司來說,使用C99符合模式並不是一個切實可行的決策,即使是在某些編譯器的情況下,也是如此。

我已經使用了確實有內聯函數擴展的C89編譯器,但是擴展是錯誤的(例如,當不應該有多個定義錯誤時),這樣的事情可能會阻止程序員使用內聯函數。

另一件事是宏版本有效地強制該功能實際上將內聯。 C99 inline關鍵字只是一個編譯器提示,編譯器可能仍然決定生成一個鏈接爲非內聯函數的函數代碼的單個實例。 (如果該函數不平凡並返回void,我仍然使用的一個編譯器會執行此操作。