最近 CodeGear 的工程师开始谈论[1][2][3][4][5] Delphi 全面支持 Unicode 的问题了。尽管这个是十年前的新闻,但对于 Delphi 的粉丝来说,迟到总比不到要强。本文是我对目前 Unicode 封装计划的一些看法。
如果你不了解 AnsiString 和 WideString,请先去网上查阅相关资料。微软的 Visual C++ 在提供 Unicode 方案的时候,提供了一套宏机制[6]。在 Delphi 中也有类似的方法,比如大家最熟悉的 string/Char/PChar。
类型
/函数别名
|
未使用
编译指令
UNICODE
|
使用编译指令
UNICODE[7]
|
string
|
AnsiString
|
WideString
|
Char
|
AnsiChar
|
WideChar
|
PChar
|
PAnsiChar
|
PWideChar
|
GetCommandLine
|
GetCommandLineA
|
GetCommandLineW
|
宽字节版的字符串类型和 Windows API 函数很早就出现在 Delphi 中了。只是类型/函数别名一直只是对应到了传统的单字节版本上。简单来说,我们需要的是自由将类型/函数别名对应到单字节或者宽字节版本的数据类型上。
很多人想必和我一样对“程序全面支持 Unicode”这个概念懵懵懂懂。这里所谓的支持,并不是说要让应用程序既可以在 Windows9x 上运行,又可以在 WindowsXP 上显示 Unicode 字符。而是指一个应用程序应该针对 Windows9x 和WindowsXP 分别编译单字节或者宽字节版本。
“一版通杀”的解决方案比较的累人。首先,字符串相关的数据类型在程序内部都是以宽字节版本定义的。当在 Windows9x 上调用 Windows API 的时候,开发者必须手动进行类型转换。这种做法费时费力,维护的工作量相当大。目前 Delphi 制作的兼容 Unicode 的应用程序,基本都采用这个方法。在此,要特地感谢一下牺牲最大的先驱 Troy 和他的 TntControl[8]。
比较可以接受的方案是编译二个版本。这样就的项目可以继续以 Ansi 模式顺利编译。而新的项目,只要遵循国际化编程准则的,也都可以成功的编译出单字节或者宽字节版本。尽管在处理宽字节 Windows API 函数的时候你需要加倍小心,比如有些数据类型要求字符串指针包含二个 #0结束符,但比起“一版通杀”的工作量,你真的要偷笑了。
我实在不明白,究竟CodeGear 叫苦了10年的 Unicode 的瓶颈到底在哪里?难道他们之前一直在往“一版通杀”的方向努力?
现有的 WideString (D4-D2007) 类型是个比较奇怪的东西。不同于 AnsiString,它为了兼容 COM 调用中常用的BSTR 类型,违背了引用计数原则。因此它就性能而言,完全无法与 AnsiString 相提并论[9]。想必是为了保证现有的项目能顺利在 Delphi2008 (codename Tiburon) 中通过,下个版本并不打算改造现有的 WideString,而是另起炉灶,引入一个新的类型 UnicodeString。我个人是强烈反对这个方案的。请看下面的表格
类型别名
|
单字节版本
|
宽字节版本
|
string
|
AnsiString
|
UnicodeString
|
Char
|
AnsiChar
|
WideChar
|
PChar
|
PAnsiChar
|
PWideChar
|
显而易见,UnicodeString 这个名字和 WideChar/PWideChar 是不一致的。我的想法是:CodeGear 应该把现有的WideString (D4-D2007) 命名成 BSTR,然后再引入一个全新的 WideString (即 UnicodeString) 以及一个新的类型别名 T_BSTR。
类型别名
|
未使用
编译指令
UNICODE
|
使用编译指令
UNICODE[7]
|
string
|
AnsiString
| WideString (UnicodeString) |
Char
|
AnsiChar
|
WideChar
|
PChar
|
PAnsiChar
|
PWideChar
|
T_BSTR
| WideString (D4-D2007) |
BSTR
|
当你以单字节方式编译你的现有项目时,一切照旧,程序顺利编译。当你以宽字节方式编译同样的项目时,编译器会在 BSTR 被强制转换成新的 WideString 发出警告或者错误提示。并要求开发者认真考虑是使用 BSTR 还是 WideString。
用 UTF-16 肯定好过 UTF-8。原因很简单,UTF-8 是一种给单字节系统提供双字节支持的权宜之计[10]。尽管它使用了可变长度定义一个字符,节约了部分的内存空间,但是它在编码和解码上需要花费更多的时间。一来一去,你说谁更加划算呢?
正如 Delphi 的首席构架师 Allen[4] 所说,Windows API 函数别名会和 UnicodeString 一样被很好的处理。不过我希望今后的 Delphi 能有一种函数重定向指令,以方便开发者定义自己的单字节和宽字节函数版本。例如:
在全面支持 Unicode 的过程中,确实会面临很多问题。既然微软在十年前就已经成功的解决了此问题,相信 CodeGear 的工程师不可能在今时今日仍然愁眉不展吧。让你的应用程序顺利支持 Unicode,你准备好了吗?