首页> 中国专利> 基于约束的语言中过程的高效自动翻译

基于约束的语言中过程的高效自动翻译

摘要

提供一种方法,该方法基于对每个约束集的可实例化对象的定义将约束集声明翻译成命令性代码序列,在状态修改时插入对通知回调机制的调用,并将约束上下文中的调用定义为命令性代码序列,该命令性代码序列响应于这些回调采取动作以维持这些约束。该通知和回调机制还可与外部事件一起采用,由此在约束编程语言中提供事件序列化的命令性过程的高效实现。

著录项

  • 公开/公告号CN102227712A

    专利类型发明专利

  • 公开/公告日2011-10-26

    原文格式PDF

  • 申请/专利权人 奥普塔姆软件股份有限公司;

    申请/专利号CN200980147969.9

  • 发明设计人 D·R·谢里登;

    申请日2009-11-23

  • 分类号G06F9/44;

  • 代理机构上海专利商标事务所有限公司;

  • 代理人李玲

  • 地址 美国加利福尼亚州

  • 入库时间 2023-12-18 03:34:35

法律信息

  • 法律状态公告日

    法律状态信息

    法律状态

  • 2014-09-03

    授权

    授权

  • 2011-12-07

    实质审查的生效 IPC(主分类):G06F9/44 申请日:20091123

    实质审查的生效

  • 2011-10-26

    公开

    公开

说明书

技术领域

本发明涉及计算机语言的自动翻译。

背景技术

在计算机技术的发展中,已开发了各种编程范例。这些范例中的两个范例是命令式编程和声明性编程。命令性编程需要提供逐步的命令性指令(例如设定等于2、增量c、调用过程f()),程序员将这些命令性指令组合起来以解决感兴趣的问题。声明性编程需要提供顶层信息(例如一组要强制实施的逻辑约束),该顶层信息经常以更透明地涉及感兴趣的问题的方式来指定。命令性编程往往提供改善的执行性能,但命令性程序可能非常复杂并且难以开发和维护。声明性程序往往相对容易开发和维护,但声明性编程也往往提供相对糟糕和/或受限制的执行性能(例如缓慢的执行和/或无通用性)。命令性和声明性范例的这些优缺点是本领域内公知的。

由于计算机的底层硬件模型是命令性的,并且处理器执行被动存储器上的命令性指令,因此最早的编程语言(例如汇编语言)是命令性的,且当今广泛使用的许多语言(例如C、C++)仍旧主要是命令性的。当前,声明性语言(例如Prolog、Haskell、Siri和ThingLab)往往是研究语言而不是商用开发语言。

在实践中,计算机语言可包括命令性和声明性两种范例的元素,尽管将任何特定语言归类为主要是命令性或主要是声明性很少会是不确定的。例如,C++提供类、虚函数和继承,这允许单段代码在各种对象类型上操作同时仅按照这些类型的某一基类来表达。这是C++的声明性方面,但C++语言本身主要是命令性的。

尽管纯粹的声明性语言尚未表现出通用性,然而在受限域内已获得令人印象深刻的结果。例如,SQL是涉及关系型数据库的查询和更新的受限域声明性语言。例如,查询是由定义“命中”的属性来指定的,而非命令性地指定如何定位这些记录。SQL在数据库应用中具有广泛商业用途。另一受限域语言是JavaFX,它包括类似SQL的触发机制以声明要在值改变时调用的过程。该工具明显受限于仅允许在当前上下文中定义数据元素上的触发条件。这些JaVaFX触发器也类似于AspectJ和其它所谓面向方面的编程语言中的“连接点”。这些连接点允许在对象初始化、字段读写和异常处理程序期间在过程的开始和结束时调用代码。

受限域声明性语言的另一示例是用来向解析器生成器指定语法的语言。该语法被指定为声明性生成规则。解析器生成器将程序作为这些产生规则的集合翻译成实现针对所指定语法的解析器的命令性代码。在指定语法来识别时明显是声明性的,这依赖于语言翻译器来确定如何实现解析器。这种声明性方法已被证明在语言翻译器生成中具有重大价值,这避免了手动实现解析器的耗时且易于出错的任务。然而,编译器或解释器的其余部分是以单独的命令性编程语言来实现的,这导致与共同使用两种不同语言和翻译器相关联的问题。

在某些情形下,由声明性编程提供的有益结果已推动了各种在单个通用编程语言中提供两种范例的主要优点的尝试。迄今为止的方法主要侧重于将命令性编程结构映射到声明性模型(例如在Kaleidoscope’90(万花筒’90)中的运行时间约束解算器)。另一示例是C++的Turtle库,其中Turtle语言的约束模型被结合到C++中。然而,这些方法看来不能解决纯声明性编程模型的固有低效率问题。

因此,提供对命令性和声明性结构两者进行更为高效和系统的使用的通用编程将是本领域的进步。

发明内容

根据本发明的各实施例,声明性编程的所选方面被纳入到否则为命令性的框架中。具体地说,提供具有约束的声明性指定。本发明的各实施例的其它重要方面包括:在程序单元不需要事先知道其数据成员中的哪一些服从约束的情形下在翻译时对约束自动解析以及对程序单元单独翻译。在实践感兴趣的许多情形下,以声明性方式指定的约束可在编译时由编译器来解析,以提供包括用于强制实施约束的自动提供的命令性代码的命令性程序表示。如此,命令性编程所特有的效率不会因为添加了对约束的声明性指定而受到实质性损害。贯穿本申请,“翻译”包括“编译”和“解释”,并且对“编译”等的引用可与对“解释”等的引用互换,除非另外专门有说明。

参照附图开始对本发明的各实施例的一些基本概念的概述是有益的。图1示出已知编程结构的伪代码示例。在该例中,程序单元102包括准声明性语句“do A if X改变”。作为该语句的结果,当程序员(例如在“set X(设定X)”行内)更改X时,则该语言自动提供代码以在“set X”指令后立即“do A(进行A)”。贯穿附图,由语言自动提供的代码由虚线包围。图1的示例类似于一些已知的编程结构(例如JavaFX的触发器)。

从概念上说,图1结构的执行是相对简单的。具体地,当声明性语句如图所示地处于与正被改变的输入相同的程序单元和相同的上下文中时,则执行A的代码可由编译器在更改X的每一行之后立即自动插入。然而,存在这种结构的麻烦的概念性方面。具体地,该示例的A可以是任意命令性代码,能造成状态的任意改变(即任意的副作用)。因此,这些结构具有复杂化软件开发的可能性。获得相同认知的另一方法是观察到“do A”本质上是命令性的,因此语句“do A if X改变”不是纯声明性的。

由本发明的各实施例提供的主要能力在两个显著方面与图1的示例存在区别。第一方面专门针对作为声明性语句的约束的情形。由于约束条件是纯声明性结构,这消除了前述概念上的困难。第二方面是显著扩展了自动命令性代码生成能力以适应约束条件处于与其相应数据成员不同的上下文和/或不同的程序单元的情形。

图2示出相对于图1的这两种改变的示例。在图2的示例中,第一程序单元202包括约束的声明性指定(即“强制实施Y=f(X)”行)以及在约束范围之内和约束范围之外对X的改变的实例。第二程序单元204也包括对X的改变的实例。本发明的各实施例对于对X的改变的所有实例自动提供如图所示的“更新Y”的命令性代码。此外,程序单元204可被单独编译而不必事先知晓其哪些数据成员是约束的输入或服从约束。该顶层功能由通知反应器结构提供,其在图2中示意性地表示为206。在实现结构206时,用于通知和反应的代码在从输入源程序自动派生的中间表示中自动生成。该中间表示的进一步处理提供输出代码,该输出代码包括自动生成的用于强制实施约束的命令性结构,由此减轻程序员这方面的负担。

图3a-5b的示例示出当应用于特定示例时前述过程的步骤。更具体地,根据本发明的一个实施例的用于将包括约束的输入代码自动翻译成计算机可执行输出程序表示的方法在图3a-5b的示例中示出。

该方法的第一步骤是提供以也提供对约束的声明性指定的命令性编程语言表达的输入源程序,其中输入源程序包括根据所述编程语言指定的一个或多个约束。在该示例中,图3a-b示出输入源程序的两个代码段,其中图3a的代码段可处于与图3b的代码段不同的上下文中,并且还可处于单独编译的程序单元中。附图和以下示例中采用C++式伪代码,但本发明的实践不依赖于语言句法的细节。图3a的代码段包括类F的定义,F的实例F1和F2的声明以及对F1.X的赋值。图3b的代码段包括对约束集CS的定义和对F2.Y的赋值。约束集CS包括若干约束条件:tot等于F1.X+F1.Y;tot2等于F1.X+F2.Y;而F1.X等于F2.X*F2.Y。还指定如果F1.X改变则F2.Y保持不变(F2.Y保持不变ifF1.X改变)。约束是约束数据成员(例如tot、tot2、F1、F2)的关系。约束集CS也提供哪些约束数据成员是输入和/或输出的标识。这里tot、tot2专门为输出,而F1和F2既可以是输入又可以是输出。约束输入的标识可以是显式的(如前所述)和/或隐式的。输入可基于其在约束指定内的位置而隐式地标识(例如在等式约束的右手侧的数据成员可隐式地被视为约束输入)。

该方法的第二步骤是自动提供约束的约束表示。每个约束表示包括一个或多个约束反应器对象以用于注册在实例化时的输入改变通知。约束反应器对象包括用于强制实施约束的命令式过程,并且在相应约束表示的范围内是可访问的。在这里和本申请全文中,“包含代码”意指文字内联代码包括和/或(例如使用指针)间接提供对范围外代码的范围内访问。对于该示例的约束表示在图4b中示出,如前所述,其中虚线包围自动提供的代码。

在该示例中,类CS的实例cs是约束表示,而FR1和FR2分别为约束反应器对象实例F1Reactor和F2Reactor。F1Reactor包括用于在F1改变时强制实施约束的代码(即,真正的更新过程)。更具体地,如果F1.X改变,则F1Reactor中的onX()过程根据约束命令性地更新tot、tot2和F2.X。如果F1.Y改变,则F1Reactor中的onY()过程命令性地更新tot。同样,F2Reactor包括用于在F2改变时强制实施约束的代码。在该示例中,F1Reactor和F2Reactor包含在类CS中,该类CS实例化为cs。该实例化为FR1和FR2提供对输入F1和F2的指针访问。优选地但非必需地如本示例所示那样将约束分组成若干约束集。在这些情形下,对约束集的每个输入通常存在一个反应器,每个反应器可包括从多个约束派生的命令性语句,并存在与每个输入约束集(例如图3b上的约束集CS)对应的一个约束集对象表示(例如图4b上的类CS)。

该方法的第三步骤是,对输入源程序的部分或全部程序数据成员中的每一个自动地提供相应的通知代码,以提供改变通知并调节约束表示对输入改变通知的注册,其中相应通知代码在相应程序数据成员的范围内是可访问的。在该示例中,图4a示出该自动提供的通知代码的一部分。将子类Notifiee(被通知者)插入类F内,其中Notifiee包括用于对其输入的改变的虚更新过程(即virtual onX()、virtual onY()和virtual onZ())、指向其实例的指针(即fp)、以及当改变发生时拟通知的约束反应器对象的实例列表(即notifieelist(被通知者列表))。在翻译期间,自动管理被通知者列表以使每个数据成员具有全部完整列表和仅那些该数据成员是其输入的反应器实例。当且仅当相应数据成员改变时,才调用虚更新程序。通常为全部数据成员提供通知能力,即使没有相关约束(例如F1.Z、F2.Z没有约束)。这较为有利地避免了对特定类中的哪些数据成员被涉及在约束中的任何先验知识的要求。在一些情形下,可能需要有选择地对一些数据成员禁用通知。如果对作为约束输入的数据成员禁用通知,则生成翻译错误。

本方法的第四步骤在命令性输出程序表示中实例化通知代码和约束表示,以使对约束输入的改变自动导致命令性约束驱动的更新,并将该命令性输出程序表示作为输出提供。例如,该命令性输出程序表示可通过编译被进一步处理成低级机器语言。

在图4a-b中,一个重要的细节是F1Reactor和F2Reactor类型是从F::Notifiee类型派生的。以此方式,调用虚更新过程(例如F1::Notifiee中的virtual onX())通过类型继承自动导致调用正确反应器(FR1::on(X))中的正确约束更新过程。在图5a-b的代码段中示出该示例的代码的运行。在图5a中,向将赋值提供给F1.X的程序员自动补充虚线中所示的过程调用集。同样,在图5b中,对F2.Y的赋值同样得到虚线中所示的过程调用集的自动补充。

更具体地,改变图5a中的F1.X造成对反应器实例FR1的onX过程作出调用。该FR1.onX()过程根据约束更新tot、tot2和F2.X(通过执行赋值tot=F1.X+F1.Y、tot2=F1.X+F2.Y和F2.X=F1.X/F2.Y)。对F2.X的更新自动地导致对反应器实例FR2的onX()过程作出调用。该FR2.onX()过程通过执行赋值F1.X=F2.X*F2.Y来更新F1.X。然而,该赋值实际上不改变本例中F1.X的值。因此,由于对FR2.onX()的调用实际上不改变F1.X,因此约束更新处理在该调用之后终止。为了更清楚地理解,假定在对F1.X最初赋值前(F1.X,F1.Y))=(u,v)且(F2.X,F2.Y)=(w,x)。该第一次赋值给出(F1.X,F1.Y)=(a,v)。对FR1.onX()的调用导致(F2.X,F2.Y)=(a/x,x)。对FR2.onX()的调用设定F1.X=F2.X*F2.Y=a,a是F1.X在计算中的该点处已经有的值。由于F1没有改变,因此不需要进一步的通知和反应。

图5b中的赋值F2.Y=b的效果可以用相同的方式来分析。对前一段的记法进行缩写,假设初始状态为F1=(u,v)且F2=(w,x)。将F2改变至(w,b)调用反应器FR2的onY()过程,这将F1改变至(w*b,v)并将F2“改变”至(w,b),假设对F1的改变是在改变F2之前作出的。由于F2没有真正改变,因此FR1.onX()是由于F2的改变而调用的唯一反应器。该反应器将F2“改变”至(w,b),这不是对F2的值的改变。约束更新从而终止。

在图5b的示例中,如果FR2.onY()在更新F1前更新F2,则获得不同的最终状态。在这种假设下,FR2.onY()的效果是将F2设定为(u/b,b)并将F1“改变”至(u,v)。由于F1没有真正改变,因此FR2.onX()是由于对F2的改变而调用的唯一反应器。FR2.onX()将F1“改变”至(u,v),这不是对F1值的改变。约束更新从而终止。

约束更新处理通常将不终止,除非计算达到同时满足所有相关约束的状态。在满足约束的情形下,该状态可以不是唯一指定的。尽管约束驱动的编程的这些方面对底层声明性范例而言是固有的,但本发明的各实施例的一个重要方面是实践中的实现,将相对简单的约束添加至命令性框架是非常有力的。

约束驱动的编程的一般模型通常将全部约束视作多输入约束(例如a+b+c=0可具有输入a、b和/或c)。在实际感兴趣的许多情形下,多输入约束可表达为一组单输入约束。这是有益的,因为单输入约束更容易在上述通知反应器框架中实现。例如,约束a+b+c=0可表达为下面的单输入约束集:c=-a-b(a是输入);a=-b-c(b是输入);以及b=-c-a(c是输入)。这种多输入约束到单输入约束的缩减可由程序员提供,或在足够简单的情形下其可自动产生。

注意,图3a-5b的示例包括按照单输入约束表达的多输入约束的实例。更具体地,多输入约束F1.X=F2.X*F2.Y被实现为F2.X=F1.X/F2.Y(F1是输入),F2.X=F1.X/F2.Y(F2是输入),以及F1.X=F2.X*F2.Y(F2是输入)。这种按照单输入约束的特定表示是由如果输入F1.X改变则保持F2.Y固定的指示、结合常见的lhs/rhs惯例(例如,左手侧(lhs)上的变量通常因赋值而改变,而右手侧(rhs)上的变量一般不因赋值而改变)来驱动的。与约束复杂度相关的其它考量在详细说明中提供。

前面的描述提供对本发明典型实施例的主要性能特征和基本实现特征的介绍。为了更好地理解本发明各实施例的一些变例和改进,在下面的概述的其余部分将对其作简单描述。

在一些实施例中,在数据成员中自动提供通知代码包括提供两个或更多个改变通知过程,并提供根据对程序数据成员的改变的性质对要调用这些改变通知过程中的哪一些的选择。例如,假设程序数据成员V是数字数组,并且按照约束,总计等于V的各个元素之和。在一些情形下,对V的改变是涉及其部分或全部元素的一般改变,并且在这样的情形下,总计的完全重计算在更新期间是无法避免的。然而,在其它情况下,V的改变已知为单个元素的改变(例如V[3]从4变为6)。在这种情形下,总计可更高效地更新(即,将总计递增2),因为其前一值可被假设为因先验约束驱动更新而是正确的。这种性质的情况可提供给多个改变通知过程(例如,分别用于一般元素和单元素更新的onV()和onV[i]())。在这些通知过程之间进行选择是由对数据成员的改变的性质和改变的情形来驱动的。

存在输入源程序包括能更高效地实现一些更新的约束的情形。更具体地,如果输入源程序包括对与其部分或全部输入处于同一范围的约束的声明性指定,则可通过将范围内命令性代码自动插入到命令性输出程序表示中来在其范围内强制实施该约束。这些约束响应于对范围外输入的改变的强制实施可如上所述地处理。

在实践中,有利的是提供对用以执行每个约束反应器对象的程序控制的线程的选择。当今的编程环境通常提供这种线程选择。

通知和反应机制也能很好地与其它编程方法相组合。例如,结合如上所述的通知,对数据结构的封装以使改变只能通过增变器过程来作出是有益的,这是因为仅增变器过程可对要求约束驱动更新的状态作出改变。

约束的概念可扩展至表示专用约束,该表示专用约束涉及具有不同表示的数据成员。这里,表示指的是如何物理地表示数据成员。常见表示包括普通的存储器内、串、未类型化和网络表示。可采用适配器来根据需要将一种表示翻译成另一种表示以监视和强制实施约束。

如前所述约束驱动的命令性更新具有许多应用。约束可涉及复杂事件(例如交易或贸易)处理。也可采用约束来以声明性方式指定目标编程语言的语法。目标编程语言可以是或可以不是输入编程语言,并且可以是或可以不是在本发明的自动实践实施例中采用的任何中间表示的语言。

本发明的各实施例的约束处理能力可通过提供用于处理在翻译期间未解析(即,翻译成命令性代码)的约束集的运行时约束引擎来扩展。尽管这在一些情形下可能是有益的,但常常更为优选的是避免对运行时约束引擎的依赖性以保持命令性编程的效率。

上文描述的通知和反应器机制不局限于约束的实现。反应器也可包含任意命令性代码,尽管优选的是这些命令性代码至少为幂等的(即在相同输入上将其执行两次或更多次具有与对其执行一次相同的效果),以便避免意想不到的副作用。因此,本发明的一些实施例除约束外还包括过程触发器(PT)。

在这些情形下,PT根据其编程语言在输入源程序中指定,并且对于每个PT,标识其触发输入。每个PT包括当相应的触发输入改变时要调用的一个或多个触发器过程。

每个PT的触发器表示是自动提供的,其中触发器表示包括触发反应器对象以在实例化时注册输入改变通知,触发反应器对象包括相应的触发器过程。通知代码还调节触发器表示对输入改变通知的注册。触发器表示在命令性输出程序表示中被实例化,以使对触发输入的改变自动导致所述触发器过程的执行。如前所述,触发器过程优选地为幂等的。这些触发器表示和触发反应器对象分别类似于前述的约束表示和约束反应器对象。

基于通知的约束集翻译提供若干重大益处:

1.编程语言中关系和不变式的简洁表达,如由常规约束编程提供的,但在谨慎地手动指定的命令性编程的执行效率的情况下,匹配硬件命令性执行模型。支持对象关系、应用专用算法、一般约束解算、分析器生成、复杂事件处理、分布式操作和类属接口的自动维护的可扩展性。

2.对显式约束集对约束处理进行的分组、提供受控制的单独调度的线程执行以及立即执行、通知处理共享和每个约束集的共享状态的控制优化约束处理开销。相反地,约束可被划分成单独的集合以允许作为单独约束集的并发执行。

3.状态的不同表示之间的约束的可扩展性,包括网络表示、存储表示、类属/无类型标识、串/XML表示等。

4.用于为数据成员的修改以及响应于这些数据成员被修改而进行的约束维护两者自动生成命令性代码序列的统一机制。

在本发明的实施例中,源自翻译器的约束驱动的执行能够进入无限循环而不是收敛。然而,存在许多方式使命令性程序能以没有约束的无限循环告终,因此该约束机制不改变命令性编程的这个基本特征。主要目的是通过支持约束来提供命令性程序的更简洁表达,但不更改或对抗命令性程序的基本属性。此外,可获得命令性语言的完整能力以应付该约束实现不合适的情形。最后,在包括约束解算器实现的实施例中,解算器可在其行为优良的情形下调用。

根据上述原理,可通过利用命令性编程结构高效地实现约束编程。一旦约束编程以该方式提供,可能出现的问题是如何处理软件任务包括约束和命令性元素的情形(例如,硬件设备的过程通电序列),这种情形可用命令形式最清楚地表达。第一方法是简单地表达用于实现约束编程的底层命令性语言中的这种命令性元素。第二方法是提供约束编程环境内的结构,其提供命令性过程的高效实现。该第二方法可通过使底层命令性编程环境有效地对于用约束编程环境工作的程序员不可见,来提供具有更优透明性和简化编码的优势。相反,第一方法可能要求程序员同时考虑2个抽象层(即,约束环境和底层命令性环境),这可能给编程和调试增加不期望的复杂性。本发明的优选实施例可包括约束编程环境内的结构,其提供命令性过程的高效实现。

上述的约束实现方法可视作基于将约束翻译成命令性代码的子例程,这些命令性代码的子例程在约束依赖改变的状态下由触发机制调用。然后,由于响应于变量改变的回调结果,经编译的子例程被简单执行,且子例程根据约束更新应用状态。在这种情形下,执行可能非常高效,因为这些子例程可预先编译成高效机器代码,从而提供与精心编写的常规命令性软件类似的高效执行。但是,该性能依赖于主程序活动,主程序活动包含改变一个或多个约束所依赖的值,这与添加和移除许多约束相反。这是因为添加和移除约束包含用回调通知工具注册和注销回调,这样做成本非常高,且还分配空间和解除分配空间以便存储约束,这是第二高的成本。幸运的是,许多约束应用可表征为主要涉及对约束参数值的改变,与以任何高频添加和移除约束相反。

无关于约束编程的简明性,存在用于将行为表达为指定动作序列的过程(procedure)的更自然和清楚的情形。例如,一装置的通电序列要求一系列时间上的步骤,其通常最清楚和最简明地表达为过程。经常,该序列要求在步骤之间等待一段时间或等待条件变为真,如由表达来指定。在常规编程语言中,其可在线程的上下文中执行该过程,该线程可通过一些等待结构来挂起和恢复其执行,等待结构允许其等待时间间隔或特定条件。

约束表达的命令性序列的简单表示意味着添加和移除约束以高效地驱动约束解算器以便贯穿该通电序列变迁该程序,以及还意味着贯穿时间(挂起以等待执行恢复的时间)记录过程执行状态,并意味着在满足条件后恢复该状态。该方法的一个示例称作约束命令性编程(CIP)。在常规CIP中,通过将过程建模为在一时刻的状态和在另一时刻的状态之间添加一次性约束来提供过程支持。例如,过程中的常规赋值可被建模为在时刻T的右手侧的值和时刻T+1的左手侧的值之间的一次性约束。类似地,等待一表达变为真可被建模为添加指定该表达的一次性约束。

虽然该方法可提供与约束编程环境一致的正确过程语义,其还可导致低效实现。在约束解算方法中,约束解算器被调用以重新评价在各步骤上的约束存储,从而确定添加对应于下一过程步骤的新约束的效果并移除对应于前一个的约束的效果。这种级别的约束存储的改变可抵消常见于通用约束解算器的优化,从而造成成本相当高。任何情况下,这比命令性编程语言过程的常规命令性实现昂贵得多。

即使在约束被翻译成由改变通知机制触发的子例程的约束实现中,如本发明的一些实施例中,用约束简单实现的过程代码可导致显著低效,因为在每个步骤中添加一个约束并移除一个约束,招致用改变通知机制注册和注销的显著开销。这与相对极少添加和移除约束的上述情形相反。

在约束编程语言中,通常将过程代码直接翻译成对应常规过程实现是不可行的,因为约束编程语言不具有控制或调用过程处理的线程概念。即,过程概念需要集成为如上的约束语义的延伸。

在本发明的实施例中,约束命令性计算机编程语言的翻译器接受一个或多个过程的指定,并自动生成以与CIP模型一致的语义来高效执行约束的代码。

过程可被认为是一次性约束的集合。但是,在一个实施例中,可由一般过程编程赋值结构加上一般控制流语句来指定过程,一般过程编程赋值结构对应于将对应约束添加到约束存储/从约束存储移除对应约束,而一般控制流语句(if、else、while等以及等待(wait))控制赋值操作的顺序。“等待”控制流语句具有在前进到过程中的后续步骤之前挂起执行直到超时期间或直到任选条件成立的常规过程语义(“等待”语句视作类似于常规goto(转到)、return(返回)等语句的过程流语句,因为其确定控制流,即在该情形下挂起控制流除非超时或所需条件为真)。

在优选实施例中,约束被翻译成由改变通知机构调用已生成子例程的基于回调的实现,用该改变通知机构根据其指定输入和依赖性注册这些子例程。在优选实施例中,约束集对象类型显式地被声明,指定输入和输出,任选地由在输入和输出中指定的元素来触发某局部状态和一个或多个约束。然后在约束集的范围中声明一过程,且作为聚集约束,可被指定成由输入数据元素和局部状态表达的一些表达式来触发。在本说明书中,过程上下文状态自动生成为局部状态作为该约束集的一部分。在该设定中,不指定输入参数或返回值,因为这些隐含地是约束集中的那些部分。

在该优选实施例中,多个过程可在约束集内声明,如果各个过程共享相同输入和输出的话。单个过程可具有对于一个或多个输入的零(null)依赖性并对一个或多个输出具有零效果。即,其可仅使用输入和输出的子集。此外,一过程可具有约束集的一个或多个局部数据元素作为其参数/输入或输出/返回值。

在该优选实施例中,在约束集之外声明的过程可被实现成仅包含该过程的约束、且其输入参数为过程的输入、且其返回值是该约束集的输出且不具有局部变量的约束集。即,其可实现成根据过程指定直接确定的约束集的退化情形,并因此被限制。其不能具有局部状态且不能与其它过程共享任何指定。

以下描述本发明的一些实施例的基本翻译方法(约束编程语言的过程),描述了假设如上所述的嵌入的约束集实现。术语子例程(subroutine)用于描述可在底层计算机平台执行的命令性指定。

为了将约束编程语言中的利用回调机构实现的过程翻译成子例程,在遭遇过程声明时,翻译器生成包含情形指示变量的过程上下文状态类型和可能需要保存的其它上下文状态,如以下所阐述地。然后其生成封闭约束集的范围中的该上下文类型实例的声明。如果该过程未在封闭约束的上下文中声明,其首先生成对应于封闭约束集的代码,根据参数和过程的返回值(如果有)指定输入和输出。在实施例中,特定异常输出得到支持,对应于过程的异常返回。

翻译器还针对各输入生成过程的回调子例程,包括超时回调。这些子例程的每一个安排成由改变通知机构根据其分别的输入来回调。在使用面向对象结构的实施例中,改变通知机构允许注册关于输入对象的回调,从而在该对象的任何通知数据成员改变时接收通知。

各回调子例程生成为命令性代码块的集合,各个命令性代码块具有确定在回调时代码块是否被执行的“启用(enable)”变量。各块对应于一个或多个约束的命令性动作。在一个实施例中,启用被实现为常规“if”语句,即

if(enableConstrainti为真){

...约束命令性代码

}

在优选实施例中,生成一组代码块,使得或者在给定回调子例程中在任何时刻单个代码块被启用,或者在子例程中所启用的一组代码块是连续的。因此,可由诸如在C/C++编程语言中可用的switch(切换)语句来执行该启用,其中case(情形)变量的采用对应于第一被启用代码块的值。以下描述假设该实施例。还假设面向对象的实现,在该实现中回调机制支持注册根据对象(即,其所有被监视数据成员)而非个别数据成员的回调。

在翻译期间,翻译器维持这些回调子例程的当前(current)集的指示以及这些回调子例程内的当前块和case语句。图6a-12c的示例示出在该框架中可如何实现各种语句的翻译。图6a示出具有自变量a、b及c的输入过程。图6b示出图6a过程的输入“a”的回调子例程,紧跟在语句R的翻译后。图7-11示出图6b的回调子例程如何由依赖于图6a的语句S的翻译器修改。在此为了简单起见假设,语句R在回调_a(callback_a)子例程中被处理。

图7涉及其中语句S未被标记且不是控制流语句的情形。如上文指示地,翻译器在各回调子例程中跟踪当前块。在这些示例中,当前块变为块‘i’,且语句S的翻译需要在各回调子例程中在各当前块结束时添加S。在该示例中,仅示出用于输入自变量a的回调子例程,因为自变量b和c的回调子例程遵循相同翻译模式。

图8涉及语句S是return语句的情形。在此,翻译需要添加代码以将switch语句的变量设定给end case(最终情形)(即cvar=clast),且在主switch语句后将返回语句添加到各回调子例程。在该示例中,仅示出输入自变量a的回调子例程,因为自变量b和c的回调子例程遵循相同翻译模式。

图9涉及语句S是throw(抛出)语句的情形。在此,翻译需要根据由throw语句提供的任何(多个)参数添加代码以设置exception(异常),并添加代码以在各回调过程中终止执行。在该示例中,仅示出输入自变量a的回调子例程,因为自变量b和c的回调子例程遵循相同翻译模式。

图10涉及语句S是标记语句(即,可能的入口点)的情形。在此,翻译需要分配新case号(在此为‘i+1’),添加代码以开始新case块(在此为case i+1),并将语句S的代码添加到新case块,所有这些动作在各相关回调过程中进行。优选地,如在该示例中,根据标准C/C++switch语句语义,case i的执行容易变为case i+1。在该示例中,仅示出输入自变量a的回调子例程,因为自变量b和c的回调子例程遵循相同翻译模式。在优选实施例中,case变量设置为新块号(即,cvar=i+1),使得保证case变量指示当前块。这种一致性可有助于处理异常。

图11涉及语句S是goto(转到)语句的情形。在此,翻译需要添加代码以将case变量设置到对应于指定标记的case,从而通报合适的条件且返回,所有这些动作在各相关回调过程中进行。适当条件的通知包括用于将程序控制转换成受指示入口点的各种可能性。对于同一过程中的入口点而言,可采用常规goto结构(如图12b的示例)。对于不同过程中的入口点而言,可对包含期望case的子例程作直接调用(如图12c的示例)。到达不同过程中的接入点的另一方式为调用“信号(signal)”例程,其通过通知和回调机制来间接调用相关联的回调子例程(例如,将变量a标记为具有改变的值,以便使callback_a被通知和回调机制所调用)。在该示例中,仅示出输入自变量a的回调子例程,因为自变量b和c的回调子例程遵循相同翻译模式。

图12a-c涉及语句S是等待(wait)语句的情形。图12a示出示例性wait语句。图12b-c示出对应于图6a的语句S是图12a的wait语句的情形的回调例程。在该示例中,wait语句包括条件测试(即a<b)、超时参数(即1.5)、“主体(body)”,如果在自调用wait语句起的消逝时间超过超时参数之前条件测试变为真,执行该主体,且如果在自调用wait语句起的消逝时间超过超时参数之前条件测试未变为真,执行“超时(timeout)”。在此,翻译需要以下动作:

1)在对应于条件测试的输入的各回调子例程中(即callback_a和callback_b,但不是callback_c),针对后续执行对case号赋值(即i+1),添加具有新case号并包括代码的case块以便在条件测试为真时执行wait语句的“主体”。在当前块中,添加代码以在已经满足等待条件测试时执行等待“主体”。在当前块(即块i)中,添加代码以设置当前块以便在后续处理中成为新块(即cvar=i+1)。用return或break(中断)语句终止当前块。图12b示出以上该示例中的步骤1的效果。

2)在超时回调子例程中,对case号赋值以用于后续执行(即i+1),添加具有新case号并包括代码的case块以便执行等待语句的“超时”。在当前块(即块i)中,添加代码以测试等待条件(如果条件为真跳到等待主体(wait body)),并用于设置当前块以便在后续处理中成为新块(即cvar=i+1)。用return或break语句终止当前块。图12c示出以上该示例中的步骤2的效果。在一些情况下,要到达回调_时间(callback_time)过程中的当前块(即块i)是不可能的。在这些情况下,翻译器可从callback_time过程省略当前块作为优化。在图12a-c的示例中,注意已翻译等待语句可视作包含在两个case块(当前块(例如,块i)结束和下一块(例如,块i+1)起始处)中可能是有帮助的。

在这些示例中,注意到回调子例程的case变量的值(即cvar)不受仅从回调子例程退出的影响,然后再次调用回调子例程是重要的。因此,在图12的示例中,由回调机制如下地提供等待语义。当在回调子例程中遭遇对应于等待语句的代码时,首先检查等待条件测试。如果等待条件测试为真,等待主体被执行且执行继续。如果等待条件测试为假,由return/break语句停止回调子例程中的处理。此外,对应于等待条件测试的输入的回调子例程的case变量被设置成这些输入的任一个的任何改变导致由等待条件测试构成的另一检查。如果任何这种测试为真,则等待主体被执行且然后执行继续。

同时,以由适当时序变量的状态改变确定的方式来检查回调的时刻。例如,当等待语句被调用时,调用等待语句的时刻可被保存,且超时回调子例程可具有周期性地更新为其输入的系统时间变量。该系统时间变量的改变然后将造成执行超时回调,如果超时发生时其将执行等待语句的超时分支。状态变量(例如,以下示例28-34中所描述)的适当使用是确保一旦执行等待语句的“主体”分支就不可能执行等待语句的“超时”分支的优选方式。在一些实施例中,各约束集具有其自身的超时变量,且各约束集在单个控制线程上实现。在该情况下,约束集线程或者执行回调子例程或者执行超时子例程,由此消除执行同一等待语句的“主体”和“超时”两者的可能性。

翻译器维持嵌套在当前范围中的先前代码块列表,那些代码块未被return或throw语句终止。从该范围退出时,诸如else语句结束时,如果存在这种先前的代码块,在遭遇新的可执行语句时,其创建case块以包含该新的可执行语句,其包括代码以将该case变量设置到这些块的每一个中的case,从而它们各自将控制转移到新case块,且然后其从先前代码块的集合中移除这些块。遭遇过程结束时,翻译器向保留在该未终止块列表中的任何代码块添加语句以使它们转移到过程的终止状态。

翻译器还在等待点处添加“活动(live)”(即活跃)的任何局部过程变量的保存,并添加代码以还原活动的各新case块中的这些变量的每一个,从而按需延伸上下文状态类型中的字段。

在优选实施例中,输入指定可包括如for和while环的结构化控制流结构。通过利用本领域公知的编译器技术将这些翻译成goto结构,然后得到的goto结构可如上所述地被处理。

在一实施例中,翻译器生成对应于过程的直到第一等待的初始步骤或过程中的标记的启动子例程,其执行这些步骤,且然后安排控制转移到使用上述控制流实现的后续步骤(即设置case变量并安排相关条件下的回调)。从而,将不包含标记或等待语句的过程翻译成该单个子例程。

在优选实施例中,当过程不处于初始状态过程被触发成开始执行时,其不采取动作。可任选地,其可被指定成设置异常输出。对于未嵌入在约束集范围中的过程而言不发生该情形,因为新约束集在每次“调用”时用其过程上下文实例化,如同以下结合示例28-34所描述的。

本发明实施例允许作为命令性结构的过程以约束编程语言指定,支持一般过程语义,包括条件性地挂起执行等待一时间周期或等待条件表达变为真的能力,再自动翻译成常规子例程以便以与高效常规命令性编程可媲美的效率执行。与针对约束编程语言的过程的其它提供方式相比,这种效率是由本发明实施例提供的显著优势。

有效地,执行回调子例程以实现执行过程可被认为由特定逻辑处理器来实现,其中回调子例程中诸case的case号可被认为是专用指令代码,回调子例程可被认为是专用指令执行装置,且上下文状态数据可被认为是专用上下文。因此,根据上述原理翻译的过程仅招致上下文保存和还原以及由该过程特点所要求的空间的最小开销。

另一方面,在约束集范围之外指定的过程的调用成本更高,因为一般情况下,隐式约束集需要动态地实例化/分配,输入需要注册回调,且调用者(caller)需要等待过程完成,过程完成时注销和释放这些资源。

将“事件序列化命令性过程”认为是命令性过程是有帮助的,其具有对外部事件的某依赖性。实践中通常遭遇这种过程(例如,检查超时条件的设备启动过程)。从这一观点,前述原理(以及以下的示例28-34)可被认为是针对约束提供通知和回调代码的思想的延伸,从而还针对事件提供通知和回调代码。

更具体地,可用约束编程语言表达输入源程序,约束编程语言还提供事件序列化命令性过程的指定。输入源程序可被自动翻译成中间命令性表示,其中中间表示中约束的通知和回调代码提供约束的高效实现,且其中中间表示中事件的通知和回调代码提供事件序列化命令性过程的高效实现。该命令性中间表示可被自动翻译成计算机可读命令性输出表示,其反过来可作为输出提供(例如,给用户)。

如上所述的条件等待结构可被采用以提供约束框架内的控制线程。例如,对于事件序列化命令性过程的调用可被翻译成过程执行开始,其后跟随条件等待结构,一旦所调用的事件序列化命令性过程终止,条件等待结构使执行返回到过程调用后的点。

下面给出本发明实施例的这些和其它的方面的进一步描述。

附图简述

图1示出已知编程结构的一个示例。

图2示出由本发明的一个实施例提供的功能的示例。

图3a-b示出涉及本发明一个实施例的示例的输入代码段。

图4a-b示出与图3a-b的示例的输入代码段对应的自动生成的表示。

图5a-5b示出与图3a-3b和图4a-4b的示例的输入代码段对应的命令性输出程序表示。

图6a-12c示出根据本发明实施例的控制流语句的翻译示例。

具体实施方式

在本发明的一个实施例中,编程语言包括用于指定被称为约束集的一组约束的一个或多个结构。“约束”表示需要在所有时间或在特定一个或多个时间保持的数据成员之间的关系的声明性指定。在前一种情形下,约束本质上等同于不变式。后一种情形的例子是仅在一些数据成员初始化时保持的关系。另一示例是引用另一对象中的数据成员的约束;该约束仅在其它对象已知时保持。“主动约束”表示其中当涉及关系的状态改变时采取命令性动作以建立和维持这个关系的约束。相比主动约束,被动或检查约束仅仅在检测到关系不保持时生成出错或异常指示。关系的性质可由约束运算符来指示。例如,相等关系可由“=”运算符来表示。

术语“数据成员”用来指示能够存储数据的已命名状态单元。之所以将其命名为“成员”是因为它是名称某一范围的成员,例如类型、名称空间或过程。该术语也用于C++编程语言中。然而,使用该术语不是将这些实施例局限于C++扩展或其任意派生物。

在一个实施例中,编程语言提供面向对象的语言的传统特征,包括类、继承、动态函数(虚函数)分派,另外还用每个数据成员的约束集(constraintSet)结构和约束上下文来扩展。约束集结构可由关键字“constraintSet”指示并可另行遵循语言中的类型指定的句法。数据成员上下文可被表示为以一数据成员的声明之后紧随的一个“{”符号开始并由符号“}”终止。其它实施例可使用不同句法,例如“begin(开始)”和“end(结束)”关键字以及受约束的数据成员的显式指定,而不是如上所述使其隐式地耦合于数据成员的声明。有了约束集,可定义由集中的约束引用的对象的局部名。在一个实施例中,在约束集中的局部数据成员的声明中的类型指示之前使用“input(输入)”关键字限定词,指示仅用作对该集合中的各约束的输入(只读)的数据成员,如果局部变量仅写入约束集中则使用“output(输出)”,且如果该集合中的各约束可能使该对象既能读出又能写入,则使用“inputOutput(输入输出)”。例如,在例1中,一个简单的约束集“ReportMaintenance(报表维护)”

指定了关于inventroy(目录)中的全部part(部件)和cost(成本)以及集合part的report(报表)之间的约束。(这里,inventory中的“part”数据成员指定对inventory对象中的每个类型part有一条目的集合,每一条目提供了inventory中该类型的part的计数。)当ReportMaintenance约束集被实例化为特定inventory、accounting(记账)和report对象时,根据由这些约束指定的关系来设定total(总计)和cost的受约束的数据成员,并随后当任一引用的数据成员改变时维持该受约束的数据成员。

约束可用其中指定了该约束的一个或多个属性的子上下文来指定,如例1所示。这里,在审计期间应用(AppliedOnAudit)的属性被设为真,使翻译器生成审计代码以检查指定的约束在软件审计期间保持。第二属性“isScheduled(被调度)”被设定为假以指示相关联的约束处理永远被立即调用,而不是在独立线程上调用(如下面描述的那样)。

该例示出约束工具的关键优点。它允许程序员指定report的要求值(即那些需要的值),从而使语义对软件的任何审阅者来说更为清楚。它还减轻了程序员确定软件中输入数据成员改变的全部位置并手动地插入在这些点执行约束维持的代码。如此,避免了对软件的修改无意地增加输入改变而不增加相应的更新代码的问题。它还缓解了程序员编写约束命令性代码以使其对输入改变的全部组合正确地维持这种关系的需要。因此,自动约束驱动的更新降低了软件的开发成本,减少出错可能性并提高了可靠性并因此提高了可维护性。同时,所得到的实现可与传统命令性实现具有相同的效率。

约束集也可被定义为之前定义的约束集的扩展,如例2所示,其被称为扩展的约束集。

在本例中,定义附加的inputOutput对象“supervisor”并重新定义report::cost上的约束。

约束集的显式指定允许基于共同输入、共同输出、共同处理优先级分配和例如存储器分配等可能的其他属性来对这些约束进行分组。

下面的说明更详细地描述了约束集的实现以提供声明性指定的关键优势(即指定的简洁性),而没有声明性语言的关键缺陷,即实现的低效率,并且没有约束范围的显著限制,例如仅限于局部实例变量。

A.约束集实现

在一优选实施例中,翻译器生成约束集的内部表示作为具有指定名称的类。例如,例1的约束集的内部表示可使用C++指定为:

其中基类“Fwk::RootConstraintSet”提供全部约束集所共有的基本功能集。“thread_”数据成员指示与该约束集相关联的过程将要在其上执行的处理资源。这里,标志“...”指示完成该实现所需的C++中的附加实现,这将在下文中描述。C++中所需的公共内容、受保护内容等的表示与指针和“const(常量)”的指示一样为简化目的而被省去。

扩展的约束集可被实现为从对应于它所扩展的约束集的类继承的类,如例4中的C++表示中所示那样。

在一个实施例中,该实现依赖统一通知机制来确定输入何时改变,这要求处理确保约束集中的约束被维持。这在下文中描述。

A.0 基于通知的约束实现

在一个实施例中,翻译器采用了称为通知工具的统一回调机制,该机制提供了注册要在数据成员被修改时调用的过程的能力。当一对象被指定为约束集中的一个输入时,翻译器定义回调过程,该回调过程在该对象中的数据成员改变时对该约束执行所需处理,并生成将该过程注册来自该对象的回调的命令性指令。

通知工具一般可通过被称为“监听器”设计模式来实现。在一特定实施例中,翻译器为每一对象类型Foo生成用于Foo的回调接口的内部表示,其中对Foo中可充当输入的每个数据成员有一回调原型函数。例如,用于Inventory类型的回调接口的内部表示可使用C++指定为嵌套类Notifiee,如例5所示。

″onPart″虚函数对应于Inventroy类的“part”数据成员并且默认地不做任何事。具有参数的那一个虚函数对应于已知的指定条目改变而另一个则对应于对part集合的未知改变集。

翻译器还将“notifieeList”加至Inventory类型的内部表示,该notifieeList存储Inventory::Notifiee实例的列表。每个这种Notifiee实例也是被定义为Notifiee类型的派生类型的反应器实例,如下文中更详细描述的那样。最后,翻译器在程序的内部表示中可修改数据成员“dm”的每个点处插入其相应的notifieeList上的迭代,从而对于该列表中的每个对象的dm调用“on”虚函数(如果有的话)。例如,对于由“key(关键字)”指定的Inventory::part条目的情形,该插入的代码的内部表示可使用C++如例6那样指定。

该代码仅在由key指定的part条目已实际改变的情况下被调用。具体地,将该条目设定为其当前值的处理序列不生成通知回调。

为了实现例1的约束集,翻译器自动生成从每个输入的notifiee接口导出的每个输入的内部表示,从而用在数据成员被修改时执行所需命令性动作来强制实施约束集中的约束的函数来替换表现为对约束的输入的每个数据成员的默认空“on”函数。例如,对于输入的inventory::part,该内部表示可使用C++类代码被指定为从Inventory::Notifiee派生的嵌套类,从而覆盖了“onPart”函数以执行所需处理,如例7所示,并重复例3中指定的上下文。

InventoryReactor类是从Inventory::Notifiee类派生的,这允许其通过类型继承来覆盖或替换″onPart″回调函数的实现。″oldPart″是由翻译器添加并在约束应用中用作优化的关键字索引的数组数据成员,其示出作为约束实现的一部分对约束集添加状态。

在指定的“part”条目改变的情形中,可基于与存储在约束集类中的旧值的差异来高效地更新report中的total。在未指定的对“part”集合的改变的情形下,完全重新计算total,从而刷新该约束集的上下文中的“oldPart”值。翻译器定义InventoryReactor类来包含“inventory”数据成员,该数据成员当被实例化时被初始化以引用相关联的目录。还定义数据成员以对每个集合数据成员指示通知数据成员以及与该通知数据成员相关联的关键字,用来通过后述的已调度通知来存储这些值。

如例7所示,输入被实现为称作“inventoryReactor”的数据成员,该数据成员实际上是包含指向实际Inventory对象的指针的InventoryReactor的实例。在一个实施例中,提供用于访问和修改该输入的功能。具体地,“inventoryIs”函数修改相关联的inventory对象,调用相关联的“onPart()”函数以当inventory对象改变时重新计算report中的total。此外,对InventoryReactor实例中的inventory字段写入的效果是将该反应器实例从当前inventory对象中的notifieeList中移除并将该反应器实例添加至由inventory字段引用的新inventory对象的被通知者列表。(为简化起见,省去在任何情形下对inventory对象为空的考虑。)

对“report”输出的引用通过指向所封装的约束集类实例的反向指针(如“cs_”数据成员所指定的)而在反应器类型中间接地访问,该指针在反应器对象被实例化时被初始化为指向该实例。一个实施例能改为提供对反应器为局部的数据成员,该数据成员参照与用作该反应器的通知对象的输入和输出不同的输入和输出。然而,需要有当例如“report”等实际输入改变时更新该局部数据成员的手段。

例7示出如何通过在与约束集关联的上下文中提供状态——在本例中为oldPart——来优化约束实现。在其它约束的情形下,例如指定输出为输入的一阶导数的近似值的情形下,约束集可能需要额外状态以执行要求的计算。

在指定为集合的输入的情形下,相应的内部表示可将反应器类用作集合的条目类型,包括集合实现所需的“next(下一)”指针和key字段。即,使用反应器类来代替用来实现非入侵集合数据结构(例如散列表、树和链表)的“条目”结构,从而相比使用单独的条目对象节省了空间和额外代码。

在一个实施例中,内部表示可包括用于约束集的结构函数过程,该过程用所要求的反应器实例来初始化约束集的状态。具体地,在例7中,该构造函数过程将InventoryReactor实例化以供目录输入并设置InventoryReactor数据成员为指向该反应器实例。

回顾前面的实现,当inventory中的指定part条目改变时,例1中指定的约束集的第一约束如下地维持。改变调用插入该位置的代码以在被通知者列表上迭代,从而用关键字标识该条目来在列表中的每个对象的上下文中调用“onPart”函数。如果ReportMaintenance(报表维护)约束集是用该inventory对象实例化的,则针对该约束集生成的InventoryReactor类的实例存在于该被通知者列表中。当针对该对象调用“onPart”函数时,如例7实现的那样,该函数的反应器实现被调用,这致使report对象中的“total”数据成员被更新。

例1中的第二约束将因报告总数的改变而被触发。该约束可用类似结构实现,其具有用于report total数据成员以及记账“avgPrice(均价)”数据成员的反应器。因此,对总计的更新致使对反应器的调用,这会造成report中的“cost”数据成员由“onTotal”函数的实现来重新计算。

作为一种优化,一个实施例可对该约束集实现确定更新report->total也必须更新report->cost,并且report->total不得通过其它手段来更新。因此,实现可省去由对report->total的改变驱动的用于report->cost的单独的反应器,并且简单更新report cost,作为如例8中的“onPart”的扩展版本所示出的那样维持报告总计的函数的一部分。

这里,该函数实现的最后一行直接更新report“cost”数据成员,而不是依赖于来自“cost”数据成员本身的回调通知。第二约束的实现仍然需要反应器实例对accounting对象中的“avgPrice”数据成员的改变作出反应。

在一些约束集中,多个约束具有一个或多个共同的输入。在该例中,约束集中具有共同输入数据成员“dm”的每个约束的命令性代码可被组合到针对该输入的反应器中定义的回调函数“onDm”的实现内。该命令性代码可通过以约束出现在约束集中的次序使用该数据成员dm对每个约束插入命令性代码来组合。如此,整个约束集的每个实例需要一个回调和反应器。

在优选实施例中,翻译器使用前述技术和例如共有子表达式消除等其它常见的优化来跨约束集中的全部约束实现多个优化以使开销减至最小。

在一特定实施例中,可在constraintSet的范围内指定局部数据成员,并可定义约束以将这些用作输入和/或输出。翻译器可识别何时约束的输入是局部数据成员并使相关联的命令性代码从当前模块中所述数据成员被修改的位置被直接调用。该优化通过通知机制和反应器类型消除了调用命令性代码序列的开销。

通过单独翻译或编译(其是实践编程的标准要求),一个或多个从属数据成员和访问这些数据成员的代码可在单独翻译的模块中实现。该单独翻译的代码无法由当前翻译进程直接修改。具体地说,不能将代码加至这些单独的模块以实现约束。使用通知工具,单独翻译的单元提供了单独模块中的任何约束集确定其所依赖的任何数据成员何时改变的手段,而该单独编译的单元在其被翻译时无需知道在程序执行期间要指定和出现哪些约束(如果有的话)。在没有这些约束的情形下更新数据成员dm的额外成本是检查被通知者回调列表以及可能调用空函数的成本。将该数据成员用作输入的一个或多个约束的情形下的成本是调用约束生成的命令性代码以确保该约束不变式仍然保持,即采取任何必要的动作以使约束为真的成本。该处理成本与已由程序员显式地指定该命令性代码的情形相同。实质上,该方法用该通知机制扩展了传统命令性编程的被动存储或状态以支持约束,并与用于合并约束和依赖于约束编程的低效率约束图方法(以及其运行时约束存储和约束解算器)的命令性编程以将命令性编程纳入其中的传统方法形成鲜明对比。

在一个实施例中,通知工具可用该类型的每一数据成员的Notifiee接口、以及每个数据成员的单独的NotifieeList或具有指示每个对象的感兴趣数据成员的一些手段的共享列表来实现。因此,对每个感兴趣数据成员可以有一个反应器对象。这种替代方案相比每个输入对象的反应器减少了对不那么感兴趣的数据成员的回调,但在多个数据成员用于约束集时增加了空间成本和复杂度。

A.1 反应器执行

约束集可具有相关联的线程指定,该线程指定规定了用来执行反应器过程的(多个)线程。这一指定可导致“thread(线程)”数据成员由翻译器作为约束集实现的一部分来插入,如例3中发生的那样。在一特定实施例中,如果thread数据成员是非空的,则反应器对象在该线程上排队以供后续执行(即,调度)而不是直接调用其“on”函数。反应器包括记录正通知的数据成员以及在集合情形下该数据成员的关键字的单个条目。

在一个实施例中,反应器可任选地在单独的线程上被调度。翻译器为每个通知数据成员生成唯一标识符。它还为具有数据成员标识符参数的反应器类型生成多路分解过程,该过程的实现基于数据成员标识符来选择并为指定的数据成员调用“on”函数。该过程也识别对应于对象范围的专门的“this_”数据成员标识符。当用该值调用时,该过程调用反应器中的每个“on”过程,不传递任何参数。(即,使用集合数据成员,调用无参数“on”函数)。例9中示出支持线程调度的“onPart”函数的反应器实现中的命令性代码。

如果thread数据成员是非空的并且不是当前线程,则“onPart”调用被推迟以由之后的线程调度执行来执行。为此,反应器本身如果尚未入队则连同通知数据成员的标识符(在本例中为“part”)及其关键字(如果有的话)一起在指定线程上入队。如果反应器已在指定线程上入队,则通知数据成员指示符改变至“this_”以指示对象级通知。随后当调度指定的线程来执行该反应器类型时,该线程调用多路分解过程以将数据成员标识符映射到相关联的“on”函数并执行它。如果通知数据成员被指定为“this_”,则调用每个“on”函数。由于该线程在执行该“on”函数时则为当前线程,因此当由该线程调用时,反应器“on”函数的命令性实现被立即执行。

使用这种机制,可在运行时配置约束集以执行单独调度的通知处理或立即通知,这是通过将thread数据成员设为非空值或将其清零来选择的。例如,在负载下,约束集可动态地分配线程并设定该thread数据成员以使其执行与其输入中的处理并行地发生,这增加了应用程序的并行性和响应时间。另外,应用程序可在足够并行线程可用时使用单独调度的约束执行来基于其执行可用的并行线程的数目在运行时间配置。

一般过程触发机制的关键问题源自触发器调用的过程的处理要求。如果触发的过程在与调用该触发器的动作相同的控制线程中执行,则因该处理使动作完成延迟,结果正在执行的整个应用程序任务被延迟,这降低了应用程序性能。在某些情形下,其结果是应用程序无法满足响应要求。替换方案是将触发事件排队以便稍后由单独的控制线程来处理。然而,在负载下,触发器生成速率可能超过处理这些触发事件的速率。在这种情形下,要么队列无限制地增长直到系统存储器溢出,要么队列达到极限并阻塞触发动作或触发事件被丢弃。通常,这些选择方案中没有一个方案是可接受的。第一个方案明显导致系统故障。第二个方案可能造成应用程序阻塞。另外,常见的是许多排队的触发器是冗余的。例如,在网络系统中,链路可快速地向上和向下“抖动”,这造成对应于链路状态改变的触发事件序列。然而,处理经常仅涉及链路的当前状态,而不是整个事件序列。触发机制可包括指示何时如此的选项、合并重复事件和互补事件的选项,但这显著增加了执行成本并且不消除命中队列极限的一般问题。丢弃触发事件的最后一个选项意味着被触发的过程在程序员规定其要被调用的某些情形下不被调用。这种替代方案也导致一般情形下的不正确应用程序行为。缺乏合理语义可能是不在任何通用实践性编程语言中提供触发机制的原因,即使该理念已到处皆是并用于各种专门的语言。例如,JavaFX是针对性能通常不是关键因素的图形用户界面来设计的。

如上所述的基于通知的处理可通过优选实施例的若干关键技术特征来避免一般过程触发机制的这些问题。首先,每一通知的处理动作受约束语义的限制以“确保”约束关系保持,而不执行任意动作。因此,每个“on”函数是幂等的,从而“on”函数的额外调用不造成不正确的行为。其次,当在处理当前一个通知前接收到对反应器对象的第二个通知时,该通知被实质上被折叠成对象通知,这意味着通过将notifyingDataMember(通知数据成员)指示改变为“this_”来改变通知对象的一个或多个数据成员。第三,当分派一线程来执行具有“this_”指示的经调度的通知处理时,该线程调用该反应器中的每一个零参数“on”函数,这确保以该通知对象作为输入的全部约束被维持。第四,如有需要,通知处理从通知对象本身接收通知数据成员的当前值,而不是依赖于排队的值,由此确保当前值的使用。最后,由于通知的谨慎实现,实际上不改变目标数据成员的作为该通知处理的一部分执行的任何更新动作不造成通知。结果,由于当线程被调度时即使实际特定触发事件可能被忘记也执行要求的通知处理的父集,甚至在过载状况下也能(最终)维持约束。此外,由于动作的幂等性,任何额外处理不导致不正确的行为,包括不生成无关系的通知。这种方法具有附加的功效,即响应于改变的多个通知在负载下折叠成单个通知,这相比顺序地处理这些通知的队列允许较少的处理开销。这些技术的组合允许使用通知机制而不引发通用过程触发机制的问题。

在一个实施例中,代表集合的数据成员被认为具有与集合中不存在的关键字对应的每个条目的默认值。因此,添加一条目实质上是将其设定为非默认值,而将其移除是将其重设为默认值。例如,在对象指针的集合中,默认值是空指针。此外,反应器提供指向输入对象的指针,并且集合中改变的条目的关键字被传递给反应器。结果,反应器可确定新的值并且无需将额外信息存储作为通知的一部分,例如是否添加或是删除了条目。一实施例可扩展至以额外空间和代码复杂度为代价在反应器中使用用于通知数据成员和关键字的多个条目,仅当排队数量达到所允许的最大条目数时采用前述折叠的对象通知方法。

一实施例可通过使用立即通知(无线程分派)和简单地将该通知在constraintSet状态下提供的队列中排队来支持应用专用基础下的单独的通知排队。然后可分派单独的线程以处理排队的通知记录,该通知记录应当包括数据成员的标识符和关键字。

遵循前述方法,具有带单个输入的约束的约束集可由以与C++规范相比拟的形式生成该约束集的内部表示的翻译器来实现。翻译器随后可使用公知的技术将该内部表示翻译成可由目标执行环境执行的高效的命令性表示。

A.2 处理多输入约束

以最简单形式,约束具有一个输入数据成员。即,存在其改变可能使一些动作变得必须的单个数据成员。例如,a=b的单向约束仅因b改变而被触发。在单输入约束的情形下,存在维持这种约束所需的单个命令性代码序列,即通过b改变的通知而被调用(不包括来自b的初始化)。

然而,在更一般的情形下,约束具有多个输入。例如,例1和例2中的约束集的第二个约束具有两个输入,即report::total和accounting::avgPrice。

在一特定实施例中,翻译器具有其数据成员自变量之一被规定为触发器的约束的内部表示。在处理要翻译的语言并遇到约束集中的多输入约束时,翻译器用多个单触发器约束表示来替换该约束,其中对原始约束集的每一个输入有一个这样的单触发器约束表示。(它能保持原始约束指定以供初始化)然后,后续的翻译处理基于相关联的代码模板和对于单触发器约束的特定数据成员自变量将每个单触发器约束翻译成相关联的代码序列。每个单触发器约束具有指定响应该触发器要调用的命令性代码序列的命令性代码模板。使用术语“代码模板”是因为实际可执行代码可能基于约束数据成员的类型、基数和其它属性而有不同。

注意,所谓的多向约束,即对约束关系改变的左手侧数据成员和右手侧数据成员两者作出响应的约束,作为将左手侧数据成员识别为是输入的多输入约束的另一种情形来处理。即,将其变换为若干单触发器约束。单触器发约束是单向约束的特例。

如之前描述的那样,翻译器可将优化施加于约束集,这可能消除一些单触发器约束,如针对例1中的report::cost约束所描述的那样。

在一些情形下,关联于一个单触发器约束的命令性代码与由同一原始约束生成的其它命令性代码不同。使用一个简单示例,如果约束“total=part1+part2+part3”约束是多向的,则“total”是输入。在“total”改变的情形下,翻译器可能产生例10中的命令性代码的等效物。

-------------------------------------------------

part1=total-(part2+part3);

例10由total的更新触发的命令性代码

-------------------------------------------------

在一个实施例中,翻译器可用识别并能为多向约束的要求情形产生表达式的知识来编程,从而当指定了无法处理的多向约束时生成输入处理上的错误。程序员然后需要使用更基本的约束和/或命令性编程的组合来实现约束(如果合用的话)。由于翻译器能被编程为识别它所能处理的内容(并因此也识别它无法处理的内容),翻译器不产生作为所指定的约束的不正确实现的执行。此外,由于本文中的主要目的是通过添加约束而非消除除约束外的全部表达手段来提高命令性编程的简洁性,因此本发明的各实施例的优势能由处理多向约束的共同情形的翻译器来实现,这在许多应用中是相对简单的。最后,有可能指定无法被简化为由其输入触发的一组高效额命令性编程序列的约束集。因此,优选的翻译器实现在这种情形下会产生错误消息,而不是默默地提供对输入和输出空间的耗时的穷尽性搜索。在过度复杂的约束的情形下抛出异常是与本发明的实施例的目的相一致的。

A.3 抽象句法树(AST)表示

在一个实施例中,翻译器在其解析其输入时记录被指定为抽象句法树中的节点的每个约束,这类似于传统编译器或解释器中的初值化语句节点,但以指定“触发器”数据成员的能力来扩展。如果约束是如前所述的多输入约束,则后续处理在AST中可修改dm的点处为原始约束的每个触发数据成员dm将单触发器约束节点插入到AST中。例如,在例1的约束集中,将在AST中创建第二约束的初始约束节点以应用于该约束集的初始化中(即构造函数过程),但也可在反应器的“onTotal”和“onAvgPrice”过程的实现中各自复制至report和accounting输入对象中。

翻译器随后遍历AST以完成翻译,这只需要考虑每个过程表示中的单触发器约束指定,从而简化了输出处理。

A.4 局部约束实现

在一特例中,约束可按照在与输入相同的上下文中定义的一个或多个输入来表达。在这种情形下,可通过将相关联的命令性代码直接插入到输入修改所在的位置而对这些输入实现约束。在一个实施例中,翻译器标识约束集中具有该相同上下文属性的约束并如所述那样实现、优化。替代地,编程语言可提供与数据成员相关联的声明性上下文以指定这些约束。在例11中,数据成员“total”的声明用声明性约束上下文来扩展。

其中该声明性约束上下文包含指示该total等于数据成员part1、part2和part3的总和的语句(这里,与被称为“total”的数据成员相等基于该上下文被声明为“total”的声明的扩展而是隐式的)。采用这种声明,软件能高效地访问这些part数据成员之和,从而依赖于该约束来确保“total”数据成员在任何时候包含这些part数据成员的最新总和。

对于例11中的约束,针对该编程语言的翻译器插入命令性指令以当实例化时将该数据成员初始化至该总和,并在程序中“part1”、“part2”或“part3”中的任何一个被修改的每个位置处更新“total”的值。例如,如果“part2”在源代码的第34行被置新值,则翻译器在34行后面插入以下C++类行的等效物。

-------------------------------------------------

total=part1+part2+part3;

例12对“total”的插入命令性更新代码

-------------------------------------------------

注意,该代码指定命令性语句将“total(总计)”赋值为“part1”、“part2”和“part3”之和。因此,在翻译器是编译器的情形下,该代码是直接可编译的并在约束的静态类型上下文中被优化为机器语言实现。因此,这种实现与提供相同功能的手动指定的命令性指定一样高效。

一个或多个数据成员可使用用于数据成员声明的标准句法,即类型后面跟名称以及没有input、output或inputOutput限定词的其它指定,而被定义为对约束集是局部的。集合中的约束可使用这些局部数据成员中的一个或多个作为输入或输出。在输入的情形下,采用如前所述的局部优化,即直接调用约束命令性处理而不是使用通知机制。

A.5 集合数据成员的约束

在一优选实施例中,编程语言允许声明例如“集合”的复杂数据成员,即存储或引用变化数目的其它数据成员的数据成员。例如,存储对由Name(名称)值类型索引的类型Child(子)的对象的引用或指针的被称为“child”的集合数据成员可被声明为:

-------------------------------------------------

Child::Ptr child[Name];

例13集合数据成员声明

-------------------------------------------------

该声明跟随在用于声明跟有名称和其它限定词的类型的常规句法之后。该集合方面由指示该集合数据成员由类型“Name”的值来索引的“[Name]”指定的存在来指示。

为了保持底层集合状态的完整性,语言翻译器可生成用于修改集合的一个或多个过程并要求修改该集合的全部软件调用这些过程中的一个而不是直接访问用来实现该集合的数据成员。这些各自被称作“增变器”的修改过程可包括将给定关键字的值插入到集合中的过程、删除指定成员的程序、以及可能递增给定成员的值(在集合成员类型支持递增的情形下)的过程。在该实施例中,翻译器可在这些增变器过程的每一个中插入调用前述通知回调机制的命令性代码,从而避免在应用程序中否则将修改集合的每个点处插入通知代码的需要。同样,翻译器可将与该数据成员相同的范围中指定的约束所要求的命令性代码直接插入到这些增变器的每一个中。如此,这些增变器过程的生成减小了实施例中所需的代码空间,因为其避免了集合增变和约束实现代码的重复。

一支持集合数据成员的实施例可基于集合语义支持具有更复杂语义的约束。例如,前述“child”集合可支持child字段上的约束,即其要求该字段被设定为指向父对象的“parent(父)”字段,如下文指定的。

“this”值表示如C++中的封入的范围对象。该约束指定当child被实例化时将Child的“parent”字段设为实例化父对象,并且当将child对象从该集合中删除时清空该字段。该约束可完全在用于该集合实现的增变器中实现,如果这些过程如前所述是更新该集合的唯一手段的话。

该例示出直接支持例如集合的复杂数据成员的语言如何允许约束以比简单的单元素数据成员具有更复杂的语义。在优选的支持集合的实施例中,语言能支持集合之间的约束。例如,将ChildMonitor视为对来自从如例14中的child类型的对象的改变的回调作出反应的类型。则,ChildMonitor的集合可被声明为与例15中所示的指定父对象“parent1”中的Child集合对应。

第一个约束声明的解释是对于作为parent 1的“child”集合的成员的每个child对象存在一个“childMonitor”的成员。(在优选实施例中,单独的对“ChildMonitor”的指定而不是将“ChildMonitor::Ptr”指定为该数据成员的类型意味着ChildMonitor实例将被实例化)。

该例还示出可在一个上下文中指定多个约束。在例15中,关联于childMonitor的“parentMonitor”字段被约束为指向childMonitor的范围,这匹配于用于“child”集合的结构。

在指定“child”数据成员是单独翻译的模块的一部分的情形下,翻译器通过生成当修改“child”集合时拟由通知回调机制调用的过程来实现该约束,并且该过程在这种情形下取决于child集合在该次修改中添加了还是删除了条目而添加或删除条目。这种相同实现可在该约束在与“child”相同的编译单元中指定的情形下使用,如果child的范围不包含对“parent1”的直接引用的话。

对集合成员的约束的示例在局部约束上下文中被指定,而不是为简化起见作为约束集的一部分来指定。然而,这些类型的约束也可在约束集中指定。

A.6 扩展的约束集约束实现

在例如例2所示的扩展约束集中,可指定需要覆盖或扩展在原始约束集的实现中采取的动作的一个或多个约束。在该例中,重定义对report::cost的约束,因此响应于report::total和accounting::avgPrice采取的动作必须改变。在这种情形下,翻译器可为其动作需要改变的每个输入生成一反应器类型,该反应器类型是原始constraintSet中的反应器类型的派生类型,它用修改或扩展的实现覆盖了要求改变的函数。例如,对于accounting输入对象的派生反应器类型的内部表示可如例16那样使用C++来指定:

此外,用于扩展约束集的构造函数过程实例化这些扩展输入的派生反应器类型。在前面的例子中,accounting输入是使用由ExtendedReportMaintenance::AccountingReactor表示的派生类型来代替在基本约束集中使用的ReportMaintenance::AccountingReactor来实现的。这些过程的每一个的实现中的...由用于为每个相应数据成员通知执行的扩展动作来替换。

扩展约束集可指定具有附加数据成员和附加约束的扩展输入对象,该扩展输入对象将这些附加数据成员用作输入。在这种情形下,C++中的内部表示可以是从基本反应器类和作为输入对象的派生类的notifiee类多重继承的反应器类。派生反应器类因而包含附加“on”函数的实现以及覆盖基本反应器类中的那些函数的实现。在被覆盖的函数的动作是基础反应器中的那些动作的超集的情形下,反应器中的被覆盖的函数可调用基本反应器中的相应函数,这使派生反应器中的动作的重新实现减至最小。

对输入反应器实现和约束集实现两者使用该形式的面向对象的继承,向扩展约束集提供了超过基本约束集的最少机制重复。

B.使用约束的统一代码生成

翻译器应当“理解”它在其中插入实现约束集中的约束的命令性代码段的基本命令性代码的结构。具体地,应当确定每种实现中要添加通知所需的附加处理的正确点。例如,用于集合的插入过程可测试对象何时具有冲突的关键字并返回错误而不是插入对象。应当实现对约束采取动作的指令以在对象实际上未被成功添加至集合或集合中的指定条目已包含该值的情形下使这些指令不被执行(避免关于不作出状态改变的更新动作的通知所需要的)。此外,添加的指令应当在考虑命令性代码所提供的上下文的情况下生成。例如,一定不能引入与那些已由该过程定义的变量冲突的局部变量,但仍然能根据高效约束实现的需要来引入局部变量。出于效率考虑,也应当使用已有的参数和局部变量来访问状态,从而避免重做状态访问、计算和检查基本命令性代码已被执行,例如检查参数为非空的。

在一优选实施例中,每个集合增变器的实现是按照一个或多个约束来指定的。例如,曾变器的动作可被指定为将数据成员约束为该过程的值参数的值。因此,AST中存在与数据成员对应的节点以及与增变器过程对应的节点。约束表达了从增变器节点至数据成员节点的单触发器等同关系。此外,支持前述类属通知回调机制的命令性代码可作为该实现的一部分通过专用约束来生成。因此,增变器的实现是通过生成由增变器节点触发的命令性代码来生成的。注意,该约束一直是由与增变器对应的伪数据成员触发的单触发器约束。

使用这种方法,翻译器因而具有统一代码生成模块,该模块使用允许其遵循前述正确性和效率因素来进行合成的惯例基于约束节点来输出用于增变器的命令性代码段,而不论约束节点是生成增变器的主体还是实现依赖于该集合数据成员的单独的约束。具体地,在约束集中定义的局部数据成员以及访问其值并更新其值的相关联的手段可通过指定为AST内部表示的一部分的单触发器约束来生成。

C.别名数据成员

在一优选实施例中,可使用例17所示的局部约束将一个数据成员定义为另一数据成员的别名。

-------------------------------------------------

ChildMonitor::Ptr monitor=childMonitor;

例17使用约束指定别名

-------------------------------------------------

翻译器将“monitor”解释为“childMonitor”的别名,其意思是访问“monitor”等效于访问“childMonitor”且“monitor”的状态完全依赖于“childMonitor”。这在“monitor”约束于“childMonitor”的实现的意义上是一种约束。具体地说,如果childMonitor数据成员改变,这种改变反映在monitor数据成员的值中。

在一优选实施例中,访问集合数据成员的被称为访问器的集合过程可通过与前述别名机制相似的约束节点来生成其实现,这进一步提供统一的基于约束的代码生成器模块。别名也可按照例18中所示的表达式来定义。

-------------------------------------------------

bool isNullPtr=!ptr;

例18使用约束指定表达式别名

-------------------------------------------------

作为(伪)数据成员定义,可使用表达式别名来定义当条件改变时要采取的动作,这与将普通数据成员用作约束输入相同。

在一优选实施例中,事件的复杂组合可使用约束集和表达式别名来指定。这在例19中示出。

input MarketTicker在证券价格改变时提供通知。Portfolio对象指示当前财产和目标价格以及限价。对overTarget(超出对象)和underLimit(下限)的局部表达式别名的定义是对关于这种具体证券的定货单中的销售条目的约束的输入。在该约束指定中,用于作为集合的输入数据成员的关键字或索引值是隐式的,并且需要跨约束的各输入和输出进行匹配。在例19中,证券的符号用来对portfolio和tkr集合作出索引。使用这种工具,可在命令性语言中将复杂事件处理序列指定为对约束的输入。

D.基于通知的过程触发器

在一优选实施例中,还可提供指定响应于通知来触发的过程的能力,如例20所示。

这里,特殊的“:”符号表示当集合“fileDir”改变时要执行下面的语句。这种机制允许应用程序指定要响应于数据成员的改变执行的命令性处理,从而处理了超出语言中可表达为声明性语句的关系的需求。更一般地说,它允许如例21所示在声明性上下文中指定命令性上下文。

在该例中,由“...”表示的任意命令性代码被指定为响应于“fileDir”数据成员改变而被调用。为了适应通知工具的受限语义,该过程应当被写成幂等的(即,对于约束来说,在同一输入执行该过程多次完全等效于对该过程的一次执行)而不是采取完全任意的动作。

过程触发器可在表达式别名中指定,以使其仅当表达式为真或非零时被调用。语言翻译器插入在作为表达式的一部分的数据成员改变时调用的命令性代码,并当表达式求值为真或非零时调用该过程。

该机制展示出过程触发机制也被支持作为用来指定和实现约束集的语言和机制的扩展,虽然受到通知工具的面向约束的语义所限制。

E.使用约束的检验

在一优选实施例中,检验约束可通过生成命令性代码来提供,该命令性代码在数据成员的修改发生前检验针对该修改所指定的关系,并且如果修改可能造成违背指定关系则抛出异常。该异常确保修改不发生,假设诸如C++、Java和C#等其中错误是通过抛出异常来处理的现代编程语言。因此,维持这种关系的命令性代码抛出异常。检验约束的指定可如例22那样完成,在这种情形下将speed(速度)数据成员局限为在0和100之间的范围内。

在一优选实施例中,检验形式的约束的实现利用统一的基于约束的生成,从而方便了如检验约束语义所要求的那样在修改发生前插入检验约束代码。即,统一的基于约束的生成可确定命令性代码中与增变器相关联的点以插入执行检验的附加命令性代码。

F.使用约束的初始化

在一优选实施例中,将初始化作为约束的特定情形来处理,从而生成仅在初始化数据成员时将数据成员设定为指定值的代码。这在例23中示出,其中将“speed(速度)”数据成员初始化为“initialSpeed(初始速度)”。

前缀“`”用来表示数据成员的属性。“default(默认)”属性对应于数据成员的初始值。

G.使用约束来指定数据成员属性

在一优选实施例中,数据成员可具有包括前面指定的初始值或默认值的众多属性。例如,集合数据成员可具有“ordering(排序)”属性,该属性可具有值“unordered(无序)”、“by insertion(按插入)”或“by key(按关键字)”。类似于前面提供的初始化的指定,与数据成员相关联的局部约束声明性上下文可用来指定这些属性,如例24所示。

在这种情形下,如果fileDirOrdering(文件目录排序)是数据成员,则翻译器插入基于fileDirOrdering数据成员的改变根据需要改变“fileDir(文件目录)”的实现的命令性代码。在这些示例中,属性名之前放置“`”以表示这是声明性范围的数据成员的属性,而不是对另一数据成员的引用。

在一优选实施例中,数据成员的属性默认地被设为最常见的适用值以使显式指定属性值的需要减至最小。在一优选实施例中,翻译器允许在单个局部约束上下文中指定多个约束,包括不同数据成员属性,同时如果这些指定的约束具有一个或多个冲突则检测这些约束间的逻辑冲突。

H.使用约束的解析器生成器

在一优选实施例中,约束集数据成员可使用一个或多个约束定义为其它数据成员的与或树的别名。其它数据成员可以是相似的别名。这种形式的复杂别名可视为语法中的非终止符号。这种性质的复杂别名的指定通过例25所示的约束示出。

这里,由空格分开的数据成员名称或文字被视为串联或联接,而特殊符号“|”表示“或”连接或分离。在例25中,数据别名“typeDecl(类型声明)”被定义为串“type(类型)”后跟名称数据别名,然后是文字字符“{”,然后是bodyNode(主体节点)数据别名,之后是文字字符“}”或串“type(类型)”,然后是名称数据别名,最后是文字字符“;”。

这种约束指定使语言翻译器插入处理响应于第一条件和第二条件的回调的代码,这进而被结构化为检测对应于每个数据别名的数据成员序列的状态和处理。然后当该“typeDecl”别名数据成员被识别为真时调用“handleType(处理类型)”过程。

语法中的终止符号被指定为按照常量或文字定义的别名,如例26所示。

-------------------------------------------------

bool newToken=inputStream->token==″new″;

例26指定语法中的终止符号

--------------------------------------------------

这里,当输入令牌被设定为串“new(新)”时“newToken(新令牌)”为真。

用于解析器的constraintSet具有一个输入,即例26中使用的“inputStream(输入流)”对象。其它约束通常按照约束集中定义的表达式别名来定义。

使用这种机制,应用程序可指定形成描述语言句法的“语法”的约束集。具体地说,每个“令牌”事件或基本事件对应于一个表达式别名,该表达式别名对应于变得与文字值相等的数据成员。每个“非终止符”对应于其它表达式别名。可通过附加约束或使用过程触发器来将进一步的处理附加于这些非终止符。使用这种工具,翻译器可在命令性编程语言中提供解析器生成器的能力。即,该翻译器可将约束集翻译成内部语言语法表示并随后使用业内公知的技术以供解析器生成器生成用于实现高效解析器的命令性代码。这种方法允许在一种编程语言中指定语言翻译器以易于指定和维护,同时仍然提供语法的声明性指定和所需解析器代码的自动生成的优势。即,在通常使用单独的解析器生成器来完成这个任务的编译器和解释器的上下文中,约束集机制允许编程语言在命令性编程语言中(即在单个编程环境中)包含解析器生成器的能力。

H.约束异常通知

在一具体实施例中,当还原约束关系的动作失败时,翻译器可生成通知。例如,例27中对于非否定“difference(差异)”数据成员的约束在count1小于count2时不能得到维持。

因此,翻译器生成在这种情形下调用通知机制的命令性代码。应用程序可使用与count1小于count2对应的表达式别名来指定命令性处理在这种情形下执行,这调用普通的约束过程或依赖于该表达式别名的或过程触发器。因此,它可使拟调用的动作对故障作出反应以维持原始约束,这有可能撤销对引发故障的状态的改变。

I.应用程序定义的约束运算符

在一特定实施例中,应用程序定义的约束运算器可用名称和参数输入和输出的列表来定义。当翻译器遇到应用程序定义的约束运算器时,如其名称指定的那样,它生成将约束运算器实例化为对象的指令,并将自变量输入连接到参数输入且将自变量输出连接到参数输出。应用程序定义的约束可对应于具有输入接口和输出接口以及两者间的专门的应用专用处理的对象,这可能由耦合于命令性编程的过程触发机制来提供。使用这种机制,可使用简洁的约束指定来调用任意专门算法,而翻译器和编程语言不需要包含所有可能的应用专用算法。

在一个实施例中,约束运算器实现可以按类型参数化的形式来指定,即C++术语中的模板。这被称为约束运算器模板。在这种情形下,输入和输出被映射到自变量输入和输出的特定类型。结果,同一约束运算器可用于不同类型,只要它们包括所需的数据成员。

在一个实施例中,可使用与由应用程序定义的约束模板使用的相同的基本机制来定义与传统约束编程对应的专门的运行时约束集。具体地,当翻译中遇到被指定为运行时约束集的约束集时,插入代码以实例化与该约束集对应的约束存储对象以及类似于用于应用程序定义的约束的特定约束对象,但其指定关系作为输入自变量。在运行时,当该对象被实例化时,它将与该约束相关联的关系表达式记录在相关联的运行时约束存储中。它进一步向通知工具注册其从属数据成员。然后当添加或移除约束或当存储中的一个或多个约束的从属数据成员被修改时在该约束存储上调用约束解算器。因此,该工具提供常规的通用约束解算器方法,但局限于以这种形式指定的约束子集,而且是由通知机制调用而不需要将每个赋值或增变器动作实现为约束的附加。

这些工具允许应用程序超出翻译器直接支持的“内置”约束以利用专门的应用专用算法,该算法对通用约束解算器具有优越的性能并且在例如各种组合数学和优化问题等没有更好的算法已知的情形下仍然使用通用约束解算器。相反地,人们可将该通知机制视为允许使用通用约束解算器而无需改变命令性存储模型使其超出提供通知工具,并进一步允许在优选命令性解法已知的情形下的优化,如应用专用约束运算器和(语言)内置约束运算器所指定的那样。

由约束指定的可能关系可根据其触发属性、状态量和时间分量来分类。多触发器约束的处理之前已描述过。状态关系可用约束集本地的所声明的数据成员来处理。时间属性可使用前面描述的线程机制或类似的处理控制工具通过对通知和来自该通知的命令性动作的结果执行之间的这些对象中的一个引入调度延时来实现。这些能力与对所有基本运算器和这些运算器的合成的支持一起提供全面的约束能力。

J.表示专用约束

在许多应用中,需要在状态的普通存储器内表示和例如符串表示、未类型化表示和网络表示等其它表示之间实现一组约束。例如,需要适配器以在实际编程语言的未类型化编程环境(例如脚本语言、命令行接口或图形用户接口)与常见的强类型化环境之间接口。具体地,命令行解释器或图形接口提供依照串或指向例如用户接口框架对象等不是与内部应用程序对象一致地类型化的对象的指针的输入指定。该适配器需要采用数据成员、关键字和值的串指定,并使实际的强类型化的应用程序对象与这些值一致或抛出异常。(由于对每个应用程序对象存在一个适配器对象,所以应用对象隐含在适配器对象中。)

在一优选实施例中,翻译器允许指定表示作为指定约束的一部分。表示的一个示例是输入或输出的特定网络协议表示。该表示可由关于输入或输出或关于特定约束的限定词来指定。默认表示是普通的存储器内表示。

在一优选实施例中,翻译器还支持将每个表示的扩展模块添加至翻译器,该模块处理与该特定表示相关联的约束的翻译。当翻译器遇到具有非默认表示的约束时,它调用相关联的表示模块来翻译该约束。

在分布式操作的情形下,为网络表示提供表示模块,按照类型处理约束集,该约束集将数据成员约束到在指定为该约束集的一部分的网络输入输出连接中的表示。所得的命令性代码维持该类型的数据成员与网络表示一致,这通过经由网络连接将一类型的实例连接到另一实例确保了这两个实例保持一致。具体地,对于需要通过网络访问的每种类型T,翻译器生成一种适配器类型,该适配器类型通过经由网络连接传送对类型T的对象的更新来对该更新作出反应,并同样能接收这一更新并将其变换成对同一类型的对象的调用,从而根据该更新来设定该对象的状态。此外,使用对象范围通知来使得整个对象状态通过网络来传送,以在网络故障或设置初始化时处理连接的重新同步。

在其中真实对象在一端而同一类型的“代理”对象在另一端的网络连接的每一侧上使用该机制,代理对象被约束以维持与真实对象相同的状态值。此外,其行为可与真实对象完全相同,包括在远程节点生成通知以调用约束,从而允许如章节K中描述的分布式约束的实现。

在一个实施例中,另一种表示对应于未类型化表示。约束集因而可使用通过该表示标识指定的约束来指定普通的类型化表示和该未类型化表示之间的关系。具体地,在未类型化表示中指定的数据成员可通过约束在修改时调用命令性处理以将未类型化值转换成适当的类型化值(带有适当的检验和错误通知),并随后对相应的类型化数据成员执行更新。

在一优选实施例中,翻译器在给定表示的指示的情况下为应用程序定义的类型自动生成约束集。具体地,对于应用程序定义的类型的每个数据成员,表示模块将等同约束添加至其输入为该数据成员且其输出为目标表示对象(例如网络表示情况下的网络流)的约束集。

K.分布式约束集

在分布式应用中,约束集可指定在系统的不同节点上实现的输入和输出。在一个实施例中,对要被指定为约束集的输入或输出的每个远程对象创建一代理。该约束集然后用指向这些(代理)对象的每一个的指针来实例化。如之前所述地,代理对象是使用基于表式的约束实现的。结果,当远程对象被更新时,该更新被传播至每个代理对象,这使导致通知的生成,与局部对象相同。该通知如同对于局部对象一样调用约束处理。如果约束处理修改这些代理对象中的一个,则更新被传回真实对象并生成对代理对象的本地客户机的通知。

使用该实施例,约束集可在分布式应用程序中相对于其输入和输出两者都是远程的节点上实例化和执行,而在命令性处理中没有变化并对该约束处理具有高效执行。

L.用约束实现的过程

以下示例涉及基于通知和回调来提供约束框架内的过程结构的高效实现,且可被视作结合图6-12提供上述原理的更多扩展示例。

例如,用于使设备通电的简单过程可被指定为:

例28:设备通电

在此,过程通过将设备状态设置为开始来启动该设备,等待设备就绪(在标记为“waitDevice(等待设备)”语句处),然后启动正常操作,如等待块中所指定地。但是,如果设备在1.5秒后未能就绪,如在同一等待语句中所指定地,其报告失败并终止执行,如在相关联“else”块中所指定地。

在以下说明作为约束集一部分的例28的过程的指定。

例29:约束集中声明的设备通电

在该情形下,在约束集范围处声明输入和输出,且对过程是隐式的。此外,触发约束也在约束集范围中声明,在该情形下由deviceConfig状态的表达式将示例中的最后一行设置为初始化。该行是基于通知的过程触发器(如以上的部分“D”),其中在deviceConfig::state被设置为初始化时,“powerUp(通电)”过程将成为被触发的过程并发生通知。

在示例过程的纯约束解释中,实现将需要把约束添加在deviceStatus->state上以等于下一时刻步骤处的开始,然后移除该约束并添加一约束,使得该时刻现在晚了1.5秒或deviceConfig->ready字段等于真。然后取决于哪个条件满足约束来移除该约束并添加对应于deviceStatus等于操作或等于关闭的约束,并类似地处理deviceStatus::mode字段。约束执行环境中的过程语义的建模还要求作为赋值结果保持变量值(即,当约束被移除时值不改变,除非是响应于其余约束的显式要求)。清楚地,以该方式实际执行的实现将极其低效,尽管这些约束还翻译成在每个步骤注册和注销的回调子例程。

在优选实施例中,翻译器将如下地实现例28-29的通电过程的实现,使用C++类编程表示以说明其特定实现。首先,翻译器将向对应于constraintSet DeviceControl的类定义添加该过程状态的enum声明和数据成员以便存储该状态,类似地处理异常结果。这些数据元素对应于前面描述的过程上下文状态。且,其向对应于过程的4个成员函数添加声明、过程的异常处理、deviceConfig通知处理以及时间通知处理,如例30所示。

例30:DeviceControl类声明

在此,在对“DeviceControl constraintSet”类的以上添加之前和之后两种情况下的“...”指示用于指示附加代码作为独立于过程实现的constraintSet实现的一部分提供,如constraintSet实现所要求地。这种约束集实现代码在上文中详细描述(例如,部分A-K)。

作为初始化子例程的powerUp成员函数的实现可如例31中以C++类形式实现。

例31:powerUp成员函数实现

实际上,如果当相关联enum数据成员设置成初始状态时被调用,其将设备状态设置为开始,将该enum case数据成员设置为下一状态(即waitDevice),且然后检验deviceConfig::ready数据成员是否已为真,如果结果为真则调用另一生成的成员函数(powerUpHandledeviceConfig),而否则将时间通知回调时间设置为当前时间加1.5秒。如果在任何其它状态中调用,其默认行为不采取行动并返回,因为powerUp过程已在执行中。如果针对该过程指定其它动作,它们将由翻译器插入到以上的“default(默认)”case或插入到以上switch语句中的单独case语句。

来自deviceConfig输入的成员函数处理通知,即powerUpHandleDeviceConfig,可如例32中的以下C++类代码所示地实现。该代码对应于上述翻译器算法中的回调子例程。

例32:powerUpHandleDeviceConfig实现

仅当powerUp enum数据元素具有如在powerUp过程中设置的waitDevice值时,该成员函数才执行动作,从而允许其忽略当不适于采取动作时的来自deviceConfig输入的通知回调。实际上,在标记为powerUp_waitDevice_的case块中的执行与等待设备后的下一步骤相关联的动作的命令性代码仅当该数据元素具有该值(enum)时由enum数据元素启用,从而相比于实际添加和移除约束触发注册提供用于启用/禁用这些约束动作的高效手段。注意,DeviceControl constraintSet可包含具有作为输入的deviceConfig的其它过程和约束,且deviceConfig对象可按照与powerUp过程无关的方式来改变,因此可存在对于该成员函数的许多通知回调,这些通知回调由switch语句根据switch语句的结构化适当地被忽略。

如果powerUp过程包含其它涉及等待取决于deviceConfig输入的条件的语句,该翻译器将针对这种条件向该成员函数添加附加case块。

注意,该实现具有嵌入在“try(尝试)”块中的switch语句,从而造成在相关联catch(捕捉)块中捕捉任何异常,其调用相关联异常处理成员函数即powerUpHandleException,且然后通过执行goto到try块的顶部来重新尝试该动作。该结构允许powerUpHandleException通过将enum数据成员设置为powerUp_NullCase_来终止该过程,或执行某动作并返回,从而重新尝试造成异常的进程中的当前步骤。

可如下地实现成员函数处理超时通知,利用类似C++类指定:

例33:powerUpHandleTime实现

在该实现中,如果在enum数据元素具有值powerUp_waitDevice时调用,则其采取原powerUp过程指定中的“else”块的动作。仅当过程在等待设备且在从指示就绪的deviceConfig输入接收通知前1.5秒的指定时间周期已流逝时,才发生这种情况。否则该成员函数忽略超时回调,如果其它过程或该constraintSet的约束要求基于时间的通知这可能发生。其遵循与例32中实现相同的异常捕捉和处理结构。

如果powerUp过程具有取决于基于时间的通知的附加语句,则翻译器将把附加case块添加到该过程中的对应于这些其它语句的switch语句,由enum数据元素来标记且由此控制以便确定它们是否对时间通知有影响。

powerUp过程中的已生成成员函数处理异常抛出的实现、即powerUpHandleException可如下地利用C++类指定来实现:

例34:

该实现记录异常并将enum数据元素设置到null case(空情形)以便指示powerUp过程执行的终止。如果powerUp过程指定的动作要在特定处理步骤中的异常时执行,翻译器将把对应于处理步骤的case添加到以上实现,从而当该异常处理过程在enum数据元素指示在该处理步骤中过程已发生异常时被调用时采取那些动作。这些动作可包括:

在终止前报告附加信息,

执行一些修补动作并重新尝试该失效步骤,和/或

继续进行其它处理。

这样,异常处理动作可对于招致异常的处理步骤、以及异常的类型以及可访问该异常处理代码的其它状态而特定。

该过程的实现也被封闭在“try”块中以便处理更多复杂动作的情形,复杂动作在异常处理进程期间可自己抛出异常。如果经翻译的过程包括贯穿处理步骤需要保存的局部变量,翻译器将把对应数据元素添加到enum和异常数据元素之外的constraintSet类。其还插入代码以贯穿步骤按需保存和还原它们的值。

M.用约束实现的嵌套过程

过程调用语句嵌入在另一过程可被解释为一次性约束触发了该过程。如果被触发或调用的过程在约束集范围之外声明,该调用被翻译成实例化相关联约束集,注册输入并绑定输出,且然后触发过程约束的执行,且然后等待设定该返回输出。

如果被调用过程在约束集内部声明,语句等于触发过程使之由显式触发约束来执行,除非调用语句被处理成等待过程的所得值。

N.用约束实现过程的优化

在优选实施例中,翻译器不针对该过程不使用的约束程序的任何输入生成回调子例程,因此需要回调子例程。

在优选实施例中,作为代码空间优化,翻译器可针对各case号生成共享执行子例程,否则需要将代码放入两个或更多个回调子例程、并从该两个或更多个回调子例程的每一个将调用插入到共享执行子例程。这样,在一处生成共用代码,从而以附加子例程调用的最小成本来节省代码空间。

在优选实施例中,如果已知时间回调工具提供正确超时回调,翻译器可从时间回调子例程消除条件块,从而识别出仅当超时已精确届满时其case语句号设置为该case时调用该回调子例程。在优选实施例中,存在用针对执行中的各事件序列化过程的系统时间工具来注册的回调。因为过程仅可等待给定时间的最多一次超时,过程将该回调注册的状态设置为仅在由该等待语句指定的超时时间下回调。此外,如果在超时已届满前等待条件变为真,过程实现可重设该超时回调时间。因此,该过程实现可依赖于仅在当前等待语句已发生超时时调用的超时回调子例程。因此,超时回调过程在被调用时不需要检验实际上超时周期已流逝。相反,如果时间回调子例程仅在每K毫秒被调用,时间回调子例程需要在每次调用时明确检验该超时对当前等待语句是否已届满(如果有)。

在优选实施例中,翻译器插入代码以测试所指定条件,且如果该条件为真,分支到相关联下一case以执行。尤其,如果该下一case是在同一switch语句中连续的下一case,该代码被指定成刚好落入通过该后续case块。(否则,其显式地分支到相关联case块。)如果该case仅在某其它回调子例程中实现,其直接调用该单独的回调子例程。通过依赖通用通知和回调机制,该优化可消除调用单独的回调子例程的开销。

在优选实施例中,执行以上优化的翻译器还可:1)在还原上下文的代码后向case提供附加入口点,如果有;2)在条件测试前插入条件测试代码;以及3)使条件分支将附加入口点设为目标。由此,其允许在条件成立的优化case中的执行以避免上下文保存和还原的开销。该方法的示例在图12b中示出,其中如果当第一次测试时(即,在块i中)条件a<b成立,控制跳转直接到块i+1中的等待主体。

翻译器可识别何时标记的非等待语句未在标记之前的case语句和标记之后的case语句中的代码外部被引用(用goto语句),且然后将这两个case组合为一个。两个case的组合消除从一个case切换到后续case的开销。

优选地,根据本发明实施例的翻译方法在计算机上实现以便根据上述原理将输入代码的快速和自动翻译提供到计算机可执行输出表示。这种翻译可被视作将技术制品(即,输入代码)到另一形式(即,计算机可执行输出表示)的自动转换。

前面的描述是示例性的而非限定性的。本领域内工作人员很清楚如何在各类似情形中实现本发明各实施例的原理。具体地,提供用于如前所述地从输入源程序中的约束指定自动生成中间通知和反应器表示的代码可通过编程语言变换领域公知的方法来实现,并且因此在本文中不予说明。

去获取专利,查看全文>

相似文献

  • 专利
  • 中文文献
  • 外文文献
获取专利

客服邮箱:kefu@zhangqiaokeyan.com

京公网安备:11010802029741号 ICP备案号:京ICP备15016152号-6 六维联合信息科技 (北京) 有限公司©版权所有
  • 客服微信

  • 服务号