元编程有什么用?

时间:2010-08-12 13:40:23

标签: language-agnostic code-generation metaprogramming

我读过:

我在元编程/代码生成背后的目的上承认了一些困惑。

有没有人具有使用元编程/代码生成的具体示例?更好的是伴随解释为什么它比替代方案更好。

修改: 将Thistle视为元编程吗?

11 个答案:

答案 0 :(得分:109)

想象一个制造汽车的人。说它与使用电脑是一回事 在某些时候,他意识到他总是做着同样的事情,或多或少 所以他建造工厂来制造汽车,而且它要好得多。他现在正在编程!
尽管如此,在某种程度上,他再次意识到他在某种程度上总是在做同样的事情 现在他决定建立工厂,建造汽车制造厂。这是元编程。

元编程非常强大,但系统中的一个小故障使得所有优势变成了怪物的困难。所以掌握并使用它......或者远离它!

答案 1 :(得分:20)

我认为metaprogamming是“编写(或修改)其他程序的程序”。 (另一个答案说“制造工厂的工厂”,很好比喻)。

人们可以找到各种用途:自定义应用程序,生成样板代码, 针对特殊情况优化程序,实现DSL,插入代码来处理 正交设计问题(“方面”)......

有多少不同的机制被发明来做这个零碎的事情是多么值得注意的: text-templates,macros,preprocessor conditionals,generics,C ++ - templates,aspects,reflection,... 通常,这些机制中的一些内置于某些语言和其他机制中 到其他语言,大多数语言都没有元编程支持。 这种分散的功能分布意味着您可以做一些 一种语言中的各种元编程,有局限性,但却无法在另一种语言中进行。这是加重的: - }

我一直关注的一个观察是人们可以构建泛型 元编程机制,以任何语言的形式使用 program transformations。 程序转换是一种参数化模式:“如果您看到语法,请将其替换为 语法”。

一个转变本身通常并不令人印象深刻,但数十或数百可以做到 代码的惊人变化。因为(复杂的)程序转换可以在 效果模拟图灵机,它们可以执行任意代码更改,包括 所有那些你发现散点图的点技术。

接受语言定义的工具。语言特定的转换和生成 另一个应用这些转换的是一个 meta -metaprogramming工具: 编写“编写程序的程序”的程序。

价值在于您可以应用此类工具来执行各种变更 任意代码。并且,您不需要语言设计委员会来实现您 想要一种特殊的元编程支持,并快点提供它 所以你今天可以继续工作。

一个有趣的教训是,这种机器需要强有力的程序分析(符号 表格,控制和数据流分析等) 支持帮助它专注于代码中的问题,以便元编程 机器可以在那一点做某事(这是一个非常弱的例子 方面的切入点规范,即“在看起来像这样的地方做出改变”。)

OP询问了应用元编程的具体示例。 我们使用了“meta” - 目标编程工具(DMS Software Reengineering Toolkit)自动在大型代码库上执行以下活动:

  • 语言迁移
  • 实施测试覆盖率和分析器
  • 实施克隆检测
  • 大规模架构再造
  • 工厂控制的代码生成
  • 嵌入式网络控制器的SOA化
  • 大型机软件的架构提取
  • 从阵列计算生成矢量SIMD指令
  • 将代码反向工程回到概念

跨多种语言,包括Java,C#,C ++,PHP,......

OP还问道,“为什么这比替代方案更好?” 答案与规模,时间和准确性有关。

对于大型应用程序,代码库的庞大规模意味着您没有资源 或者是手工进行此类分析或更改的时间。

对于代码生成或优化任务,您可以这样做 手动,但工具可以更快,更准确地完成。

从本质上讲,这些工具是人类根本无法做到的。

值得注意的是,这些工具没有创造力;你还在 需要人类确定他们做什么,例如,决定 这个任务是什么(参见上面的例子列表)并确定 如何定义分析/转换以实现效果。 你仍然需要 meta - 程序员。但是,当一个元程序员 拥有这样一个具有正确知识的工具,由此产生的代码可以 似乎是由一个非常快速,有创意的专家编码器构建的。

答案 2 :(得分:5)

我最大程度地利用元编程来桥接不同的API。

一个工作示例是FireBreaths JSAPIAuto 1 ,它可以简化编写暴露给JavaScript的C ++类。通过为要公开的函数提供注册工具,可以检查参数类型,并从编译时生成的适配代码检查,该代码代码从脚本API类型转换为本机C ++类型并返回,甚至直接支持{ {1}},map

作为一个简单示例,请考虑使用某些脚本API类型的公开vector函数:

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));

...并且框架负责为您生成必要的代码。

1:虽然我会做一点......更清洁......如果我不得不重新开始

答案 3 :(得分:3)

我最近(最近6个月)代码生成的具体示例:

  1. 我有一个SQL Plus脚本,可以生成并执行其他SQL Plus脚本。 generate脚本对一些具有时间戳字段的表运行查询,当我设计脚本时,无法知道要选择的时间窗口。因此,主脚本完成其工作,并确定子脚本中需要的时间范围。然后它通过将代码写入文件(并用占位符代替实际的开始和结束时间)来生成下标。最后它执行下标。我现在已经在一些情况下使用了这个技巧(虽然通常比这个更复杂),其中子步骤的结构取决于早期步骤的结果。

  2. 我曾经有一个电子表格,将数据从XSD映射到数据库中的表列。可以使用宏和VBA从电子表格生成XSL片段和完整查询。这些片段和查询被复制并粘贴(主要是原样,没有neede更改)到执行它们并处理结果的系统中。这不是一个漂亮的解决方案,但它确实做了一个非常乏味的工作,并且所产生的代码可能比我花了一两个星期的时间用手写的更加一致。

    < / LI>

    元编程示例列表:What are the coolest examples of metaprogramming that you've seen in C++?

答案 4 :(得分:3)

我可以举出具体的例子:我正在开发ABSE,这是一种元编程方法。使用ABSE,您可以创建一个模型(实际上是一棵树),其中每个项目都是“Atom”。此Atom代表一个“概念”,并包含其定义所需的元数据。

在ABSE中,概念的实现实际上是一个“迷你程序”。

然后,与ABSE一起开发的主机建模器(AtomWeaver)接受模型并从其所有原子中“编织”一个生成器程序。然后运行该程序,生成所需的工件(源代码,数据等)。

因此,ABSE工作流程是:

  1. 创建一个离散的概念(元元节目的一小部分)
  2. 在模型中重用该概念(有效构建元程序)
  3. 主机建模器编织并运行元程序
  4. 元程序生成您的最终程序
  5. 乍一看,这看起来像是一个多余的,复杂的工作,但如果你掌握这个概念,它实际上非常简单。

    元编程的优点(不仅限于ABSE)?:

    • 更改模型并重新生成一个完整的系统(想象一下重构功能而不是源代码行)。
    • 更改模型中的一些定义可能会导致不同的程序(软件产品系列)。
    • 通过重复使用模板,您可以更改模板的代码,重新生成并在几十个地方更改代码。
    • 很多其他人,真的

    元编程,代码生成,程序转换是软件开发中令人兴奋的新世界,恕我直言。然而,元编程需要一种新的技能:元思维。

    我们可以将元思维定义为“思考你如何看待自己的发展”。一种适用于自己的课堂反思。在实践中,您必须找出自己的开发模式,隔离它们,使它们通用,然后使用您喜欢的技术将它们变成元程序,如ABSE,DSL,DSM等。

答案 5 :(得分:2)

基于元编程的库/代码有助于直接编写显式和简单的代码,根据使用的参数为您生成实现细节代码。

Boost充满了(C ++)库,展示了元编程可以实现的目标。一些好的(也许很难理解)示例Proto允许实现允许在代码中直接使用DSL语法编写编译器的SpiritEBNF,以及许多其他令人瞩目的图书馆。

答案 6 :(得分:2)

我将尝试解释使用元编程技术的具体示例。

我创建了一个程序工具,该工具将从任何MS Access数据输入表单生成ASP.NET网页源代码。我使用的技术是为每种类型的表单控件创建自己的ASP.NET文本模板。我只是简单地从MS Access表单对象元数据中插入了TOP,LEFT,HEIGHT,WIDTH,CONTROLSOURCE等值。 例如,我的ASP.NET文本框模板如下所示:

 <asp:TextBox ID="**ID**" runat="server" style="z-index: 1; left: **LL**px; top: **TOP**px; position: absolute"  Text='<%# Bind("[**CTLSOURCE**]") %>' />

获取文本框控件元数据值后,我的程序为文本框生成代码

<asp:TextBox ID="txtCustomerID" runat="server" style="z-index: 1; left: 50px; top: 240px; position: absolute"  Text='<%# Bind("[CustomerID]") %>' />

我的程序在2-3秒内为一种MS Access表单生成了整个网页源代码。另一种方法是从头开始手工编写ASP.NET网页;一项可能要花费数小时甚至数天的任务。

想象一个具有24-35个表格的MS Access数据库。要将每个表单的代码作为ASP.NET网页源代码进行手工处理,可能需要数周甚至数月的时间。 在这种情况下,使用具有元编程技术的转换工具可以将网页的开发时间从数周和数月缩短到数小时。

答案 7 :(得分:1)

它可能是一种有用方法的具体例子。

您有一组第三方类,您希望在其中添加通用行为 - 例如某种安全/访问控制,将对象映射为JSON等。

您可以为所有内容编写或生成子类,添加包装方法以添加访问控制并调用超类。使用元编程,您可以在运行时执行此操作,并且您的更改也将自动应用于任何其他/更改的第三方类。

使用JSON示例,通过使用类的内省,您应该能够生成序列化序列的代码,然后将其作为方法添加到类中。另一个极端是生成或编写代码(在编译之前)并在每次类更改时产生影响,或者每次要映射时对每个单独对象使用内省的完全通用方法。

根据所讨论的语言和运行时,元编排方法可能比完全通用/内省方法更快,但前端代码更慢,因为您已将大量数据查找减少为代码。

如果元编程不直接存在于一种语言中,在我看来它通常是通过框架(即像Spring这样的IoC样式容器)重新发明的。

答案 8 :(得分:1)

启动Visual Studio(Eclipse,Netbeans,其他任何东西)。创建一个新项目。惊喜 - 您刚刚使用了一些元编程,通过从模板创建项目。这不实用吗?

答案 9 :(得分:0)

您可以查看Common Lisp的宏或C ++模板,看看它们是如何使用的。两者都是你正在使用的意义上的元编程。你会发现它们在很多代码中被大量使用。

Lisp宏通常用于重新定义语言。作为一个例子,Paul Graham的On Lisp的最后一章为Common Lisp创建了一个面向对象的工作扩展。另一个例子是现已解散的Garnet

旧的C ++标准模板库(主要包含在标准库中)是一种引入大量容器和算法的方式,这些容器和算法就像它们构建在语言中一样,至少在集成和效率方面是如此(不是语法上的。)

答案 10 :(得分:0)

我们经常使用元编程来在VBA中创建属性。我们有各种带有许多标题的Excel电子表格,并且我们希望为每个标题定义getter / setter属性,以允许我们操纵该标题下的单元格。手动执行此操作将是一场噩梦。

我们选择的元编程框架是Notepad ++及其查找/替换正则表达式的功能。这是我们对属性进行元编程的方式:

  • 将标头列表从Excel复制到Notepad ++
  • 记录一个Notepad ++宏以清理数据(删除空格和特殊字符)。最后,我们列出了用换行符分隔的字符串。
  • 手动将列表复制到另一个.CSV文件,然后使用Excel生成行号列表。然后复制回Notepad ++。
  • 编写一个正则表达式将属性名称转换为属性定义,并添加所有空格,关键字等。在我们的属性定义中使用行号作为列号。

最后,我们有了一个混合了手动步骤,记录的宏和一个正则表达式的过程,可以在每次需要图纸的属性时重新应用它。我们做到了!效果很好。

这就是元编程的力量。何时使用,取决于经验/直觉。但是我建议回答这个问题:

  

如果我可以更快地直接编写代码,还是可以自动化部分/全部过程并加快过程?

这给了你一条画线,超出此范围,元编程将不再有用。如果您可以更快地编写代码,即使它是10次重复,那就去做吧!仅当它是数百次重复,或者您希望将来重复使用多次,然后对其进行元编程。

另一点是这里有学位。我曾经写过一个Java程序来创建一堆文件,用于向检查编码项目中添加新的IntelliJ检查。那是相当大的开销:创建Java项目并进行编译等。另一方面,Notepad ++查找/替换只是您自己手动键入内容的一小步。这里的建议是手动开始做事,然后根据您的需要自动执行,直到合理为止。 Notepad ++可以使用时不需要Java程序。手动输入时无需使用Notepad ++。