360 likes | 537 Views
第二部分 CORBA 的核心 第四章 OMG 接口定义语言. 4.2 简介 OMG IDL 是 CORBA 的基本抽象机理,它从实现中分离出对象接口。在客户机和服务器程序之间建立起了一个契约,用它来描述在应用程序中需要用到的类型和对象接口。 IDL 定义由一个 IDL 编译器编译一个具体的实现语言。编译器将这些与语言无关的定义翻译成特定语言的类型定义和 API ,开发者使用这些类型和 API 来提供应用程序的功能和与 ORB 交互。 IDL 只描述接口,不描述实现,它是一个纯说明性语言。
E N D
第二部分 CORBA的核心第四章 OMG接口定义语言 • 4.2 简介 OMG IDL是CORBA的基本抽象机理,它从实现中分离出对象接口。在客户机和服务器程序之间建立起了一个契约,用它来描述在应用程序中需要用到的类型和对象接口。 IDL定义由一个IDL编译器编译一个具体的实现语言。编译器将这些与语言无关的定义翻译成特定语言的类型定义和API,开发者使用这些类型和API来提供应用程序的功能和与ORB交互。 IDL只描述接口,不描述实现,它是一个纯说明性语言。 IDL定义把焦点集中在对象接口、其它接口所支持的操作和操作时所可能引发的异常上, 。IDL的大部分内容涉及到数据类型的定义,这些数据用来提供客户机和服务器程序之间的交互。
OMG接口定义语言 • 4.3 编译 一个IDL编译器生成源文件。源文件必须与应用程序代码一起生成客户机和服务器的可执行文件,不同的ORB可能生成不同文件名和文件数目。 4.3.1 编译 IDL编译器生成四个IDL定义的文件,两个头文件types.hh和serv.hh,一个存根文件stubs.cc和一个框架文件skels.cc。 头文件types.hh包含在IDL中所有的相应类型的定义(数据类型和接口)。 头文件serv.hh包含在IDL中所有的相应类型的定义(特指服务器端的)。 存根文件stubs.cc提供客户程序发送消息给远程的对象所需要的API。 框架文件skels.cc中提供一个从这个ORB到开发人员所编写的服务器源代码的上端调用接口,并且提供ORB的网络层和应用程序代码之间的连接。 客户机和服务器程序还必须与ORB库连接,它提供必要的运行时支持。
OMG接口定义语言 联邦制的服务器程序提供了一种单一的逻辑服务,它将大量的进程分布在不同的机器上。在这种联邦机制中每个服务器程序实现相同的接口,但是控制着不同的对象。 • 4.3.2 客户机和服务器的不同开发环境 不同语言或不同ORB开发的客户机和服务器程序不能共享任何源代码或二进制组件。JAVA不能包含C++头文件,不能共享不同的ORB。 客户用JAVA在ORB(A)上开发,服务器用C++在ORB(B)上开发。客户机和服务器完全独立(开发环境、语言映射和ORB)。客户机和服务器程序之间唯一的连接就是IDL定义。
IDL Developer ORB A ORB B IDL Source Client Developer Server Developer IDL-TO-Java Complier IDL-TO-C++ Complier app.java stubs.java types.hh stubs.cc serv.hh skels.cc impl.cc RPC C++ ORB Run-Time Library Java ORB Run-Time Library Clinet Executable Server Executable 不同开发环境下的开发过程 OMG接口定义语言 客户和服务器连接如下图
OMG接口定义语言 • 4.4 源文件 4.4.1 文件的命名 包含IDL定义的源文件的命名必须以idl为扩展名。编译器拒绝编译其它扩展名。对于不区分大小写的DOS,扩展名用大小写都可。对于区分大小写的UNIX,扩展名必须用小写。 4.4.2 文件格式 IDL是一种自由的格式语言。允许自由的使用空格、水平和垂直制表符、换行和换页符,没有规定页面布局和缩进方式。任何文本编辑器都可对其进行编辑。 4.4.3 预处理 IDL源文件是经过预处理的。预处理作为编译器的一部分来实现,它也可以是外部的程序。预处理常见的有#include和#define等。 4.4.4 定义的顺序 IDL结构,比如模块、接口或类型定义,顺序可任意,但标识符必须在使用将说明。
OMG接口定义语言 • 4.5 词法规则 • 4.5.1 注释 IDL定义允许使用C++和C两种形式的注释 /*……………………*/和// • 4.5.2 关键字 IDL中关键字必须用小写字母。三个例外:Object、TRUE和FALSE • 4.5.3 标识符 标识符由字母、数字和下划线组成,以字母开头,不能以下划线开头(转义标识符)。不能有非英语字母(映射困难)。 标识符是不区分大小写的,但必须以大写字母开头。否则非法。 IDL允许创建凑巧在某种实现语言中正好是关键字的标识符,编译器使用前缀来避开这个关键字。入while加前缀后_cxx_while。
OMG接口定义语言 • 4.6 基本的IDL类型 在CORBA中,不一定所有的IDL提供的类型在任何语言中都与要求相符。这就要求实现部分提供与要求的取值范围有任何差别的文档资料。 IDL中仅仅指定了下线尺寸,而未指定上限。这是为了避免限制那些可能的目标环境和语言。CORBA规范对IDL基本类型的尺寸和取值范围保留了灵活性。如有些CPU体系结构没有8位字符或16为整数,在这种CPU中,映射的取值范围为大于的类型。 类型 范围 尺寸 短整型 -215~215-1 ≥16位 长整型 -231~231-1 ≥32位 无符号短整型 0~216-1 ≥16位 无符号长整型 0~232-1 ≥32位 浮点型 IEEE单精度 ≥32位 双精度型 IEEE双精度 ≥64位 字符型 ISO Latin-1 ≥8位 字符串型 ISO Latin-1,expect ASCII NUL 变量长度 布尔型 TRUE or FALSE 未指定 八进制型 0~255 ≥8位 any 运行时可标识的任意类型 变量长度
OMG接口定义语言 • 在IDL中没有指针类型的原因: (1) 指针类型在面向对象的编程中要比在非面向对象的语言用的少。 (2) 某些实现语言不支持指针。 (3) 指针使得ORB的软件平台的编组的实现复杂化,并增加了允许时的开销。 • 4.6.1 整型 IDL中没有int型,只有short和long类型。 JAVA中不支持无符号类型,unsigned short和unsigned long映射为short和int。 • 4.6.2 浮点类型 这种类型遵循单精度和双精度浮点表达式的IEEE规范。那种语言不支持应提供差异文档资料。 • 4.6.3 字符 IDL字符支持ISO的Latin-1字符集,它是ASCII的超集。这种考虑允许多数欧洲语言使用这8位字符集。
OMG接口定义语言 • 4.6.4 字符串 IDL字符串支持ISO的Latin-1字符集,除了ASCII NUL(0)以外。IDL字符串可以有界或无界,无界用string,有界用string<100>。字符串的边界不能包含任何表示结尾的NUL字符。Hello为string<5> • 4.6.5 布尔量 有两种:TURE和FALSE。IDL不要求所有的语言用相同的表达式和尺寸。 • 4.6.6 八位字节 IDL的八位字节(octet)类型是一个8位类型,它保证在地址空间之间传输这些类型时,表达式不会发生任何改变,这样就确保了任何类型的二进制数据交换,以便在交换时不出现干涉。其它类型在数据进行传输时表达式都容易发生改变。 • 4.6.4 any类型 类型any是一个通用的包容器类型,any可做任何类型来用。 any类型常用于编译时,并不知道需要在客户机和服务器之间实际传输何 种IDL类型时用。
OMG接口定义语言 • 4.7 用户定义类型 • 4.7.1 命名类型 用typedef来创建一个类的新的名称: typedef short YearType; typedef short TempType; typedef TempType Tem; //bad style (1) 使更具有可读性和更易于理解 (2) 不必要创建一个现有类型的别名,而应引入一个不同概念的类型。 • 4.7.2 枚举类型 IDL的枚举类型的定义与C++相似。 enum Color {red,green,blue,black,grey} ; IDL没有定义序数值如何赋给这些枚举值。IDL中只能确保枚举的序数值从左向右递增。连不连续也不一定。 只能发送其本身,发送red时不能发送0,只能发送red。ORB收到后在对其进行序数值得转换。
OMG接口定义语言 • IDL的枚举值在封闭的命名域内有效 enum IColor {white,black,grey}; enum IColor {red,black,blue}; //错误,黑色被重新定义 IDL不允许空的枚举。 • 4.7.3 结构 IDL支持包含一个或多个已命名的任意类型成员的结构 struct TimeofDay {……}; 结构定义形成了一个名字空间,结构成员的名称在封闭的结构内必须是唯一的。 • 4.7.4 联合 IDL的联合与C++有相当大的差异,尤其时在判别上。IDL的联合允许多个case标记作为一个单独的联合成员并支持一个可选的default。
OMG接口定义语言 • IDL增加了一个鉴别器,用来表示当前哪个成员是活动的。 union ColorCount switch(Color){ case red: case green: case blue: unsigned long num_in_stock; case black: float discount; default: string order_details;}; 联合的类型可以是任何类型,鉴别器类型必须是整数类型(字符、整数、布尔量或枚举类)。不能是octet。 在封闭的联合内,成员必须是唯一的。 联合的default是可选的,但如果出现,则在鉴别器取值的范围内至少有一个在case的标记中未显式使用,否则非法。 case FALSE:……;case TRUE:……;default:……;//没值为默认
OMG接口定义语言 • 建议 不使用default。也不要使用多于一个的case标记。 union AgeOpt switch(boolean){ case TURE: unsigned short age;}; 这种联合的方式可用来实现可选值,只要为TRUE,类型AgeOpt的值就包含一个age。否则为空。 IDL不支持可选或缺省参数,但可以用以上的联合来模拟这种函数。 联合常用来模拟重载,通过将若干个成员作为一个参数传递给一个联合,便可用一个单独的操作来实现用若干独立定义的操作才能完成的操作。 • 4.7.5 数组 IDL支持任意元素类型的一维和多维数组。 typedef Color ColorVector[10]; typedef string IData[10][20];
OMG接口定义语言 • 必须用typedef来声明数组类型。 所有数组的维数必须是确定的,IDL不支持开放式数组。因为IDL不支持指针(在C++和C中,开放式数组恰好是伪装了的指针)。 IDL中 并没有明确数组在不同的实现语言中如何被索引。有的语言为0开始,有的语言为1开始。因此有时要转换。 实际上,发送数组元素本身比下标更容易实现应用程序和直观。 • 4.7.6 序列 序列是可变长度的向量,可包含任何元素类型,可有界也可无界。 (1) 一个无界序列可以拥有任意个元素,最多可达你的软件平台的内存极限。 (2) 一个有界序列可以拥有边界所限定的任何数目的元素。 (3) 有界和无界都可为空。 序列元素本身可是一个序列。 typedef sequence<Numbers> ListNumber;
OMG接口定义语言 • IDL允许创建元素的类型是匿名的序列。 typedef sequence<sequence <long,100> > ListNumber; 此为内联的嵌套的序列。外层序列已被严格的定义ListNumber类型,而内层序列long是匿名类型。应该避免使用这种匿名类型。 >>被认为是右移运算符,必须在>>之间插入空格或一个注释符。 • 4.7.7 序列与数组 怎样选择使用数组还是序列: (1) 如果需要一个可变长度的列表,则用序列。 (2) 如果元素的数目是固定不变的列表,而且在任何时候他们都存在,则用数组。 (3) 使用序列来实现递归数据结构。 (4) 使用序列来传递一个稀疏的数组(sparse Array)给一个操作,可提高效率,序列只传递非缺省值的元素,而数组要传全部。
OMG接口定义语言 • 4.7.8 递归类型 递归只适用于结构和联合,不论哪种情况,递归都表示为这种不完全类型(递归类型)的一个匿名序列。 • 递归与结构 结构可以包含结构定义上的序列作为数据成员。 struct Node{ long value; sequence<Node> children;}; • 递归与联合 一个递归的序列的必定有一个不完全的结构或联合作为它的元素类型。 union Node switch(Color){ case red: long value; case blue: struct Colors{Color c1;sequence<Node,1> child;} u_c1; case black: struct Colors{Color c2;sequence<Node,2> child;} u_c2;};
OMG接口定义语言 • 多层递归 递归可以扩展到多层。 struct TwoLevel{ string id; struct Nested{ long value; sequence<TwoLevel> children;} data;}; 这个不完全类型TwoLevel上的递归篏套在另一个结构Nested中。
OMG接口定义语言 • 互递归结构 struct Astruct{ long data; sequence<Bstruct,1> nested;//非法,Bstruct没有定义}; struct Bstruct{ long data; sequence<Astruct,1> nested;}; 以上定义是错误的,因为在IDL中,除了接口外,不允许对任何东西进行提前声明。可以用联合来实现。 enum StructType{A_TYPE,B_TYPE}; union ABunoin switch(StructType){ case A_TYPE: struct AC{ long data; sequence<Abunion,1> nested;} A_m; case B_TYPE: struct BC{long data; sequence<Abunion,1> nested;} B_m; };
OMG接口定义语言 • 4.7.9 常量定义和字面值 IDL允许使用常量定义,其句法与语义与C++一致。 const float PI=3.1415926; IDL不允许定义any类型的常量和用户定义的复杂类型。 应避免定义一些只是一些毫无价值的别名,而未给这个定义添加任何值。Const long ZERO=0; 基本类型的别名也可用来定义常量。 typedef short TempType;const TempType MAX_TEMP=35; 与C++一样,IDL完全支持字面值(Literal)。例如整型常量可以指定为十进制、八进制和十六进制形式;浮点字面值可表示为C++约定的指数和分数;字符和字符串常量支持标准C++转义字符。 const long l1=oxaB; const double d1=5.0e-10; const char c1=‘\n’; const string s1=“hello””world”;
OMG接口定义语言 • 4.7.10 常量表达式 IDL中的运算符与C++类似,但不是所有的都与C++有相同的行为。 算术运算符的语义: 算术运算不支持混合模式的算术运算,也没有显式的转换。这主要是为了简化IDL编译器的实现。 整数表达式通常被看作为unsigned long类型。除非表达式中包含一个负整数,则为long。运算结果被强制返回目标类型。如果表达式中的中间值超出了long或unsigned long的范围,或者运算结果不符合目标类型,那么它的行为就不可预测。 位运算符的语义: 为运算符用于整型表达式。将一个short或unsigned short数值移16位,或将一个long或unsigned long数值移32位都将导致不定行为。 在C++中,右移一个负数是由实现所定义的行为(符号扩展),相反,在IDL中,右移总是执行一个逻辑移位。 const long ALL_ONES=-1; //0ffffffff const long LHW_MASK=ALL_ONES<<16; //0xffff0000 const long RHW_MASK=ALL_ONES>>16; //0x0000ffff
OMG接口定义语言 • 4.8 接口和操作 访问一个接口的某一操作将引起这个ORB发送一个消息给相应的对象实现,同一地址空间,调用于一般函数的调用形式一样;不同地址空间,ORB运行时发送一个远程过程调用给这个实现。 IDL接口相当于C++的类;IDL操作相当于C++的成员函数 IDL与C++不同的地方: (1) IDL接口没有公有、私有或保护部分。接口的每一个部分都是公有的。 (2) IDL接口没有成员变量。IDL不存在成员变量的概念,甚至没有共有成员变量。成员变量用来存储状态,而对象的状态与实现有关。当然可以创建对象来存储状态,并且允许客户机控制这个状态。但客户机必须通过这个接口的对象来实现这一目的,同时,如何改变对象状态的细节是隐藏在它的接口内部。 CORBA对象相当于C++类的实例。 区别在于:CORBA对象可以在许多不同的地址空间中被实现。
OMG接口定义语言 • 4.8.1 接口语法 IDL接口形成了一个名字空间,标识符只有在他们封闭的接口的作用于内才有效,并且要唯一。可以在接口定义内篏套下列结构: (1)常量项定义 (2)类型定义 (3)异常定义 (4)属性定义 (5)操作定义 注意:不可以将一个接口定义在另一个接口内,IDL接口不支持篏套。 interface Haystack{…… typedef long Needle;…… void find(in Needle n) raises(Notfound);}; 类型Needle用在find操作的定义中,它们都定义在同一作用域。由于这个篏套的定义并没有被隐藏,因此可以通过使用作用域解析运算符::,使用在不同作用域的类型来限定一个标识符名。 interface FeedShed{…… void add(in Haystack s) ;…… boolean find(in Haystack::Needle n) raise(Haystack::NotFound); void hide(in Haystack s,in Haystack::needle n);};
OMG接口定义语言 与C++一样, Haystack::Needle 也可表示为::Haystack:Needle(前缀::表示全局变量) • 4.8.2 接口语义和对象引用 可以通过传递Haystack的参数给add操作,将一个干草堆加到储藏室中。以此说明了: (1) 接口名在它们的右边变成了类型名。 (2) 接口实例可以作为参数传递。 通过对象引用完成添加,它的语义与C++类实例指针十分相似,只是对象引用还能指向调用程序地址空间外的对象。与C++指针一样,对象引用是强类型的,对象引用的类型安全性是在编译阶段被强制要求的,与C++一致。 CORBA定义了一个特殊的空对象引用,可实现“可选项”或“没找到”这类语义。
OMG接口定义语言 • 4.8.3 接口通信模型 IDL操作和属性是定义了对象间的通信通道,在通信通道中所传递的信息类型只是参数、返回值和操作的异常信息。 使用隐含的通信对象接口有时称为合作接口(Cooperating Interfaces)。事实上,合作接口几乎总是由同一进程实现的,用这种隐含的方式将能实现两个对象之间的通信。例如,hide操作。 • 4.8.4 操作定义 一个操作定义只是作为一个接口定义的一部分出现的,包括: (1)一个返回结果的类型 (2)一个操作名 (3)零个或多个参考声明 方向属性: (1) in 表示参数是由客户发送给服务器的 (2) out 表示参数是由服务器发送给客户的 (3) inout 表示参数由客户初始化后,发送给服务器,服务器程序能够修改参数的值,所以,在操作完成时,客户通过的参数值可能已被服务器程序改变。
OMG接口定义语言 • 需要方向属性的原因: (1) 方向属性可以提高效率(双向比单向要有的时间多)。 (2) 方向属性可节省传输费用。 (3) 方向属性决定了内存的管理责任。它决定是由客户还是由服务器来负责参数的内存分配与释放。 定义风格: (1) 如果接口操作接收一个和多个参数并返回一个结果,那么结果应作为返回值。 (2)如果操作有若干个同样重要的返回值,所有值都应当作为out参数返回,并且操作的返回值的类型应为out。 (3)如果操作需要返回若干个值,但有一个是特别重要的,将这个值作为返回值,其它值作为out参数返回。 这种类型的交互主要用于迭代器操作。 boolean get_next(out ValueType value); 递增检索一个结果的集合,每次检索一个值,返回值是boolean。它只是表示什么时候这些值的集合已经到头了。 while(get_next(val)){ //process val }
OMG接口定义语言 • inout参数 使用inout参数时,接口的设计者假定,调用程序从不想保持原始值并且可以将它们重写。因此,inout参数必须遵循这条原则,即如果客户程序想保持原始值,它必须首先做一分拷贝。 inout参数唯一节省的是所需要的临时缓存空间的数量,因客户和服务器程序只需要单个内存空间来保存调用之前和调用之后的这些数据。 • 重载 在IDL接口内,操作名必须是唯一的,因此操作不可能重载。 • 匿名类型 在IDL中,参数和返回值必须使用一个已命名的类型来声明常量操作。匿名类型作为返回值及在参数声明中是非法的。 • 常量操作 在IDL中并不区分是读还是访问操作。 SomeType read_value() const; //错误,非法的静态变量应用
OMG接口定义语言 • 4.9 用户异常 IDL用户异常定义于IDL结构相似,并允许异常包含任意类型出错信息,且不限定它的数量,但不允许篏套。 异常创建一个名字空间,异常成员名在此名字空间内必须唯一。 异常是类型,但是不能用来作为用户定义类型的数据成员。 一个操作使用一个raise表达式来表示它可能出现的异常。 一个操作可能出现多种异常类型,操作必须表示它们可能出现的所有异常,对一个操作来说,发送一个raise表达式中没有被列入的用户异常是非法的。 raises表达式不能是空的。 IDL不支持异常的继承。
OMG接口定义语言 • 4.9.1 异常设计问题 (1) raises异常只用于异常的状态(不能是结果) 。 (2) 要确保异常镌带有用的信息。 (3) 要确保异常传递准确信息。 (4) 要确保异常带有完整信息。 (5) 在设计接口时,应更多的考虑调用者的需要,而不是实现者的需要。 (6) 一般不要使用返回值和参数来表示出错。 • 4.10 系统异常 IDL调用了29种系统异常,名称不同,但使用同一异常模块。 enum completion_status{ COMPLETED_YES, COMPLETED_NO, COMPLETED_MAYBE}; #define SYSEX(NAME) exception NAME{\ unsigned long minor;\ completion_status completed;\}
OMG接口定义语言 • 操作定义在它的raises表达式种不能包括系统异常。 (1) COMPLETED_YES。错误有时出现在服务器操作完成之后。也就是说,已经发生了由于错误调用所产生的任何状态变化。 操作不是幂等的话(idempotent),了解是否在服务器端完成了操作是非常重要的。两次调用与一次调用作用相同,就是幂等。X=1是幂等,x=x+1不是幂等。 (2) COMPLETED_NO。错误也可能发生在出客户机地址空间和在进服务器地址空间时出现。这就保证了目标操作并没有被调用,或者已经被调用了,但操作的两端都没有产生作用。 (3) COMPLETED_MAYBE。完成的状态不确定,客户已调用,可与服务器失去了连接,而调用仍处于连接状态。 minor数据成员用来传递有关出错代码的附加信息,可未给出含义。 • 4.11 系统异常或用户异常 服务器上的操作实现可能引发系统异常,以及在操作的raises表达式中的用户异常。 对应用程序级的出错状况定义合适的用户异常。