剛剛寫了一個簡單至極的小程式,是的,就是那個第一千次 commit 的 class,::mmi::util::version。這個 class 把三個 uint32_t 包起來,分別賦予 major version number、minor version number 以及 revision number 這三個意義。可是,想 porting 回 GCC 時,竟然產生 compilation error。錯誤訊息是這個樣子的:

c++ -Wall -I./include -g `env PKG_CONFIG_PATH=. pkg-config --define-variable=prefix=. --cflags libMMI` -o src/mmi/util/version.o -c src/mmi/util/version.cpp
src/mmi/util/version.cpp:72: error: expected unqualified-id before '(' token
src/mmi/util/version.cpp:72: error: abstract declarator `uint32_t' used as declaration
src/mmi/util/version.cpp:72: error: `uint32_t mmi::util::version::$_12' is not a static member of `class mmi::util::version'
src/mmi/util/version.cpp:72: error: expected primary-expression before "int"
src/mmi/util/version.cpp:72: error: expected `)' before "int"
src/mmi/util/version.cpp:102: error: expected `,' or `;' before '}' token
*** Error code 1

而出錯的函式長這樣:

uint32_t version::major() const
{
	return major_;
}

這真是令人費解,在 MSVC++6 上面沒什麼問題,程式也簡單到不行,為什麼換到 GCC 上就出了問題了呢?仔細檢查之後,發現在錯誤訊息裡有這麼一行:

src/mmi/util/version.cpp:72: error: `uint32_t mmi::util::version::$_12' is not a static member of `class mmi::util::version'

函式名字從 major() 變成詭異的 $_12 了,於是猜測是 macro 的問題,major 和 minor 被 #define 成某個奇怪的東西了。果不其然,在 FreeBSD 5 下找到 /usr/include/sys/types.h 裡的這段程式:

#ifndef _KERNEL
/*
 * minor() gives a cookie instead of an index since we don't want to
 * change the meanings of bits 0-15 or waste time and space shifting
 * bits 16-31 for devices that don't use them.
 */
#define major(x)        ((int)(((u_int)(x) >> 8)&0xff)) /* major number */
#define minor(x)        ((int)((x)&0xffff00ff))         /* minor number */
#endif /* !_KERNEL */

而我需要的 <algorithm> 裡面又輾轉 #include 到了這個檔案,於是產生了這個奇怪的問題。解決方法很簡單,把函式名字改成 major_version()minor_version()revision_number() 即可。

檢查了一下 Linux,在 Redhat 7 下的 /usr/include/sys/sysmacros.h 也有同樣的 macros。原來不僅 MSVC++6 會笨到把 max/min 給 #define 成自己的東西,連 FreeBSD/Linux 也會幹這種鳥事。