Use numeric_limits<>::max() with while avoiding VC6 min/max macros
多年以前,Microsoft 幹了一件比 #define N 3 還要蠢的蠢事,那就是在 <windows.h> 放入了 min/max 這兩個 macros。因此,我們沒辦法好好地使用 C++ Standard 的 min/max 這兩個 algorithms,也沒有辦法好好地使用 numeric_limits<>。前者 Microsoft 用了另外兩個 macros,_MIN 與 _MAX 來補救,但考量到跨平台程式設計,這兩個 macros 有跟沒有一樣。後者就真的很傷腦筋了,很多時候,numeric_limits<> 的使用是無法避免的[1],但當直接或間接 #include 進 <windows.h> 的時候,VC6 就會出現 C4003 warning 然後後面的程式就爛掉了。
通常來說,碰到這種相容性問題,先找看看 Boost 怎麼作就對了[2]。於是我在 boost/config/suffix.hpp 裡,發現了 BOOST_PREVENT_MACRO_SUBSTITUTION 這一個 macro,被安插在可能被 VC6 搞爛的 min/max 後面,括弧前面。其用法如下:
std::max BOOST_PREVENT_MACRO_SUBSTITUTION(value1, value2); // or std::numeric_limits<Foo>::max BOOST_PREVENT_MACRO_SUBSTITUTION();
實際上,BOOST_PREVENT_MACRO_SUBSTITUTION 是空的,會被代換成沒有任何東西,其目的應該是為了騙過 preprocessor,使其認為 min/max 不是 VC6 雞婆定義的那個 macro,而留給 compiler 來剖析之。
不過,照著這招做實驗後發現,沒有用。也許是 Boost 還有其他機制,讓這招成功,但總之我就是弄不出來,BOOST_PREVENT_MACRO_SUBSTITUTION 會先被 preprocessor 代換成沒有任何東西,然後原來的 min/max 的問題就又出現了。
只好求助 Google,幸好讓我發現了這串討論:《problems with new boost lib》,裡面提到可以使用括弧,來避免 preprocessor 作怪。也就是說,程式改成這麼寫:
(std::max)(value1, value2); // or (std::numeric_limits<Foo>::max)();
把 min/max 連同前面的 namespace 括弧刮起來,再接上呼叫用的 (),這樣就既是合法的 C++ 語法,又可以避免愚鈍的 preprocessor 被白爛 VC6 的 min/max macro 騙去。
6 Comments
對於 [1] 的小小補充, 如果已經把 boost 拉進來了, 那更好的方式是用 compile-time 的常量 boost::integer_traits::const_max.
boost::integer_traits<size_t>::const_max.
fr3@K,
Wow!之前沒發現 Boost 有 boost/integer_traits.hpp 這個東西,研究了一下,當真不錯。謝啦。兩點心得如下:
Boost 真是學習 C++ 的寶庫。
是可以的。一個 class template 的各個 specialization 之間其實是可以沒有任何關係的,例如 member 與 inheritance hierarchy。它們是 *不同* 的 class instance ;) 。
再廢話兩句。
一個class template 的各個 specialization 之間的關係,或是說相同之處,其實是 programmer 賦予的。Programmer 不 *硬* 把它們寫成 *長相* 類似的東西,它們自然就不一樣。
fr3@K,
感謝指教。我想,我可以理解「各 specialization 的相同/不同之處,由 programmer 賦予」的意思。
C++ 的高度彈性,一直是我喜歡它的原因。只可惜,考量到實際世界的需求,通常我們可能還是必須將之「硬」寫成長相類似的東西。
Post a Comment