VS2005配置和编写第一个ICE程序

 

主要遇到的问题就是对vs2005的环境不熟悉,vs也有一年多没有碰了。在运行程序中,对工程之间的引用不知道在哪里设置,熟悉了一下才找到,在project->property->common property中设置就行了。 但先要需要引用的工程添加进来才行。

 

 

 

经过痛苦的三天奋斗,终于成功见到了“HELLO,WORLD!”,鉴于网上的文章基本每篇都有BUG,致使我们落入陷阱,浪费大好时间,所以把我的所有步骤和示例代码一一描述,但愿后来者能少走弯路。

以下步骤没多做解释是因为本人也是初学,尚未入门

一、下载
 Ice-3.2.0-VC71.msi:  http://www.zeroc.com/download/Ice/3.2/Ice-3.2.0-VC71.msi
 Ice-3.2.0-ThirdParty-VC71.msi: http://www.zeroc.com/download/Ice/3.2/Ice-3.2.0-ThirdParty-VC71.msi

二、安装
 1、Ice-3.2.0-VC71.msi 安装到c:/Ice-3.2.0-VC71
 2、Ice-3.2.0-ThirdParty-VC71.msi到C:/Ice-3.2.0-VC71/ThirdParty
 3、在OS系统环境变量中添加ICEROOT,指向c:/Ice-3.2.0-VC71
 4、在OS系统环境变量path添加“%ICEROOT%/bin;”、“%ICEROOT%/lib;”、“%ICEROOT%/ThirdParty/bin;”、“%ICEROOT%/ThirdParty/lib”;

二、设置
 1、打开VS2003,
  “工具”/“选项”/“项目”/“VC++目录”
   选择“可执行文件”: 增加“$(ICEROOT)/ThirdParty/bin”、“$(ICEROOT)/bin”
   选择“包含文件”: 增加“$(ICEROOT)/include”、“$(ICEROOT)/ThirdParty/include”
   选择“库文件”: 增加“$(ICEROOT)/ThirdParty/lib”、“$(ICEROOT)/lib”
三、示例程序
 1、新建ICEDEMO空白解决方案
 2、在方案中添加新建Visual C++项目Slice,选择Win32静态库空白项目。
 3、命令行中敲入notepad Printer.ice(必须是.ice),
 4、文件printer.ice内容如下:
注意分号:
 module demo
 {
  interface Printer
  {
   void printString(string s);
  };
 };
 保存在目录/slice下。
 5、用命令行程序编译printer.ice:slice2cpp printer.ice,生成Printer.h、Printer.cpp两个文件
 6、将pritner.h、printer.cpp加入Slice工程
注意: 7、修改Printer.cpp中的第一行“#include <printer.h>”为“#include "printer.h"”
 8、设置Slice项目属性还需要进行如下设置:
  C/C++选项卡/代码生成/运行时库/“多线程调试DLL(/MDd)”;
  C/C++选项卡/语言/启用运行时类型信息/“是(/GR)”;
 注意:MD表示RELEASE,MDD表示DEBUG
 9、新建一个Visual C++项目Server,选择Win32控制台空白项目
 10、在项目server中添加引用项目Slice
 11、在项目server中添加空白CPP文件Server.cpp:
#include "../Slice/Printer.h"
#include <Ice/Application.h>
#include <Ice/Ice.h>

using namespace std;
using namespace demo;


class PrinterI : public Printer
{
 public:
  virtual void printString(const string& s,const Ice::Current&);
};

void PrinterI::printString(const string& s, const Ice::Current&)
{
 cout << s << endl;
}

int  main(int argc, char* argv[])
{
 int status = 0;
 Ice::CommunicatorPtr ic;
 
 try
 {
  ic = Ice::initialize(argc, argv);
  Ice::ObjectAdapterPtr adapter= ic->createObjectAdapterWithEndpoints("SimplePrinterAdapter", "default -p 10000");
  Ice::ObjectPtr object = new PrinterI;
  adapter->add(object,ic->stringToIdentity("SimplePrinter"));
  //如果您按网上盛传的文章写成adapter->add(object,ICE::stringToIdentity("SimplePrinter"));您就死翘翘了
  //就这个问题缠了我3天,后来发现官方文档是ic->stringToIdentity("SimplePrinter"),总算解围了,否则还得死绕
  //本想偷懒不看英文,只看马维达的翻译或网上的文章,他们的代码与官方文档代码就这一句不同,不仔细看根本发现不了,想想懒没偷成更费一身汗。
  //话又说回来,官方文档也有陷阱,纯看文档同样死得难看。
  //坑人啊……
  adapter->activate();
  ic->waitForShutdown();
 } catch (const Ice::Exception& e)
 {
  cerr << e << endl;
  status = 1;
 } catch (const char* msg)
 {
  cerr << msg << endl;
  status = 1;
 }
 if (ic)
 {
  try
  {
   ic->destroy();
  } catch (const Ice::Exception& e)
  {
   cerr << e << endl;
   status = 1;
  }
 }
 return status;
}
 12、项目属性设置:
  C/C++/代码生成/运行时库/“多线程调试DLL(/MDd)”;
  链接器/输入/附加依赖项/“Iced.lib IceUtild.lib”;
 13、添加新建Visual C++项目Client,选择Win32控制台空白项目
 14、在项目Client中添加引用项目Slice
 15、在项目Client中添加空白CPP文件client.cpp:
#include <Ice/Ice.h>
#include "../Slice/Printer.h"

using namespace std;
using namespace demo;

int main(int argc, char * argv[])
{
 int status = 0;
 Ice::CommunicatorPtr ic;
 
 try
 {
  ic = Ice::initialize(argc, argv);
  Ice::ObjectPrx base = ic->stringToProxy("SimplePrinter:default -p 10000");
  PrinterPrx printer = PrinterPrx::checkedCast(base);
  if (!printer) throw "Invalid proxy";
  printer->printString("Hello World!");
 } catch (const Ice::Exception & ex)
 {
  cerr << ex << endl;
  status = 1;
 } catch (const char * msg)
 {
  cerr << msg << endl;
  status = 1;
 }
 if (ic) ic->destroy();
 return status;
}
 16、项目属性设置:
  C/C++/代码生成/运行时库/“多线程调试DLL(/MDd)”;
  链接器/输入/附加依赖项/“Iced.lib IceUtild.lib”;
 17、编译、连接
 18、先运行server.exe,然后运行client.exe,client.exe一晃而过,然后server.exe打印出hello world。
 19、路漫漫其修远兮,good luck

 

初读代码

这一节大部分内容整理自ICE中文手册,在这里我特别感谢马维达同志的翻译给我们的学习带来了方便。

读服务端代码

文件server.cpp. 

#include <Ice/Ice.h>
#include "../print.h"
using namespace std;
using namespace Demo;
//惯例,用后缀I 表示这个类实现一个接口
class PrinterI : public Printer {
public:
virtual void printString(const string& s, const Ice::Current&);
};
/*
打开print.h,看看PrinterI父类的定义
namespace Demo {
class Printer : virtual public Ice::Object {
public:
//纯虚函数,不能实例化
virtual void printString(const std::string&,
//第二个参数有缺省值,实现中可以不使用
const Ice::Current&= Ice::Current()) = 0;
};
};
*/
void PrinterI::printString(const string& s, const Ice::Current&)
{
cout << s << endl;
}
int  main(int argc, char* argv[])
{
//程序的退出时的状态,就是否成功执行
int status = 0;
//来包含Ice run time 的主句柄	(main handle)
Ice::CommunicatorPtr ic;
try {
//初始化Ice run time (argc和argv是run time命令参数;
//就这个例子而言,服务器不需要任何命令行参数)。
//initialize 返回一个指向Ice::Communicator对象的智能指针,
//这个指针是Ice run time 的主句柄。
ic = Ice::initialize(argc, argv);
//调用Communicator 实例上的createObjectAdapterWithEndpoints,
//创建一个对象适配器(比如:网卡就是一种适配器)。
//参数是"SimplePrinterAdapter" (适配器的名字)
//和"default -p 10000"(用缺省协议(TCP/IP),侦听端口10000 的请求。)
//显然,在应用中硬编码对象标识和端口号,是一种糟糕的做法,
//但它目前很有效;我们将在以后看到在架构上更加合理的做法。
Ice::ObjectAdapterPtr adapter
= ic->createObjectAdapterWithEndpoints(
"SimplePrinterAdapter", "default -p 10000");
//服务器端run time 已经初始化,实例化一个PrinterI 对象,
//为我们的Printer 接口创建一个servant(serv 服务+-ant人,背一下单词)。
Ice::ObjectPtr object = new PrinterI;
//我们调用适配器的add,告诉它有了一个新的servant ;
//传给add 的参数是刚才实例化的servant,再加上一个标识符。
//在这里,"SimplePrinter" 串是servant 的名字
//(如果我们有多个打印机,每个打印机都可以有不同的名字,
//更正确的说法是,都有不同的对象标识)。
adapter->add(object,
Ice::stringToIdentity("SimplePrinter"));
//调用适配器的activate 方法激活适配器
//(适配器一开始是在暂停(holding)状态创建的;
//这种做法在下面这样的情况下很有用:
//我们有多个servant,它们共享同一个适配器,
//而在所有servant实例化之前我们不想处理请求)。
//一旦适配器被激活,服务器就会开始处理来自客户的请求。
adapter->activate();
//最后,我们调用waitForShutdown。
//这个方法挂起发出调用的线程直到服务器实现终止
//——或者是通过发出一个调用关闭run time,
ic->waitForShutdown();
}
catch (const Ice::Exception& e) {
cerr << e << endl;
status = 1;
} catch (const char* msg) {
cerr << msg << endl;
status = 1;
}
if (ic) {
try {
//必须调用Communicator::destroy结束Ice run time。
//destroy 会等待任何还在运行的操作调用完成。
//此外, destroy 还会确保任何还未完成的线程都得以汇合(joined),
//并收回一些操作系统资源,比如文件描述符和内存。
//决不要让你的main 函数不调用destroy 就终止,
//否则,后果无法想象。
ic->destroy();
} catch (const Ice::Exception& e) {
cerr << e << endl;
status = 1;
}
}
return status;
}

注意,尽管以上的代码不算少,但它们对所有的服务器都是一样的。你可以把这些代码放在一个辅助类里,然后就无需再为它费心了(Ice 提供了这样的辅助类,叫作Ice::Application,参见 10.3.1 节) 。就实际的应用代码而言,服务器只有几行代码:六行代码定义PrinterI 类,再加上三2 行代码实例化一个PrinterI 对象,并向对象适配器注册它。

读客户端代码

文件client.cpp. 

#include <Ice/Ice.h>
#include "../print.h"
using namespace std;
using namespace Demo;
int main(int argc, char* argv[])
{
int status = 0;
Ice::CommunicatorPtr ic;
try {
ic = Ice::initialize(argc, argv);
//stringToProxy 返回的代理(Proxy)类型是Ice::ObjectPrx,
//这种类型位于接口和类的继承树的根部(接口的基类)。
Ice::ObjectPrx base
=ic->stringToProxy(	"SimplePrinter:default -p 10000");
//但要实际要与我们的打印机交谈,
//我们需要的是Printer 接口、不是Object 接口的代理。
//为此,需要调用PrinterPrx::checkedCast 进行向下转换(向下转型)。
//这个方法会发送一条消息给服务器,
//询问“这是Printer 接口的代理吗?”
//如果回答“是”,就会返回Printer 的一个代理;
//如果代理代表的是其他类型的接口,返回一个空代理
PrinterPrx printer = PrinterPrx::checkedCast(base);
//测试向下转型是否成功,若不成功,就抛出出错消息并终止客户。
if (!printer) 	throw "Invalid proxy";
//现在,我们在我们的地址空间里有了一个激活的代理,
//可以调用printString 方法,
//把享誉已久的 "Hello World!" 串传给它。
//服务器会在它的终端上打印这个串。
printer->printString("Hello World!");
}
catch (const Ice::Exception& ex) {
cerr << ex << endl;
status = 1;
} catch (const char* msg) {
cerr << msg << endl;
status = 1;
}
if (ic)
ic->destroy();
return status;
}

如果出现任何错误,客户会打印一条出错消息。例如,如果我们没有先启动服务器就运行客户,我们会得到:

Network.cpp:471: Ice::ConnectFailedException:
connect failed: Connection refused

(由于windows下的命令行窗口在出错后会一闪就消失,不过我们可以在client.cpp的main函数的return status;之前加上system("PAUSE");然后再在VS2003.net中把client设置为启动项目,重新编译,运行。OK,可以看到结果了。)

Slice语言

首先,请大家读ICE中文手册中的Slice语言一章。 这一部分除了model(模块),在 ICE 1.3中文手册中都有描述

图 2.1. ice网络编程示意图(服务器端和客户端采用同种编程语言C++)

ice网络编程示意图(服务器端和客户端采用同种编程语言C++)

图 2.2. ice网络编程示意图(服务器端和客户端采用不同编程语言)

ice网络编程示意图(服务器端和客户端采用不同编程语言)

基础知识

含有Slice 定义的文件必须以.ice 扩展名结尾,例如, Clock.ice就是一个有效的文件名。编译器拒绝接受其他扩展名。

Slice 支持#ifndef、#define、#endif,以及#include 预处理指令。它们的使用方式有严格的限制:你只能把#ifndef、#define,以及#endif 指令用于创建双包括(double-include)块。例如:

#ifndef _CLOCK_ICE
#define _CLOCK_ICE
// #include 文件 here...
//定义 here...
#endif _CLOCK_ICE

我们强烈建议你在所有的Slice 定义中使用双包括(double-include)块(所上),防止多次包括同一文件。

#include 指令只能出现在Slice 源文件的开头,也就是说,它们必须出现在其他所有Slice 定义的前面。此外,在使用#include 指令时,只允许使用<> 语法来指定文件名,不能使用""。例如:

 #include <File1.ice> // OK
#include "File2.ice" // 不支持!

你不能把这些预处理指令用于其他目的,也不能使用其他的C++ 预处理指令 (比如用/ 字符来连接行、token 粘贴,以及宏展开,等等)。

在Slice 定义里,既可以使用C 的、也可以使用C++ 的注释风格:

Slice 关键字必须小写。例如, class 和dictionary 都是关键字,必须按照所示方式拼写。这个规则有两个例外:Object 和LocalObject 也是关键字,必须按照所示方式让首字母大写。

标识符以一个字母起头,后面可以跟任意数目的字母或数字。Slice 标识符被限制在ASCII 字符范围内,不能包含非英语字母,与C++ 标识符不同, Slice 标识符不能有下划线。这种限制初看上去显得很苛刻,但却是必要的:保留下划线,各种语言映射就获得了一个名字空间,不会与合法的Slice 标识符发生冲突。于是,这个名字空间可用于存放从Slice 标识符派生的原生语言标识符,而不用担心其他合法的Slice 标识符会碰巧与之相同,从而发生冲突 。

标识符(变量名等等)是大小写不敏感的,但大小写的拼写方式必须保持一致(看了后面的话,再理解一下)。例如,在一个作用域内, TimeOfDay 和TIMEOFDAY 被认为是同一个标识符。但是,Slice 要求你保持大小写的一致性。在你引入了一个标识符之后,你必须始终一致地拼写它的大写和小写字母;否则,编译器就会将其视为非法而加以拒绝。这条规则之所以存在,是要让Slice 既能映射到忽略标识符大小写的语言,又能映射到把大小写不同的标识符当作不同标识符的语言。(可以这样理解,变量名区分大小写,并且不可以是相同的单词)

是关键字的标识符:你可以定义在一种或多种实现语言中是关键字的Slice 标识符。例如,switch是完全合法的Slice标识符,但也是C++和Java的关键字。语言映射定义了一些规则来处理这样的标识符。要解决这个问题,通常要用一个前缀来使映射后的标识符不再是关键字。例如, Slice 标识符switch 被映射到C++ 的_cpp_switch ,以及Java 的_switch。对关键字进行处理的规则可能会产生难以阅读的源码。像native、throw,或export 这样的标识符会与C++ 或Java(或两者)的关键字发生冲突。为了让你和别人生活得更轻松一点,你应该避免使用是实现语言的关键字的Slice 标识符。要记住,以后Ice 可能会增加除C++ 和Java 以外的语言映射。尽管期望你总结出所有流行的编程语言的所有关键字并不合理,你至少应该尽量避免使用常用的关键字。使用像self、import,以及while 这样的标识符肯定不是好主意。

转义的标识符:在关键字的前面加上一个反斜线,你可以把Slice 关键字用作标识符,例如:

struct dictionary { // 错误!
// ...
};
struct /dictionary { // OK
// ...
};

反斜线会改变关键字通常的含义;在前面的例子中, /dictionary 被当作标识符dictionary。转义机制之所以存在,是要让我们在以后能够在Slice 中增加关键字,同时尽量减少对已有规范的影响:如果某个已经存在的规范碰巧使用了新引入的关键字,你只需在新关键字前加上反斜线,就能够修正该规范。注意,从风格上说,你应该避免用Slice 关键字做标识符(即使反斜线转义允许你这么做)。

保留的标识符:Slice 为Ice 实现保留了标识符Ice 及以Ice (任何大小写方式)起头的所有标识符。例如,如果你试图定义一个名为Icecream 的类型, Slice 编译器会发出错误警告3。以下面任何一种后缀结尾的Slice 标识符也是保留的:Helper、Holder、Prx,以及Ptr。Java 和C++ 语言映射使用了这些后缀,保留它们是为了防止在生成的代码中发生冲突。

(注:ICE 1.3的中文手册上没有“模块”这一部分)模块来组织一组相关的语句是为了解决名字冲突。模块可以包含所有合法的Slice语句和子模块。你可以用一些不常用的词来给最外层的模块命名,比如公司名、产品名等等。

module ZeroC {
module Client {
// Definitions here...
};
module Server {
// Definitions here...
};
};

Slice要求所有的定义都是模块的一部分,比如,下面的语句就是非法的。

interface I { // 错误:全局空间中只可以有模块
// ...
};

多个文件可以共享同一个模块,比如:

module ZeroC {
// Definitions here...
};
//另一个文件中 :
module ZeroC { // OK, reopened module
// More definitions here...
};

把一个大的模块放到几个文件中去可以方便编译(你只需重新编译被修改的文件,而没有必要编译整个模块)。

模块将映射的语言中的相应结构,比如 C++, C#, 和 Visual Basic, Slice的modules被映射为namespaces;java中被映射为package.

除了少数与特定的程序语言相关的调用之外,ice的绝大部分API(应用程序接口)都是用Slice来定义的 。这样做的好处是可以用一个ICE API定义文件来支持所有的程序语言。

[注意]
注意

为了保证代码的简洁,以后文章中提及的Slice定义没有写出包含的模块,你要假定该语句是在一个模块中。

表 2.1. Slice的数据类型

类型
取值范围
大小(单位:bit)

bool
false or true
≥ 1

byte
-128-127或0-255
≥ 8

short
2-15至215-1
≥ 16

int
2-31至231-1
≥ 32

long
2-63至263-1
≥ 64

float
IEEE的单精度
≥ 32 bits

double
IEEE的双精度
≥ 64 bits

string
所有Unicode 字符,除了所有位为零的字符
变长

用户定义的类型
  • 枚举:enum Fruit { Apple, Pear, Orange };

    这个定义引入了一种名为Fruit 的类型,这是一种拥有自己权利的新类型。关于怎样把顺序值(ordinal values)赋给枚举符的问题, Slice 没有作出定义。例如,你不能假定,在各种实现语言中,枚举符Orange 的值都是2。Slice 保证枚举符的顺序值会从左至右递增,所以在所有实现语言中,Apple 都比Pear 要小。与C++ 不同, Slice 不允许你控制枚举符的顺序值(因为许多实现语言不支持这种特性):

    enum Fruit { Apple = 0, Pear = 7, Orange = 2 }; // 出错
        

    在实践中,只要你不在地址空间之间传送枚举符的顺序值,你就不用管枚举符使用的值是多少。例如,发送值0 给服务器来表示Apple 可能会造成问题,因为服务器可能没有用0 表示Apple。相反,你应该就发送值Apple 本身。如果在接收方的地址空间中, Apple 是用另外的顺序值表示的, Ice run time 会适当地翻译这个值。

    与在C++ 里一样, Slice 枚举符也会进入围绕它的名字空间,所以下面的定义是非法的:

    enum Fruit { Apple, Pear, Orange };
        enum ComputerBrands { Apple, IBM, Sun, HP }; // Apple已经被定义!
        

    Slice 不允许定义空的枚举。

  • 结构

    Slice 支持含有一个或多个有名称的成员的结构,这些成员可以具有任意类型,包括用户定义的复杂类型。例如:

    struct TimeOfDay {
        short hour; // 0 - 23
        short minute; // 0 - 59
        short second; // 0 - 59
        };
        
    与在 C++ 里一样,这个定义引入了一种叫作TimeOfDay 的新类型。结构定义会形成名字空间,所以结构成员的名字只需在围绕它们的结构里是唯一的。在结构内部,只能出现数据成员定义,这些定义必须使用有名字的类型。例如,你不可能在结构内定义结构:
    struct TwoPoints {
        struct Point { //错误!
        short x;
        short y;
        };
        Point coord1;
        Point coord2;
        };
        
    这个规则大体上适用于Slice:类型定义不能嵌套(除了模块支持嵌套)。其原因是,对于某些目标语言而言,嵌套的类型定义可能会难以实现,而且,即使能够实现,也会极大地使作用域解析规则复杂化。对于像Slice 这样的规范语言而言,嵌套的类型定义并无必要——你总能以下面的方式编写上面的定义(这种方式在风格上也更加整洁):
    struct Point {
        short x;
        short y;
        };
        struct TwoPoints { // Legal (and cleaner!)
        Point coord1;
        Point coord2;
        }
        
  • 序列

    序列是变长的元素向量:

    sequence<Fruit> FruitPlatter;
        

    序列可以是空的——也就是说,它可以不包含元素;它也可以持有任意数量的元素,直到达到你的平台的内存限制。

    序列包含的元素自身也可以是序列。这种设计使得你能够创建列表的列表:

    sequence<FruitPlatter> FruitBanquet;
        

    序列可用于构建许多种collection,比如向量、列表、队列、集合、包(bag),或是树(次序是否重要要由应用决定;如果无视次序,序列充当的就是集合和包)。

    序列的一种特别的用法已经成了惯用手法,即用序列来表示可选的值。例如,我们可能拥有一个Part 结构,用于记录小汽车的零件的详细资料。这个结构可以记录这样的资料:零件名称、描述、重量、价格,以及其他详细资料。 备件通常都有序列号,我们用一个long 值表示。但有些零件,比如常用的螺丝钉,常常没有序列号,那么我们在螺丝钉的序列号字段里要放进什么内容?要处理这种情况,有这样一些选择:

    • 用一个标记值,比如零,来指示“没有序列号”的情况。

      这种方法是可行的,只要确实有标记值可用。尽管看起来不大可能有人把零用作零件的序列号,这并非是不可能的。而且,对于其他的值,比如温度值,在其类型的范围中的所有值都可能是合法的,因而没有标记值可用。

    • 把序列号的类型从long 变成string。

      串自己有内建的标记值,也就是空串,所以我们可以用空串来指示.“没有序列号”的情况。这也是可行的,但却会让大多数人感到不快:我们不应该为了得到一个标记值,而把某种事物自然的数据类型变成string

    • 增加一个指示符来指示序列号的内容是否有效.

      struct Part {
              string name;
              string description;
              // ...
              bool serialIsValid; // true if part has serial number
              long serialNumber;
              };
              

      对于大多数人而言,这也让人讨厌,而且最终肯定会让你遇到麻烦:迟早会有程序员忘记在使用序列号之前检查它是否有效,从而带来灾难性的后果。

    • 用序列来建立可选字段

      这种技术使用了下面的惯用手法:

      sequence<long> SerialOpt;
              struct Part {
              string name;
              string description;
              // ...
              SerialOpt serialNumber; // optional: zero or one element
              };
              

      按照惯例, Opt 后缀表示这个序列是用来建立可选值的。如果序列是空的,值显然就不在那里;如果它含有一个元素,这个元素就是那个值。这种方案明显的缺点是,有人可能会把不止一个元素放入序列。为可选值增加一个专用的Slice 成分可以纠正这个问题。但可选值并非那么常用,不值得为它增加一种专门的语言特性(我们将看到,你还可以用类层次来建立可选字段)。

  • 词典

    词典是从键类型到值类型的映射。例如:

    struct Employee {
        long number;
        string firstName;
        string lastName;
        };
        dictionary<long, Employee> EmployeeMap;
        

    这个定义创建一种叫作EmployeeMap 的词典,把雇员号映射到含有雇员详细资料的结构。你可以自行决定键类型(在这个例子中是long 类型的雇员号)是否是值类型(在这个例子中是Employee 结构)的一部分——就Slice 而言,你无需让键成为值的一部分。

    词典可用于实现稀疏数组,或是具有非整数键类型的任何用于查找的数据结构。尽管含有键-值对的结构的序列可用于创建同样的事物,词典要更为适宜:

    • 词典明确地表达了设计者的意图,也就是,提供从值的域(domain)到值的范围(range)的映射(含有键-值对的结构的序列没有如此明确地表达同样的意图)。

    • 在编程语言一级,序列被实现成向量(也可能是列表),也就是说,序列不大适用于内容稀疏的域,而且要定位具有特定值的元素,需要进行线性查找。而词典被实现成支持高效查找的数据结构(通常是哈希表或红黑树),其平均查找时间是O(log n),或者更好。词典的键类型无需为整型。例如,我们可以用下面的定义来翻译一周每一天的名称:

      dictionary<string, string> WeekdaysEnglishToGerman;
              

      服务器实现可以用键-值对Monday–Montag、Tuesday–Dienstag,等等,对这个映射表进行初始化。

    • 词典的值类型可以是用户定义的任何类型。但词典的键类型只能是以下类型之一:

      • 整型(byte、short、int、long、bool,以及枚举类型)

      • string

      • 元素类型为整型或string 的序列

      • 数据成员的类型只有整型或string 的结构

      复杂的嵌套类型,比如嵌套的结构或词典,以及浮点类型(float和double),不能用作键类型。之所以不允许使用复杂的嵌套类型,是因为这会使词典的语言映射复杂化;不允许使用浮点类型,是因为浮点值在跨越机器界线时,其表示会发生变化,有可能导致成问题的相等语义。

  • 常量定义与直接量

    Slice 允许你定义常量。常量定义的类型必须是以下类型中的一种:

    • 整型(bool、byte、short、int、long,或枚举类型)

    • float 或double

    • string

    下面有一些例子:

    const bool AppendByDefault = true;
        const byte LowerNibble = 0x0f;
        const string Advice = "Don't Panic!";
        const short TheAnswer = 42;
        const double PI = 3.1416;
        enum Fruit { Apple, Pear, Orange };
        const Fruit FavoriteFruit = Pear;
        

    直接量(literals)的语法与C++ 和Java 的一样(有一些小的例外):

    • 布尔常量只能用关键字false和true初始化(你不能用0和1来表示false和true)。

    • 和C++ 一样,你可以用十进制、八进制,或十六进制方式来指定整数直接量。例如:

      const byte TheAnswer = 42;
              const byte TheAnswerInOctal = 052;
              const byte TheAnswerInHex = 0x2A; // or 0x2a
              

      [注意]
      注意

      如果你把byte 解释成数字、而不是位模式,你在不同的语言里可能会得到不同的结果。例如,在C++ 里, byte 映射到char,取决于目标平台, char 可能是有符号的,也可能是无符号的。

      [注意]
      注意

      用于指示长常量和无符号常量的后缀(C++ 使用的l、L、u、U)是非法的:

      const long Wrong = 0u; // Syntax error
              const long WrongToo = 1000000L; // Syntax error
              
      • 整数直接量的值必须落在其常量类型的范围内,否则编译器就会发出诊断消息。

      • 浮点直接量使用的是C++语法,除了你不能用l或L后缀来表示扩展的浮点常量;但是, f 和F 是合法的(但会被忽略)。下面是一些例子:

        const float P1 = -3.14f; // Integer & fraction, with suffix
                    const float P2 = +3.1e-3; // Integer, fraction, and exponent
                    const float P3 = .1; // Fraction part only
                    const float P4 = 1.; // Integer part only
                    const float P5 = .9E5; // Fraction part and exponent
                    const float P6 = 5e2; // Integer part and exponent
                    
      • 浮点直接量必须落在其常量类型(float 或double)的范围内;否则编译器会发出诊断警告。

      • 串直接量支持与C++ 相同的转义序列。下面是一些例子:

        const string AnOrdinaryString = "Hello World!";
                    const string DoubleQuote = "/"";
                    const string TwoSingleQuotes = "'/'"; // ' and /' are OK
                    const string Newline = "/n";
                    const string CarriageReturn = "/r";
                    const string HorizontalTab = "/t";
                    const string VerticalTab = "/v";
                    const string FormFeed = "/f";
                    const string Alert = "/a";
                    const string Backspace = "/b";
                    const string QuestionMark = "/?";
                    const string Backslash = "//";
                    70 Slice 语言
                    const string OctalEscape = "/007"; // Same as /a
                    const string HexEscape = "/x07"; // Ditto
                    const string UniversalCharName = "/u03A9"; // Greek Omega
                    和在 C++ 里一样,相邻的串直接量会连接起来:
                    const string MSG1 = "Hello World!";
                    const string MSG2 = "Hello" " " "World!"; // Same message
                    /*
                    * Escape sequences are processed before concatenation,
                    * so the string below contains two characters,
                    * '/xa' and 'c'.
                    */
                    const string S = "/xa" "c";
                    

        [注意]
        注意

        Slice 没有null 串的概念

        const string nullString = 0; // Illegal!
                    
        null 串在Slice 里根本不存在,因此,在Ice 平台的任何地方它都不能用作合法的串值。这一决定的原因是, null 串在许多编程语言里不存在
接口、操作,以及异常

见手册........抄书好累.........

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值