元编程有什么用?
我读了:
- 维基百科
- 代码生成与元编程
- 元编程的艺术
- 在c2.com上进行元编程
我承认在元编程/代码生成背后有一些困惑。
有没有人有他们使用元编程/代码生成的具体例子? 更好的解释是为什么它比另一个更好。
编辑 : 蓟会被视为元编程?
想象一下build立汽车的人。 说和使用电脑一样的东西。
在某个时候,他意识到他总是在做同样的事情,或多或less。
所以他build厂build汽车,好多了。 他现在正在编程!
尽pipe如此,在某种程度上,他也意识到自己总是在某种程度上做同样的事情。
现在他决定build厂build工厂。 这是元编程。
元编程是非常强大的,但系统中的一个小故障使所有的优势变成怪物难题。 所以掌握它,并使用它…或远离!
我认为metaprogamming是“编写(或修改)其他程序的程序”。 (另一个答案是“制造工厂的工厂”,很好的类比)。
人们可以find各种各样的用途:自定义应用程序,生成样板代码,针对特殊情况优化程序,实现DSL,插入代码以处理正交devise问题(“方面”)…
有什么值得注意的是已经发明了多less种不同的机制来完成这样的工作:文本模板,macros,预处理器条件,generics,C ++ – 模板,方面,reflection…通常这些机制中的一些被构build到某些语言中其他机制转换成其他语言,大多数语言根本没有元编程支持。 这种分散的function分布意味着你可能能够用某种语言进行某种元编程,但是有局限性,而不能在另一种语言中进行。 这是加重的: – }
我一直在观察的观察是,可以构build通用的元编程机制,以程序转换的forms与任何语言一起工作。 程序转换是一个参数化的模式:“如果你看到这个语法,用这个语法replace它”。
一个转变本身一般不令人印象深刻,但数十或数百可以做出惊人的变化的代码。 因为(复杂的)程序转换实际上可以模拟一个图灵机,所以他们可以执行任意的代码改变,包括你发现的所有那些点分明的技术。
接受语言定义的工具。 特定于语言的转换并产生另一个应用这些转换的是一个元计算工具:编写“编写程序的程序”的程序。
值的是你可以应用这样的工具来对任意代码进行各种各样的变化。 而且,你不需要语言devise委员会意识到你想要一种特定的元编程支持,并且快速提供它,所以你今天可以继续工作。
一个有趣的教训是,这样的机器需要强大的程序分析(符号表,控制和数据stream分析等)支持,以帮助它专注于代码中存在问题的地方,这样元编程机器可以在这个时候做一些事情这方面的一个弱点就是在各个方面的切点规范,即“在这样的地方做出改变”)。
OP询问应用元编程的具体例子。 我们已经使用了我们的“元”程序工具( DMS Software Reengineering Toolkit ),在大型代码库中自动执行以下活动:
- 语言迁移
- 实现testing覆盖率和分析器
- 实施克隆检测
- 大规模的架构再造
- 用于工厂控制的代码生成
- embedded式networking控制器的SOA化
- 大型机软件的体系结构抽取
- 从数组计算中生成向量SIMD指令
- 将代码逆向工程回到概念
跨越多种语言,包括Java,C#,C ++,PHP,…
OP还问:“为什么这个比替代品好?” 答案与规模,时间和准确性有关。
对于大型应用程序,代码库的庞大规模意味着您没有资源或时间手动进行此类分析或更改。
对于代码生成或优化任务,您可以手动完成,但这些工具可以更快,更准确地完成任务。
实质上,这些工具是人类根本无法做到的。
值得注意的是,这些工具没有创造力; 你仍然需要人类去决定要做什么,比如决定什么是任务(参见上面的例子),并确定如何定义分析/变换以达到效果。 你仍然需要元编程器。 但是,当一个元程序员使用正确的知识武装这样的工具时,所得到的代码似乎是由一个令人难以置信的快速,创造性的专家编码器构build的。
我已经最多地使用元编程为不同的API之间的桥接。
一个可行的例子是FireBreaths JSAPIAuto
1 ,它可以轻松编写暴露于JavaScript的C ++类。 通过为将要暴露的函数提供注册function,可以检查参数types,并从编译时生成的适配代码将脚本APItypes转换为本机C ++types并返回,甚至直接支持map
, vector
等
作为一个简单的例子,考虑使用一些脚本APItypes的公开的add(a, b)
函数:
ScriptVariant add(const std::vector<ScriptVariant>& values) { // have to check argument count if(values.size() != 2) throw script_error("wrong number of arguments"); try { // have to convert from scripting-API types long a = values[0].convert_cast<long>(); long b = values[0].convert_cast<long>(); return a+b; // potentially need to convert back too } catch(ScriptVariant::bad_cast& e) { // need to handle conversion failure throw script_error("conversion failed :("); } }
埋在那里的实际逻辑只有一条线,检查和转换是烦人的和多余的。 与前面提到的注册设施(例如在构造函数中):
registerMethod("add", make_method(this, &MyClass::add));
现在可以简单地写成:
long add(long a, long b) { return a+b; }
…框架负责为您生成必要的代码。
1:虽然我会执行一点…清洁…如果我不得不重新开始
我最近的(过去6个月)代码生成的具体例子:
-
我有一个SQL Plus脚本,生成并执行其他SQL Plus脚本。 生成脚本针对某些具有时间戳字段的表运行查询,当我devise脚本时,不可能知道要select什么时间窗口。 所以,主要脚本完成它的工作,并且指出子脚本需要什么时间范围。 然后通过将它们的代码写入文件(并用占位符replace实际的开始和结束时间)来生成下标。 最后它执行下标(s)。 现在我已经在一些情况下使用了这个技巧(虽然通常比这个更复杂),子步骤的结构依赖于前面步骤的结果。
-
我曾经从XSD获取电子表格映射元素到数据库中的表列。 可以使用macros和VBA从电子表格生成XSL片段并完成查询。 这些片段和查询被复制并粘贴到执行它们并处理结果的系统中(大多数情况下不需要更改)。 这不是一个漂亮的解决scheme,但它确实做了一个非常单调乏味的工作,结果的代码可能比我花了一两个星期的时间写得更加一致。
所以元编程示例列表: 什么是你在C ++中看到的元编程最酷的例子?
我可以举个具体的例子:我正在开发ABSE ,这是一个元编程的方法。 通过ABSE,您可以创build一个模型(实际上是一棵树),其中每个项目都是“Atom”。 这个Atom代表了一个“概念”,包含了定义所需的元数据。
在ABSE中,实现一个概念实际上是一个“小程序”。
然后,主模型( AtomWeaver ,与ABSE 共同开发)采用该模型,并从所有primefaces中“编织”一个生成器程序。 然后运行该程序,生成所需的工件(源代码,数据等)。
所以,ABSE工作stream程是:
- 创build一个离散的概念(元元程序的一部分)
- 在模型中重用这个概念(有效地构build元程序)
- 主机build模器编织并运行元程序
- 元程序产生你的最终节目
乍一看,这看起来像很多冗余的,复杂的工作,但如果你掌握了这个概念,那实际上是非常简单的。
元编程的优点(不是排除在ABSE之外)?
- 更改模型并重新生成一个完整的系统(想象重构function而不是源代码行)。
- 改变模型中的一些定义可能导致不同的程序(一个软件产品系列)。
- 通过重复使用模板,您可以更改模板的代码,重新生成并在几十个地方更改您的代码。
- 其他许多人,真的
元编程,代码生成,程序转换是软件开发中令人兴奋的新世界,恕我直言。 然而,元编程需要一个新的技能:元思维。
我们可以将元思维定义为“思考如何思考自己的发展”。 一种阶级反思,适用于你自己。 在实践中,你必须找出你自己的开发模式,把它们分离出来,把它们变成通用的,然后用你喜欢的技术把它们变成元程序,比如ABSE,DSL,DSM等等。
基于元编程的库/代码可以帮助直接编写显式和简单的代码,根据使用的参数生成实现细节代码。
Boost充满了(C ++)库,展示了元编程可以实现的function。 一些好的(也许很难理解)的例子是允许实现DSL , Spirit允许直接在代码中使用EBNF语法编写编译器的Proto ,以及许多其他的打击库。
一个具体的例子,它可能是一个有用的方法。
您有一组第三方类,您想要添加通用行为 – 例如某种安全/访问控制,将对象映射为JSON等。
你可以编写或生成所有的子类,添加包装方法来添加访问控制并调用超类。 使用元编程,您可以在运行时执行此操作,而且您的更改将自动应用于任何其他/更改的第三方类。
用JSON的例子,通过使用这个类的自省,你应该能够生成代码来串行化一个对象,然后把它作为一个方法添加到这个类中。 另一个极端是在编译之前产生或编写代码,并在每次类更改时都会影响代码,或者每次想要映射它时都使用对每个单独对象进行自省的完全通用的方法。
根据所讨论的语言和运行时间,metaprogamming方法可能比完全通用/内省的方法更快,但是前面的代码会更慢,因为您已经减less了大量数据查找代码。
在元语言编程不是直接存在于语言中的情况下,在我看来,它通常是通过框架(即像Spring这样的IoC风格的容器)重新发明的。
启动你的Visual Studio(Eclipse,Netbeans,其他)。 创build一个新的项目。 惊喜 – 你刚刚使用了一些元编程,通过模板创build一个项目。 这不实用吗?
你可以看看Common Lisp的macros或C ++的模板,看看它们是如何使用的。 两者都是你正在使用的元编程。 你会发现两者在很多代码中都被大量使用。
Lispmacros通常用于重新定义语言。 作为一个例子,Paul Graham的On Lisp的最后一章为Common Lisp创build了一个面向工作对象的扩展。 另一个例子是现在已经不存在的石榴石 。
旧的C ++标准模板库(主要集成在标准库中)是一种引入大量容器和algorithm的方法,至less在集成和效率方面(不是语法上) 。