Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Symbian OS Explained - Effective C++ Programming For Smartphones (2005) [eng].pdf
Скачиваний:
60
Добавлен:
16.08.2013
Размер:
2.62 Mб
Скачать

LITERAL DESCRIPTORS

69

5.6 Literal Descriptors

Let’s move on to take a look at literal descriptors (constant descriptors that are compiled into ROM), which are equivalent to static char[] in C. Literal descriptors can be created by a set of macros defined in e32def.h. It’s a bit unnerving at first sight, so I’ve only included the explicit definitions for 8- and 16-bit literals. The implicit definitions for the neutral macros _L, _S and _LIT are exactly the same, where _L is equivalent 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 macros first, 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 macro builds a named object (KMyLiteralDescriptor) of type TLitC16 into the program binary, storing the appropriate string (in this case, The quick brown fox jumps over the lazy dog). As you’d expect, _LIT8 and

_LIT16 behave similarly. The reason why the macros subtract one byte for the length of the data and divide by two, in the case of _LIT16, is that the macro is converting the C byte string to data which can be used as a descriptor.

For reference, here’s the definition of class TLitC16, from e32des.h and e32des.inl, where __TText is typedef’d to a wide, 16-bit character. The TLitC8 class, which has an array of 8-bit characters, has a 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 clarity

70

DESCRIPTORS: SYMBIAN OS STRINGS

public:

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 or TDesC16 but they have the same binary layouts as TBufC8 or TBufC16. This allows objects of these types to be used wherever TDesC is used.4

You 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. If you use sizeof() on a _LIT constant, the size of the corresponding TLitC object is returned, which is the size of the descriptor contents – in bytes – plus 8 extra bytes (a TUint for the stored length and the NULL terminator). If you want to use a stack-based buffer, you must take these extra bytes into account.

For a heap buffer, you can use the actual length of the descriptor contents to allocate the buffer then copy the contents of the descriptor. To get the correct length you can use the public iTypeLength member variable, or, more simply, use operator()() to reinterpret_cast the literal object to a descriptor and use the resultant object to determine the length of the contents. However, the simplest technique is to use operator()() to cast the literal object to a descriptor, then call one of the 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 literal

4 Actually, the string stored in the program binary has a NULL terminator because the native compiler string is used to build it. However, as I described above, the length is adjusted to the correct value for a non-terminated descriptor by the _LIT macro as it constructs the TLitC object.

LITERAL DESCRIPTORS

71

TBufC8<(sizeof(KExampleLit8)-8)> theStackBuffer(KExampleLit8);

//Create a heap buffer copying the contents of the literal HBufC8* 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 for literal descriptors created using _L and _LIT.

_LIT (KHello, "Hello World!")

 

TPtrC hello (_L("Hello World!"))

 

 

 

 

 

ROM

StackTemporary

ROM

 

 

 

 

 

 

 

 

12

HelloWorld!\0

 

 

iLength

iPtr

 

Hello World!\0

 

 

12

 

 

 

 

 

 

 

 

 

Figure 5.4 Memory layout for literal descriptors

 

Incidentally, literals have already been defined in Symbian OS to represent 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 of which is now deprecated in production code, though they may still be used in test code (where memory use is less critical). The advantage of using _L (or the explicit forms _L8 and _L16) is that you can use it in place of a TPtrC without having to declare it separately from where it is used (besides saving an extra line of code, one benefit is that you don’t have to think up a name for it!).

User::Panic(_L("example.dll"), KErrNotSupported);

The string (”example.dll”) is built into the program binary as a basic, NULL-terminated string, with no initial length member (unlike the TLitC built for the _LIT macro). Because there is no length word, the layout of the stored literal is not like that of a descriptor and, when the code executes, each instance of _L will result in construction of a temporary TPtrC, with the pointer set to the address of the first byte of the literal as it is stored in ROM. The use of such a run-time temporary is safe as long as it is used only during the lifetime of the function in which it

72

DESCRIPTORS: SYMBIAN OS STRINGS

is created, or if it is copied for use outside of that lifetime.5 However, the construction of a temporary, which requires setting the pointer, the length and the descriptor type, is an overhead in terms of inline constructor code which may bloat binaries where many string literals are used.

The _S macro is equivalent to the _L macro in terms of the way the string is stored in the binary, but it does not construct the temporary TPtrC around the string. These macros are useful if you wish to use the literal directly as a NULL-terminated string; you will incur no overhead if you do so.

Prefer the use of _LIT to _L for declaring literal descriptors, because the latter has an overhead associated with constructing a run-time temporary TPtrC.

5.7 Summary

This chapter introduced Symbian OS descriptors and discussed the following:

The descriptor model treats string and binary data in the same way because there is no reliance on trailing NULL terminators to indicate the length of string data.

The descriptor classes offer native ”wide” 16-bit character support, but the same APIs can be used explicitly with 8-bit binary or string data.

Non-modifiable descriptor functionality is defined by the TDesC base class 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 28 bits of the first machine word of the object to hold the length of the descriptor data. TDesC also defines Ptr() to access that data – it uses hardcoded logic to determine the correct memory address, based on the descriptor subtype which is indicated by the top 4 bits of the first machine word of the object.

5 Code like this will not work in a DLL because, although marked const, the creation of such a runtime temporary constitutes the use of modifiable static data, which is disallowed on Symbian OS (as described in Chapter 13).

const TPtrC KDefaultPath=_L("C:\\System\\Messages")

SUMMARY

73

Modifiable descriptor functionality is defined by the TDes class, which derives from TDesC, adding an extra machine word to store the maximum allowed length of a descriptor. The TDes class implements typical data modification operations.

The five concrete descriptor classes can be subdivided in terms of their memory layout (pointer or buffer), whether they are constant or modifiable and whether they are heapor stack-based. However, there is significant interoperability between the classes, and the base class APIs make no assumptions about their underlying data layout. Descriptor data can be stored in RAM (on the stack or heap) or ROM; the APIs are consistent regardless of location or memory layout.

For efficiency reasons, descriptors do not dynamically extend the data area they reference. You, the programmer, are responsible for memory management.

Descriptor member functions check that access to the descriptor lies within its data area and raise a panic (in both debug and release builds) if a descriptor overflow would result. This ensures that descriptor code is robust and that programming errors are easy to track down.

_LIT literals are not part of the TDesC inheritance hierarchy, but have an equivalent memory layout in ROM to TBufC and can thus be used interchangeably.

The advantage of using the _L literal macros is that you can use them where you would use a temporary TPtrC, without having to predefine and name them. The disadvantage is the extra run-time overhead associated with construction of a temporary descriptor – which is why they are deprecated in production code.

Symbian OS descriptors may take some getting used to, but cannot be avoided when programming for Symbian OS because many API functions use them.

The next chapter shows how to use descriptors effectively and examines the more frequently used descriptor functions. It also describes some common descriptor mistakes and misconceptions.