Symbian OS Explained - Effective C++ Programming For Smartphones (2005) (779885), страница 18
Текст из файла (страница 18)
Providing a separate HBuf class wouldrequire the programmer to decide whether the buffer would ever requiremodification. It is possible that many would opt to use HBuf rather thanHBufC ”just in case” the descriptor later needed modification.
Besidesthe additional code required for an HBuf class, the extra 4-byte overheadof HBuf objects created unnecessarily might add up over time. Providingthe Des() method on the HBufC allows the programmer to modify heapbuffers when needed but keeps the code associated with heap descriptorobjects compact.A counter-argument to this might be that the HBufC::Des() call isnon-trivial and the resultant TPtr will occupy 12 bytes of stack space. Incases where a modifiable heap descriptor is definitely needed, creating a68DESCRIPTORS: SYMBIAN OS STRINGSnon-modifiable buffer and an additional, modifiable pointer to update itmay be seen as a waste of processor instructions and memory.
Perhaps thesolution in future will be to provide both non-modifiable and modifiableheap buffers, with clear documentation as to the consequences of usingeach. With education, we should be able to trust developers to makethe right choice of descriptor for the right operation, using constant heapdescriptors by default and modifiable heap descriptors where necessary.I hope this book, and this chapter in particular, will go some way inhelping to reach this goal!To summarize, the inheritance hierarchy of the descriptor classes isshown in Figure 5.3.TDesCiLengthiTypeLength()Ptr()...constant descriptormethodsTPtrCTBufCBaseTDesiPtriMaxLengthMaxLength()...modifiable descriptormethodsTBufC<n>HBufCTPtriBufiBufiPtrTBufBaseTBuf<n>iBufFigure 5.3Class inheritance hierarchiesHeap descriptors can be created dynamically to the size you require,but are not automatically resized should they need to be extendedbeyond their maximum length.LITERAL DESCRIPTORS695.6 Literal DescriptorsLet’s move on to take a look at literal descriptors (constant descriptorsthat are compiled into ROM), which are equivalent to static char[]in C.
Literal descriptors can be created by a set of macros defined ine32def.h. It’s a bit unnerving at first sight, so I’ve only included theexplicit definitions for 8- and 16-bit literals. The implicit definitions forthe neutral macros _L, _S and _LIT are exactly the same, where _L isequivalent to _L16 on a Unicode build and _L8 on a narrow ASCII build:#define _L8(a) (TPtrC8((const TText8 *)(a)))#define _S8(a) ((const TText8 *)a)#define _LIT8(name,s) const static TLitC8<sizeof(s)>name ={sizeof(s)-1,s}#define _L16(a) (TPtrC16((const TText16 *)L ## a))#define _S16(a) ((const TText16 *)L ## a)#define _LIT16(name,s) const static TLitC16<sizeof(L##s)/2>name ={sizeof(L##s)/2-1,L##s}Don’t worry; I’ll go through these slowly. Let’s look at _LIT macrosfirst, since these are the most efficient, and preferred, Symbian OS literals.The typical use of the macro would be as follows:_LIT(KMyLiteralDescriptor, "The quick brown fox jumps over the lazy dog");KMyLiteralDescriptor can then be used as a constant descriptor,for example written to a file or displayed to a user.
The _LIT macrobuilds a named object (KMyLiteralDescriptor) of type TLitC16into the program binary, storing the appropriate string (in this case, Thequick brown fox jumps over the lazy dog). As you’d expect, _LIT8 and_LIT16 behave similarly. The reason why the macros subtract one bytefor the length of the data and divide by two, in the case of _LIT16, isthat the macro is converting the C byte string to data which can be usedas a descriptor.For reference, here’s the definition of class TLitC16, from e32des.hand e32des.inl, where __TText is typedef’d to a wide, 16-bitcharacter. The TLitC8 class, which has an array of 8-bit characters, hasa similar definition.template <TInt S>class TLitC16{public:inline const TDesC16* operator&() const;inline operator const TDesC16&() const;inline const TDesC16& operator()() const;...
// Omitted for clarity70DESCRIPTORS: SYMBIAN OS STRINGSpublic:TUint iTypeLength;__TText iBuf[__Align16(S)];};template <TInt S>inline const TDesC16* TLitC16<S>::operator&() const{return REINTERPRET_CAST(const TDesC16*,this);}template <TInt S>inline const TDesC16& TLitC16<S>::operator()() const{return *operator&();}template <TInt S>inline TLitC16<S>::operator const TDesC16&() const{return *operator&();}As you can see, TLitC16 (and TLitC8) do not derive from TDesC8 orTDesC16 but they have the same binary layouts as TBufC8 or TBufC16.This allows objects of these types to be used wherever TDesC is used.4You can form a pointer descriptor from the literal as follows:TPtrC8 thePtr(KMyLiteralDescriptor);It’s slightly trickier to form a buffer descriptor from the literal. Ifyou use sizeof() on a _LIT constant, the size of the correspondingTLitC object is returned, which is the size of the descriptor contents – inbytes – plus 8 extra bytes (a TUint for the stored length and the NULLterminator).
If you want to use a stack-based buffer, you must take theseextra bytes into account.For a heap buffer, you can use the actual length of the descriptorcontents to allocate the buffer then copy the contents of the descriptor.To get the correct length you can use the public iTypeLength membervariable, or, more simply, use operator()() to reinterpret_castthe literal object to a descriptor and use the resultant object to determinethe length of the contents. However, the simplest technique is to useoperator()() to cast the literal object to a descriptor, then call one ofthe TDes::AllocL() methods upon it.
For example:// Define a 44 character literal_LIT8(KExampleLit8, "The quick brown fox jumped over the lazy dog");TInt size = sizeof(KExampleLit8); // 52 bytes (contents + 8 bytes)TInt descriptorLength = KExampleLit8.iTypeLength; // 44 bytes// Form a stack buffer descriptor around the literal4Actually, the string stored in the program binary has a NULL terminator because thenative compiler string is used to build it. However, as I described above, the length isadjusted to the correct value for a non-terminated descriptor by the _LIT macro as itconstructs the TLitC object.LITERAL DESCRIPTORS71TBufC8<(sizeof(KExampleLit8)-8)> theStackBuffer(KExampleLit8);// Create a heap buffer copying the contents of the literalHBufC8* theHeapBuffer = KExampleLit8().AllocL();// Similar behaviour for wide literals_LIT16(KExampleLit16, "The quick brown fox jumped over the lazy dog");size = sizeof(KExampleLit16);// 96 bytes (contents in bytes + 8 bytes)descriptorLength = KExampleLit16.iTypeLength; // 44 bytes (contents)Figure 5.4 illustrates the difference between the memory layouts forliteral descriptors created using _L and _LIT._LIT (KHello, "Hello World!")ROM12TPtrC hello (_L("Hello World!"))StackTemporaryHelloWorld!\0iLength12iPtrROMHello World!\0Figure 5.4 Memory layout for literal descriptorsIncidentally, literals have already been defined in Symbian OS torepresent a blank string.
There are three variants of the ”NULL descriptor”,defined as follows:Build independent:_LIT(KNullDesC,"");8-bit for non-Unicode strings: _LIT8(KNullDesC8,"");16-bit for Unicode strings:_LIT16(KNullDesC16,"");Let’s move on to look briefly at the _L and _S macros, the use ofwhich is now deprecated in production code, though they may still beused in test code (where memory use is less critical). The advantage ofusing _L (or the explicit forms _L8 and _L16) is that you can use it inplace of a TPtrC without having to declare it separately from where it isused (besides saving an extra line of code, one benefit is that you don’thave to think up a name for it!).User::Panic(_L("example.dll"), KErrNotSupported);The string (”example.dll”) is built into the program binary as abasic, NULL-terminated string, with no initial length member (unlike theTLitC built for the _LIT macro). Because there is no length word, thelayout of the stored literal is not like that of a descriptor and, whenthe code executes, each instance of _L will result in construction of atemporary TPtrC, with the pointer set to the address of the first byte ofthe literal as it is stored in ROM.
The use of such a run-time temporary issafe as long as it is used only during the lifetime of the function in which it72DESCRIPTORS: SYMBIAN OS STRINGSis created, or if it is copied for use outside of that lifetime.5 However, theconstruction of a temporary, which requires setting the pointer, the lengthand the descriptor type, is an overhead in terms of inline constructor codewhich may bloat binaries where many string literals are used.The _S macro is equivalent to the _L macro in terms of the way thestring is stored in the binary, but it does not construct the temporaryTPtrC around the string.
These macros are useful if you wish to use theliteral directly as a NULL-terminated string; you will incur no overhead ifyou do so.Prefer the use of _LIT to _L for declaring literal descriptors,because the latter has an overhead associated with constructing arun-time temporary TPtrC.5.7 SummaryThis chapter introduced Symbian OS descriptors and discussed thefollowing:• The descriptor model treats string and binary data in the same waybecause there is no reliance on trailing NULL terminators to indicatethe length of string data.• The descriptor classes offer native ”wide” 16-bit character support, butthe same APIs can be used explicitly with 8-bit binary or string data.• Non-modifiable descriptor functionality is defined by the TDesC baseclass and inherited by all its subclasses.• For compactness, no virtual functions are used in the descriptor hierarchies, so no additional 4-byte vptr is added to each descriptor object.• The base class, TDesC, defines Length() and uses the bottom 28bits of the first machine word of the object to hold the length of thedescriptor data.
TDesC also defines Ptr() to access that data – ituses hardcoded logic to determine the correct memory address, basedon the descriptor subtype which is indicated by the top 4 bits of thefirst machine word of the object.5Code like this will not work in a DLL because, although marked const, the creation ofsuch a runtime temporary constitutes the use of modifiable static data, which is disallowedon Symbian OS (as described in Chapter 13).const TPtrC KDefaultPath=_L("C:\\System\\Messages")SUMMARY73• Modifiable descriptor functionality is defined by the TDes class,which derives from TDesC, adding an extra machine word to store themaximum allowed length of a descriptor.