GCC and customizable input source charset
研究過 BBS source 的人應該都知道一個很有名的 BIG5 衝碼問題:。由於 GCC 跟 VC6 在此處的作法不太一樣。對於 GCC 而言,輸入的程式碼,是一串 bytes,而 VC6 在中文 Windows 裡,則是會把輸入的程式碼,看做是一串 big5 字元,而不只是一串 bytes 而已,所以必須用醜陋的 "壞蛋許\\n"#ifdef 法,特別為含有「許功蓋」的 string literals[1],各寫一個版本,像這樣:
#if (ADK_COMPILER == ADK_COMPILER_MSC)
printf("壞蛋許\n");
#else if (ADK_COMPILER == ADK_COMPILER_GCC)
printf("壞蛋許\\n");
#endif
把輸入的程式碼,看做是一串 bytes,因此,含有反斜線的「許」字放在 string literals 裡,就必須多加一個反斜線在後面。這樣其實很難用,我常為了要讓程式可以同時 porting 在奮鬥。不過,難用歸難用,但按照 C++98,GCC 的作法,才是比較對的 (雖然也沒有算是完全正確)。
在 Standard C++ 2.2.1 裡指出:
The basic source character set consists of 96 characters: the space character, the control characters representing horizontal tab, vertical tab, form feed, and new-line, plus the following 91 graphical characters:
a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 _ { } [ ] # ( ) < > % : ; . ? * + - / ^ & | ~ ! = , \ " '
也就是說,在 C++ 程式碼裡面,只有這 96 個字元,才是正確可以被使用的字元,如果有需要放入這 96 個字元以外的字元,就必須要使用 Standard C++ 2.2.2 所描述的 universal-character-name construct[2] 來寫,也就是像 \u0707 與 \U07071324 這種東西,前者後接四個十六進位數字,後者後接八個。這個數字對應到 ISO/IEC 10646 裡的字元,也就是 Unicode。
因此,實際上,我們根本不應該在程式裡面寫入任何 big5 字元,因為那不在 basic source character set 範圍裡面。
比較正確的作法,應該是引入如 gettext 或 windows resource 裡的 string table 等機制,處理這 96 個字元以外的字元。但是這樣子,真的、真的很難用。為了一個一百行不到的作業或測試程式,就要引入引入一整套 7mb 的 gettext library,以及複雜的 build 機制,甚至還得應付跨平台的問題[3]。為了殺雞還得扛把超大牛刀,甚至一整組應付不同環境的牛刀組,標準的小題大作。更別提其中隱含的歧視問題了:這 96 個字元差不多就等於 ASCII 使用者會用到的字元集,憑什麼只有 ASCII 使用者的字才是字,其他人的字就不是字?
為了這些問題,Steven Underwood 和 Nathan Myers 曾經在 gtk-i18n-list 上「熱烈地」討論過一番。不過我們也不必太悲情,basic source character set 被限制在這 96 個字元裡,多半是技術因素。C++ 已經太先進了,七年後的現在,完全達到 C++ Standard 要求的 compiler/library,還是沒有看到,如果那時候再把 Unicode 的觀念弄進來,更是沒完沒了。
事實上,C++ 的創造者 Bjarne Stroustrup[4] 曾寫過一篇有趣的論文:《Generalizing Overloading for C++2000》,裡面根本是把 source charset 推廣到極致,程式已經變成是一堆圖形的堆砌了。
延伸閱讀:
- Re: Unicode & C++
- gcc and utf-8 source
- GCC 3.4.4 manual:
- kernel iconv support?
- CVSWeb of FreeBSD: /src/sys/libkern/ - 注意一下 iconv_* 這幾個檔案。
- generic locale - 追 C++ locale 追到 libstdc++ 裡面最終會呼叫到的
locale::facet::_S_create_c_locale()的 comment 裡寫的這個 URL。這個locale::facet::_S_create_c_locale()很髒,只接受"C",連""都不可以用。換句話說,libstdc++ 裡的 C++ locale 是個廢物。libstdc++ 把 locale 分成幾個 mode:generic、gnu、ieee_1003.1-2001 以及 GCC4 新增的 darwin,可以在 compile GCC 的時候選擇。這幾個裡面,只有 gnu 這個 mode 因為可以搭配 glibc,locale::facet::_S_create_c_locale()運作正常,其他都是廢物。 - The Standard C++ Locale
- Notes on the locale implementation.
- 1.3 The Standard C Locale and the Standard C++ Locales
- 葉秉哲先生將 string literal 翻作「字串定字」。我習慣仍然用原文,如果硬要用中文的話,我認為葉秉哲先生的翻法我比較喜歡。 ↩
- 葉秉哲先生將 (language) construct 翻作「語言設施」。這個字比 string literal 還要難翻,我還是傾向於使用原文,但這個原文比起 string literal 來說,一般人比較看不太懂。一般來說,我認為葉秉哲先生的名詞翻譯,比較「信」也比較「達」,但相對來說,就不那麼「雅」了。翻譯真是門大學問,要信、 雅、達兼顧,真難。 ↩
- 因為 VC 用 string table resource ↩
- 太久沒造訪,Bjarne Stroustrup 首頁的照片好像換了,從精神奕奕的鶴髮童顏,變成現在這個樣子。歲月催人老,嘆。 ↩



One Backlink
[...] 差不多在幾個月前開始,用 TortoiseSVN 時,若程式裡有放 expansion keywords 的話,$Date$ 會被改成用 zh_TW.UTF-8 顯示。由於 C/C++ standard 規定,程式碼裡只應該使用 ASCII 碼,所以使用中文顯示 $Date$,會違反 C/C++ standard 規定。果不其然,上禮拜我就碰到了。為了要把程式移轉過去,測一測 Visual Studio .Net 2005,結果一編譯,洋洋灑灑跑出三百多個 warning,仔細一看,通通都是一樣的 warning: [...]
Post a Comment