客户端的Slice-to-C++映射
1. 引言
其映射定义:怎样把Slice数据类型翻译成C++类型,客户怎样调用操作、传递参数、处理错误。
C++映射完全是线程安全的。例如,类的引用机制针对并行访问进行了互锁,所以如果有许多线程共享一个类实例,引用计数不会被破坏。
2. 标识符的映射
Slice标识符映射到相同的C++标识符。
3. 模块的映射
Slice模块映射到C++名字空间。映射会保持Slice定义的嵌套层次。
4. Ice名字空间
Ice run time的所有API都嵌在ICE名字空间中。
5. 简单内建类型的映射
Ice::Byte是unsigned char的类型定义。因此字节值的范围在0…255内。
6. 用户定义类型的映射
Slice支持用户定义的类型:枚举,结构,序列,以及词典。Slice的词典会映射到C++的std::map。
7. 常量的映射
8. 异常的映射
9. 运行时异常的映射
ConnectTimeoutException可以作为下面的任何一种异常类型来处理:
a.Ice::Exception 这是挣个继承树的根。
b.Ice::UserException 这是所有用户异常的根。
c.Ice::LocalException 这是所有运行时异常的根异常。
c.Ice::TimeoutException 这既是操作调用超时,也是连接建立超时的基异常。
d.Ice::ConnectaTimeoutException 如果初次尝试建立与服务器的连接时超时,就会引发这个异常。
10. 接口的映射
要调用一个远地操作,你要调用一个本地类实例的成员函数,这个实例代表的是远地的对象。
10.1 代理类和代理句柄
客户端应用永远不会直接操纵代理类。事实上,你不允许直接实例化代理类。它总是由ice run time替客户实例化。
10.2 代理句柄上的方法
复制构造函数负责确保能根据另一个代理句柄构造出一个代理句柄。在内部,这会使代理的引用计数加一;析构器会使引用计数减一;一旦计数降到零,就释放底层的代理类实例,避免内存泄露。
宽化赋值与窄化赋值:你总是可以把派生类型赋值给基类型,但反过来不行。
11. 操作映射
11.1普通操作
因为idempotent和nonmutating影响的是调用分派(call dispatch)而不是接口的某个方面,因此在映射到C++时,有些方法是一样的,例如:
11.2 传递参数
a. in参数
参数或者通过值或者通过const引用传递,调用肯定不会改变参数的值。
b.out参数
C++映射通过引用传递输出参数。调用者简单的把变量传给操作;一旦操作完成,服务器就会设置这些变量的值。
12. 异常处理
13 类的映射
Slice类映射到同名的C++类。对于每一个Slice数据成员,生产的类都一个public数据成员与之对应,而对于每一个操作都有一个对应的虚函数。
13.1 从Ice::Object继承
与接口一样,类也隐式的继承自一个共同的基类Ice::Object。
13.2 类的数据成员
对于Slice定义中的每一个数据成员,生成的类都有一个对应的public数据成员。
13.3 类的操作
在生成的类中,类上的操作被映射成纯虚的成员函数。这意味着,如果类含有操作,你必须从生成的类中派生出一个类,并在这个类中提供操作的实现。
13.4 类工厂
13.5 用于类的智能指针
Slice编译器会为每种类型生成智能指针。对于Slice类<class-name>,编译器会生成叫做<class-name>Ptr的C++智能指针。
服务端的Slice-to-C++
1. 服务端main函数
Ice run time 的主要进入点是由本地接口Ice::Communicator表示,在程序的开始必须首先调用Ice::Initialize,对Ice run time 进行初始化;Ice::Communicator返回一个智能指针,指向一个Ice::Communicator实例。
2. Ice::Application
2.1在客户端使用Ice::Application
从Ice::Application中派生一个子类,将客户端代码写进run方法中就可以。
2.2 捕捉信号
成员方法:
shutdownOnInterrupt:用于关闭应用程序,这是缺省行为。
ignoreInterrupt: 这个函数是信号被忽略
holdInterrupt: 这个函数临时阻塞信号传递。
releaseInterrupt: 这个函数使信号传递回复成先前的安排。
interrupted: 如果此前是信号造成通信器的关闭,这函数返回true,否则返回false。
2. Ice::Service类
使用Ice::Service类的Ice应用程序至少要定义一个子类,并重新定义start成员函数。应用的main函数必须实例化这个子类,调用其main成员函数,把程序的参数向量作为参数传给他。
Ice::Service虚成员函数
Ice::CommunicatorPtr initializeCommunicator():初始化通信器
void interrupt(): 信号处理器调用它来表示收到了信号
void shutdown(): 让服务器开始关闭的进程。
bool stop(): 允许子类在终止之前进行处理。
void waitForShutdown(): 无限期的等待服务关闭。
Ice::Service非虚成员函数
void disableInterrupt(): 禁用Ice::Service的信号处理行为。
void enableInterrupt(): 启用Ice::Service的信号处理行为。
static Service * instance(): 返回Ice::Service的单体实例。
3. 接口的映射
服务器端的接口映射为Ice run time提供了一个向上调用(up-call)API:通过在servant类中实现虚函数,你提供的挂钩可以把控制线程从服务器的Ice run time引到你的应用代码中。
3.1 骨架类
在服务端,接口映射到骨架类。对于相应接口的每一个操作,骨架类都有一个对应的纯虚方法。骨架类是抽象基类,因为他的成员是纯虚函数。
4. Servant类
在定义servant类时总是使用虚继承是个好主意。使用虚继承并无害处,同时,如果在开发的过程中给接口层次增加多继承,无需回去给你的所有servant类增加virtual关键字。
普通的,idempotent以及nonmutating操作:只有nonmutating操作会映射成const成员函数;普通操作和nt操作会映射成平常的函数。
5. 参数传递
对于一个Slice操作的每一个参数,C++映射都会为骨架中对应的虚成员函数生成一个对应的参数。
服务器端的参数传递所遵循的规则和客户端一样:
a. in参数通过值或者const引用传递
b. out参数通过应用传递。
c. 返回值通过值传递。
6. 引发异常
要从操作中实现抛出异常,你只需要实例化异常,初始化,然后抛出它。
7. 对象体现
7.1 要提供Ice对象的实现,以servant类举例,必须采取遵循以下步骤:
a. 实例化servant类
b. 为这个servant所体现的Ice对象创建标识
c. 向Ice run time告知这个servant的存在
d. 把这个对象的代理传给客户,以让客户访问它。
e.
7.2 以servant为例,智能指针定义:
a. 当有新的servant被实例化操作时,它的引用计数被初始化成0.
b. 把servant的地址赋给servant,智能指针会使servant的引用计数增加到1。
c. 在调用add、把servant智能指针传给对象适配器操作时,对象适配器会在内部保留该句柄的一个副本。这时会使servant的引用计数增加到2.
d. 当activitesServant返回时,servant变量的析构器会使servant的引用计数减到1.
7.3 用UUID做标识
Ice对象模型假定对象标识是全局唯一的。确保这样的唯一性的一种途径是使用UUID(Universally Unique Identifiers)做标识。
8. 章节总结
对于客户和服务器而言,Slice数据类型的映射是一样的,与客户端相比,服务器端映射只额外增加了几种机制:一个用于初始化和结束run time的小API,再加上一些规则,用于处理怎样从骨架派生servant类,以及怎样向服务器run time 注册servant。