几个月前,我在CSDN上发表了一篇短文,叫《风格的选择》。在那篇文章里,我提出一个观点:面对不同应用领域和环境,C++的开发者应该主动剪裁语言,选择最合适自己领域的C++子集进行具体开发。这是从我的实际工作中总结出来的想法,始终只是一种经验主义的东西。我觉得这是正确的选择,但是如果有人问我为什么,我自己也深感困惑,无法回答。最近一段时间,因为工作方向的调整,我逐渐偏向低层技术,对于计算机体系结构、操作系统等以前不太注意的领域投入了相当大的精力学习,逐渐对上面那个曾经令我困惑的问题有了一些想法,并且对我之前的观点有了进一步的扩展。我想在这里再次拿出来,与大家探讨。
首先要说明的一点是,语言绝对不是一个无关紧要的问题。很多人喜欢引述的一句话是:编程语言只是工具,关键是背后的算法和思想。这种说法起源于某些讲授算法的资料。应该说,对于算法描述来说,语言之间的差异是无关紧要的,但是现代的编程语言并不仅仅是用来表述算法的工具,更重要的是作为构件单元来构造系统。语言的能力、表达风格、思维方式、特性以及习惯用法将在很大程度上决定系统的架构、功能和基本特点,决定人们设计和生产软件的方式。因此,我们才有必要始终对编程语言的发展报以巨大的关注。按照我一个朋友的说法,编程语言始终是计算机领域中最本质的东西之一,特别是在计算机工程(而不是科学)领域中,“用什么形式来控制机器”始终是一个核心问题。
说实在的,这个话题很大,我也想到了很多东西。但是我没有足够的时间来详细解释,更没有时间精心组织这些观点。所以,在这篇文章里,我只是简单地罗列和阐述我的基本观点。我相信真正对这个话题感兴趣的朋友不会介意这样的形式。李泽厚曾经说,他不喜欢德国式的写作方法,写了厚厚的两三卷,上百万字,还只是一个“导论”,他喜欢简洁明了的“提纲”。我发现,当时间不够用的时候,李先生的观点是颇有诱惑力的。
【基本观点】
1. 编程语言与软件系统一样,处于不同层次。层次越高,对人越友好,控制机器的能力越差;层次越低,控制机器的能力越强。选择开发语言时,根据需要解决的问题选择合适的语言层次,是非常必要的。下面是我对于常见语言所划分的层次:
低层语言:汇编,C,Forth
应用场合:控制系统、操作系统、虚拟机、解释器、高速数据处理、核心计算引擎
中层语言:Java, C#, VB.NET, Delphi(Object Pascal), Eiffel, Oberon-2, ...
应用场合:大部分通用和专用软件
高层语言:Perl, Python, Ruby, HTML, SQL, JavaScript, ShellScript, VB, ...
应用场合:主要面向专用软件的开发
通用语言:Ada, C++ 应用场合:所有
专用语言:COBOL, Fortran 应用场合:大型机,商务和数值运算
如果在开发时错误选择了编程语言,则至少是不经济的、复杂的,严重的话会导致整个项目的彻底失利。
2. 什么是低层语言?通常对这个问题有很多不同的答案。我个人对于低层语言有以下一些描述:
1) 直接反映机器行为,而不是人的行为;
2) 强调对机器的控制能力,兵器能够利用这种控制能力最大限度优化操作;
3) 存在到机器语言的清晰映射关系;
4) 能够满足系统编程工作的需求。
所谓系统编程,在我看来,就是值得为优化性能(这里不仅仅指速度性能)而付出高昂代价的编程工作。有个德国人说得更加直接了当:“所谓系统编程,就是那些CPU时间比程序员的时间更重要的工作。” 显然这不是一个很学术的描述,甚至也不是一个让人感到很愉快的说法,但是确实是一个很好的描述。
在低层语言中,C是一种值得大声赞美的语言。C是一个如此了不起的杰作,它以高级语言的形式,构造了一个“可移植的优化汇编代码产生器”,高效、灵活而且完备。然而,有太多的人和组织把大量的精力用来将C变成一种更高层的语言,比如以各种奇怪的宏给它加入面向对象的特性。虽然这被证明是可能的,但从来没有得到广泛地应用,原因很简单——这脱离了C所处的层次,既不经济,也不直观。
3. 中层语言是一般程序员的最佳选择。中层语言可能是我自己“造”出来的一个词汇。我觉得当前最流行的几种语言都属于中层语言。它们的共同特点是,既提供了比较强的控制能力,又强调了对程序员的友好。生产效率较之低层语言有几倍甚至十倍的提高。一般来说,中级语言遵循某一种编程思想,提供了比较强大但是简单的抽象机制,通过完备的库提供高层和低层的控制能力。在可以遇见的将来,中级语言仍将继续是最受欢迎的开发语言。中级语言有其局限性,一方面它对机器的控制能力有限,无法进行最精细的优化。另一方面其背后的指导思想比较单纯,因此需要做出很多妥协才能达成完备性。此外,中级语言通常变化很快,不太稳定。
中级语言中,Java和C#等运行在用C/C++写成的虚拟机上,Eiffel编译产生C语言代码,这形象地表明它们并非直接面向硬件的低层语言。同时,这些语言又被用来构造基础类库 (Foundation library),运行时环境甚至控制系统这些传统上被认为是“系统编程”的工作,所以它们在生产力与控制力之间取得了良好的折中。对于普通程序员来说,是最好的学习选择。而对于一般项目来说,也是优选的开发工具。
4. 通用语言(general purpose)。C++, Ada等通用语言,其实是试图摆脱上述的语言分层,希望以单一语言适应各层需求,面对各个应用领域。因此,它们必须立足某一个层次,通过某种形式的抽象提高或者降低自己的层次。比如,C++立足于低层语言C,通过类、模板、继承等机制提高自己的层次;而Ada,立足于“基于对象”这样比较高层的思想,通过各种package来提供底层控制能力。可以说,某种形式的抽象,是提高(和降低)语言层次的唯一手段。然而,抽象是要付出代价的,抽象能力的增强,要么伴以控制能力的减弱,要么伴以复杂度的剧增。Ada和C++不约而同地选择了强大的抽象能力和控制能力,而构造了复杂无比的语言系统。
5. 一旦提供良好的语言互操作性,各种不同层次语言的有效结合将是构造系统最经济、最有效的方式。相比之下,使用某一种通用语言构造整个系统将是非常不经济的选择。也就是说,让各种语言产生的代码在二进制层面上互相合作,比让程序员在不同的语言层次之间跳跃,要更加经济可靠。
6. 我们可以把一种通用语言看成是纵跨若干层次的几个语言子集的统一体。这也就使得对语言的剪裁在客观上成为合理的行为,同时也是非常困难的过程。因为由几个语言子集组合而成,所以我们可能剪裁语言,以适应各层次的需要。而由于是统一体,这种剪裁难度很大。
7. C++目前作为一种系统语言(也就是低级语言)是完备的,但是作为中高级语言,其支持库、语言机制还有不少缺陷(比如缺乏统一的对象级回调机制)。这给那些希望把C++当成高级语言来使用的人带来很大的麻烦。在这些问题没有解决之前,C++最佳的应用领域仍然是系统开发中——也就是说,把纵跨各层的通用语言C++当成一种低级语言来运用,只在绝对合适的时候恰到好处地运用较高层的机制。
根据P. J. Plauger的论述,对于C++,大致存在从小到大若干个剪裁方案,图示如下:
ANSI C
|
|
| 加入引用、重载等简单扩展
|
|
A better C
|
|
| 加入非虚成员函数、具体类
|
|
Object-based C++
/ / 加入模板 / \ 加入单继承和多态
/ / Object-based Embedded C++
Template C++}
\ /
\ /
\ /
\ /
\ /
\/
Embedded Template C++
|
| 加入多继承、虚基类、异常
|
|
ANSI C++
希望这个图能够对面对实际领域的C++程序员能有帮助。对我自己而言,Better C没有吸引力,我不如直接用移植性更好的C本身。Object-Based C++提供了类和封装,但是提高有限,用C语言也可以很容易的达到相同效果。Embedded C++是日本人制定的一个标准,基本上就是1987年的C++,可用性很高。文档中并没有Object-based Template C++这种说法,只是理论上存在这种可能,即完全不使用继承、多态、异常,只用具体类和模板(STL)进行开发,这与Herb Sutter在1998年之前所倡导的编程风格非常接近。Embedded Template C++是一个非正式的称呼,主要由Green Hill公司、Metrowerk(Code Warrier)等公司维护,最适合嵌入式应用软件的开发。微软的Embedded Visual C++ 3.0就是一个优秀的Embedded Template C++实现,不过它支持多继承和虚基类,以支持COM开发。最后当然就是ANSI C++了。现在嵌入式开发工具的制造商都以全面支持ANSI C++为荣,但是我觉得嵌入式系统的开发人员很长时间(也许永远)都不会需要用到ANSI C++的全集。