Symbian OS Explained - Effective C++ Programming For Smartphones (2005) (779885), страница 21
Текст из файла (страница 21)
It is more efficient simply to de-referencethe HBufC pointer when a non-modifiable TDesC& is required.6.4 Externalizing and Internalizing DescriptorsMoving on from the accidental complexity that may be introduced whenusing heap descriptors to a more general issue – that of externalizingdescriptor data to file using a writable stream and re-internalizing it usinga readable stream.
Consider the following sample code:// Writes the contents of iHeapBuffer to a writable streamvoid CSampleClass::ExternalizeL(RWriteStream& aStream) const{// Write the descriptor’s lengthaStream.WriteUint32L(iHeapBuffer->Length());// Write the descriptor’s dataaStream.WriteL(*iHeapBuffer, iHeapBuffer->Length());}// Instantiates iHeapBuffer by reading the contents of the streamvoid CSomeClass::InternalizeL(RReadStream& aStream){TInt size=aStream.ReadUint32L(); // Read the descriptor’s lengthiHeapBuffer = HBufC::NewL(size); // Allocate iHeapBuffer// Create a modifiable descriptor over iHeapBufferTPtr ptr(iHeapBuffer->Des());// Read the descriptor data into iHeapBufferaStream.ReadL(ptr,size);}The code above implements descriptor externalization and internalization in a very basic manner, using four bytes to store the descriptorlength, then adding the descriptor data to the stream separately.
In theInternalizeL() method, the descriptor is reconstructed in four ratherEXTERNALIZING AND INTERNALIZING DESCRIPTORS85awkward stages. In fact, Symbian OS provides a templated stream operator(operator<<) for externalization, which compresses the length information to keep descriptor storage as efficient and compact as possible.Furthermore, descriptors externalized in this way may be re-created fromthe stream, as heap descriptors using the NewL() overloads of HBufC,passing in the read stream and an additional parameter to indicate themaximum length of data to be read from the stream. This is a significantly more efficient approach. To use operator<< you must link toestor.lib.
The stream classes are documented in your preferred SDK.void CSampleClass::ExternalizeL(RWriteStream& aStream) const{// Much more efficient, no wasted storage spaceaStream << iHeapBuffer;}void CSampleClass::InternalizeL(RReadStream& aStream){iHeapBuffer = HBufC::NewL(aStream, KMaxLength);}You can also use the templated operator>>, which assumes that thedescriptor was externalized using operator<<.class TSomeClass{...
// Omitted for clarityprivate:TBuf<12> iBuffer;...};void TSomeClass::ExternalizeL(RWriteStream& aStream){aStream << iBuffer;...}void TSomeClass::InternalizeL(RReadStream& aStream){aStream >> iBuffer;...}You’ll notice that the internalization and externalization methodsabove are leaving functions. This is because the templated streamingoperators, operator>> and operator<<, may leave.
They are notsuffixed with L, as is conventional for all leaving functions, because theyare operators, and to do so would interfere with their usage. The factthat they can leave isn’t picked up by the leave-checking tool LeaveScan(described in Chapter 2) either. When writing code which uses them youshould make a special point of checking that your code is leave-safe andthat functions are named according to Symbian OS.86GOOD DESCRIPTOR STYLESymbian OS also provides WriteL() and ReadL() methods on theRWriteStream and RReadStream classes, respectively.
TheWriteL() method writes only the contents of the descriptor, not thelength. The ReadL() method reads the contents into a target descriptorup to its maximum length.Symbian OS provides templated stream operators for externalization and internalization of descriptors, to store the descriptorefficiently.
These should be used in preference to hand-craftedcode when reading and writing a descriptor to a stream. Whereonly the contents of the descriptor need to be stored, ReadL() andWriteL() should be used.6.5 The Overuse of TFileNameAnother potential hazard when using descriptors occurs through theoveruse of TFileName objects. TFileName is defined as followsin e32std.h:const TInt KMaxFileName=0x100; // = 256 (decimal)typedef TBuf<KMaxFileName> TFileName;Since each wide character is equivalent to two bytes, each time youcreate and use a TFileName object on the stack you are setting aside524 bytes (2 × 256 descriptor data bytes + 12 for the descriptor objectitself) 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 rulenever to allocate stack-based TFileName objects or pass them around byvalue, 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 youare unlikely to need the full path length, you should aim to use an HBufCor some other descriptor type, since it is good practice to consume aslittle memory as necessary.As an example of where you can often avoid using an unnecessaryTFileName 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 classesinstead; these offer the same functionality (implemented by the base classTParseBase) while storing a reference to the file name rather thana copy.USEFUL CLASSES FOR DESCRIPTOR MANIPULATION87Do not waste valuable stack resources by using TFileName orTParse unnecessarily.6.6 Useful Classes for Descriptor ManipulationHaving 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 implementsgeneral-purpose lexical analysis, and effects syntactical element parsingand string-to-number conversion, using the locale-dependent functionsof TChar to determine whether each character is a digit, a letter or asymbol. An object of type TLex stores a pointer to the string data to beanalyzed. In addition, the class maintains an extraction mark to indicatethe current lexical element and a pointer to the next character to beexamined.
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) andpackage pointers (TPckg and TPckgC) which are thin template classesderived 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.
Thepackage classes allow flat data objects to be stored conveniently withindescriptors, which is useful for example for inter-thread or inter-processdata transfer (which I will describe further in Chapters 10, 11 and 12). Ineffect, a T class object may be packaged whole into a descriptor (I like tothink of this as ”descriptorizing”) so it may be passed easily in a type-safeway between threads or processes.There are two package pointer classes, creating either modifiable(TPckg) or non-modifiable (TPckgC) pointer descriptors which refer tothe existing instance of the template-packaged class.
Functions may becalled on the enclosed object, although if it is enclosed in a TPckgC, aconstant reference to the packaged object is returned from operator().The package buffer TPckgBuf creates and stores a new instance ofthe type to be encapsulated in a descriptor. The copied object is ownedby the package buffer; it is modifiable and functions may be called onit, after calling operator() on the TPckgBuf object to retrieve it.Because the package buffer contains a copy of the original object, if amodification function is called it is the copy that is modified – the originalis unchanged.88GOOD DESCRIPTOR STYLEThe following code shows an object of a simple T class encapsulated inthe 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 functionpackagePtrC().ConstantSampleFunction();packageBuf().SampleFunction();TPtrTPckg<TSample>iLengthiMaxLengthiPtrTSample theSampleTPtrCTPckgC<TSample>iLengthiPtrTBufTPckgBuf<TSample>iLengthiMaxLengthcopy of theSampleFigure 6.4 Memory layout of the TPckg, TPckgC and TPckgBuf classes6.7 SummaryIn this chapter, I discussed the following:• You should not attempt to instantiate objects of the base descriptorclasses TDesC and TDes.
However, you will typically use them whenSUMMARY89defining descriptor function parameters because they give a callerflexibility as to the type of descriptor passed to the function. Forexample, the Read() and Write() methods of class RFile take ageneric (8-bit) descriptor.• One of the benefits of descriptor parameters is that the function candetermine the length of the descriptor, and its maximum allowedlength if it is modifiable.
This means that a function does not requireseparate parameters for this information.• TDesC defines a Size() method, which returns the total size inbytes of the current descriptor contents, and a Length() method,which returns the number of characters currently contained by thedescriptor. From v5U, the native character on Symbian OS is 16 bits(two bytes) wide; as a result, Size() will always return a valuedouble that of Length().• Conversion between 16-bit and 8-bit descriptors can be effectedusing the Copy() function defined by TDes.
However, this modeof copying assumes basic character data (i.e. values not exceeding255) because it performs rudimentary zero-padding for narrow towide conversion and zero-stripping for wide to narrow conversion.Symbian OS provides the charconv library to convert betweengenuine 16-bit Unicode and 8-bit, non-Unicode character sets (orbetween Unicode and the UTF-7 and UTF-8 transformation sets).• If you are working with 8-bit text or binary data you should explicitlyuse the 8-bit descriptor classes, using TText8 pointers as returned bythe Ptr() operation to access characters. Otherwise, you should usethe neutral descriptor classes unless you wish to highlight the fact thatthe 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 thancalling TDesC::AllocL() or TDesC::AllocLC()• using the Des() method to return a const TDesC& rather thansimply dereferencing the HBufC pointer• calling MaxLength() on a heap descriptor, which can return aspurious value• The templated >> and << stream operators should be used tointernalize and externalize descriptors using RReadStream andRWriteStream respectively; or alternatively, the ReadL() andWriteL() methods of the stream classes should be used.