前几天看了荣耀的《C++模板元编程技术与应用》演讲,提到了元编程的一些好处和不利。
由于模板具备展开的特性,特化提供了选择的能力,因此很容易得到以下好处:
1. 编译期计算
比如我们计算Fibonacci数列,就可以避免以往的运行期计算,直接在编译期由编译器计算出需要的结果。
2. 编译期选择
作者给出了通过条件选择两个模板类型之一的示例。
并能在以下方面取得较好的应用前景
1. 编译期结构控制
因为具备了特化选择的能力,很容易实现编译期的if流程;加上模板展开能力,将非常容易的做到for等的循环开 解;条件的选择,同样相当方便的就做到了switch。
2. 编译期数据结构
数据的存储,同样可以通过特化能力进行分支选择。
3. 数值计算
4. 类型计算
5. 代码生成
6. 编译期断言和约束
通过不存在的展开代码和条件,导致编译无法通过,从而实现断言。
7. 库设计
8. DSEL(domain-specific embedded language)设计
模板元编程的不利:
1. 代码的可读性较差
2. 调试困难
3. 编译时间延长
4. 结果程序性能未必一定最优化
5. 编译器局限性
6. 可移植性较差
作者准确、严格、精确的描述,给我们指出了一片广阔的空间。C++本身就是一个程序员负责的语言,程序员的稍有大意,就会造成无法预期的结果。我们生成的代码,编写的程序、软件,本身就是交给机器去运行的,不管是直接在硬件上执行,还是在VM上运行。元编程无疑是将部分代码交给了编译器去解释执行。
1. 编译期计算真的那么重要?
拿作者给出的Fibonacci数列,编译器的解释,为什么不能在运行期获得,而直接赋值呢?一个是编译器先解释,一个是另一个程序先运算。通常我们垢弊的就是直接赋值可读性差,元编程天生的毛病这里也有。当我们对所赋值有着提示性的注释 时,结果并全然不一样了。
2. 编译期选择真的那么重要?
这一技巧性的特化所得到的结果最终是固定的代码,直接也可以写出我们需求的这个固定的代码。一个是我们编写给机器生成的代码(我们间接编写),一个是我们直接编写的代码。
对应更具体的应用,带给了我们什么?
1. 编译期结构控制
结构流程的控制,本来是我们控制程序运行的。编译器的流程控制:If选择流程,除了一层奇怪的包装,并没有带来更清晰的流程,更高的效率;开解的循环,同样对运行期无能为例,对编译时本身就固定的循环,只是比手动的开解书写的更快, 但却带来了奇怪的语法;switch跟是奇怪的语法。它们带来的,究竟是什么?除了新的奇怪的语法(其实也不新了,从模板的引入, 这种语法就已经诡异的出现在了我们面前),恐怕更难有明显的好处。
2. 编译期数据结构
把本来就复杂的结构,变得更扑朔迷离,诡异难辨。
3. 数值计算
元编程对数值的计算,主要体现在编译器的展开解释上,预计算的元代码改为运行期代码,最终需要的结果也一样。同样是编程,编写运行期的这段代码,由于已有的经验和较少的技巧,大多数人可以更快的编写;对元程序需要的编译时间,运行 期的编译显得更少,多的只是新建工程的时间;对于C++标准要求的至少12次迭代能力,运行期显得更为宽松自由。
4. 类型计算
数据类型通常是程序员手动编写的,对程序员而言并不需要判断,尤其是C++这种由程序员负责的语言。
5. 代码生成
在遇到大量类同相似的代码时,为了减少编码时间,我通常采用宏生成代码。同样的调试困难,但清晰的宏将使得你一看便知。对于采用编译期分支、展开能力生成代码,可读性不佳并难以调试,我更倾向于手动去编写这部分代码。
6. 编译期断言和约束
采用特化的技巧,使得编译时失败,面对编译器里输出的那一堆信息,通常要查找上半天,才能知道问题所在。对于C/C++程序员,我们会确保表达式求值的正确。去编写一个static_assert,并没有习惯了自身确保表达式求值正确,更快捷,效果 可能也不太那么明显。当然,当编译器提供了这一支持时,对我们来说,也可带来适度的便利。
7. 库设计
在C++,已经漫天飞舞着template的今天,库设计,可能是元编程带来的好处可以被极大发挥和应用的地方。
8. DSEL设计
采用C宏编写脚步的例子以前也曾见过。
由于以上的优点和缺点,而这些优点在通常编程中并不明显,估计也就是今天我们看到的元编程的现状结果。
附:我见过一个元编程实现的max函数,不过,相对于宏,我委实没有发现它的好处所在。
由于模板具备展开的特性,特化提供了选择的能力,因此很容易得到以下好处:
1. 编译期计算
比如我们计算Fibonacci数列,就可以避免以往的运行期计算,直接在编译期由编译器计算出需要的结果。
2. 编译期选择
作者给出了通过条件选择两个模板类型之一的示例。
并能在以下方面取得较好的应用前景
1. 编译期结构控制
因为具备了特化选择的能力,很容易实现编译期的if流程;加上模板展开能力,将非常容易的做到for等的循环开 解;条件的选择,同样相当方便的就做到了switch。
2. 编译期数据结构
数据的存储,同样可以通过特化能力进行分支选择。
3. 数值计算
4. 类型计算
5. 代码生成
6. 编译期断言和约束
通过不存在的展开代码和条件,导致编译无法通过,从而实现断言。
7. 库设计
8. DSEL(domain-specific embedded language)设计
模板元编程的不利:
1. 代码的可读性较差
2. 调试困难
3. 编译时间延长
4. 结果程序性能未必一定最优化
5. 编译器局限性
6. 可移植性较差
作者准确、严格、精确的描述,给我们指出了一片广阔的空间。C++本身就是一个程序员负责的语言,程序员的稍有大意,就会造成无法预期的结果。我们生成的代码,编写的程序、软件,本身就是交给机器去运行的,不管是直接在硬件上执行,还是在VM上运行。元编程无疑是将部分代码交给了编译器去解释执行。
1. 编译期计算真的那么重要?
拿作者给出的Fibonacci数列,编译器的解释,为什么不能在运行期获得,而直接赋值呢?一个是编译器先解释,一个是另一个程序先运算。通常我们垢弊的就是直接赋值可读性差,元编程天生的毛病这里也有。当我们对所赋值有着提示性的注释 时,结果并全然不一样了。
2. 编译期选择真的那么重要?
这一技巧性的特化所得到的结果最终是固定的代码,直接也可以写出我们需求的这个固定的代码。一个是我们编写给机器生成的代码(我们间接编写),一个是我们直接编写的代码。
对应更具体的应用,带给了我们什么?
1. 编译期结构控制
结构流程的控制,本来是我们控制程序运行的。编译器的流程控制:If选择流程,除了一层奇怪的包装,并没有带来更清晰的流程,更高的效率;开解的循环,同样对运行期无能为例,对编译时本身就固定的循环,只是比手动的开解书写的更快, 但却带来了奇怪的语法;switch跟是奇怪的语法。它们带来的,究竟是什么?除了新的奇怪的语法(其实也不新了,从模板的引入, 这种语法就已经诡异的出现在了我们面前),恐怕更难有明显的好处。
2. 编译期数据结构
把本来就复杂的结构,变得更扑朔迷离,诡异难辨。
3. 数值计算
元编程对数值的计算,主要体现在编译器的展开解释上,预计算的元代码改为运行期代码,最终需要的结果也一样。同样是编程,编写运行期的这段代码,由于已有的经验和较少的技巧,大多数人可以更快的编写;对元程序需要的编译时间,运行 期的编译显得更少,多的只是新建工程的时间;对于C++标准要求的至少12次迭代能力,运行期显得更为宽松自由。
4. 类型计算
数据类型通常是程序员手动编写的,对程序员而言并不需要判断,尤其是C++这种由程序员负责的语言。
5. 代码生成
在遇到大量类同相似的代码时,为了减少编码时间,我通常采用宏生成代码。同样的调试困难,但清晰的宏将使得你一看便知。对于采用编译期分支、展开能力生成代码,可读性不佳并难以调试,我更倾向于手动去编写这部分代码。
6. 编译期断言和约束
采用特化的技巧,使得编译时失败,面对编译器里输出的那一堆信息,通常要查找上半天,才能知道问题所在。对于C/C++程序员,我们会确保表达式求值的正确。去编写一个static_assert,并没有习惯了自身确保表达式求值正确,更快捷,效果 可能也不太那么明显。当然,当编译器提供了这一支持时,对我们来说,也可带来适度的便利。
7. 库设计
在C++,已经漫天飞舞着template的今天,库设计,可能是元编程带来的好处可以被极大发挥和应用的地方。
8. DSEL设计
采用C宏编写脚步的例子以前也曾见过。
由于以上的优点和缺点,而这些优点在通常编程中并不明显,估计也就是今天我们看到的元编程的现状结果。
附:我见过一个元编程实现的max函数,不过,相对于宏,我委实没有发现它的好处所在。
评论列表
小明2014-4-7 15:43:00
re: 关于“元编程”的浅思考
C++元编程有些太复杂,但是确实也很强大,就是普通程序员用到的可能性不大。
代码生成方面,Java中XDoclet和类似于Velocity之类的模板引擎,学起来简单一些,功能也不弱。
C++元编程有些太复杂,但是确实也很强大,就是普通程序员用到的可能性不大。
代码生成方面,Java中XDoclet和类似于Velocity之类的模板引擎,学起来简单一些,功能也不弱。
Diviner2014-4-7 15:43:00
re: 关于“元编程”的浅思考
任何把C++变得很复杂的做法我都反对。这东东有点hacker的味道,但却又价值不大。
任何把C++变得很复杂的做法我都反对。这东东有点hacker的味道,但却又价值不大。
清风雨2014-4-7 15:43:00
re: 小明、Diviner
小明:
看了你对java还很有研究啊!我只知道java的语法。现在的范型java,由于长期没有学习过了,估计现在连语法都不会了。
Diviner:
我和你对这个的感觉很类同。我总觉得模板的引入可能是C++的一个败笔。不过,自己了解不深,所以,只能感觉。
小明:
看了你对java还很有研究啊!我只知道java的语法。现在的范型java,由于长期没有学习过了,估计现在连语法都不会了。
Diviner:
我和你对这个的感觉很类同。我总觉得模板的引入可能是C++的一个败笔。不过,自己了解不深,所以,只能感觉。
周星星2014-4-7 15:43:00
re: 关于“元编程”的浅思考
我没有看出模板元编程有任何不利:
1. 代码的可读性较差
2. 调试困难
3. 编译时间延长
--- 想吃饭就得张嘴,张嘴也很累,但不能说吃饭有一个不利点:累。因为你完全可以不去吃。如果是因为C++提供了元编程的能力,使得不使用元编程的代码其可读性也变差了,调试也变困难了,编译时间也变长了,那才可以说这是元编程的一个缺点。
4. 结果程序性能未必一定最优化
--- 我就想不通了,既然未必一定最优化,那你为什么要这么设计?
随手拿个工具就使用,也不先了解一下这个工具适不适合现在的这个工作需要,却反口责备工具不行。
5. 编译器局限性
6. 可移植性较差
--- 现在说的是C++的元编程,还是说的是某一个编译器特有的元编程?如果是前者,那么一切都按照标准C++规定的语法来,既然是标准C++,又哪来的编译器局限性和可移植性较差?
--------------------------------------------
如果C++可以增加编译期代码执行能力的话,那么元编程就是不惜要的,比如
<--
int foo( int m, int n ) { return pow(m,n); }
-->
<-- if( 123 == foo() ) -->
fun1();
<-- else -->
fun2();
但要知道元编程是在不更改C++原有语法的前提下实现的编译期代码执行能力,所以 元编程 和 那个XX语言的YY功能 是不可以进行比较的,因为存在基础不一样。
我没有看出模板元编程有任何不利:
1. 代码的可读性较差
2. 调试困难
3. 编译时间延长
--- 想吃饭就得张嘴,张嘴也很累,但不能说吃饭有一个不利点:累。因为你完全可以不去吃。如果是因为C++提供了元编程的能力,使得不使用元编程的代码其可读性也变差了,调试也变困难了,编译时间也变长了,那才可以说这是元编程的一个缺点。
4. 结果程序性能未必一定最优化
--- 我就想不通了,既然未必一定最优化,那你为什么要这么设计?
随手拿个工具就使用,也不先了解一下这个工具适不适合现在的这个工作需要,却反口责备工具不行。
5. 编译器局限性
6. 可移植性较差
--- 现在说的是C++的元编程,还是说的是某一个编译器特有的元编程?如果是前者,那么一切都按照标准C++规定的语法来,既然是标准C++,又哪来的编译器局限性和可移植性较差?
--------------------------------------------
如果C++可以增加编译期代码执行能力的话,那么元编程就是不惜要的,比如
<--
int foo( int m, int n ) { return pow(m,n); }
-->
<-- if( 123 == foo() ) -->
fun1();
<-- else -->
fun2();
但要知道元编程是在不更改C++原有语法的前提下实现的编译期代码执行能力,所以 元编程 和 那个XX语言的YY功能 是不可以进行比较的,因为存在基础不一样。
局部变量2014-4-7 15:43:00
re: 关于“元编程”的浅思考
我需要的是“元元编程”和“元元元编程”:)
我需要的是“元元编程”和“元元元编程”:)
Diviner2014-4-7 15:43:00
re: 关于“元编程”的浅思考
反正我的建议是在实际的项目中尽量少用模板(标准库的除外),坚决不用比较复杂的模板(连阅读都成问题的),元编程更是否决。
反正我的建议是在实际的项目中尽量少用模板(标准库的除外),坚决不用比较复杂的模板(连阅读都成问题的),元编程更是否决。
清风雨2014-4-7 15:43:00
个人看法
我觉得除非做库设计,甚至做库设计也最好少用模板。
别的方面,尤其是实际应用中,最好是不要用模板。
已有的STL,因为已经是标准,采用使用,而不是开发的心态来利用。
对于模板的范型,个人更倾向尽量的用继承和多态来完成。
对于模板的特化,不妨也采用多态来努力。(不过,我打出了多态后,自己犹豫了,当我google以后,也确实值得犹豫了,overload是否该算Polymorphisn? 这个是题外话了。)
我觉得除非做库设计,甚至做库设计也最好少用模板。
别的方面,尤其是实际应用中,最好是不要用模板。
已有的STL,因为已经是标准,采用使用,而不是开发的心态来利用。
对于模板的范型,个人更倾向尽量的用继承和多态来完成。
对于模板的特化,不妨也采用多态来努力。(不过,我打出了多态后,自己犹豫了,当我google以后,也确实值得犹豫了,overload是否该算Polymorphisn? 这个是题外话了。)
周星星2014-4-7 15:43:00
re 清风雨:
建议你看看《C++设计新思维》,不是想让你见识一下模板的伟大,而是想让你知道 (模板) 和 (继承和多态) 是完全不同功能的方法。对于一个用户需要的功能,你可以不使用(模板),也可以不使用(继承和多态),但(模板)实现不了任何(继承和多态)的功能,同时(继承和多态)也实现不了任何(模板)的功能。
《C++设计新思维》讲得很清晰,哪些能力和数据是(继承和多态)可以提供的,哪些能力和数据是(模板)可以提供的。
建议你看看《C++设计新思维》,不是想让你见识一下模板的伟大,而是想让你知道 (模板) 和 (继承和多态) 是完全不同功能的方法。对于一个用户需要的功能,你可以不使用(模板),也可以不使用(继承和多态),但(模板)实现不了任何(继承和多态)的功能,同时(继承和多态)也实现不了任何(模板)的功能。
《C++设计新思维》讲得很清晰,哪些能力和数据是(继承和多态)可以提供的,哪些能力和数据是(模板)可以提供的。
清风雨2014-4-7 15:43:00
re: 周星星
你觉得在哪些情况最好采用继承和多态,哪些情况最好采用模板呢?具体些,谢谢!
你觉得在哪些情况最好采用继承和多态,哪些情况最好采用模板呢?具体些,谢谢!
diclogic2014-4-7 15:43:00
re: 关于“元编程”的浅思考
《C++设计新思维》推荐看一下。
>>1. 代码的可读性较差
>>2. 调试困难
个人觉得尽量轻量化工具化会比较好,诚然某些复杂的compile-time逻辑会出现不易维护的弊端,但将这样的“编译期工具”实现得尽可能功能单一且范适,那么我们可以用严密的unit test来保证它的质量。这样我们在调试的时候就可以降低对他的怀疑程度,就像我们通常会将对c库函数的怀疑放在最后一样。
>>3. 编译时间延长
既然是compile-time的逻辑,会延长编译时间是理所当然的,但比起运行时间的延长我猜大多数人宁愿选前者。。。
>>4. 结果程序性能未必一定最优化
如果是一些compile-time数值工具那么通常会得到常数结果,如果是compile-time多态那么通常会得到一个inline call(可能是一个硬jump也可能是直接将代码内嵌),仅就这两种较常见的应用来说效率显然是提高的。当然任何获益通常都是有代价的--我们牺牲了run-time活动性。
>>5. 编译器局限性
>>6. 可移植性较差
这两个问题应该算是同一个问题,既然template成为了标准,我们只能期望慢慢地他会被广泛支持。记得好像gcc支持对经过预处理期的中间代码的输出,不知道会不会支持对template处理后的中间代码的输出,如果能的话就不担心局限性问题了。
滥用run-time多态一样会造成不易维护的代码,所以问题出在“滥用特性”上而不是特性本身。
以上观点有不对的地方请指正
《C++设计新思维》推荐看一下。
>>1. 代码的可读性较差
>>2. 调试困难
个人觉得尽量轻量化工具化会比较好,诚然某些复杂的compile-time逻辑会出现不易维护的弊端,但将这样的“编译期工具”实现得尽可能功能单一且范适,那么我们可以用严密的unit test来保证它的质量。这样我们在调试的时候就可以降低对他的怀疑程度,就像我们通常会将对c库函数的怀疑放在最后一样。
>>3. 编译时间延长
既然是compile-time的逻辑,会延长编译时间是理所当然的,但比起运行时间的延长我猜大多数人宁愿选前者。。。
>>4. 结果程序性能未必一定最优化
如果是一些compile-time数值工具那么通常会得到常数结果,如果是compile-time多态那么通常会得到一个inline call(可能是一个硬jump也可能是直接将代码内嵌),仅就这两种较常见的应用来说效率显然是提高的。当然任何获益通常都是有代价的--我们牺牲了run-time活动性。
>>5. 编译器局限性
>>6. 可移植性较差
这两个问题应该算是同一个问题,既然template成为了标准,我们只能期望慢慢地他会被广泛支持。记得好像gcc支持对经过预处理期的中间代码的输出,不知道会不会支持对template处理后的中间代码的输出,如果能的话就不担心局限性问题了。
滥用run-time多态一样会造成不易维护的代码,所以问题出在“滥用特性”上而不是特性本身。
以上观点有不对的地方请指正
周星星2014-4-7 15:43:00
re diclogic:
您的观点和俺一致
您的观点和俺一致
刘未鹏2014-4-7 15:43:00
re: 关于“元编程”的浅思考
我现在越来越觉得mp的作用主要在于构建库组件的interface上面。宽泛一点来讲,就连DSEL也是一种interface.另外,元编程在构建active library方面很可能会一展宏图(实际上Blitz++就是一个Active Library)。C++应该算是主流语言里面第一个实现真正意义上的Active Library的语言,而这正归因于mp的编译期图灵完备能力。
我现在越来越觉得mp的作用主要在于构建库组件的interface上面。宽泛一点来讲,就连DSEL也是一种interface.另外,元编程在构建active library方面很可能会一展宏图(实际上Blitz++就是一个Active Library)。C++应该算是主流语言里面第一个实现真正意义上的Active Library的语言,而这正归因于mp的编译期图灵完备能力。
发表评论
- 访问:34578次
- 积分:490分
- 排名:第20名
- 随笔:49篇
- 评论:275条
随笔分类
随笔归档
个人相册
阅读排行榜
- 特定条件下,ftell返回值错误 (1737)
- linux移植建议 (1316)
- 真实的陷阱1 — 错误的宏定义 (1247)
- 自己的lzw实现 (1242)
- 关于“元编程”的浅思考 (1145)
- windows上的简单读写锁实现 (1107)
- 真实的陷阱2 — 多线程案例 (1104)
- 关于几个C字符串库函数的思考 (1044)
- 关于质数(素数)的算法 (1033)
- 贴一个头文件,恳请大家意见和想法 (991)
评论排行榜
- 有感公司一次郁闷的服务器重写 (19)
- 关于质数(素数)的算法 (16)
- 我是一个中国人,但是我却很羞愧 (16)
- 贴一个头文件,恳请大家意见和想法 (15)
- 关于几个C字符串库函数的思考 (15)
- 我犯的一个很愚蠢的错误,当时还以为没错 (13)
- 今天刚批下来,特别高兴! (13)
- 关于“元编程”的浅思考 (12)
- 经常发生的故事!请平静看待。 (12)
- 用自己的话浅谈封装 (12)
最新评论
- 自己的lzw实现
MukkAlomo:<a href=></a>. . . &nb...
- 自己的lzw实现
XertJak:10mg zolpidem tartrate can you take more than one ...
- 自己的lzw实现
Hytcem:zolpidem tartrate 10 mg price ingredien...
- 自己的lzw实现
Sadnus:where can i buy ambien ambian pill ambien hallucin...
- 自己的lzw实现
KoipSiny:10mg ambien buy astelin . ambien classi...
- 自己的lzw实现
FreaBox:ambien uses ambiencr ambien cr buy online <a hr...
- 自己的lzw实现
Merjoum:ambien where to buy sleep pill ambien ....
- 真实的陷阱2 — 多线程案例
aab:这代码,看着是有点累!
- 特定条件下,ftell返回值错误
wifecooky:re: 特定条件下,ftell返回值错误 以r+b方式打开就没问题
- 特定条件下,ftell返回值错误
清风雨:re: 周星星 在windows上我运行得到的结果也是“0,3,0,6,0,9”; 在centos...