Symbian Security Studio

About symbian software programming ,security analysis and other things about symbian.

Thursday, August 30, 2007

Symbian的descriptor



不做Symbian已经有些日子了,不过看到好的文章,总是心痒痒的,转来给大家看看,如果觉得好的就吆喝几下,鼓励大家共享这方面的心得。

这是一篇关于descriptor的文章,如果不知道symbian或者descriptor的,请关闭这个tab,descriptor相关的文章很多,特别是Nokia自己出的文档就有详细的描述,不过这种系统性质的只能是作为了解用,而在实际碰到问题时候还是要看一些经验教训类的文章才行,下面的文章就是关于实用方面的,我觉得还不错,就是稍微长了点,如果对你有用就看看,推荐给入门不久的xdjm们:

一个小提示:对于VS不能显示unicode string的问题,大家可以参考以前的autoexp的文章,或者用指针加",su"(e.g. "strPtr,su")来查看descriptor内容。

原文:Symbian中的descriptor
作者:ilovevc


好久没有写blog了,最近一直在symbian上开发,对symbian中的字符串进行了一点总结,格式没有编排,而且比较混乱,实在懒得整理了。

TDesC本身只包含两个成员变量,type和length。
type用于避免虚拟函数,length表示对象的长度。这样也就决定了symbian的descriptor是一个封闭的系统,即开发者无法再从系统提供的Descriptor class通过继承以扩展其功能。

主要的成员函数有:
TInt Length() const;
const TUint8 *Ptr() const; 返回一个不可修改的指针。

TDes由于涉及到修改,因此增加了一个MaxLength,返回能够容纳的最大长度。
麻烦的是,它并没有提供一个函数返回一个可写的指针。WPtr是保护的。

因此,TBuf由于从它间接派生,也有一个iMaxLength成员。
而TBufC,HBufC就没有。

这两个类都是抽象类。但是可惜的是,symbian源代码中没有将编译器自动产生的拷贝构造函数去掉。
因此下面的代码和合法的。
extern TPtr ptr;
TDesC des = ptr;


TPtrC从TDesC派生,多了一个类似const char ×的成员变量指针
同样,TPtr从TDes派生,也多了一个char *成员变量Ptr指针
注意;TPtrC和TPtr本身并不“拥有”内存,而只是指向一个已经分配好的内存。而且TPtrC/TPtr的析构函数理所当然也不free它

指向的内存了。
另外,当调用TPtr/TDes提供的函数修改内存,当然实际的内存就修改了。
另外,甚至TPtr本身的这个指针成员变量iPtr可能并不一定就指向实际内存的首地址,唯一保证得到首地址的方法就是调用

TDesC中的Ptr()函数。例如如果你通过调用TBufC::Des()得到的TPtr对象实际它的iPtr实际指向前4个字节位置(在Windows

Simulator中)。


如果TPtr是通过TBufC/HBufC的Des()函数得来,那么调用TDes里面的函数影响到iLength的时候,TBufC/HBufC中的iLength也改

变了。

如果通过构造函数得到TPtr,因为TPtr指向的内存可以是任何可写的数据,因此它只影响自己的长度。
这样,就实际上有两种类型的TPtr,一种当长度改变的时候,需要同时设置TBufC/HBufC的长度,另外一种不要。
估计是通过TDesC里面的iType来区分。



TBufC间接从TDesC派生,有一个类似const char[]的成员变量。基本上没有什么存在的必要。
毕竟我们在栈中分配内存,基本上就是为了“可写”的使用它,而TDesC没有提供任何“写”函数。
幸亏它还提供了Des()函数获得一个可写的TPtr/TDes对象.不过还是麻烦了一些。

TBuf间接从TDes派生,

kO,中Z]垠(网业56
有一个类似char[]的成员变量。
但是它的父类TDes也没有方法返回一个可写的指针。有一个WPtr()还是Protected的。sigh!
HBufC从TDesC派生,搞不清为什么没有一个HBuf类。
提供了一个Des()函数返回一个可写的TPtr对象。


另外,由于Descriptor既可以表示字符,又可以表示binary数据。因此默认内存分配的时候并不保留一个额外的''''\0''''位置。
例如TBuf<3> buf; 那么这个buf的大小就确实只是3。
TLitC就是特别为string准备的,因此使用sizeof操作符,char[]中已经包含了''''\0''''字符。但是cast回的TDesC的长度可能仍然

是strlen


_LIT(KStr, "hello");
TDesC desc = KStr;
int len = desc.Length(); //check the length

HBufC8 * hbuf1 = HBufC8::NewLC(1);
HBufC8 * hbuf2 = HBufC8::NewLC(4);
HBufC8 * hbuf3 = HBufC8::NewLC(8);

TDes des1 = hBuf1.Des();
des1 = hBuf2.Des();
des2 = hBuf3.Des();


CleanupStack::PopAndDestroy(3); //HBufC8



TDes / TDesC 是抽象,%6Gf.KM2Ks;2TPtr/TPtrC是辅助,TBuf/HBufC/TBufC这些才是具体数据保存的地方。



在VC中检查symbian的内存泄漏:
F5 直到程序Panic,然后在output window中找到类似Thread panic ALLOC: f90b380,找到对应的内存地址。
重启emulator,检查该地址对应的内容。可以在退出的时候检查,也可以在启动的时候设置“Data"断点。





Symbian中的Descripor不要互相赋值。例如
TPtr ptr = buf.Ptr(0);
ptr = buf.Ptr(1); //ptr可能并没有被修改,$$vC8$p网r*导致不期望的行为。
这个地方需要极度留心,所有可写的Descriptor,例如TBufC,TBuf,TPtr,HBufc,symbian都重载了operator=()函数,实际

的语义并不是你可能想要的类似于赋值语句,而是将右值的内容拷贝到左值Descriptor所指的内存。

例如:
TBuf8<5> buf1(_L8("hello"));
TBufC8<5> buf2(_L8("world"));
TPtr8 ptr( (TUint8*)buf1.Ptr(), 5, 5);
ptr = buf2.Des();

最后一句,可能原来的意思是使ptr指向buf2,但是实际上变为了buffer之间的Copy赋值语句,即将buf2的内容拷贝到ptr所指

的内存中,运行以后buf1和buf2的内容都为world。

同时,TPtr的拷贝构造函数和等于asignment操作符的含义不同。
extern TPtr b;
TPtr a = b; //调用a的拷贝构造函数,a,b指向同一块buffer

TPtr a; //编译错误,没有默认构造函数。必须先分配内存
a = b; //将b中的内容copy至a所指的内存中。

symbian中的字符串基本上可以以两种眼光来看待:

const和非const,即只读和可写的。
unicode和非unicode,即8bit和16bit字符。

因此假设一个类TPtr8,则后面加C,TPtrC8表示const版本,8变为16表示unicode版本。
TPtr被在unicode定义的情况下被typedef成TPtr16,否则为TPtr8。

symbian的字符串总是使用unicode。

symbian使用descriptor来表示一段数据,该数据既可以在内存中,也可能在只读的ROM中,既可以表示字符串,也可以表示二

进制数据,因此不依赖''''\0''''字符,甚至根本就没有''''\0''''字符。

所有descriptor的基类是TDesC,该类实际上为一个抽象类,在symbian的头文件中,该对象的所有构造函数都为protected。因

此你无法创建TDesC,也永远不会(应该说你不应该)出现如下的语句: TDesc des。
但是symbian可能疏忽了,也比较程序容易出错的是,它没有屏蔽C++自动产生的拷贝构造函数。
因此下面的语句没有任何编译警告:
extern TPtrC ptr; //TPtrC是TDesC的派生类
TDesC des = ptr; //调用默认的拷贝构造函数,

中yVv件HKJ)]
造成slice切割
foo(des); //调用一个函数,需要const TDesC&参数,由于数据已被切割,结果无法预料。

它主要用于函数参数传递,作为函数参数时,不是const TDesC& des,就是const TDesC× des,它表示一段只读的数据,有数

据成员iLength表示该段内存的长度。

以下是TDesC的数据成员定义
private:
unsigned int iLength:28;
unsigned int iType:4;

由此可知,Descriptor能代表的最大内存长度为0x0FFFFFFF,剩下的4bit,iType表示派生类的类型。

其中,TDesC还有一个重要的成员函数:
public:
const TUint *Ptr() const; 返回一个指向该段数据的指针。通常来说,这个应该定义成一个纯虚拟函数,由派生类实现。

symbian为了避免虚函数带来的开销,使用iType来表示派生类的具体类型。不难想像,TDesC根据iType就可以得到具体类的数

据头地址。

TDesC所有的成员函数都是const修饰,即任何函数都不会修改this指针。

TDesC实际上是一个typedef,对应unicode的版本,它定义为TDesC16,非unicode则为TDesC8,如果表示二进制数据时,可以显

示使用TDesC8。

TDes表示一段可写的数据区域,前面说了,所有的descriptor都从TDesC派生,TDes也不例外。增加了一个数据成员,
protected:
TInt iMaxLength;

表示该段区域的最大有效长度,由于TDes不是const了,因此可写。iMaxLength可以防止越界内存访问。
该类也是一个抽象类,即无法直接构造。

一个基本的原则就是,只读的Descriptor总是直接或间接地从TDesC派生,而不会从TDes派生。
可写的Descriptor总是直接或间接地从TDes派生,当然由于TDes的基类是TDesC,因此也从TDesC派生。

TPtrC表示一段只读的内存,从TDesC派生。由一个指向只读数据的指针和该段数据的长度初始化。
增加了一个成员变量: protected: const TUint *iPtr;
例如:
const char * psz = "hello";
TPtrC8 ptr(psz, 5); //ptr指向psz,长度为5。
TPtrC8 ptr(psz + 1, 2); //ptr指向 ‘e'''' 到 ''''l'''',长度为二。

TPtr表示一段可写的内存,从TDes派生。由一个指向可写数据的指针和该段数据的长度初始化。
增加了一个成员变量:protected: TUint *iPtr;

例子:
char psz[] = "hello";
TPtr8 ptr(psz, 5); //ptr指向psz,长度为5。
TPtr8 ptr(psz + 1, 2); //ptr指向 ‘e'''' 到 ''''l'''',长度为二。


TPtr并没有提供额外的函数来操纵、修改数据,都由基类TDes提供。
例如;
ptr.LowerCase(); //小写转换

TPtr和TPtrC并不真正“拥有”数据,它们只是“指向”该段数据,因此析构函数自然也不free掉这段内存。



上面的 char psz[] = "hello";在symbian中,一般使用TBuf来定义。
TBuf是一个模板类,唯一的模板参数是拥有数据的大小。简化后,定义如下:

template class TBuf8 : public TDes
{
protected:
TUint8 iBuf[S];
};

因此如果我们在栈中定义上面的“hello"字符串,[oGxn_V$-JV-供S"x_M就可以如下:

//由于TBuf没有提供适合const char×的合适的构造函数,sEQ国Cc3sq提因此我们需要将const char ×
//强制转换成const unsigned char *。sigh!
TBuf8<5> buf((const TUInt8*) "hello");

然后我们利用TDes提供的函数可以:
buf[0] = ''''H''''; // "Hello"
buf.LowerCase()等 // "hello"

还有一个对应的TBufC,提供只读的版本,从TDesC派生,照我看,基本上是照顾TBuf有个匹配的const class,用处不大。
nokia series 2.0 sdk里面的几十个examples,没有一个使用它。我也不想用。:)
就好像你使用下面的语句:
const char hello[];
你在栈中分配一块内存,类型为const char。除非你一定义就初始化它,否则使用const修饰只是自找麻烦。


最后一个就是在堆中分配内存的class,例如在c中我们一般如下:
char * psz = (char *)malloc(20);
free(psz);
在symbian中,提供了一个HBufC类,注意最后带C的总是直接/间接从TDesC派生,而和TDes没有任何关系。
那么启不是只能使用基类TDesC中提供的只读的成员函数?
下面是HBufC的定义:

class HBufC8 : public TDesC8
{
public:
IMPORT_C static HBufC8 *New(TInt aMaxLength);
IMPORT_C TPtr8 Des();
private:
TText8 iBuf[1];
};

注意iBuf[1]这种写法在c中经常用来定义可变大小结构。
前面提到HBufC8间接从TDesC8派生,

ANVEg.YiZ育G&+4国h
因此只能使用TDesC提供的“只读”成员函数,幸亏它还提供了一个Des()函数返回一个TPtr8
指针,由于TPtr由TDes继承,因此我们通过TPtr就可以修改它的内容了。sigh!

期待HBuf class?对不起,symbian没有提供。到此为止了。
例子:
HBufC8 * pbuf = HBufC8::New(100); //等价于 const char * psz = (const char *)malloc(100);
delete pbuf; //等价于 free(psz);

注意上面的const char *,sigh!分配内存后返回的指针用const修饰,那我分配一块只读的内存干什么?

因此如果要干点正事,必须调用HBufC的Des()函数得到一个可写的TPtr对象。

HBufC8 * pbuf = HBufC8::New(100); //等价于 const char * psz = (const char *)malloc(100);
TPtr8 ptr = pbuf->Des(); //类似于 char * p = (char *) psz; 好了,rPA9A(64网"md^A_6业总算可写了。
ptr[0] = ''''H''''; // *p = ''''H''''; 调用TDes中的函数
ptr.SetLength(1); // p[1] = ''''\0''''; 调用TDes中的函数
delete pbuf; //等价于 free(psz);

不知道基于什么理由symbian定义了一个HBufC,而不是HBuf。估计是为了省一个TDes中的iMaxLength的数据成员?
不过从”const“的HBufC 调用Des() 函数转换到“非const”的TPtr,这个额外的iMaxLength,也不知道symbian是如何实现的?
^_^,原来是调用static TInt User::AllocLen(const TAny* aCell)函数。

Labels:

在Symbian应用里嵌入汇编代码


Symbian手机一般用的都是ARM的CPU(当然也有xscale或者其他的),所以都支持通用的汇编指令,我们可以通过加入汇编指令Source到代码里面,就能直接执行特殊指令并操作各种寄存器,使得程序能实现一些特殊的功能。
SYMBIAN应用的开发都是C/C++的程序,已经封装了大部分需要的功能。为了加快运行速度和实现一些特殊功能,我们可以使用汇编来写一些子函数。ARM汇编优化后一般能达到原来C/C++代码的30%以上(当然最优先的优化是在框架级别进行调整,在逻辑上最优后再考虑用汇编重写代码会更有效优化性能),最快的能提高几倍,所以对一些性能要求比较高的,应该通过汇编来实现。

这里我举个简单的setjump.S的汇编例子.
首先,在src目录下增加一个setjump.S的文件。然后写上如下的汇编代码。

CODE:
.text
.global GetCpuId
.global setjmp
.global longjmp

setjmp:
mov r12,#0
stmia r0,{r4-r12,sp,lr}
mov r0,#0
mov pc,lr

longjmp:
ldmia r0!,{r4-r12,sp,lr}
movs r0,r1
moveq r0,#1
mov pc,lr

GetCpuId:
mrs r0,cpsr
and r0,r0,#15
cmp r0,#15
bne UserMode
mrc p15,0,r0,c0,c0,0
nop
nop
mrc p15,0,r2,c0,c0,1
nop
nop
str r0,[r1,#0]
str r2,[r1,#4]
UserMode:
...

然后,在其他需要用到jump函数的C/C++程序中,增加对它使用的代码:

CODE:
extern "C"{ //for C++ only
int setjmp(unsigned long *__jmp);
void longjmp(unsigned long *__jmp, int __ret);
}

汇编写出来的都是C语言的函数,声明后直接调用函数即可:
CODE:
setjmp(data->jmpbuf);
...
longjmp(data->jmpbuf, 1);

完成代码后把.s文件保存到Source目录下,并修改MMP文件,加入SOURCE setjump.s(9.0后的Symbian编译器能够自动识别汇编还是C/C++文件,记得以前是需要手工编译成独立的库的):

CODE:
SOURCE setjump.s

这个代码只能是在gcce/armi环境下来跑,模拟器上是不行的,所以请在MMP上用编译宏分割开,#ifdef MARM ...#endif等。X86上需要再写一段for PC的汇编才行。

ARM CPU的版本不同,支持汇编的指令也不同,所以很可能必须对不同的CPU做不同的汇编代码以达到最佳性能或者执行效果。

Labels:

开发Symbian S60应用的一些tips

以前开发Symbian S60,SybianS6.1 0.9~v2 的应用的一些tips,希望对大家有用:
------------------------------------------------------------------------------------------

重新启动设备代码
#include
#include
RDebug::Fault(0);
UserSvr::ResetMachine(EStartupWarmReset);

取ControlEnv
CAknAppUi* pUi =STATIC_CAST(CAknAppUi*, ControlEnv()->AppUi() );
CCoeControl和CCoeAppUi的类用iCoeEnv类成员访问CONE。这些类也提供ControlEnv方法
使用一个静态函数CCoeEnv::Static(),它会向CCoeEnv返回一个指针。使用CCoeEnv::Static()函数,这是因为:静态本地线程(thread-local static,TLS)的运行速度相对较慢。

扩展窗口到全屏
CCoeControl::SetExtentToWholeScreen 或者
CCoeControl里面直接设置大小:SetRect(ApplicationRect());

CFbsBitmap的管理和操作
在FBS中,位图根据其大小被划分到两种堆中。大于4KB的位图和小于4KB的位图被存放到不同的堆栈中。这样的划分是为了防止产生文件碎片。当频繁创建并删除一些大位图时,用于大位图的栈就会自动进行碎片整理。由于碎片整理的需要,当正使用大位图文件的内容时,需要锁定这些堆。为了防止碎片整理和位图操作同时发生,TBitmapUtil类提供了对堆进行锁定和解锁的操作。范例假定这个位图针对每个像素使用16位数据,这对12位位图和16位位图都是有效的。

//当大位图时所定堆
TBitmapUtil bitmapUtil( bitmap );
if( bitmap->IsLargeBitmap())
{
bitmapUtil.Begin( TPoint(0,0) );
}
// 编辑位图
TSize bitmapSize = bitmap->SizeInPixels();
TUint16* bitmapData = (TUint16*)bitmap->DataAddress();
TUint16 colour = 0;
for ( TInt y = 0; y < bitmapSize.iHeight; y++ )
{
for ( TInt x = 0; x < bitmapSize.iWidth; x++ )
{
*bitmapData++ = colour++;
}
}
// 释放
if ( bitmap->IsLargeBitmap() )
{
BitmapUtil.End();
}


DSA开发
DirectScreenAccess
查询屏幕的内存地址,这可以用UserSrv类实现
TPckgBuf infoPckg;
TScreenInfoV01& screenInfo = infoPckg();
UserSvr::ScreenInfo(infoPckg);
TUint16* screenMemory = screenInfo.iScreenAddress + 16;
该屏幕内存有一个32位的头,在写入内存时必须考虑到这个头;

得到剩余内存
Get free memory is available.
TMemoryInfoV1Buf info;
UserHal::MemoryInfo(info);
TInt freeMemory = info().iFreeRamInBytes;

读写GIF的帧信息
Symbian Native API for GIF
Read a frame. from a GIF image

TFrameInfo frameInfo;
// Find the desired frame. from the GIF // image and assign it to gifFrameIndex
//Get the frame. info
iConverter->FrameInfo(gifFrameIndex, frameInfo);
//Create a bitmap based on the size of the gif
TInt err = iBitmap->Create(frameInfo.iOverallSizeInPixels, KDeviceColourDepth);
if (err == KErrCouldNotConnect)
{ // handle errors }
//Convert the GIF frame. into a bitmap
TRAPD(convertErr, iConverter->ConvertL(*iBitmap, gifFrameIndex));

得到系统的软件版本号
Get System SW Version.
TBuf<64> swBuf; SysUtil::GetSWVersion(swBuf);

Labels:

讨论:SIS 能力限制(Symbian OS9.1 capability limitation) - cding's 个人空间 - IT人社区 - powered by X-Space



SIS 能力限制(Symbian OS9.1 capability limitation)
在UIQ3.1上开发,发现关于capability的限制问题,导致某些特性不能在设备上跑,而且安装也发现问题。在MotoZ8设备上不能安装,security check fail.
1. 关于CAPABILITY的问题,通过查询UIQ文档后,加入对应的能力属性就可以解决,但如果需要没有限制的地方跑,还需要通过symbian sign。
解决方法:在mmp上加入对应的能力,如:CAPABILITY SWEvent。
详细的属性,通过run command:tail -f %temp%\epocwind.out 来得到:
more details:
Symbian introduced what is called Platform. Security. Basically this is a way of limiting what a application/process can do in the system, e.g:
- An application cannot read certain directories (data cageing).
- An application can only call certain API's by default.

What an application can do is decided upon the Capabilities it possess.
Each application/server specifies its Capabilities in the MMP-file.

Please find more information about platform. security in the SDK
documentation:
> UIQ 3 SDK > Symbian OS v9.1 > Symbian OS guide > Platform. security > UIQ 3 SDK > UIQ Developer Library > UIQ Migration Quick Guide

Regarding the KErrPermissionDenied you experience. It is hard to say what is wrong, but if you develop on for the emulator there is a handy trick that helps pin-point the problem.

While running the emulator and reproducing the error you get, run this command in a Command shell:
tail -f %temp%\epocwind.out

This file will produce alot of output from the emulator, one of the things written to this file are PlatSec errors. From that file you should be able to see the type of Capability required for you operation.

E.g:
...
*PlatSec* ERROR - Capability check failed - ....and was found to be missing the capabilities: CapabilityName

Here the CapabilityName is the capability required for that operation.
This should be added to the project's MMP-file, rebuild and retest.

Please note that it is not a good idea, to keep adding alot of Capabilities to the project. All capabilities needs to be signed for, and some are more or less impossible to get.

Also, the SDK documentation states the capability required for a
function:
E.g. CTelephony:: DialNewCall() requires the capability NetworkServices.

2. 关于sis 无法安装的问题:
取消install选项上的security check后还是出现 check fail的问题,可以通过签名来解决:
createsis create -pass DefaultPassword "yourpkg_gcce.pkg"
SignSIS yoursis_gcce.sis YourSignedsis.SIS \Symbian\UIQ3.1\epoc32\tools\cert\AllCaps.cert \Symbian\UIQ3.1\epoc32\tools\cert\AllCaps.key

Labels:

Monday, August 27, 2007

小糊涂学symbian日记(12)


[Character Conversion Overview]
Converts text between Unicode and other character sets, and between 16-bit Unicode and the Unicode transformation formats UTF-7 and UTF-8. The API can be used to provide a list of all of the foreign character sets available on the device, to select a specific character set to convert to or from, then to do the conversion.

The Character Conversion Plug-In Provider API can be used to write plug-in DLLs. These extend the range of foreign character sets available for conversion beyond the ones already provided by the device.

这组API有两个重要组成:Unicode/non-Unicode转换和Unicode format conversion.

[Introduction to Character Conversion]
在symbian platform中使用的是Unicode字符集,但是要和很多非此字符集的外部程序交互的话,我们就需要字符的转换了。

[Character sets and Unicode]
文本数据在电子设备中是以一定字符集的形式存储的,一个字符集是一个组字符组成的,其中每个都被编码成不同的数字。每个字符的外观并不是字符集的属性,是属于字体范畴的。因此一个字符可能呈现出多种外形,但它们都有个唯一的数字编码。

字符集中,一般西方字符是8-bit编码的情况,而东方字符一般是双字节的,ie16-bit的编码,在Unicode中,给个字符都是被编码为双字节的,有些列外可能编码为四字节。

[Character conversion to and from Unicode]
CCnvCharacterSetConverter类用来在Unicode(UCS-2)和其他字符集之间做转变,一般用16-bit的descriptor表示在UCS-2中的字符,用8-bit的descriptors来表示其他字符集编码。

The first stage of the conversion is to define the non-Unicode character set to be converted to or from, and the second is to convert the text. Selecting the character set to be converted to or from is done using one of the overloads of PrepareToConvertToOrFromL(). Text conversion itself is done using one of the overloads of ConvertFromUnicode() or ConvertToUnicode().

The conversion functions allow piece-by-piece conversion of input strings. Callers do not have to try to guess how big to make the output descriptor for a given input descriptor; they can simply do the conversion in a loop using a small output descriptor. The ability to cope with an input descriptor being truncated is useful if the caller cannot guarantee whether

the input descriptor will be complete, e.g. if they themselves are receiving it in chunks from an external source.

没有直接的办法可以转换两个非unicode字符集,如果要这样做那就分两步好了convert from one character set to Unicode and then from Unicode to another character set.

[Transformation formats]
Unicode字符集中的UCS-2格式将每个字符编码为2个字节。
While this is not important within a system, it does mean that text encoded as UCS-2 cannot easily be shared between systems using a different endian-ness. To overcome this problem the Unicode Consortium has defined two transformation formats for sharing Unicode text. The transformation formats explicitly specify byte order, and cannot be misinterpreted by computers using a different byte order.

于是有了两种不同的格式:UTF-7和UTF-8
*UTF-7:
UTF-7允许Unicode字符集被编码和转换为8-bit的字节,但只有7个bit被使用,UTF-7被分为三个不同的字集,它们分别被不同的编码和定义,如下
1、D集,做为一个单独的字节来编码的,它包括大小写的A到Z,数字和9个其他字符。
2、O集,包括字符! " # $ % & * ; < = > @ [ ] ^ _ { | },这些也都被做个一个单个字节被编码,or with the modified base 64 encoding used for set B characters, When encoded as a single byte, set O characters can be misinterpreted by some applications — encoding as modified base 64 overcomes this problem.
3、Set B comprises the remaining characters, which are encoded as an escape byte followed by 2 or 3 bytes. The encoding format is a modified form of base 64 encoding.

*UTF-8
UTF-8 encodes and transmits Unicode characters as a string of 8-bit bytes. All the ASCII characters 0 to 127 are encoded without change

[Conversion to and from Unicode transformation formats]
有个单独的类CnvUtfConverter,用来转换文本,在Unicode(UCS-2)和两个Unicode变化格式UTF-7和UTF-8之间。它有四个静态成员函数,分别是,ConvertFromUnicodeToUtf7(),ConvertFromUnicodeToUtf8(),ConvertToUnicodeFromUtf7()和ConvertToUnicodeFromUtf8()。

There is no direct way to convert between UTF-7 and UTF-8. Though it is possible to convert from one transformation format to UCS-2 and then from UCS-2 to the other transformation format.

<>
[Design and implementation of STDLIB]
这里描述了symbian软件平台中C标准库的主要特性(一般就是纸STDLIB)。但并不试图给出库的完整API,如果你需要获得ANSI C和POSIX的所有文档,查看下列相关组织地址:http://www.ansi.org和http://standards.ieee.org/index.html

[Porting Guide]
*Overview
这里讲述了如果将一段传统的C代码移植到symbianOS上。我们使用了三个列子:Hello, ConsoleApp和GUIApp。头两个是基于简单控制台的。The third is a command-line driven program which has been converted into a standard symbian application. It demonstrates how to link STDLIB into a C++ project. 它也显示了在转换C代码到symbian OS中可能会遇到的几个问题,包括在DLL中写入数据的限制。这些列子代码都可以在epoc32ex\stdlib\表现出来。

*Installing STDLIB on the target machine
如果需要的话,可以使用stdlib.sis安装STDLIB在目标机器上,在\epoc32\release\wins\udeb\ and \epoc32\release\wins\urel\都有,使用这个sis安装文件有以下的好处:
the DLL is installed in the correct location

the correct version of the DLL is installed

if you supply a .sis file to install your product, STDLIB can easily be included as a sub-component of it

*Hello World—A minimal console application
这里只有个最简单的main()调用标准库的printf()函数。
这里列子的工程项目文件(Hello.mmp)包括如下的内容:
TTARGET hello.exe
TARGETTYPE exe
UID 0
SOURCEPATH .
SOURCE slhello.c
SYSTEMINCLUDE \epoc32\include\libc \epoc32\include

LIBRARY estlib.lib euser.lib
STATICLIBRARY ecrt0.lib

这个项目必须包括重要的库文件estlib.lib(也就是C标准库),和euser.lib(E32的用户库),whose services are used by STDLIB

这个工程也可以使用STATICLIBARY关键字来链接ecrt0.lib库。This file provides the E32Main() entrypoint for a .exe. It also provides other services including command-line parsing, and it calls main().

The SYSTEMINCLUDE path specifies \epoc32\include\libc\. This is the directory in which STDLIB's header files are installed.

这里不做多深入的叙述了,如果有这方面的需求,可以参见SDK:)

<>
[Data And Time Handling Overview]
处理数据和时间值。
系统时间的设定是通过系统静态函数API User class
System locale settings, which affect date and time formatting, are set through TLocale in the Locale Settings API.

这组API有三个重要组成:point in time, time utilities, and time interval.

[Using Date and Time Handling]
Points in time是由TTime类存储和操作的,TTime使用了64-bit的整数来表现时间,它可以精确到微秒。一共有580000年的时间跨度,够用了吧:)

日期/时间和它的组成部分都可以在TDateTime中设置和得到,这个类是TTime的更友好的表现形式,它允许用户很方便的访问到year, month, day, hour, minute,second和微秒这些时间的组成部分。It does not support manipulation of the date/time, or of its components, but may be converted into a TTime, and vice versa.

<>
[DBMS overview]
提供了一个关系数据库的接口。
DBMS定义了一个常用的关系数据库访问API,允许不同的数据库被访问。这里有两个解决方案:a small and relavtively lightweight client-side implementation; and, for when multiple clients must have write access to a database, a client-server implementation.

The File Stores API defines a sophisticated file storage, called permanent file stores, that allows individual entries in a file to be modified.数据库使用这些存储机制来完成底层的数据存储。

[Interface to DBMS database overview]
Defines interfaces for opening databases, defining database schemas, and performing transactions.

这组API有三个重要的组成:abstract database, store database, and named database.

[DBMS rowsets overview]
Allows data contained in a database to be retrieved, searched, and modified.

这组API有三个重要的组成:rowset base, table rowset, and SQL view

[DBMS columns, column sets, and keys overview]
Defines column, column set, and index key structures.

这组API有三个重要的组成:column, column set, and index key.

[DBMS sharing databases overview]
Provides access to a server that allows databases to be accessed by multiple clients.

这组API有两个重要的组成:DBMS server session, and database change notifier.

[DBMS incremental operations overview]
Allows long-running database operations to be performed in steps, so that a user program can remain responsive to other events.

<>
[Descriptor Arrays Overview]
为descriptors提供专用的动态数组,元素可以自由的添加进数组,而不管描述符是什么类型。

<>
[Dynamically Loading Link Libraries Overview]
在程序执行时动态加载库。
These interfaces are used by programs that need to load libraries at run-time, that have a common interface, but different concrete behaviour. Because of this relationship between interface and behaviour, such libraries are called polymorphic DLLs.

<>
Notifies a program of changes in the system environment relating to time, local, power, and thread death.

<>
[File Server Client Side overview]
Provides an application interface to file systems.

文件访问是由EPOC server提供的,the file server。它提供了本地文件系统(ROM, RAM和removable media)还有一个允许动态安装文件系统的接口,如通过网络连接到远程的磁盘上。

这个磁盘、目录和文件层次就是VFAT,它可以使得文件系统与桌面PC兼容。

<>
[File Stores Overview]
提供基于文件的结构数据存储,using networks of streams.

The File Stores API is based on the abstract store types defined by the Stores API. The underlying file handling is provided by the File Server.

The permanent file stores defines in this API are used by the relational database API, DBMS,for data storage.

<>
[Handle overview]
允许一个线程或进程被另一个线程或进程标识和管理。Handles allow a client to access, or refer to, Kernel Object.


<>
从左到右比较两个文本缓冲区。

在两个文本缓冲区中做渐增匹配的比较操作,如,在"garage"和"gander"中匹配的是"ga"。
这组API有四个重要组成:incremental matcher base class, buffer matcher, pointer to descriptor matcher, and reaizable buffer matcher.

<>
Resource files contain data separate from executable code. Their main uses are for defining user interfaces components and for storing localisable data.

[Introduction to the resource file interface]
Resource files contain data in numbered resources. A resource file has the following format:
---------
header
--------
resource1
resource2
resource3
...
last resource
-------------------
index
------
资源文件使用资源编译器生成的,这里的index可以凭一个数字id来查找到合适的资源,在一个资源文件中可以有最多4096个资源。每个资源都包括二进制数据。

那些处理资源的函数都封装在RResourceFile类中,这个提供了如下的函数:
1、打开和关闭文件
2、读取资源
3、支持使用多个资源文件

一般来说,读取资源后都将其放在一个指定大小的缓冲区中,随后逐步的分析并将数据放在C++类中,最后再释放这个缓冲区。
TResourceReader类可以帮助简化资源数据分析的过程。

[Basic resource file use]
1、in a resource file, define structs or use appropriate pre-defined structs and then define resources that use those structs.

2、run the resource compiler to produce both a generated header file and a resource file. The generated header file defines the symbols which a program uses to refer to the resources.

那些希望使用资源的程序应该使用如下步骤:
1、it includes the generated header file in the appropriate source file to get symbolic access to the ids of the resources contained within the file

2、opens a RResourceFile object, specifying the generated resource file name

3、reads any resource it wishes; the resource is identified by a symbolic id which has been #defined as a number; the content of the resource is read into a binary descriptor, derived from TDesC8.

4、converts the binary descriptor into whatever target format is appropriate for the data in the resource file

5、discards the descriptor, if appropriate, when the binary descriptor has been fully converted into its target format

6、closes the RResourceFile when all operations on the resource file are complete

The resource text can be changed and the resources recompiled without altering or recompiling the C++ program. For example, to alter the language used by text strings.

<>
Provides idioms and frameworks by which processes can pass requests and data to other running processes.

[Asynchronous Services Overview]
Provides low-level and high-level idioms by which one thread or process in EPOC can request services from another.

Client programs typically use these idioms when accessing system services such as windowing, or telephony. This process is so fundamental that the basic structure of nearly all EPOC applications is based on its encapsulation in the active object framework.

<>
提供和本地密切相关的信息,包括日期和时间格式,度量单位等。

<>
This API should be used by applications that display names of plug-ins to users. Some devices allow users to switch between different languages. On such a device, lists of plug-in names displayed to users need to be localised.

<>
提供有关实型数操作。

<>
提供有关内存是如何分配和使用的函数。

EPOC程序中的函数都是和内存密切相关的,这是个有限的资源,必须要小心对待。特别是the event of error conditions. 基于这个理由,异常的处理和内存的管理在支持Cleanup的API中是息息相关的。

一般来说,清除栈只处理基于CBase的类和未知类型(TAny*)对象——这可以通过简单的内存释放来清除。

大部份客户程序都不需要直接使用函数,它们重要是在线程间或进程中共享内存区域上时使用。


<>
提供引用计数对象和相关的包容器。
这个东西是server端代码中大量使用的,主要用来记录server提供的给多个client的资源的使用情况。The major motivation for the form of the API is to provide such functionality for the Kernel, which uses them in this way for system objects such as mutexes, threads, etc. The various container classes in the API provide the support for tracking the use of groups of such objects.

<>
[Stores Overview]
Provides structured data storage, using networks of streams.

<>
[Store Streams Overview]
Provides stream types suitable for use in store(structured data storage).

Store Streams API is based on the abstract stream types defined by the Streaming API. They are designed to be used in conjunction with the structured data storage defined by the Stores API.

<>
[Streaming Overview]
Streams abstract the external storage of data(typically objects)

The Streaming API is abstract. It defines interfaces for reading and writing data to and from storage, but not what that storage is. Derived classes in other APIs provide particular implementations of streams:
file streams, as defined in the File Stores API

streams used as part of a network of related streams, called a store, as defined in the Stores API memory-based streams, as defined in the Memory Streams API

encrypted streams, as defined in the Encrypted Streams And Stores API

<>
提供各种系统函数。系统静态函数,由User类提供,typically either relate to the current thread, or its heap, or system-wide properties, such as system time.

<>
Provides a core set of error codes that may be returned by System APIs.

<>
Provides access to processes, threads, thread-local storage, and access synchronisation.
These APIs provide support for a programming style that employs multiple threads or processes that share resource protected by synchronisation mechanisms such as mutexes.

<>
Provides timers that asynchronously notify an application after an interval or at a specific time.

The User class in the System Static Functions API provides simple functions to suspend a thread for a given interval or until a specific time.

<>
Manipulates globally unique identifiers (UIDs).

To guarantee uniqueness, all UID values are assigned to developers centrally by Symbian.

<>
Encapsulates version information.

Labels:

小糊涂学symbian日记(11)



[Application Engines Overview]
Purpose
----------
提供访问核心应用程序的数据的能力。

这些API允许第三方应用程序和核心程序整和到一起,如提供一个读取通讯簿数据的连接。

Description
-------------
1、Agenda Entry and Instance Overview
The interface to individual entries and instances in an agenda file.

2、Agenda File Overview
The interface to the data stored in an agenda file

3、Agenda Server Client Side Overview
Client side interface to the agenda server.

4、Agenda Model Utilities Overview
Utilities uesed by the agenda model-related APIs, e.g for data and time conversion

5、To-do List Overview
Manages lists of to-do entries

6、Cantacts Model Overview
Stores, manipulates and retrieves contacts data

(以上详细可以看SDK,:)

[Application Framework Overview]
Purpose
-------------
为应用程序提供核心的框架和库。这些包括为EPOC应用程序所准备的框架,它们定义了应用程序的结构和基本UI操作。

Description
------------
这里提供了一系列的API用来完成以上功能。
*Print Framework Overview
Handles set up of print jobs

*Print Preview Overview
Provides a framework for creating print previews

*System Sounds Overview
为应用程序中的一般事件如信息或错误提供一个简单的和统一的声音调用方法。
这里的声音是指逻辑上的,可以是wav,铃声或一个fixed tone sequences

*Text and Text Attributes Overview
提供一个可以使用可编辑文本框的框架,The API does not store the layout information needed to display the text. This is done by a separate API called Text Views.

*Text Views Overview
Provides access to the layout information needed to format text for display
The text itself and its formatting are accessed separately, using the Text and Text Attributes API. Text views obtain text and formatting information via an abstract interface class (MLayDoc).

*UI Control Framework Overview
Provides a framework for creating user interface controls; a framework for application to handle user interface events; and environment utilities for control creation and access to windowing functionality.

*UI Graphics Utilities Overview
Provides miscellaneous user interface utility functions, relating to fonts, colours, and drawing.

*Uikon Overview
Provides the top-layer of the framework for application, and a set of core controls that are present on all device families.

*View Server Overview
Allows applications (including system applications such as shells) to make and receive requests to show a particular view of their data.

Clock Overview
----------------
提供了一个显示数字和模拟时钟的图面。
时钟根据系统时间会有所改变的。

Front End Processor Overview
-----------------------------
Provides a framework for components that enable the input of characters not supported directly by the keyboard (if present). This includes hand-writing and voice input.

Message Window Overview
---------------------------
提供一个窗口来给用户显示信息,然后就消失了。

Open Font System overview
---------------------------
提供一个框架以便在symbian平台上使用各种各样的字体。

Recognizers Overview
------------------------
Plug-in code that can examine data in a file, or sample data supplied in a buffer, and return, if recognized, the data type of that data. A data type is also commonly known as a MIME type.

Uikon Core Overview
----------------------
Provides the top-layer of the framework for application, plus various user interface utility classes. The framework is fundamental to all GUI applications.

Uikon Core Controls Overview
--------------------------------
Provides core controls that are common to every device family. Further controls are available through the device family UI APIs.

[Application Serveices Overview]
可以提供一系列的功能服务给我们应用程序,包括警铃、logging、系统信息和vCard以及vCalendar操作。
Description
-------------
由下列一组函数提供这样的功能,分别是
*Alarm Server Overview
提供警铃服务功能,里面分Alarm Service和Alarm,仔细请看SDK:)

*Calendar Conversion Overview
在Gregorian和Chinese日历之间转换,TGregorianCalendar存储了一个Gregorian日期,而TChineseCalendar则存储了一个Chinese日期。

这两个都是从TCalendar派生的。

*Log Engine Overview
记录引擎,记录那些用户很感兴趣或很重要但暂时无暇顾及的事件,如来电记录。这些时间都可以被一个viewer程序捕捉,然后显示给用户看。它有五个重要因素:log engine wrapper, log client, event, view and filter.

*System Agent Overview
Provides a central repository of information about the state, particularly relating to power and communications, of the EPOC machine.Applications can ask to be informed when certain conditions are true, and they can also retrieve the current state.
这个System Agent使用client/server结构。这组API有两个重要组成方面:System agent client interface和system agent variable.

*Versit Overview
Reads and writes the vCard(electronic business cards) and vCalendar(calendaring and scheduling) standards.
这组函数支持版本2.1的vCard和版本1.0的vCalendar,这些资料可以在http://www.imc.org里找到。

Agenda Model API可以导入或导出vCalendar
Contacts Model API可以导入或导出vCards

这组API包含四个重要的组成方面:parser, property, property parameter和property value.

*World Server Overview
提供反问城市和国家的数据库的能力,这组函数包含三个重要组成部分:world server, city information和country information.


<>
[Application Utilities Overview]
可以让我们很方便的使用文件系统的实用程序。

这组函数主要完成了定义在File Server Client端的文件系统处理功能,调用者必须拥有一个file server session(RFs)。
Functions include:
1、checking whether a file, folder, or path exists
2、copying, renaming, and deleting files
3、parsing and manipulating file names
4、comparing UIDs

所有的函数都是BaflUtils的静态成员。

[Arrays And Lists]
提供了数组和链表的对象。
下面的几组函数提供了这样或那样的功能,请看
*Fixed Size Arrays Overview
Provides a simple wrapper to c++ arrarys with additional automatic checking of index values.

固定大小数组提供一个简易但很实用的编程方式(和一般的c++数组比起来),如果用越界的索引来访问数组会引起不必要的麻烦。
固定大小数组(fixed sized array)由TFixedArray提供。

*Dynamic Arrarys Overview
提供一个基于堆的可动态扩展的数组家族:)
有很多具体的类为不同环境的情况提供动态数组,和EPOC的固定大小数组以及标准C++数组不同,他们的元素数目在运行时是可以改变大小的。

这类数组分成以下两个派别:
1、在大多数情况下使用的简单动态数组
2、允许带有额外属性的可扩展数组,如数组在内存中的存储组织等。

这些数组类都是模板形式出现的。

*Array Keys Overview
Defines a property of array element by which a dynamic array can sorted and searched.

Keys是由客户程序描述的,并且和在动态数组API中定义基于堆的数组类一起联合使用。
它也可以用于由系统静态函数API提供的底层存储函数中。

*Singly Linked Lists Overview
处理单向链表:an ordered, non-indexed list of elements, that can be traversed only from start to end.
The API has three key concepts: list header (TSglQue), link class (TSglQueLink), and iterator class (TSglQueIter).
注意单向链表有下面两个重要属性:
1、elements can be accessed through iterating through the list, and added to the start and end of a list, but not to the middle

2、elements in a linked list need not be objects of the same type but ought to be derived from the same base class

*Doubly Linked Lists Overview
双向链表的处理:an ordered, non-indexed list of elements, that can be traversed in both directions.
The API has three key concepts: list header (TDblQue), link class (TDblQueLink), and iterator class (TDblQueIter).

注意双向链表有下面三个重要属性:
1、the list is circular: the last element points forward to the head element and the head element points back to the last element

2、elements can be accessed through iterating through the list, and added to the start and end of a list, but not to the middle

3、elements in a linked list need not be objects of the same type but ought to be derived from the same base class

[Basic Types]
Provides implementation-independent versions of the C++ built-in types, defines the EPOC standard base class for all heap-allocated classes, and provides some useful yet simple global functions.

你不要直接使用C++内建类型,应用使用EPOC定义的类型。

这些API为integer、text、Boolean和floating point类型定义了类和typedef。此外,a utility class to help with reference arguments in variable argument functions is provided.

*Heap-allocated classes base
EPOC提供了所有从标准基类CBase派生来的类,它们拥有动态分配的资源,这样的类习惯上拥有一个C前缀,并被当做C类来看待,这个类的分配和清除都是EPOC定义的。

*Integral types
1、TInt is the natural mechine word integer ,并且应该在所有应该使用整数的地方出现。

2、TInt32, TInt16和TInt8 map onto C++ built-in types in most implementations. These types should only be used where the size of the integer is of first importance. sometimes, this is relevant for C++ arrays and struct/class members. Note that C++ passes all function arguments as TInt, so there is nothing to be gained by using narrower types in function interfaces.

3、TUint is an unsigned integer of the natural machine word size. It should be used for flag and handle words, which are manipulated using bitwise and equality-comparison operations rather than arithmetic. TUint should also be used in exceptional circumstances where the full range of a machine word is required for arithmetic: this use is exceptional and must always be carefully controlled. 注意经验表明当要使用一个正数时,我们应该用TInt而不是这里的TUint。记住这点就可以了,

详细原因在SDK里有解释。

4、TUint32, TUint16 and TUint8 are available where the specific representation width is important.

*Text types
1、TText represents a character in the natural character width for the EPOC build. For Unicode builds, this is 16 bits.For non-Unicode builds, this is 8 bits.
2、TText8 is available for build-independent 8-bit characters, and TText16 for build-independent 16-bit characters。 事实上,TText就是映射两者之一的,具体哪个看情况了。
3、The TChar class wraps a character value and provides a number of utility functions to operate upon it.such as converting the character to uppercase and testing whether it’s a control character.

*Boolean type
TBool represents a Boolean whose value may be true or false. It is implemented as a natural machine-word integer.
C语言中有0代表假,1代表真以及0代表假,非0代表真的定义,这样的混乱在TBool也可能出现。它是有可能的,但决不提倡在一个TBool中定义个数字值。
In general, a TBool should be tested using

if (x) ...
or

if (!x) ...
rather than

if (x==ETrue) ...
or

if (x==EFalse) ...
The TBool uses a whole machine word. When a class has a number of flags, they should be encoded in a flags byte, and suitable getter/setter functions, taking TBools, should be coded to access that flags byte.


*Floating-point types
1、TReal is a 64-bit double-precision type, used as the argument type for all math functions, and the default type of floating-point literals.
2、TReal64 and TReal32 are available when the precise size is important.

*value reference type
The value reference type is a utility that is provided to overcome problems with specifying a C++ reference argument before the ellipsis argument in variable argument functions.The value reference type is provided by TRefByvalue.

*Global functions
Max(), Min(), Rng()和Abs()在很多情况下都很很有用的全部函数。


*Templated functions
In e32std.h, there are also a variety of inline templated functions for performing simple operations:

return lower of two values: Min()

return higher of two values: Max()

return absolute value: Abs()

check if a value is between two other values: Rng()

These can be called on any type that supports the required operators (<,>,<=,>=,-).


<>
[Buffers And String Overview]
可以处理字符串和一般的数据缓冲区。
在EPOC中字符串和缓冲区的习惯称法是descriptors,在这里的API,如Literals, Lexical Analysis和Package Buffers提供了很多descriptor的处理应用。在有些情况下,应用程序可能需要直接管理内存中的底层数据,基于这个原因,Dynamic Buffers和Raw Memory API提供了这方面的应用。

[Circular Buffers Overview]
Provides a fixed length first-in first-out queue of fixed size objects.

注意它拥有如下的属性:
1、只有简单的添加、删除和重置操作是被允许的。
2、可以在序列的头添加元素在序列的尾巴那删除元素。
3、在circular buffer中的所有元素必须是同样类型的对象。

Circular 字符串是由CCirBuf提供的。A predefined circular buffer of unsigned integers is provided by CCirBuffer.

[Desciptors Overview]
处理字符串和数据缓冲。

Descriptors是在EPOC中使用的一组类,主要用来处理字符串。它们是用来代替Null-terminated C strings. The same classes are used for general binary data.

descriptors类的基本分类如下所示.
*Basic classification
Type——————Description——————Basic name
抽象——基本的接口,允许不同类型的descriptors在不同的状态下使用——TDes
buffer——基于堆栈的:其中包括数据,在编译时刻内有个最大长度确定——TBuf
pointer——指向那些存放在他处的属于descriptor的数据——TPtr
heap——指向那些存放在堆中的属于descriptor的数据,可以设置最大长度并且可以动态改变——HBufC

*Width
Descriptor types can store 8-bit or 16-bit data. These types are indicated by the convention of appending 8 or 16 on the basic name, e.g. TDes8. In Unicode EPOC builds, the basic names correspond to 16-bit types, for example TBuf is defined to be TBuf16.

*Modifiable and non-modifiable
可修改的Descriptor类型可以有个接口,它允许调用者改变他们的内容,如添加字符。
不可修改的Descriptor类型可以将他们内容重置,但不可修改。一般习惯上我们为不可修改的类型加上C后缀,如TBufC

Heap类型的Descriptors是一个很特殊的情况,他们只在不可修改的状态下有效,但仍然可以通过使用指针来修改内容,可见HBufC::Des()

[Dynamic Buffers Overview]
处理可变长度的数据缓冲。如果是固定长度的数据或是其长度在一个最大值范围内一般不变的情况,最好使用Descriptors

这类函数有三个关键组成:dynamic buffer base, flat dynamic buffer, and segmented dynamic buffer.

[Lexical Analysis Overview]
提供字符串分析和字符串与数字的转换。
The API has two key concepts: lexical analyser, and extraction mark.

[Literals Overview]
简单的生成喊有只读文本的Descriptors.
Literal descriptors严格意义上并不是Descriptor API的一部分,这是当他们没有从descriptor类基类TDes派生的时候,不管怎么说它们有转换操作,这样他们能做为函数参数在const TDesC&或const TRefBuvalue出现的地方。

The API has two key concepts: literal descriptor, and literal descriptor constructor macro.

[Package Buffers Overview]
允许任何值类型(T class)做为一个descriptor被封装,for the purpose of inter-thread parameter passing.

The Client/Server API expects data that is to be passed between threads to be encapsulated in a descriptor. Package buffers provide a generic way for the thread providing the data to do this. The thread receiving the data extracts the original data from the package.
APIs with client/server architectures, such as ETel and Messaging, define specialised package buffer types that encapsulate the particular data types that they require to pass between threads.

The API has two key concepts: package buffer descriptor, and package pointer descriptor.

[Raw Memory Overview]
简单的处理内存中数据的操作。

This API is mostly used by other system APIs. Client applications generally use a higher-level API, such as descriptors.

The API supplies static functions to compare, copy, move, swap, and fill areas of memory, as defined by pointers and data length parameters.

The raw memory interface is provided by Mem.

Labels:

小糊涂学symbian日记(10)

接下来,我们谈谈开发的利器——仿真器
Overviw
-----------
仿真器提供了一个完整的运行环境,可以运行在PC端的Windows上,这种Symbian平台的执行被认为是WINS——一个单个进程的Windows平台。

有了仿真器我们可以充分的利用基于PC的开发环境,只在最后测试成功后才放在目标硬件(如手机上)。但如果你要编写低层代码,要直接访问设备硬件——如为一个物理设备编写程序,那就不能使用仿真器了。

仿真器是SymbianSDK的一部分,并且支持多种开发语言,包括C++,Java以及symbian的BASIC。

Emulator highlights
---------------------
仿真器包括很多可扩展的特性:
1、Function key mappings to emulate real device hardware condition not on a PC, e.g power off.

2、提供很好的调试功能,When a thread is panicked, the debugger provides comprehensive information about the panic.

3、可以使用初始化脚本来进行完全的配置,如指定屏幕尺寸以及灰度/颜色深度等。

4、Full integration with the Windows run time environment providing support for comms, Internet, and IrDA through the host PC.

[Language-specific development]
仿真器可以支持不同语言的开发,下面就做些讨论:
Java
---------
在设计时,程序的屏幕尺寸,外观外貌,以及所支持的UI特性都有不同,因此一般来说Java程序都是为一个特定目标设计的,尽管有些很简单的Java程序确实能运行在不同的平台上。

Java程序利用特定的SDK编写,可以从仿真器上直接拷贝到其他相关平台上,因为Java是设备无关的,可以不用再编译。

C++
-----
C++开发要面对很复杂的状况,如操作系统的进程线程都直接可被程序操作,仿真器是个单进程的环境,不象真正的目标环境那样是多进程多任务的, as each process is emulated by a Windows thread. There are, however, issues regarding launching some executables, such as console applications and servers. For more information, see the C++ build tools documentation.

[Using the PC keyboard, mouse, screen, and sound]
Overview
---------
仿真器允许开发者使用PC端的鼠标、屏幕和声音,就好象实际在硬件中操作一样方便。
下面就来介绍一下具体情况

Using the PC keyboard
-------------------------
PC上的键盘可以直接发送命令到仿真器上。
下面展现额外定义的特殊功能键
F1 如果按下可以最大化菜单列表,或是切换到下个菜单项。
Alt+F2 帮助键:按下可以得到当前环境下有关上下文的帮助
F8 记录按钮,第一次按下可以开始记录,再次按下就停止记录了

Using the screen and mouse
------------------------------
你可以使用鼠标在仿真器窗口上操作,来模拟Tap,这里鼠标上只有左键被支持。

Other special hardware features
----------------------------------
Special features typical of Symbian devices are also available when developing using the Emulator, through special key assignments.

*Removable media support
The Emulator can simulate two PC-Card devices using two files in the PC's temp directory. When the Emulator is started, each device is equivalent to an empty, unformatted, volume. An F32 client under WINS may access the removable media using the y: drive

F5 Open removable media door.
F4 Switch between two simulated devices while door open

Power off/shutdown
---------------------
仿真器能用下列键模拟出一些开关事件。此外,在一段时间后仿真器会模拟出自动关闭——仿真器程序最小化自己,在模拟开启后再恢复原状。

F10 电源关闭:这将最小化仿真器窗口,你可以通过恢复窗口来重新开启
F10 紧急关闭:如果目标设备在电池耗尽时可能会发生这样的时间——the machine hasn't enough power to function normally, so it falls into a state in which data on the machine is preserved while there is still power in the main and backup batteries but no user operations are allowed。
F11 Case close toggle:在仿真器窗口栏上显示“CASE CLOSED”,这时窗口变得不可用,再次按下F11就可以退出本状态。

Using sound
---------------
仿真器可以驱动PC的声音,只需要装上声卡即可。

[files and locations]
overview
----------
仿真器在pc的文件系统上映射出symbian的文件系统。这样的设备映射是在运行时发生的。symbian平台生成两个缺省的盘符:z:,在目标硬件上它映射rom,和c:,一个ram drive.

考虑到仿真器沙锅内不同程序的不同需求,the default location for the virtual drives vary depending on where the emulator itself is installed. this section describles the different cases.

you can also add further virtual drives, and reconfigure the standard drives to map to alternative locations.

virtual drives
----------------
有两个缺省的虚拟驱动,z:和c:,可以修改它们,并且可以加上新的驱动盘——如模拟一个cf卡做为一个可移动的盘处理。

有很多方法可以完成,但最好使用修改配置文件的方法。如果需要,you can also override the loation of the emulated c: drive using command-line.

注意对z:驱动的影射所指定的目录名必须为z

[emulator in c++ sdk]
dirves z: 和 c:都映射在下列子目录中
c: .\epoc32\wins\c\
z: .\epoc32\release\wins\urel\z\, or \ .\epoc32\release\wins\udeb\z\

因此,如果是一个z:\system\apps\helloworld\helloworld.app应用程序,就映射到.\epoc32\release\wins\udeb\z\system\apps\helloworld\helloworld.app(这是经过unicode debug模式编译的)

仿真器的配置文件在\epoc32\data\中

note
where epocroot is set to point to the active \epoc32 directory the drive letter must not be included.

[alternative emulator location]
在某些产品中,仿真器能在另一个位置\epoc32\release\wins\build\运行。

[location of executable programs]
gui应用程序必须放在z:\system\apps\目录中
this usual configuration is determined by the way that the emulator locates a library file that is to be loaded.in detail, this is as follows:
1、如果文件名包括一个drive,the filename is mapped using the appropriate virtual drive mapping
2、如果文件名没有指定设备盘符,then the virtual z: parent directory is searched
3、如果库没有找到,an attempt is made to find the library in the same way as on a target machine:i.e virtual drvies y: to a:, then z:\system\libs\, are searched.可执行的应用程序,如text console applications,必须同那些能被静态连接的dll文件放在一个目录中。the usual approach is to build both exe and dll files to the z: parent directory. the location of emulated drives is calculated from the directory containing the exe.

[application data files]
application data files can be copied from a symbian machine, using epoc connect, onto a emulator virtual drive and opened from within the emulator. this is not true of application code which must be re-compiled to run on each platform.

installation .sis files can be installed on the emulator, using the application install program from the emulator's application launcher. the install program dispalys a list of .sis files to install. these should be located in .\epoc32\wins\c\. (在nokia手机里为.\epoc32\wins\c\nokia\installs)

applications are installed to .\epoc32\release\wins\udeb\z\system\apps\ for debug builds or .\epoc32\release\wins\urel\z\system\apps\ for release builds.

以上资料非常有用,是充分利用模拟器的好帮手:)

[debug facilities]
overview
----------
仿真器是symbian程序设计的主要开发和调试环境。
in debug builds, the gui provides special key combinations for resource checking and redraw testing, which can help to trap memory leaks early in the development cycle. all debug keys are accessed using a ctrl+alt+shift prefix.

debugging keys
----------------
all gui applications may be exited using ctrl+e. additionally the window server provides ctrl+alt+shift+k kill the application with current keyboard focus.

*drawing:
t——可以显示一个任务列表,我们可以随意的切换或关闭任务,note that this is a debugging aid only.
r——重绘整个屏幕,来测试程序是否完成了适当的重绘。
f——enable window server auto-flush for all programs using the current control environment
g——disable window server auto-flush for all programs using the current control environment

*miscellaneous features
v——turn on or off verbose information messages
z——send keys a through j in fast sequence to application, to test its ability to handle fast repeated keys

*resource allocation
a——show number of heap cells allocated on user heap by the current prgram
b——show number of file server resource in use by the current program
c——show number of window server resources in use by the current program
p——show dialog for the heap failure tool: see below for details
q——turn off heap failure mode

the heap failure tool artificially generates resource allocation failures. it is used by developers to test that an application copes properly with such failures. the dialog options are as follows:

(1)app heap failure type: 为程序中的资源分配需求产生错误,random means that requests fail at a random rate. deterministic means that requests fail at the rate specified in the rate box.

(2)wserv heap failure type: generates failures for resource allocation requests from the symbian platform window server. the options are as for app heap failure.

(3)file failure tool: selecting this option generates failures for file access attempts.

*window server logging keys
e——start(enable) window server logging
d——stop(disable) window server logging
w——dumps the full window tree
h——dumps the contents of the window server's heap.

[Dial-up networking]
Overview
------------
可以通过PC上的外加设备来测试仿真器里的Internet应用程序。

Using Internet applications
-------------------------------
实际设备中,网络程序利用拨号网络来连接到ISP上,一个般的ISP设置都存储在控制面板里,电子邮件和WAP帐号也都是使用这里的信息来配置。

如果要在仿真器上运行Internet程序,你应该
1、有个带modem的PC机
2、一个ISP帐号
3、SMTP/POP3电子邮件帐号

注意,仿真器的网络工具不是为基于PC的LAN设计的,(哎,可惜了我们的宽带),因此你的机器应该有个猫来做这件事情:)

另外,你应该在仿真器的控制面板里设置好你的ISP配置,因为它是不能共享你的PC控制面板里的相关信息的:)另外你的邮箱也应该在相关email程序里设置好,以便接受和发送电子邮件。

symbian平台的串口设备一般是COMM::0和COMM::1,COMM:0映射的是PC的COM1端口,COMM::1映射了COM2。

[How to start the emulator]
仿真器有两个版本,一个是调式版本,一个是发布版本。分别定位在
release: .\epoc32\release\wins\urel\

debug: .\epoc32\release\wins\udeb\

可以直接去那双击epoc.exe来执行或者在命令行里键入epoc即可:)

Note
--------
文本控制程序是为了提供测试用的,如果你要运行这样的程序,那可以直接键入他们的exe文件名即可,如那个helloworld程序。

[How to close the emulator]
仿真器是一个windows程序,可以使用一般window关闭的方法,如Alt+F4,此外在调式模式下还可以使用Ctrl+alt+shift+E来关闭。

Note:
When the Emulator simulates a power-off, it minimises the Emulator window. Restoring the application simulates a power-on.

[How to emulate c: using a command-line option]
我们可以使用命令行选项来指定c:驱动器的定位,这是非常有用的,因为我们可以直接从CD-ROM上运行仿真器了-necessary because some apps try to create files in the emulated C: drive on startup.

如果你要覆盖缺省的设置以及配置文件中的_EPOC_DRIVE_C,那就按如下步骤来做:
1、开启命令行程序(进入dos)
2、键入如下的命令
epoc -T epoc -C

T, C 和的含义如下
-C——这个参数表明将要模拟成C驱动器的目录的路径。
-T——指明将要做为C驱动器的系统临时路径。

例如:
将d:\test\目录映射为虚拟C盘
epoc -Cd:\test\ --

[How to emulate drives using the configuration file]
你也可以通过仿真器的配置文件(一般是epoc.ini)来修改virtual drive,你能替换z:和c:两个缺省驱动,或指定额外的驱动器,步骤是:

1、打开epoc.ini,(在.\epoc32\data\)
2、Enter the keyword _EPOC_DRIVE_, followed immediately by the letter for the drive, a space, and the location on the PC file system it maps to. For example: _EPOC_DRIVE_
3、Save the file

Example
指定额外的虚拟驱动d:,将它映射到A盘,可以使用如下设置
_EPOC_DRIVE_D A:\
This would allow the Emulator to treat your PC’s diskette drive as its own removable drive — which is usually mapped to d: on target machines.


[How to configure Emulator memory capacity]
The maximum total heap size can be set in the initialisaton file --- epoc.ini.这就允许开发者能模拟出不同内存容量的目标机器。按以下步骤做:
1、打开epoc.ini文件(在.\epoc32\data\)
2、输入MegabytesOfFreeMemory,后面写上堆上的剩余内存M数,例如
MegabytesOfFreeMemory megabytes-of-free-memory
3、Save the file

注意
If the parameter is not specified, the amount of free memory defaults to 64MB.

If you wish to limit the heap, it is conventional to allow one megabyte for Symbian system use: for example, specify 7 to simulate a 8MB machine.

[How to configure the key map]
KeyMap关键字可以把设备上的真实键映射到PC键盘和facia bitmap上,这些也是在epoc.ini
步骤为
1、Open epoc.ini in a text editor. It can be found at .\epoc32\data\.

2、Enter the keyword KeyMap, followed by the PC keyboard name and the Symbian key code. For example:

KeyMap PC-keyboard-name key-code

3、Save the file.

Example
KeyMap F1 EStdKeyMenu
注意,键代码定义在.\epoc32\include\e32keys.h

[How to configure virual keys]
Virtual keys are represented as rectangular regions of the screen. The VirtualKey keyword allows developers to emulate target machine keys drawn on the fascia bitmap. The process is:
1、打开epoc.ini
2、Enter the keyword VirtualKey, followed by the key code the region is to produce, the shape of the region and the shape parameters. For example:

VirtualKey key-code shape shape-parameters

3、Save the file.

Example
VirtualKey EStdKeyMenu rect 50,60 30,20

注意
1、symbian键代码的名字定义在.\epoc32\include\E32keys.h
2、The syntax will allow different shapes, but the only shape supported is rect which specifies a rectangle
3、The additional parameters are the co-ordinateds for the top left of the rectangle and its width and height
4、A non rectangular key can be approximated with one or more rectangles, for example an elliptical key can be approximated with 3 or 5 rectangles.
5、The cursor will change into a hand when over a virtual key.

[How to specify aliases for device keys]
DefineKeyName可以为设备键代码定制别名,在epoc.ini中用如下命令即可
DefineKeyName alias key-code

Labels:

小糊涂学symbian日记(9)



术语表
  1、(symbian)Active Object
  这是一个从CActive派生来的类的实例,是为了处理异步请求而准备的。

  2、(symbian)Active Scheduler
  这是一个symbianOS的核心对象,其中是一系列当前活动对象的列表,这是主要用来监视他们并且invokes the RunL function on the highest priority one that has signalled that it requires attention with a call to SetActive.

  3、(symbian)CKON
  这是一个为水晶系列symbianOS设备准备的GUI库。

  4、(symbian)descriptor
  这是一个symbianOS中的字符串,有多个类描述它,都是继承自TDesC的。

  5、(symbian)EIKON
  symbianOS中的一般图形用户接口。它为Psion系列5设备定义了一定的外观和外貌。

  6、(OOP)mixin class
  一个接口类。

  7、(OOP)Package
  一组相互关联的类,组织在一个命名域中以便处理。

  8、(symbian)双重构造
  这是构造C类对象的特有方法,以防止异常发生时内存泄漏。

  9、(symbian)Window Server
  这是一个symbian系统线程,用来管理用户输入和屏幕的显示。

  [命名约定]
  1、大部分类名都有一个前缀C、T、R或M,但由静态成员函数单独组成的类是没有任何前缀字母的。

  2、结构名,结构类型可以认为是和T类很相似的,也没有外部对象,我们习惯性在结构名前加前缀T(有时可能是S)

  3、变量名
  *成员变量的名字由i打头(如iMember),这使得它很容易的检查一些与清除有关的规则是否执行了;
  *参数的名字由a打头(如aControl或aIndex);
  *局部变量的名字没有这样的前缀字母;
  *全局变量一般是不提倡使用的,实在要用那就要以一个大写字母打头。

  4、函数
  *如果一个函数调用另一个可能引起异常的函数,那它的名字最好也有L后缀。

  *和异常机制紧紧联系在一起的是清除栈,它在一个异常发生时,会恢复相应在堆上分配的内存。一个要把数据放在清除栈上的分配或构造函数应该以...LC()结尾。

  *一个拥有对象所有权并且可以释放它的函数,应该有个...D()结尾。
  如dialog->ExectueLD(R_BOSS_SETTINGS_DIALOG),这里的ExecuteLD()函数包括双重构造,对话框的运行以及随后的析构。

  5、枚举名
  名字应该有前缀T,每个枚举的成员应该有前缀E。
  枚举的作用域应该放在相关类中
  class TDemo
  {
    public:
     enum TShape {EShapeRound, EShapeSquare};
  };
  TDemo::TShape shape = TDemo::EShapeSquare;

  6、常量名
  常量名应该有个K前缀。


  [class types]
  1、T类型
  *大多数T类型都足够简单而不需要什么构造函数,如果要,那它的功用也就是初始化成员数据。

  *一个拷贝构造函数或是赋值操作都是很少见的,这是因为T类型的拷贝很简单,一般只是成员之间的互相拷贝,而这个正是编译器自动生成的构造函数和赋值操作所完成的功能。当然这些函数有时候是必须的——如果T class是一个模板类,而其模板参数是个整数.如此的话,在一个TX<32>和TX<40>之间拷贝或赋值就需要费更多的周折,那就需要好好的对一个拷贝或赋值操作来进行明确编码了。

  *T类型没有析构函数,因为没有外部资源要被清除。

  *T类型可以在堆栈中被很安全的释放,不需要调用什么析构函数,因为它没有外部资源要去额外处理。

  *T类型在作为函数参数时可以通过值或引用来传递。

  *所有的内建类型都符合T类型的标准,因此也有T前缀,如TInt

  2、C类
  大部分类都是C类,它们直接或间接从CBase类派生。
  从CBase类派生的类拥有如下的特性:
  *它们都是在堆上分配的,并且不应该是其他类的成员。

  *在C类对象的分配中,所有的数据成员都被初始化为binary zero.

  *它们通过指针或引用被传递,除非有明确意图,否则并不需要一个明确的拷贝构造函数或赋值操作。

  *它们都拥有很特别的构造函数,因为异常随时可能发生,所以它们都用上了双重构造,一般的C++构造函数不适合有异常出现的情况,但是ConstructL()函数就可以处理异常发生的情况了。

  *他们有一个虚析构函数,用来做标准的清除工作。

  3、R类
  R类对象拥有如下的特征:
  *真正的对象是由另洋线程或地址空间的server所拥有的
  *真正的对象的处理是对client不见的,既无关的。

  下面是其关键特征:
  *如果一个R对象打开后就必须被关闭(用open和close函数)。一般来说,如果负责打开对象的那个线程被中断了,那和该对象联系在一起的资源就被自动关闭了。
  *他们没有明确的构造、析构或拷贝构造函数以及赋值操作。
  *他们没有一个统一的基类。
  *初始化函数可能有不同的名字,如Open()、Create()或Allocate()等。
  *终止函数也有不同的名字,如Close()、Destroy()或Free()等
  *当R类拥有外部资源时,那就有了清除的需要,这个处理是因地制宜的:)
   

  4、M类
  M类定义了抽象的协议或接口,其具体的处理由派生类来完成
  M类拥有以下约束
  *它们不应该包括任何成员数据
  *它们不应该包括析构或构造函数,以及对=的操作重载。
  
  M类通常包括一系列定义抽象接口的纯虚函数,有些M类可能提供成员函数的处理(尽管有上面的约束)
  M类是symbian平台上唯一使用多重继承的类。


[C++和机器架构]
1、算术类型
------
在大多数机器里,int是位字节,老的机器里可能是16位,新的机器则可能达到64位。

在symbianOS中,TInt和TUint被定义为内建的int和无符号int类型,并且至少是达到32bits。

当你需要具体尺寸时,可以使用下列几种类型:
*TInt32/ TUint32
32位signed 和 unsigned 整型。

*TInt8 /TUint8 /TText8
8-bit的signed和unsigned整型,以及8-bit character

*TInt16 /TUint16 /TText16
16-bit signed和unsigned整型,以及16-bit character

*TTint64
64-bit unsigned integer
当ARM没有支持内建的64-bit运算时,TInt64是使用C++类的完成。

*TReal/ TReal64
双倍精度的浮点数,这个是推荐使用的一般浮点数类型,ARM架构并没有提供浮点的支持,你应该尽量使用整形运算(例如,大多数GUI计算),只有当程序真的需要时(如电子表格程序)才不得不使用这些浮点数类型。

*TReal32
32-bit浮点数,这个更小也更快,不过精度不是很另人满意的。

2、复合类型
--------
*
struct TEg

  TInt iTnt; //offset 0, 4 bytes
  TText8 iText; //offset 4, 1 byte
  //3 wasted bytes
  TReal iReal; //offset 8, 8 bytes
} //total length = 16 bytes
//都是以4 bytes = 32 bits为一个单位的

*通常我们需要一个指向包含任何可能内容的内存的指针,在C++中我们通常用void*指针来表示,但在symbian平台中,我们用TAny* 来代替它。

*在symbian中处理字符串的方法是descriptors。

*参数中传递过多的数据是不明智的,事实上,任何超过2个机器字的数据都是不提倡的,因为这将引起过多的拷贝动作,相反用一个指针或引用来处理这些数据的地址要比传递数据本身好得多。

*
foo(CContainer* aContainer)

TEg s;
TEg* ps=&s;
aContainer->iMember=ps;
} 
这个地方有个严重错误,就是返回了函数的局部变量,注意比较隐含

*
symbian平台中堆栈上的对象的生命周期和标准C++中的非常相似,不过对其的控制有不同,如下:

void Fool()

  CS* s = new (ELeave) CS; //allocate and check
  CleanupStack::PushL(s); //push, just in case
  s->ConstructL(p1, p2); //finish constructing - might leave
  s->UseL(p3, p4); //use - might leave
  CleanupStack::PopAndDestroy(); //destruct, de-allocate

这里表现出四件大事情:
(1)所有的堆分配类都是C开头的,它们都是从CBase继承来的。
(2)使用了清除栈,以在异常发生后对象能被及时清除。
(3)任何可能引起异常的函数都有一个L后缀。
(4)使用new(ELeave)以防止分配空间时出现异常。

3、Descriptors
----------
*我们可以用两个类来满足字符串的基本要求:TDesC和TDes。
TDesC是一个常量、不可更改的描述符,它有一个地址和长度。我们可以象类一样使用它,但是不能对字符串做任何修改。
TDes是一个可以修改的描述符,和TDes比起来,它还有个最大长度,只要不超过最大的长度就可以对字符串做任意的处理。

*描述符有个基本的特性,那就是它们不允许进行超过分配长度的字符串操作,其他类可能支持这点,如CBufBase及其派生类,因为如果TDes溢出了,就可能发生不可预料的错误。

*描述符分几种:
(1)指针描述符
TPtrC只有长度和地址,因此它只需要两个机器字。
TPtr多了一个最大的长度。
TPtrC和TPtr有点象c中的char*指针,但由于长度已经包含在里面了,所有不需要结尾空字符。

(2)缓冲描述符
TBufC和TBuf包含数据在自身,就好象C中的char[]
同样TBuf包含着一个最大长度,如TBuf<12>就是最大长度为12
这两种描述符使用了C++的模板机制,使用了一个整形的参数指明了长度。

*堆描述符
HBufC将数据存放在一个堆单元中。
这有点象C语言的(char*)malloc(length+1),同样这也是在你不得知道最终需要缓冲区大小的情况。因为缓冲描述符总是分配在堆上的,所以它们总是通过HBufC*来使用的,好过直接使用HBufC(这样避免直接使用堆地址)

*类描述符
如果你要描述字符数据那就使用TDes等类,如果要构建一个unicode版本,那就使用TDes16等,如果要描述字节数据,那就使用TDes8等。

(参考图)



[堆栈使用]
每个线程都有一个8KB大小的堆栈,我们得小心的管理它:
1、避免直接的值拷贝,除了基本类型
2、在堆上生成大的对象或数组,而不要在栈上生成
3、适当的定义变量以使他们的生命周期为最短

[函数重载]
如果函数拥有缺省的参数,而且如果调用者可能经常使用缺省参数来调用,那就建议重载这个函数,这是因为编译器每次都要提供一个缺省的参数,那在函数调用时就会造成多余的代码产生。

For example, if you have
void FunctionOne(TInt aInt=0);

而在代码中经常这样调用它
FunctionOne();

那建议定义一个
void FunctionOne();
它可以这样处理
void FunctionOne()

  FunctionOne(0);


[内联函数]
在以下情况使用最好:
1、getter and setters for one- or two-machine word quantities: for example, inline ConEnv() const { return iConEnv; };

2、trivial constructors for T classes:
inline TPoint::TPoint(TInt aX, TInt aY) { iX=aX; iY=aY; };

[Assert]
有两个宏用来在函数中进行断言:
  __ASSERT_ALWAYS用来捕捉运行时的非法输入,release和debug编译模式下都可以执行。
  __ASSERT_DEBUG用来捕捉编程错误,只能用在debug编译模式下。

Labels:

小糊涂学symbian日记(8)


Symbian系统是个非常紧凑有效的平台,这点不光能从其精小的内核模块能看出来,而且在编程时我们时常能有所感悟,发出会心的一笑,在symbian中,这种新的体验是层出不穷的:)

做为一个嵌入时系统,其对程序的谨慎处理和小心翼翼要远远超过其他的系统,由于不是PC,其系统资源有限,我们的程序运行时并不知道自己是否能够被分配到足够的内存以便运行,于是程序很可能出现异常(如分配资源不足),symbian的解决方法是在每个可能引起异常的函数后面加上L后缀,这点下面还要多次提到,而且,这类异常的处理(通常是由捕捉模块处理的)系统会在恰当的地方提供,我们一般无须操心:)

以后的学习中,我们会逐渐发现symbian中的对象构造不是单纯的构造函数操作的(于无影无形间完成)而是大部分靠了NewL/NewLC这两个函数来完成,这两个一般是做为类的静态成员函数被调用的,以便直接由类使用。其中如果该对象是由指针变量指出的,那就用NewL,如果是一个自定义的变量(通常是局部变量了)那就要用NewLC来完成,他们间的区别是什么那?就是NewLC的C后缀指明了是在堆上创建一个新的对象,并将其压入清除栈,这样如果程序出现异常时,就可以妥善处理指针和指针所指向的对象了:)

上面提到了清除栈,这里就要好好的看看,在symbian系统中它真的是太有用了,没有了它我们的程序可能造成大量的内存泄露,为什么那?因为上面说过symbian系统可以很好的自己处理异常,它是靠的Active Scheduler,它主要是将有问题的变量都删除掉,但这个时候如果变量是个指向已经在堆上分配了对象的指针,那贸然的将它删除,会造成堆的空间泄漏,这样是symbian这个寸土寸金的系统所最不愿意看见的,那怎么办那?我们有清除栈就好办多了,它含有的是那些当异常发生时需要被释放的对象的指针。如此一来,发生异常时,我们就可以在异常发生时避免内存的泄漏了,因为现在,对象的删除也是自动的了:)

这里插播一段捕捉的介绍,i.e.TRAP
TRAP主要看看SDK的,很有帮助,
TRAP(_r, _s) {TTrap __t; if (__t.Trap(_r) == 0) {_s;TTrap::UpTrap();}}
我们是在一个异常捕捉里执行_s的,这个是一个c++段.。_r必须是一个TInt类型的值,我看过了这个是singed int的定义。也就是int。32位的。而且是要先声明。在宏外,相反,TRAPD就不用了,它在内部声明了,其他一样。如果有任何的c++段发生异常退出了,那退出的返回值就赋给了_r(事实上,是从User::Leave()的返回值)正如前面讲的,发生异常时,系统调用的是User::Leave,我们看出来了,异常发生时的顺序也就是从语言到User::Leave到Trap) ,否则它就是KErrNone这个值。

这个时候,一般系统为c++段准备一个清除栈,任何在_s中的语言发生异常时,在清除栈中的对象都会被清除掉。

Trap harness虽然很好,但是不应该大量使用它,从可执行代码的规模和执行速度来看,这个东西的成本很高,如果使用不当的话,很可能导致遗漏错误。一般来说,在函数名的最后部分加上L,从而允许异常退出,不是更好的选择时我们才用到Trap.


上面讲了,只有自定义的变量才需要C后缀的创建,这是因为他们没有自己的对象删除处理动作,而做为成员变量的对象就不需要C后缀的创建了,这是因为他们有自己的析构函数,可以自己删除对象,如果再多此一举,那就造成两次删除,就错了。

symbian中的函数发生异常时,并不是返回什么错误代码——这是其他系统如windows/Unix等所喜欢采用的,对不起,您在symbian中看不到,通常它就直接异常退出了,这时系统将调用User::Leave(),由此我们得以到函数的异常捕捉模块中去,当然这些都是自动的,Framework会做恰当的处理,我们所要做的就是在可能出现异常的函数后面加上后缀L来确保异常处理。

上面说了那么多,可见还没怎么招那,symbian就给我们个下马威,什么都不急先给我把异常学学好,呵呵。一般的异常也就是由于分配空间不足而引起的,比如c++中的new操作,如何避免那?我们前面讲了NewL和NewLC,在symbian中,原始的new现在有了新的替身,那就是new(ELeave),有了它,即使分配内存出错,我们也能正常退出了
譬如说
CSomeObject* myObject = new CSomeObject;
if (!myObject) User::Leave(KErrNoMemory);
就可以替换为
CSomeObject* myObject = new(ELeave) CSomeObject;

清爽怡人吧:)有了ELeave参数,就可以做到new对象时的大无畏:)

不过爽是爽,但这个只是针对单个对象的构造的,如果是复合对象那?(就是一个对象中有个成员指针指向另一个对象的情况)那就产生了新的问题,如果是构造子对象发生了异常,那父对象是如何处理的那?会不会造成内存的泄漏?

这就涉及到symbian中的一个精彩点——双向构造的知识了,总的来说是在NewL和NewLC中给解决的。以后会详细讲到:)

当然,百密一疏,我们自己还要检查内存是否有所泄漏,这时,最好使用__ASSERT_DEBUG来进行测试,

它可以不受限制的检测函数中的参数、指针等,不过只是在debug模式下使用的,如:
CMyClass::Function(CTing* aThing)

  __ASSERT_DEBUG(aThing, Panic(EMyAppNullPointerInFuncton));


这句话的意思是在检测到aThing为false时,我们就调用EMyAppNullPointerInFunction。

接下来,我们谈谈symbian的主要框架类:
它有四个基本框架Application、Document、AppUi和View

Application属于应用程序的启动对象,就象是MFC的CWinApp类,它的基类是CAknApplication;
Document是为存储数据用的,一个应用程序必须要有一个Document文档类的实例,这是加载AppUi的唯一要求,它的基类是CAknDocument。
AppUi,负责处理于应用有关的事件,比如说是菜单选择、文件的打开关闭等。注意它将图形绘制和屏幕上的交互操作委派给自己所拥有的Views,也负责这些view之间的切换。它的基类是CAknAppUi或CAknViewAppUi,我认为这个类有点象MFC中的CFrameWnd。
Views,这个比较复杂,它负责显示屏幕上那些可以和用户交互的数据,并把用户的操作反馈给AppUi。

它可以继承自CCoeControl 或CAknDialog或是CAknView,也就是symbian三种基本结构,反正显示的任务就交给它了,不管是传统、对话框还是视图结构。

再提纲契领的说一下,是Framework创建Application,由Application创建Document,再由Document创建出AppUi,然后AppUi负责拥有Model/Engine和View。(symbian是有界面和引擎之分的,所谓引擎就是在自己类库中实现的,主要体现应用程序的功能,处理算法等,有个术语就是Model/Engine。)

注意,构造并显示一个对话框时要使用ExecuteLD,使用这个函数时,就不应该再把对话框置于清除栈中了,因为其保证了当异常发生时会释放所分配的内存,一旦用户通过按ok或cancel退出时,ExecuteLD就会返回。

如:
void CDlgappAppUi::ConstructL()

BaseConstructL();
iAppView = new (ELeave) CDlgAppMainView;
iAppView->ExecuteLD(R_DLGAPP_MAIN_DIALOG);
AddToStackL(iAppView);


CEikonEnv是一个生成Uikon控件和那些处理这些控件的应用函数的环境,我们可以将它视为是MFC编程中经常出现的CWinApp* pApp = AfxGetApp()。View视图通过调用OfferKeyEvent()来将这个事件分配给其包含的各控件,做法就是在控件栈里向先传,控件栈里后进的控件先接受这个时间,如果不处理就返回EKeyWasNotConsumed,然后按按键事件就传给下一个控件,实在没有控件接受了,那就到控件栈的最底层AppUi,其他控件不处理的事件都归它管:)

CCoeEnv提供一个生成控件的活动环境。它可以提供一个激活的对象和一个active scheduler,当一个标准事件发生后,active scheduler调用了CCoeEnv::RunL()来处理,当一个重绘时间发生后,则调用CCoeRedrawer::RunL()函数处理。

接着谈谈程序的本地化实施,只有两个重要法则要记住:一是不要在c++源文件中嵌入文本,另一个就是不要对数据缓冲区尺寸进行绝对编码(在众多的数据转换中就看出来了)。

在程序中有众多的约定束成,我们就来一一看看:
1、Assertions
  *一个函数体可以由一系列的_ASSERT_ALWAYS()宏开始,以用来检验参数的有效性。
  如果函数不属于公共API,那建议使用ASSERT_DEBUG()来代替。
  
  *在函数的最后,最好是对返回值作个检查,来确认函数是否完成了它应该完成的任务。可以使用ASSERT()来处理这类测试。

  
2、C class
  *C类对象不能被做为一个值来传递或者是返回,但可能通过指针或者引用来使用它。

  *C类必须有个析构函数,那是CBase的一个虚函数。如果是空的,那也要使用注释如//empty

destructor

  *应该在头文件中描述所有的类,如果你要隐藏一个类,那就嵌套使用它们。

  *任何从CBase中派生的类都必须有个C前缀。
  
  *所有的M类只应虚函数,不能有其他数据。

  *C类对象必须被new创建,或者做为另一个C类的成员变量出现。

3、注释
  *一个注释块由一个“/**”开始并且由“*/”结束,每行都以*开始。

4、编译错误
  *你可以没有编译时错误,但不可避免的要发生警告,这是因为你使用的库的原因。

5、常量
  *如果是常量那就要在前面加上前缀K。

  *常量时常被做如下的处理:
  (1)、在一个类中使用枚举的形式(e.g. enum { KMagicNumber = 3};
  (2)、类中的静态成员(e.g. static const TReal KMagicNumber;)这个只能在一个cpp文件中定义一次。Note that this only works for built-in types, and that TInt64 is not a built-in type.


6、构造
  *C-class是你能new操作的唯一的类,并且这是他们被构造的唯一途径。
  *注意,所有的C-class成员变量在创建时都是空的。

7、构造函数
  *ConstructL()永远不能为虚函数,since a prtially constructed object isn't a polymorphic entity.

  *在C class中的构造函数和ConstructL()必须作为保护或私有的成员函数。

  *在C class中必须有一个NewLC()函数,除非它是一个嵌套的类才有机会不遵守这条目。NewL()是可选的,并且总是应该根据NewLC()来实现。(区别也就是最后多个出栈的操作:CleanupStack::Pop()函数)

  *在ConstructL()的实现中,你必须首先调用基类中的ConstructL(),如BaseConstructL()。

  *NewL()和NewLC()在c class中必须做为静态函数,通常是公共函数,这个是不言自喻的了:)

  *拷贝构造函数在symbian中失去了它的作用。

8、析构
  *如果洋对象被析构两次,那将是个严重的错误,在什么情况下会发生这样的情况那?一般如果一个异常发生时,在清除栈中的对象和其析构函数都遭到调用清除时就会发生了。

9、析构函数
  *在一个析构函数中或者是析构函数调用的函数中,我们并不能保证对象都被适当的构造了,因此你必须检查对象的状态并且能应付各种状况。你不必抛出任何异常。

  例如
  MyClass::~MyClass()
  {
    if(iObject) //might be NULL if constructor failed
    {
      iObject->Close();
      delete iObject;
      iObject = NULL;
    }
    delete iFred; //delete NULL is allowed/C++中是不管你null不null的,null就do nothing
    iFred = NULL;
  }

10、DLLs
  *在DLL中不能含有任何全局的非常量性数据,这包括静态成员变量。你能含有的唯一全局数据就是内建的静态常量们,既没有构造函数的那些变量:)
11、文件
  *.hrh文件中存放的是控件ID及其定义。

  *.mmp和.rss文件的命名:如果只有一个,那就用project名(如animation.mmp),如果有多个的话,那就用subproject名(如engin.mmp)

  *公共头文件应该放在一个inc目录中,或者是inc。

  *所有的文档都应该在subdirectory,或是 docs

  *所有和.aif文件有关的文件都必须放在aif目录中。(To avoid name clashes with the .rss file)

12、flow control
  *Switch语句总应该有一个缺省的cause,如果default不会发生,那就应该用assert来达到效果。
  如switch(value)
  {
   case 0:
   case 1:
   case 2:
    DoSomething();
    break;
   case 4:
   case 5:
    DoSomethingElse();
    break;
   default:
    ASSERT(EFalse);//cannot occur
  }
  *永远不要用goto语句

  13、函数
  *不要使用缺省参数,如果你的函数有n个缺省参数,那最好还是把它改写为n+个该函数的重载版本。因为呵呵,带有缺省参数的函数会有冗余的代码。

  *尽量用引用来表现参数。
  AddControl(Control& ctrl, ControlObserver* observer);

  最好设计为:
  AddControl(Control& ctrl, ControlObserver& observer);

  *不要返回错误代码,使用异常机制来代替尽量。

  *所有的内联函数都应该放在inl文件中,记住要加关键字inline。

  *如果函数名是由一个C后缀结尾的那就表明这个函数最后返回的是指向已放入清除栈的对象的指针



  14、头文件
  *在头文件中,要使用标准的anti-nesting语句,如
  #ifdef _CMYCLASS_
  #define _CMYCLASS_
  body of header
  #endif

  *注意要有很清晰的访问控制权限:首先是public成员,然后是protected成员,然后是private成员,在成员函数之后列出数据成员,每个section中都应该先列出特殊的成员函数(如NewL/NewLC/constructors/destructors/operator)。把函数按功能分类组合。把所有的overridden函数都放在一起。还要把出处标记起来(如//from CMyBase Class),并且不要在这里写上virtual关键字。

  如//from CCoeControl
  void Draw() const;

  //from MObserver
  void Notify();
  void StopObserving();

  *头文件的顺序是首先是系统头文件(用<>来指明),然后是本地头文件(用""指明)

  15、继承
  *不要使用多重继承,除非是为了设计接口(即M类),接口类不能包括成员变量。下面会讲到的:)

  *永远不要使用私有或保护式继承。

  16、局部变量
  *局部变量最好在使用他们的地方的附近定义。下面还会举个例子,以表示这样最有效率,避免不必要的对象构造。

  *不要使用静态局部变量。

  17、M类
  *注意M类对象不能通过值返回或传递,使用指针或是引用最好(这个取决于你是否要接受一个NULL值,如果不要,那就用引用)

  18、宏
  *避免使用宏,最好用enum和const static来代替#define常量,内联函数能代替大多数使用的宏的场合(这里讲的不是太清楚,以后还会说到的)

  19、成员变量
  *It is better by far to encapsulate member data than return references to it . If you must return references to member data, it is better to return a const reference. If you must return a non-const reference, you need to provide both a const and a non-const (overloaded) accessor function, or you won't be able to call it from a const member function!

  *无论你何时删除成员变量,你都要把他们设置为NULL,这个是个安全措施。

  *千万不要使用公共成员变量。

  20、操作符
  *赋值操作必须能处理自赋值,如a = a;

  21、参数
  *在源文件中使用与你在头文件中定义相同的参数名。

  *不要使用省略的操作,如func(...)

  22、指针
  *不要使用指针运算

  *不要使用强制的转换,不幸的是如果你使用的库强制你返回某个特定的值,在这种情况下要把强制转换封装在一个函数调用中,并使用c++风格的形式,而最好不要使用c风格的(如dynamic_cast)

  *初始化所有的指针为NULL(注意不是0)

  *如果你在存储数据或是处理字符串时,要考虑使用descriptors(以后会提到)和一些特定的类来代替指针。

  *如果要检查非空指针,那最好用if(ptr),而不是if(ptr!=NULL)。

  23、R类
  *你不能将R类对象放到清除栈中,因为他们不能有析构函数。

  *你不能通过值来传递R类对象,因为R类对象为系统资源,你不应该试图去拷贝它们。

  *R类必须在构造函数中处理所有的变量初始化,you cannot rely on members being zeroed.

  *R类对象不能通过new来生成。

  *一般来说,应该用“open”和“close”来处理R类型对象,你可能会在代码中看见这样的语句:
  RFile file;
  file.OpenL();
  TRAPD(err,file.ReadL());
  if(err)
  {
  file.CloseL();
  User::Leave(err);
  }
  //etc. etc.

  以上的代码很没有效率,最好改为
  class SRClassCleanup
  {
   public:

   static void OpenLC(RFile& aFile)
   {  
    User::LeaveIfError(aFile.OpenL);
    Cleanupstack::PushL(TCleanupItem(ReleaseRFileOnCleanup, &aFile));
   }

   private:

   static void ReleaseRFileOnCleanup(TAny* aObject)
   {
    REINTERPRET_CAST(RFile*, aObject)->Close();
   }

   //repeat for all R-Classes in your project.
  };

  由此我们简化前一个列子,那取决于我们是否从SRClassCleanup派生的。
  RFile file;
  OpenLC(file);
  file->ReadL();
  //etc. etc.
  Cleanupstack::PopAndDestroy(); //close file

  24、资源文件
  *应该将资源结构放在.rh文件中。

  *在资源文件中,将资源文件名写成小写。

  25、语句
  *不要在同一个语句中修改两个不同的对象。

  *Break语句、多返回值和continue语句可能使你的代码更加清晰,如果不能达到这个效果你最好不要使用它们。

  26、字符串
  *不要使用字符串常量,要使用资源文件里的内容。

  27、结构
  总是用类来取代结构。

  28、T类
*Since T class objects cannot be pushed onto the cleanup stack, they must not have a destructor as you cannot ensure that it is called in all circumstances.
  
  *T类对象不能通过new来生成。

  *T类对象可以作为一个值来返回或者是作为引用(或值)来传递。

  29、TBool类型
  *编译器不允许把TBool类型同ETrue和EFalse来比较,建议使用诸如if(!Visible())来代替。

  30、模板
  *模板绝对不能和虚函数结合起来使用

  31、类型
  *一定要使用symbianOS类型,如用TInt来代替int

  *不要使用精确的类型格式,如TInt16,除非是在一些罕见的情况中(如从资源文件中读取资料)

  *在基本类型间转换时,总是使用explicit casts(它将代码含义表露得更清楚,并祛除一些警告)

  *注意枚举类型总是由一个大写的T前缀来开头的,而枚举的成员则是大写的字母E前缀。

  32、联合
  *不要使用联合类型,除非你得写些面向机器的低层代码。

  33、变量
  *一行最好只声明一个变量。

  *在symbian中变量名常常跟着特有的前缀(如i, a, C, M, T, R, E, K, S),还可能跟着些后缀如(L, C, D),只有那些局部变量才使用小写字母。

  *参数名前应该有前缀a,后面跟个大写的字母。

  *成员变量也应该有前缀,为i,后面也是跟大写字母。

  34、虚函数
  *永远不要在一个构造或析构函数中调用虚函数。  

  *不要overload虚函数。

  *当你处理一个虚函数时,会经常调用基类的相关处理,譬如:
  SportsCar::DoSomething()
  {
    FastCar::DoSomething()
    //code here
  }//注意基类的调用只应该在头或尾,这样好维护。

  *虚函数不要写成内联的形式,除非是析构函数。

  *当你覆盖一个虚函数时,不要改变它的访问权限(public/protected/private)

Labels:

小糊涂学symbian日记(7)


多重继承的使用有如下的接口使用的限制,C++的多重继承机制并不复杂。This is perhaps recognised by the OO community now.例如Java就只允许单个继承,but the interface and extends keywords support the same facilities as are provided by M classes.下面展现了更多的限制细节:

首先,M classes主要是用来定义协议的,而不是用来完成它的。特别要指出的是,它们不具备任何成员数据。这个限制导致某些特定类型的行为(譬如说是活动对象)在接口中不能被很好的封装,但是必须按照传统的方法来继承。

其次,一个c类可能从另一个c类中派生,或者是多个M类,这个限制可能导致这样一个事实,那就是多重继承只能被使用在接口中。It implies that it is still possible to uniquely identify a primary inheritance tree(the C class hierarchy), with interfaces as a side feature.如果允许任意的多重继承,那将不可能再找出一个主继承树,这个限制也证明了C类将不可能成为一个multiple base class,which makes it unnecessary to consider the complications of multiple base class inclusion, virtual inheritance, etc.

第三,C类必须是在基类列表中首要的特殊类,This emphasises the primary inheritance tree and, importantly, it makes convnsions between any C class(including those with interface)and void* pointers freely possible.Admittedly, the C++ standards do not mandate that object layout follows the order in which base classes are specified, but in practice this is the case for most compilers, including those used for the Symbian platform.

第四,no M class may be mixed in more than once in any class, either as a direct base or as a base of any of its primary base classes. To put it another way: when deriving a C class CD from a base class CB, you may not mix in anyMclass MP which has already been mixed into the derivation of CB. This reflects the fact that CB already supports the protocol defined by MP: there is nothing to gain from mixing in this protocol class again. In addition, it makes it unnecessary to consider the complications of multiple base class inclusion, virtual inheritance, etc.

最后,although it is legal to derive one M class from another, it is not legal to include a protocol twice by including both it and a derived protocol into a C class, at any point in the C class’s base class graph. To put it another way, if there is a class MD derived from MB, then a C class cannot include both MB and MD. This is because any function in the C class which provided an implementation of MBprotocol could conflict with the implementation of MDprotocol.


[Descriptors]
Overview
---------
Descriptors are both fundamental to the Symbian platform, and an excellent example of the difference of approach between non-OO and OO designs.

在C中,一个字符串看起来时如下定义的:
char* hello = "hello";

导致产生一块包含六个字节(‘h’, ‘e’, ‘l’, ‘l’, ‘o’, ‘’)的内存。

Abstract descriptors
---------------------
我们可以用两个类来满足字符串的基本需求:TDesC和TDes。

TDesC时一个常量般,或者时不可修改的描述符。它有一个地址和长度。Using a class like this, you can do any manipulations to a string, provided they do not alter the data. As a consequence, the TDesC class has many non-modifying functions.

TDes是一个可以修改的描述符:it has, in addition, a maximum length.这就允许数据被扩展或压缩,只要不超过最大的长度,可以任意的处理。As a consequence, TDes has many modifying functions which allow string manipulation.

描述符有个基本的特性那就是他们不允许超过已分配长度的字符串操作。其他类可能支持这点,如CBufBase和起派生类。但如果TDes溢出了,那就可能发生不可预料的错误。

Pointer descriptor
-------------------
抽象的描述符有几种,其中最简单的就是指针描述符。
TPtrc只有长度和地址:它只需要两个机器字。一个TPtr多了一个最大长度,and so may be used to describe a buffer which is perhaps only partially allocated。

TPtrC和TPtr有点象C中的char*指针,但是由于长度已经包含在描述符中了,所有没有必要包含一个''空白字符。

Buffer descriptors
--------------------
Buffer descriptors,TBufC和TBuf包含数据在自身,就好象C中的char[]
同样TBuf包含一个最大长度,如TBuf<12>这样最大长度就是12。
这两种描述符使用了C++的模板机制,使用了一个整形的参数指明长度,如上:)

Heap descriptor
----------------
堆描述符,HBufC将数据存放在一个堆单元。

这有点象C语言中的(char*) malloc(length+1),同样,这也是用在你不得以知道最终需要缓冲区大小的情况。因为buffer descriptors是分配在堆上的,所有他们总是通过HBufC*来使用,好过直接使用HBufC(这样避免直接使用堆地址)。

Descriptor classes
---------------------
描述符使得描述字符数据变得很容易。当你要堆字符串使用描述符时,记得应该总时使用TDes等类,如果时要构建一个unicode版本,那就使用TDes16等。
如果你要描述字节数据,那最好使用TDes8等。

[Thin templates]
C++模板机制允许你去编译一个带参数的类,这个对于集合类来说很有用的,如数组,类的描述为:
class CArrayFixFlat ...
指明一个数组类的族群,可以包含任何类型。模板是可以被实例化的,如:
CArrayFixFlat* iControls;

这里描述了一个CCoeControl*的指针数组,其他集合类使用同样的方法,如TPckgBuf, TSglQue等。

模板也可以使用其他参数,如一个整数:
class TBufC ...;

那这个模板可以被实例化为TBufC<20> name;

用模板来实现的函数是很强大的,如果没有模板,那集合类就可能要使用void *指针,作为结果,这可是类型不安全的。

Templates can be difficult to manage. A template really defines a whole family of classes. Each member of that family that is instantiated requires its own object code. Avoidance of object code duplication is a difficult issue in a C++ implementation. For the Symbian platform, object code duplication must be avoided at all costs — the solution used is the thin template idiom.

[Code efficiency]
Stack usage
------------
每个线程都有一个8KB标准大小的堆栈,我们得小心管理它,因此:
1、避免直接的值拷贝,除了基本类型:)
2、在堆中生成大的对象或数组,而不要在栈中生成。
3、适当的定义变量以使他们的生命周期为最短。

上面第三点可以通过下面的例子来说明
void ABadFunction()

  TBigObject Object1;
  TBigObject Object2;
  TBigObject Object3;
  GetTwoObjectvalues(Object1, Object2);
  Object3 = SumObjects(Object1, Object2);
  FunctionWithUnknownStackOverhead(Object3);


在上面的代码中,Object1和Object2的生命周期一直持续到FunctionWithUnknowStackOverhead()的执行,而事实上他们在这里是不必要的,他们应该在这个函数调用前就释放掉,我们可以如下来实现:
void ABetterFUnction()

  TBigObject Object1;
  GetTotalObjectvalues(Object1);
  FunctionWithUnknowStackOverhead(Object1);


void GetTotalObjectvalues(TBigObject &aObject)

TBigObject Object1;
TBigObject Object2;

GetTwoObjectvalues(Object1,Object2);
aObject=SumObjects(Object1,Object2);


将代码分在两个函数中,你可以得到无浪费的stack。

Function overloads
--------------------
如果函数拥有缺省的参数,而且如果调用者可能经常使用缺省参数来调用,那就建议重载这个函数,这是因为编译器每次都要提供一个缺省的参数,那在函数调用时就会造成多余的代码产生。

For example, if you have
void FunctionOne(TInt aInt=0);

而在代码中经常这样调用它
FunctionOne();

那建议定义一个
void FunctionOne();
它可以这样处理
void FunctionOne()

  FunctionOne(0);


真不愧时有效率的symbian连c中的缺省函数可能造成的累赘都抛弃了,一切轻装上阵了:)


Pointers and references
-------------------------
使用引用作为函数的参数可能比使用指针更有效率。This is because the compiler has to preserve the value of the null pointer through all conversions.

总之是函数参数的传递,我们使用引用更加能使代码有效。

Floating point maths
--------------------
浮点数运算很耗时,如果可以,我们尽量使用整数。
For example, given two TInts, aTop, and aBottom, instead of:

TReal a = (TReal)aTop;
TReal b = (TReal)aBottom;
TReal c = a/b+0.5;
TReal result;
Math::Round(result,c,0);
return (TInt)result;
you should use

return((2*aTop+aBottom)/(2*aBottom));

Inline functions
-------------------
内联函数是为了提高代码执行速度,避免函数调用的开销而设计的,而且可以保留(虚假的)函数模块性。在使用他们之间,有两个地方你需要留意:

1、代码的紧凑性:内存资源的限制意味着牺牲速度的函数比大体积的内联代码更好。
2、二进制兼容性:一个内联函数的修改可能导致兼容性的更改,This is important if your code is going to be used by other developers.

下面是大多数应该使用内联函数的情况:
1、getter and setters for one- or two-machine word quantities: for example,

inline ConEnv() const { return iConEnv; };
2、trivial constructors for T classes:

inline TPoint::TPoint(TInt aX, TInt aY) { iX=aX; iY=aY; };
3、in the thin-template idiom
4、certain other operators and functions, possibly templated, whose definition, not subject to change, is to map one operation onto another, for example,

template inline T Min(T aLeft,T aRight)
{ return(aLeft
No test for NULL pointer when deleting object
-------------------------------------------------
C++指定deltete 0不做任何事情,所以别写出这样的代码:
if (iX)
delete iX;

真是好symbian,不要求出现一点冗余的代码,这里的if当然不可不必出现:)

[Defensive programming]

Overview
---------
为了帮助开发者在开发过程中更容易的发现潜在问题,symbian提供了宏来测试函数和类中的error conditions。

Casting is one well known source of hard-to-find errors.

Testing conditions with asserts and invarians
-----------------------------------------------
较早的捕捉到错误的一个方法就是在函数开头和结尾来判断一个条件是真还是假,and raise errors if they are not.
有两种用来支持这种编程风格的机制:
1、asserts
2、class invariants

——Asserts
  有两个宏用来asserting specific conditions in functions:
  __ASSERT_ALWAYS用来捕捉运行时的非法输入,release和debug编译模式下都可以执行。
  __ASSERT_DEBUG用来捕捉编程错误,只能用在debug编译模式下。

——Class Invariants
  Class invariant are used to test that an object is in a valid state.They are used only in debug builds.
  1、为重要的类使用__DECLARE_TEST来定义class invariants,The class must supply functions that specify its allowed stable states.
  2、要在执行函数之前确定函数是否处于稳定状态,call the invariant at the start of all public functions using __TEST_INVARIANT.
  3、For non-const functions, you can ensure that the object has been left in a stable state by also calling the invariant at the end of the function.

——Casting
  Casts, 在其他操作系统中,使用时将没有警告。If a cast seems to be needed, check that this does not reflect a design weakness.

  Symbian平台提供它自己的宏来封装C++ cast操作:
  1、REINTERPRET_CAST
  2、STATIC_CAST
  3、CONST_CAST
  4、MUTABLE_CAST

  注意,从6.0版本之后,开发者利用原始的C++ casting operators要好过使用这些宏,事实上,这些宏时为早期支持cast而做的。

  更加复杂的dynamic_cast将不会在EPOC中出现,因为它根本不支持运行时类型识别(RTTI),这就说明symbian的对象动态生成以及永久存储等都有它一套自己的实现基础机制。

[Static date]
Writeable static data in DLLs
---------------------------------
Symbian平台时专为ROM-based computing而设计的。DLL时长驻ROM并且不能被写入。Although DLLs may in some cases be RAM loaded,the Symbian platform makes the conservative assumption that no DLL can be written to. 因此在symbian平台中DLL时没有数据段的,从而我们得到一个结论那就是没有DLL能包含可写的静态数据,不管有没有初始化。

静态数据是任何在一个函数之外所描述的数据

TBufC<20> fileName;
void SetFileName()

...

大部分程序都要大量使用这种数据,在模拟器上,以上的运行不会引起什么不适,这是因为有windows的DLL机制,在它的下面,数据是可以被写入的。但这样的代码将不适合在非模拟器上运行。编译将会跟随着一个petran tool的警告而出错,这是在非模拟器目标编译的最后一个环节。产生的消息可能如下:
ERROR: DLL 'MENUITEMS[10008ADO].APP' has uninitialised data.

你必须注意不能有writeable static在你的DLL中,以避免这样的错误。

因为在某些情况下,在DLL中保存数据是很有用的,因此symbian平台也提供了一个机制来允许DLL在单个线程的基础提供私有数据的存储管理,比如线程局部存储。A per-thread mechanism is chosen to avoid potential ambiguities which otherwise arise over which of potentially many users of the DLL should see the data.

Porting strategies
--------------------
完全避免可写的静态数据也许是个很艰巨的任务,在一般情况下,你需要将所有的静态数据都放在一个结构中,然后通过一个指针访问这个结构。

下面有几种方法可以使你的指针有效:
1、把它做为函数的参数传递到需要它的地方。这种方法可能包含大量的代码转换——不光使调用这些可写静态数据的函数,而且包括那些间接调用这些函数的的函数。

2、使用DLL线程局部存储,这也会引起问题,因为这样的DLL函数要比一个指针引用复杂许多。

3、把指针存储在各种结构和类中,以便需要他们的函数能有效的使用他们,这也会引起代码的改动,但可以避免间接函数调用的缺点和线程局部存储的缺点。


实际上,经常使用上述三种方法的混合物,以达到代码和执行效率的平衡。

Desing Strategies
--------------------
新的代码要考虑到避免可写静态数据,在面向对象系统中,it is not considered unusual to pass pointers to global data along with object references, and the this pointer available in every non-static member function gives access to all members of that object.

只要适当的运用,大部分使用静态可写数据的情况使可以避免的。

Non-obvions use of wirteable static data
-------------------------------------------
有时候,静态可写数据会出现在一些暗处,例如:
const TRgb9(255, 255, 255) KRgbWhite;

定义了一个在symbian代码中不会被写入的常量,不管怎么说,当TRgb使一个类时,它的构造函数会被调用,为了构造出这个常量,因此这个常量还是可写入的。

最好写成这样:
#define KRgbWhite TRgb(255, 255, 255)

This will cause the class constructor to be invoked at the point of use, rather than when the DLL is loaded.

Writeable static data in EXEs
----------------------------------
在exe文件中,是没有静态可写数据的限制的,如做为server的exe就可以使用静态可写数据。

Labels:

小糊涂学symbian日记(6)

C++ and machine architecture

Arithmetic types
------------------
An int is usually implemented as the natural machine word size of the particular implementation. 在大多数的机器里是32位字节,在老的机器里可能是16位,在一些新的机器里甚至达到64位字节。

同样,一个指针(如void*)也通常被当作一个机器word来对待,不过有的机器里它可能更复杂。

It is assumed that the symbian platform is implemented on a machine with a 32-bit or greater machine word, and 32-bit pointers.The types TInt and TUint are typedefed onto the built-in int and unsigned int types, and are guaranteed to be at least 32 bits.

When you need a specific size, regardless of implementation, use a sized type.Serveral of these are available:

——TInt32 / TUint32
  32位signed and unsigned integer
  In each case, the representation is a 32-bit machine word which, in the ARM architecture, must be aligned to a four-byte boundary. The compiler ensures that this is always the case.

——TInt8 / TUint8 / TText8
  8-bit signed and unsigned integer, and 8-bit character
  In each case, the representation is an 8-bit byte, which has no specific alignment requirement.

——TInt16 / TUint16 / TText16
  16-bit signed and unsigned integer, and 16-bit character
  In each case, the representation is a 16-bit halfword, which should be aligned to a
two-byte boundary. The ARM architecture (prior to ARM8 and the Thumb instruction set) is relatively poor at handling 16-bit quantities, and their use is recommended only when space is at a premium, or for Unicode text handling.

——TInt64
  64-bit unsigned ineger
  Since ARM has no built-in support for 64-bit arithmetic. TInt64 is implemented as a C++ class.

——TReal / TReal64
  双倍精度的浮点数,IEEE754 64-bit representation
  这个是推荐使用的一般浮点数类型,ARM架构并没有提供浮点的支持,你应该尽量使用整型运算(例如,大多数GUI计算)只有点程序真的需要时(如电子表格程序)才不得不使用这些浮点类型。

——TReal32
  32-bit floating point
  This is smaller and quicker, but should only be used when space and /or time are at a true premium, as its precision is unsatisfactory for many application.

Compound types
----------------
Apart from classes, C++ inherits from C various other types of compounding

A struct maps an area of memory

struct TEg

  TInt iTnt; //offset 0, 4 bytes
  TText8 iText; //offset 4, 1 byte
  //3 wasted bytes
  TReal iReal; //offset 8, 8 bytes
} //total length = 16 bytes
//都是以4 bytes = 32 bits为一个单位的

结构被看做是T types:那是因为他们都没有自己的堆分配资源(如C type classess样)

一个数组包括很多built-ins or other types
TInt a[32]; //32 TInts, = 128 bytes

S b[3]; // 3 S's, = 48 bytes

使用C++数组的最大缺点就是没有对其索引的自动检查。基于这个理由,也为了支持更多复杂的container,C++已经开始发展STL。symbian并不支持STL,但有它自己的一套有效的解决方案。

Pointers
----------
指针存放了内存地址。
S* ps; // pointer to an S

ps = &s; //take address of existing S

通常我们需要一个指向包含任何可能内容的内存的指针,在C++中我们通常用void*指针来表示,但在symbian平台中,我们用TAny* 来代替它。

Strings
----------
在C++中,基本的字符串就是一个字符数组:
char* hello = "hello";

这段做两件事情:首先,它在内存中为'h','e','l','l','o',''分配六个字节,其次,它设定一个指针hello来指向这一内存的首地址。

在c++库中,处理字符串要顾及到其越界后可能需要的扩展,在symbian中的解决方法是使用descriptors

Functions
--------------
函数是在程序中可以任意调用执行的一段代码,而堆栈常常用来传递参数和存放局部变量,堆栈可以由机器的register而进行扩展,象ARM这种寄存器异常丰富的系统,内存都不经常被使用。But, conceptually, there is a stack, and for the purposes of this explanation it is convenient to consider the stack as if it were implemented entirely in memory.

参数通过拷贝或求值被传递到调用函数的堆栈段中,在参数中传递过多的数据是不明智的,事实上,就是任何超过2个机器字的数据量都不被提倡。因为这将引起过多的拷贝动作,相反,用一个指针或是引用来处理这些数据的地址要比传递数据本身好得多:)

在多任务的操作系统中(如symbian这样的平台),每个线程都有它自己的堆栈,这是一个预先分配的内存。使用堆栈的好处就是分配和解除分配空间是非常迅速的——只有一对指令(Push/Pop)。同样,在堆栈中的变量的生命期是很明确的:与它所属函数的生命期是一致的。

当函数返回时,它的堆栈空间仍然在那,只是没有被重新分配。当下个函数被调用时它将仍被使用。一个潜在的严重错误就是,我们在函数中创建了新的对象,最后返回一个指向它的指针:
TEg* foo()

  TEg s;
  TEg* ps = &s;
  return ps; // !!error!!

事实上,这个指针是无效的,因为该内存空间已经不属于foo函数。里面的数据也就不确定了。
This error is so obvious that a compiler will trap it. But it can occur in more subtle forms:

foo(CContainer* aContainer)

TEg s;
TEg* ps=&s;
aContainer->iMember=ps;
} //真毒:)真狡猾
These cannot be trapped so easily.
Heap
------
每个线程都有个堆,你能在需要的时候在上面创建新的对象或释放对象。使用堆的好处就是,对象的生命周期完全掌握在你的手里,这也带来了职责,你在用完该对象时可别忘记了释放它,并且不能再使用一个指针指向已经被释放的对象了。


[Object orientation basics]
Relationships
---------------
在面向对象系统中,在对象之间的关系很多。以下是一些列子:
1、the control environment(CCoeEnv)有一个window server session(RWsSession)。window server session的生命周期是和control environment一样的。

2、the app UI(CEikAppUi)使用一个文档(CEikDocument),The document has independent existence: the app UI may come into being for a while, and then be destroyed, but throughout the app UI's lifetime, it uses the document.

[Object lifetimes and cleanup]

overview
----------
在堆和栈中,对象都有下列近似的生命周期:
1、在堆或栈上为对象分配内存
2、初始化:即把内存的内容设定为有用的值
3、使用该对象
4、清除:清除任何该对象所使用的资源
5、在堆或栈上释放响应内存空间

对象的生命周期是一个基本的概念:在某些操作系统中,它会被忽视,因为在那里当程序中断后堆和栈会被释放。在symbain平台上,程序是为了数月运行而不中断的。因此当他们的生命周期结束后就立即被清除是很重要的,无论他们是在堆或栈中分配的,或者是因为正常运行或错误运行而终止的。

Lifetimes in C
----------------
在C堆栈中,可能看起来如下:
#include "s.h"

void foo()

  S s;
  sInitialize(&s, p1, p2);
  sUse(&s, p3, p4);
  sCleanup(&s);


S对象的分配是在函数入口处进行的,并且在退出时释放空间。

在C堆中,应该看起来如下:
void foo(0

  S* s = (S*)malloc(sizeof(S));
  //should really check this succeded!!
  sInitialize(s, p1, p2);
  sUse(s, p3, p4);
  sCleanup(s);
  free(s);

这里得到的s是指向对象的一个地址了,做为参数就更加简便了:)
Mostly, the lifetime of a heap-based object would not be contained within a single function like this: it might be created from one function, used from another, and destroyed from another.

Lifetimes in C++
-------------------
C++中对象的生命周期从构造函数开始,结束于析构函数。它还拥有一个new()操作,比malloc()要更好。以及相应的delete,这也是比free()好的。

在C++的堆栈中,一个对象生命周期看起来如下:
void foo()

  S s(p1, p2); //invokes constructor
  s.Use(p3, p4); //nice syntax;
} //invokes destructor

注意,如果说出现意外情况,例如use()函数调用时失败,那函数就不能正常返回。这样析构函数就不能被正确调用。我们来看一下symbian平台是如何处理这些的:

在C++堆中,对象生命周期看起来如下:
void foo()

  S* s = new S(p1, p2); //allocate, construct - should really check
  s->Use(p3, p4);
  delete s; // destruct, de-allocate


Again, the syntax is much nicer. Only one thing cannot be provided by C++: the user of a class must still remember to delete the object at the end of its lifetime.

Lifetimes in the Symbian platform
-----------------------------------
symbian平台中堆栈上的对象的生命周期和标准C++中的非常相似,不过对其的控制有不同,如下:

void Fool()

  CS* s = new (ELeave) CS; //allocate and check
  CleanupStack::PushL(s); //push, just in case
  s->ConstructL(p1, p2); //finish constructing - might leave
  s->UseL(p3, p4); //use - might leave
  CleanupStack::PopAndDestroy(); //destruct, de-allocate


上面的代码段展示了四件大事
1、all heap-based classes have names beginning with C: they are in fact derived from a single base class, CBase, which exists solely to suport easy cleanup
2、a cleanup stack is used to hold references to objects: if a leave occurs due to out-of-memory or some other error, objects held on the cleanup stack are popped from it, and destroyed. In the case of CBase* objects pushed to the stack, they are destroyed by calling their C++ destructor. The CBase class has a virtual destructor (CBase::~CBase()) which makes this possible.

3、any function which might leave is designated by a trailing L in its name. When you see a function that might leave, you must always ask what would happen if it did leave, and what would happen if it did not. The operating system provides all the program infrastructure required to allow objects to be de-allocated even when a leave occurs, but without burdening the programmer.

4、new (ELeave) is an overloaded operator new() function, which will leave if it fails to allocate the required memory. It never returns a null pointer.

另外还有两件事情值得注意:
1、since the cleanup stack itself requires memory allocation for each stack frame, a push might leave. The PushL() function reflects this in its name. The cleanup stack is guaranteed to have a free slot before a CleanupStack::PushL(), so that the object reference will always be successfully stored on the stack. If a leave occurs when allocating the next stack frame, the object will be popped and destroyed as normal.

2、the C++ constructor must not leave. For objects whose construction requires resource allocation or any other operation that might fail, this means that construction must be separated into a C++ constructor that does not leave, and another initialisation function that might leave, which is conventionally called ConstructL().清除栈、CBase以及双重构造都是操作系统的核心。

[Multiple inheritance and interfaces]

Overview
----------
多重继承是C++的一个重要特性。

在symbian平台中多重继承只有一个简单的目的:也就是,定义接口协议。(这个我们在com定义接口的方法中也能看到,一是用多重继承二是用嵌套类,很值得研究:)它们被用于下列情况:有一个协议提供者类,和一个协议使用者。最理想的状况是协议的使用者完全不关心协议提供者的(这个在com中也是的,使用接口而不关心接口是如何实现的),当然除了那些提供特殊协议的情况。这样的例子包括:

1、一个应用程序control就是一个协议提供者,它的菜单使用了菜单observing的协议。当一个菜单选项被选中以后,菜单observing 的协议就被执行,所以应用程序control就可以处理菜单命令了。除此以外,菜单control不知道关于应用程序control的任何东西。

2、一个应用程序,如电子表格,可能有一个引擎,which provides protocols for updating and getting its model contents, and a user interface, which uses these protocols to drive the engine. The engine is written with no knowledge of the user interface, and the user interface is written with minimal knowledge of the engine. 他们之间的交互依靠引擎所提供的协议:)

要理解为什么使用接口,下面可以一一说明:
1、the traditional method which uses single inheritance
2、a technique of overcoming the disadvantages of single inheritance, using protocol intermediary classes
3、a better technique, which uses multiple inheritance with interface classes
4、the restrictions on C++ multiple inheritance in Symbian OS

Protocols using classic single inheritance
--------------------------------------------
A classical use of single inheritance is to define an abstract protocol from which derived classes may inherit. A base class defines a protocol:
class CProtocol: public CBase

  public:
   virtual void HandleEvent(TInt aEventCode) = 0;
};

这个协议只包含一个函数HandleEvent(), 这里的事件由一个整型事件代码指明。

一个具体的协议提供类是从以上基类派生过来的,它提供了基类中纯虚函数的具体处理:

class CProtocolProvider: public CProtocol

  public:
   //construct/destruct
   static CProtocolProvider* NewLC();
   void Destruct();
   //implement the protocol
   void HandleEvent(TInt aEventCode); //handle protocol

  protected:
   void ConstructL();
};

另外,有个协议使用者类,它并不知道CProtocolProvider的一切,但它知道CProtocol类和指定协议的那个函数。它有个函数会使用HandleEvent():
void CProtocolUser::DoSomething(CProtocol* aProtocol)

  _LIT(KOutput1, "External system doing something\n");
  _LIT(KOutput2, "invoking protocol - event 3\n");
  testConsole.Printf(KOutput1);
  testConsole.Printf(KOutput2);
  aProtocol->HandleEvent(3); //handle an event
};

在CProtocol中定义的虚函数是由CProtocolProvider提供实现的,下面是代码:
void CProtocolProvider::HandleEvent(TInt aEventCode)

  //handle an event in the protocol user
  _LIT(KOutput1, "CProtocolProvider handling event %d\n");
  testConsole.Printf(KOutput1, aEventCode);


因此,尽管协议的使用者并不知道派生类CProtocolProvider,但可以通过一个指向这个派生类的指针来访问其成员函数
void doExampleL()

  //show use of interface with simple class
  CProtocolProvider* provider = CProtocolProvider::NewLC();
  CProtocolUser* user = CProtocolUser::NewL();
  user->DoSomething(provider);
  CleanupStack::PopAndDestroy(); //user后进先出:)
  CleanupStack::PopAndDestroy(); //provider


在上面的情况中,provider指针被强制转换成CProtocol* base,这是CProtocolUser::DoSomething()的需要.

使用这种方法的优势在于:
它促成了协议使用者于协议提供者的无关性。

这就是我们最想追求的状态,但这个方法也有很严重的缺点:
1、它强制协议的提供者要从一个协议基类派生
2、可以,如果有多个协议要被提供,那唯一的方法就是在一个single umbrella protocol中包含所有的需要协议,这是个很糟糕的封状。首先基类会变得很庞大,并且要从这么辨认这么多成员函数以及所属协议也不是件很容易的事情。其次,it may be desirable to have another provider class which provides some of the protocols provided by the first class, and others in addition. To support this requires an even larger umbrella protocol.因此,这种方法经常导致大的基类,协议间彼此无关。


Protocols using an intermediate class
----------------------------------------
如果要克服这样的缺点,我们建议使用一个间接的协议对象,它包含一个指向协议提供者的指针。基本的协议类是一样的,如下:

class TProtocol

  public:
   virtual void HandleEvent(TInt aEventCode) = 0;
};

but there is now a derived class for use with the CProtocolProvider only:

class TProtocolProviderIntermediary: public TProtocol

  public:
   //construct
   TProtocolProviderIntermediary(CProtocolProvider* aRealProvider);
   //protocol itself
   void HandleEvent(TInt aEventCode);
  private:
   CProtocolProvider* iRealProvider; //real provider
};

This class provides the protocol as far as the protocol user is concerned. The concrete implementation of HandleEvent() just passes the function call to the real protocol provider class, which has a non-virtual DoHandleEvent() to provide the required functionality:

void TProtocolProviderIntermediary::HandleEvent(TInt aEventCode)

  iRealProvider->DoHandleEvent(aEventCode);


With this system, CProtocolProvider is derived, not from the protocol definition class, but from CBase
class CProtocolProvider: public CBase

  public:
  //construct / destruct
  static CProtocolProvider* NewLC();
  void Destruct();
  //implement the protocol
  void DoHandleEvent(TInt aEventCode); //handle protocol
 
  protected:
  void ConstructL();
  
  public:
  TProtocolProviderIntermediary* iProviderIntermediary;


TProtocolProviderIntermediary是在CProtocolProvider的构造函数中被构造的,并且被它的析构函数所释放。基于这个原因,TProtocolProviderIntermediary是一个T类:它并不拥有CProtocolProvider,and cannot be orphaned.

When a function in the protocol user requiring the protocol provider is called, it must now be called passing the intermediary object as a parameter:

LOCAL_C void doExampleL()

  //show use of interface with simple class
  CProtocolProvier* provider = CProtocolProvider::NewLC();
  CProtocolUser* user = CProtocolUser::NewLC();
  user->DoSomething(provider->iProviderIntermediary);
  CleanupStack::PopAndDestroy(); //user
  CleanupStack::PopAndDestroy(0; //provider


协议使用者的DoSomething()本质上是和以前一样的,除了其参数现在变成一个TProtocol*。因此,用户只要知道基类TProtocol就可以了,虚函数机制导致间接派生的HandleEvent()被调用了,这个函数实际上调用的是真正协议提供者的DoHandleEvent()。

这种方法解决了困扰single inheritance的问题:
1、现在,任何数目的协议都可以被支持了,并且可以被分别封装,每个协议都需要一个intermediary class,并且每个间接类的对象都要指向相应的实际协议提供者类的对象。

2、no large base classes are needed to provide umbrellas for servral protocols.

但是,它还是有很严重的缺点:
1、这是很笨拙的:not only does each protocol require an abstract class (which cannot be avoided), but also, at each point in the derivation tree at which a protocol is introduced, a derived protocol class must be written which implements the protocol for the relevant class which really provides the protocol: further, the derived protocol object and the real protocol provider must be linked

2、if there are many classes which use many protocols in this way, not only is the method cumbersome to program, but it is uneconomical on memory, since each derived protocol class object requires at least two machine words of heap memory. This consideration becomes more serious if there are more small real protocol providers, providing many different protocols.

Protocols using interface classes
------------------------------------
这些问题的解决可以通过多重继承,一个基类MProtocol申明了那个协议:
class MProtocol

  public:
   virtual void HandleEvent(TInt aEventCode) = 0;
};

这次协议的提供者是从CBase和MProtocol派生的:)
class CProtocolProvider: public CBase, public MProtocol

  public:
   //construct/ destruct
   static CProtocolProvider* NewLC();
   void Destruct();
   //implement the protocol
   void HandleEvent(TInt aEventCode); //handle protocol
  protected:
   void ConstructL();
};

协议提供者类提供了HandleEvent()的明确处理。使用者可以如下调用:
LOCAL_C void doExampleL()

  //show use of interface with simple class
  CProtocolProvider* provider = CProtocolProvider::NewLC();
  CProtocolUser* user = CProtocolUser::NewLC();
  user->DoSomething(provider);
  CleanupStack::PopAndDestroy(); //user
  CleanupStack::PopAndDestroy(); //provider


这里的DoSomething函数需要一个MProtocol*参数,C++将这个CProtocolProvider*提供者指针强制转换为一个MProtocol*指针,因为MProtocol是CProtocolProvider基类之一。当DoSomething()处理HandleEvent()时,C++虚函数机制可以确保CProtocolProvider的HandleEvent()被调用。因此用户可以直接使用协议,而不需要知道任何关于具体协议提供者类的情况。

This method achieves the intended goals:
1、协议的使用者只与协议有关,而和任何特定的具体提供者无关。
2、the protocol can be introduced into a class hierarchy at any desired point, by multiple inheriting form a base class and one or more interfaces classes
3、可以形成不同协议的完全封装
4、there is no inconvenient intermediate class, with its programming difficulties and wasteful memory use

Because protocols may be mixed into the derivation hierarchy of conventional classes at any convenient point in the hierarchy, such protocol specification classes are sometimes also called mixins, the origin of the prefix M.

Labels: