Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

設計討論

Unicode 支持

本節關注一些設計的問題。

Unicode 支持

Unicode 支持是在正式評審時被請求增加的一個特性。在本文檔中,"Unicode 支持" 就是 "wchar_t" 支持的同義詞,"wchar_t" 總是使用 Unicode 編碼。同樣,在談論 "ascii" (小寫)時,我們並不是指7-位 ASCII 編碼,而是8位編碼的 "char" 字符串。

通常,"Unicode 支持" 可以代表很多事情,但對於 program_options 庫而言,它代表:

  • 每個分析器接受 char*wchar_t*, 正確地將輸入分解為選項名和選項值並返回數據。

  • 對於每個選項,可能要指定是否使用 ascii 或 Unicode 從字符串轉換為值。

  • 程序庫保證:

    • ascii 輸入傳遞給 ascii 值時不會改變

    • Unicode 輸入傳遞給 Unicode 值時不會改變

    • ascii 輸入傳遞給 Unicode 值,以及 Unicode 輸入傳遞給 ascii 值時,將使用 codecvt facet (可以由用戶指定)來轉換。

重點在於存在 "ascii options" 與 "Unicode options" 需要一起處理的可能。這有兩方面原因。首先,對於一個給定的類型,你可能沒有代碼來從 Unicode 字符串中取出選項值,要求寫出這樣的代碼也不太好。其次,想像一個帶有一些選項的可重用程序庫,它要在接口中提供這些選項描述。如果所有選項要麼都是 ascii 要麼都是 Unicode, 而該庫沒有使用 Unicode 字符串,那麼作者就很可能會使用 ascii 選項,這將使得該庫不能用於 Unicode 應用。理想的情況是,需要提供兩個版本的庫 -- ascii 和 Unicode.

另一個重點是,ascii 串在傳遞時不會被修改。換句話說,不能將 ascii 轉換為 Unicode 以進一步處理 Unicode. 問題是缺省的轉換機制 -- codecvt facet -- 不能在沒有額外設置的情況下用於 8-位的輸入。

以上的 Unicode 支持要點是不完整的。例如,我們不支持 Unicode 選項名。Unicode 支持是困難的,需要一個 Boost-範圍的解決方案。甚至於比較兩個任意的 Unicode 串都不簡單。最後,在選項名中使用 Unicode 涉及到國際化,它本身就很複雜。即如果選項名依賴於當前的 locale, 那麼所有的使用該選項名的程序部分都必須被國際化。

實現 Unicode 支持的主要問題是,使用模板和 std::basic_string,還是使用一些內部編碼以及在接口上對內外部編碼間進行轉換。

通常,要在代碼大小和執行時間上進行選擇。模板的解決方案要麼將庫代碼鏈接入每個應用程序來使用庫(從而不能共享庫), 要麼在共享庫中提供顯式實例化(增加了庫的大小)。而基於內部編碼的解決方案則需要在多處進行轉換而稍微慢些。由於在本庫中速度不是太大問題,因此第二個 方案看起來更吸引,但是我們再來仔細看看個別組件。

對於分析器組件,我們有三種選擇:

  • 使用完全的模板實現:給定一個指定類型的字符串,分析器返回一個以相同類型的字符串實例化的 parsed_options (即 parsed_options 類要模板化)。

  • 使用內部編碼:同上,但字符串需要與內部編碼相互轉換。

  • 使用並部分公開內部編碼:同上,但 parsed_options 實例中的字符串使用內部編碼。 這可以在將 parsed_options 實例直接傳給其它組件時避免轉換,但也可能有風險或誤導用戶。

第二種方案看來最好 -- 它沒有增加太多的代碼大小,也比第三個更清晰。為了避免額外的轉換,Unicode 版本的 parsed_options 也可以保存內部編碼的字符串。

對於選項描述組件,我們沒有太多的選擇。由於要求所有選項要麼都使用 ascii 要麼都使用 Unicode 不能令人滿意,更好的辦法是可以同時使用 ascii 和 Unicode 選項,這要求 value_semantic 的接口必須可以同時對兩者使用。唯一的方法是,傳遞一個額外的標誌來表明字符串是使用 ascii 還是內部編碼。value_semantic 實例可以在需要時在不同編碼間進行轉換。

對於存儲器組件,唯一受影響的函數是 store. 對於 Unicode 輸入,store 函數將選項值轉換為內部編碼。它也會將所使用的編碼通知 value_semantic 類。

最後,我們應該使用什麼內部編碼呢?選擇有兩個:std::wstring (使用 UCS-4 編碼) 和 std::string (使用 UTF-8 編碼)。兩者的不同在於:

  • 速度: UTF-8 稍慢一些

  • 空間: 在輸入為 ascii 時,UTF-8 占的空間更少

  • 代碼大小 : UTF-8 需要額外的轉換代碼。但是,它允許使用已有的分析器而無須轉換為 std::wstring,而這種轉換需要創建大量新實例。

沒有誰明顯勝出,但最後一點看起來更重要,因此我們使用了 UTF-8 .

選擇 UTF-8 編碼允許使用已有的分析器,因為 7-位 ascii 字符在 UTF-8 中保持原值,因此查找 7-位 字符串非常簡單。但是,還是有兩個敏感的問題:

  • 我們需要假定字符集使用 ascii 編碼而輸入使用 Unicode 編碼

  • 一個 Unicode 字符 (如 '=') 可以後跟 'composing character' 且該組合與單個 '=' 是不同的,因此簡單地查找 '=' 會找到錯誤的字符

不過在實踐中,這些問題都不重要,由於 ascii 幾乎是通用的編碼,而且 composing characters 跟在 '=' (或其它對於本庫而言有特殊意義的字符) 後面這種情況非常罕見。

Copyright c 2002-2004 Vladimir Prus

PrevUpHomeNext