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

86

GOOD DESCRIPTOR STYLE

Symbian OS also provides WriteL() and ReadL() methods on the RWriteStream and RReadStream classes, respectively. The WriteL() method writes only the contents of the descriptor, not the length. The ReadL() method reads the contents into a target descriptor up to its maximum length.

Symbian OS provides templated stream operators for externalization and internalization of descriptors, to store the descriptor efficiently. These should be used in preference to hand-crafted code when reading and writing a descriptor to a stream. Where only the contents of the descriptor need to be stored, ReadL() and WriteL() should be used.

6.5 The Overuse of TFileName

Another potential hazard when using descriptors occurs through the overuse of TFileName objects. TFileName is defined as follows in e32std.h:

const TInt KMaxFileName=0x100; // = 256 (decimal)

typedef TBuf<KMaxFileName> TFileName;

Since each wide character is equivalent to two bytes, each time you create and use a TFileName object on the stack you are setting aside 524 bytes (2 × 256 descriptor data bytes + 12 for the descriptor object itself) regardless of whether they are all actually required for the file name. The standard stack size on Symbian OS is just 8 KB, so it’s a good rule never to allocate stack-based TFileName objects or pass them around by value, since unnecessary use of this restricted resource is very wasteful. You could, of course, use them on the heap, say as members of C classes (which always exist on the heap, as described in Chapter 1). But if you are unlikely to need the full path length, you should aim to use an HBufC or some other descriptor type, since it is good practice to consume as little memory as necessary.

As an example of where you can often avoid using an unnecessary TFileName object, consider the TParse class (defined in f32file.h). This class takes a copy of a descriptor containing a file name to parse, and stores it in a TFileName, which may waste valuable stack space. You should consider using the TParsePtr and TParsePtrC classes instead; these offer the same functionality (implemented by the base class TParseBase) while storing a reference to the file name rather than a copy.

USEFUL CLASSES FOR DESCRIPTOR MANIPULATION

87

Do not waste valuable stack resources by using TFileName or TParse unnecessarily.

6.6 Useful Classes for Descriptor Manipulation

Having looked at a few common problems, let’s close this chapter by taking a quick look at a couple of useful classes you can use with descriptors.

Firstly, let’s look at lexical analysis and extraction, using the TLex class. TLex is provided as TLex8 and TLex16 in the same way as descriptors, though you should use the build-independent type (implicitly TLex16) unless you require a particular build variant. The class implements general-purpose lexical analysis, and effects syntactical element parsing and string-to-number conversion, using the locale-dependent functions of TChar to determine whether each character is a digit, a letter or a symbol. An object of type TLex stores a pointer to the string data to be analyzed. In addition, the class maintains an extraction mark to indicate the current lexical element and a pointer to the next character to be examined. The marker can be used to mark position and you can peek, skip and rewind as required.

Another useful set of classes are the package buffers (TPckgBuf) and package pointers (TPckg and TPckgC) which are thin template classes derived from TBuf<n>, TPtr and TPtrC respectively (see e32std.h). The classes are type-safe and are templated on the type to be packaged – I’ll discuss the thin template idiom later, in Chapter 19. The package classes allow flat data objects to be stored conveniently within descriptors, which is useful for example for inter-thread or inter-process data transfer (which I will describe further in Chapters 10, 11 and 12). In effect, a T class object may be packaged whole into a descriptor (I like to think of this as ”descriptorizing”) so it may be passed easily in a type-safe way between threads or processes.

There are two package pointer classes, creating either modifiable (TPckg) or non-modifiable (TPckgC) pointer descriptors which refer to the existing instance of the template-packaged class. Functions may be called on the enclosed object, although if it is enclosed in a TPckgC, a constant reference to the packaged object is returned from operator().

The package buffer TPckgBuf creates and stores a new instance of the type to be encapsulated in a descriptor. The copied object is owned by the package buffer; it is modifiable and functions may be called on it, after calling operator() on the TPckgBuf object to retrieve it. Because the package buffer contains a copy of the original object, if a modification function is called it is the copy that is modified – the original is unchanged.

88

GOOD DESCRIPTOR STYLE

The following code shows an object of a simple T class encapsulated in the package types, and Figure 6.4 illustrates the memory layout of each:

class TSample

{

public:

void SampleFunction();

void ConstantSampleFunction() const; private:

TInt iSampleData; };

TSample theSample;

TPckg<TSample> packagePtr(theSample);

TPckgC<TSample> packagePtrC(theSample);

TPckgBuf<TSample> packageBuf(theSample);

packagePtr().SampleFunction();

packagePtrC().SampleFunction();// Compile error! Non-const function packagePtrC().ConstantSampleFunction(); packageBuf().SampleFunction();

 

TPtr

 

 

TPckg<TSample>

iLength

iMaxLength

iPtr

TSample theSample

TPtrC

TPckgC<TSample> iLength iPtr

 

TBuf

 

 

TPckgBuf<TSample>

iLength

iMaxLength

copy of theSample

Figure 6.4 Memory layout of the TPckg, TPckgC and TPckgBuf classes

6.7 Summary

In this chapter, I discussed the following:

You should not attempt to instantiate objects of the base descriptor classes TDesC and TDes. However, you will typically use them when

SUMMARY

89

defining descriptor function parameters because they give a caller flexibility as to the type of descriptor passed to the function. For example, the Read() and Write() methods of class RFile take a generic (8-bit) descriptor.

One of the benefits of descriptor parameters is that the function can determine the length of the descriptor, and its maximum allowed length if it is modifiable. This means that a function does not require separate parameters for this information.

TDesC defines a Size() method, which returns the total size in bytes of the current descriptor contents, and a Length() method, which returns the number of characters currently contained by the descriptor. From v5U, the native character on Symbian OS is 16 bits (two bytes) wide; as a result, Size() will always return a value double that of Length().

Conversion between 16-bit and 8-bit descriptors can be effected using the Copy() function defined by TDes. However, this mode of copying assumes basic character data (i.e. values not exceeding

255)because it performs rudimentary zero-padding for narrow to wide conversion and zero-stripping for wide to narrow conversion. Symbian OS provides the charconv library to convert between genuine 16-bit Unicode and 8-bit, non-Unicode character sets (or between Unicode and the UTF-7 and UTF-8 transformation sets).

If you are working with 8-bit text or binary data you should explicitly use the 8-bit descriptor classes, using TText8 pointers as returned by the Ptr() operation to access characters. Otherwise, you should use the neutral descriptor classes unless you wish to highlight the fact that the data is explicitly 16 bits wide.

A number of common mistakes are made with HBufC heap descriptors, including:

• using long-winded allocation from other descriptors rather than calling TDesC::AllocL() or TDesC::AllocLC()

• using the Des() method to return a const TDesC& rather than simply dereferencing the HBufC pointer

• calling MaxLength() on a heap descriptor, which can return a spurious value

The templated >> and << stream operators should be used to internalize and externalize descriptors using RReadStream and RWriteStream respectively; or alternatively, the ReadL() and WriteL() methods of the stream classes should be used. These have been optimized for the most efficient storage of descriptor data.

90

GOOD DESCRIPTOR STYLE

Use of stack-based objects of types TFileName and TParse should be kept to a minimum because they reserve a large amount of stack space (524 bytes), often unnecessarily.

Symbian OS provides the TLex classes for lexical analysis and extraction.

The package descriptor classes TPckgBuf, TPckg and TPckgC are useful template classes for type-safe wrapping of the built-in types or T class objects within descriptors. This method of ”descriptorizing” flat data is particularly useful for passing objects between client and server, and is described in detail in Chapters 10, 11 and 12.