2009年2月6日 星期五

邪惡的巨集

巨集(Macro)是邪惡的。

它很好用,可以簡化我們的程式碼,節省很多Coding的時間。

但是它暗地裡在破壞我們程式碼的物件架構,在物件封裝中到處鑽孔。

所以,前兩天,我將程式碼裡頭的

#include "windowsx.h"

給拔掉。

這個標頭檔,定義了很多巨集,有些是簡化windows訊息程式碼,有些是簡化windows API。平常是很好用的啦,但是,好死不死的,我有個樹狀結構的物件,裡頭有幾個函式-- GetFirstChild, GetNextChild, ... 就跟windows API的巨集衝突到了。

好端端擺在物件中的GetFirstChild函式,被windows API的GetFirstChild巨集取代掉,所以編譯器瞬間開始混亂,看不懂我的程式碼在幹什麼,連錯誤回報也是亂報一通不知所云。

花了時間除錯,拔掉了這些個讓人偷懶的巨集。

所以這巨集真不是好東西。

裹著糖衣的毒藥。

Code Complete的作者在書裡也有說,巨集,能不用就不用,務必謹慎使用。

是啊,別偷懶了,該拔掉的就拔掉吧。

==================================================

其實今天一早,還遇見了另一個類似的問題。

我在數學函式庫裡,定義了一個靜態常數PI (就是圓周率啦),還特別放在一個Math物件中,用Math::PI這樣的語法來存取,避免這種常數定義衝突的狀況。

class Math
{
...
static const float PI;
};

不過,又來了,好死不死,Max SDK裡面也定義了一個PI,而且是直接#define,

#define PI  ((float)3.1415926535)

結果我怎麼辦? 我只能先引入自己數學函式庫的標頭檔,然後再引入Max SDK的標頭檔,順序絕對不能換,否則編譯器就會開始錯亂。

5 則留言:

Jason Chan 提到...

I would like to add prefix to my own macro.
by the way, to avoid Windows macro, I prefer using unix naming converion, such as get_first_child.

Jason Chan 提到...

I would like to add prefix to my own macro.
by the way, to avoid Windows macro, I prefer using unix naming converion, such as get_first_child.

藍斯洛 提到...

用一些命名規則來定義巨集,確實也是一種避免衝突的方法,只不過,誰也無法保證絕對不會碰到同樣的命名規則不是?

尤其是當我們引入外部的程式庫的時候,也就很容易碰到這些狀況(就像我今天一大早的體驗....)

所以,我是非常同意Code Complete作者所說的,盡量避免使用這個老是在破壞物件封裝的壞東西。

匿名 提到...

非常同意,其實絕大部分巨集的作用,都可以用其他方式取代。很多喜歡大量使用巨集的程式設計者,純粹是因為方便和懶惰之故。

PlaySound 也是一個經常會遇到的定義衝突。 XD

Milo 提到...

我碰到過 win32 的 DrawText。
我碰到這種情況,通常只是簡單地 #undef DrawText。