Friday, August 08, 2008

10 Tips when using Symbian Descriptors

Tip 1: Never instantiate a TDes or a TDesC
The default constructors of TDes and TDesC are declared private so the compiler won’t let you construct them directly. But there is no copy constructor declared for either class, so the compiler won’t complain if you make a copy of a valid descriptor (in fact, it will go as far as to help you, by invoking an implicitly generated copy constructor).

_LIT(KExample, "Fred");
TPtrC original(KExample); // a valid TDesC-derived descriptor
TDesC copy(original); // Uses the implicit copy constructor

Your code probably won’t crash if it has been written safely, but you will rarely have a valid reason for doing this. The code will fail to work anyway, because TDes and TDesC contain no string data, so in effect are abstract classes.

Tip 2: Check that there is sufficient space before writing to a descriptor.
An attempt to access an area outside the area allocated for descriptor data will cause a panic in both debug and release builds, because the descriptor functions use __ASSERT_ALWAYS internally to check for out-of-bounds access. A panic causes your code will stop executing immediately, whether running in an application, server or test framework. So be absolutely certain that there is space in your target descriptor, if necessary, by doing a check first by using the Length() or MaxLength() methods.

_LIT8(KImageGif, “image/gif”); // 9 characters
TBuf8<8> mimeType8; // Space for only 8 characters
mimeType8.Copy(KImageGif); // So Panic!

Tip 3: Need a small known length descriptor? Use TBuf or TBufC.
These are suitable for when you know the required length at compile time. The recommended maximum length is 256 bytes or fewer (remember that’s 128 characters, a TBuf<128>, because each character occupies 2 bytes).

Tip 4: Need a larger or unknown length descriptor? Use HBufC.
Heap-based descriptors, HBufC, are allocated on the heap at run time. They can be used as local variables or as class members. As with all heap-based objects, memory leaks must be avoided through use of the cleanup stack - for local variables - or deleted by a destructor if ownership is through a member variable.

Tip 5: Need to modify an HBufC? Call Des().
An HBufC is derived from TDesC, and inherits non-modifiable descriptor functionality. Because it is not derived from TDes, it isn’t modifiable. So to write into it, you have to create a modifiable descriptor over the data area. This is done by calling HBufC::Des() which returns a TPtr and thus gives you access to all the modification functions such as Format(), Append() and Fill():

HBufC* buffer = HBufC::NewL(4); // Read Only buffer
TPtr ptrBuffer(buffer->Des()); // Read Write buffer
_LIT(KBert, "Bert");
ptrBuffer.Copy(KBert); // The data area of robert now contains Bert

Remember that that there must be space in the HBufC for the required change because descriptors don’t resize themselves automatically. If there isn’t, it will still compile - but you’ll get a panic at runtime.

HBufC* buffer = HBufC::NewL(2); // Read Only buffer
TPtr ptrBuffer(buffer->Des()); // Read Write buffer
_LIT(KBert, "Bert");
ptrBuffer.Copy(KBert); // Panic! Not enough memory allocated to hold "Bert"

Tip 6: Need to read from an HBufC? Don't call Des().
To read from a descriptor, you only need it to be non-modifiable, a TDesC. Class HBufC derives from TDesC, so it has access to all the non-modifiable functions implemented by TDesC. All you need to do is dereference the pointer.

_LIT(KBert, "Bert");
HBufC* bert = KBert.AllocL();
TPtrC halfOfBert = bert->Left(2);

One of the most common mistakes made when using descriptors is to call Des() on an HBufC* when you only need a constant descriptor.

_LIT(KBert, "Bert");
HBufC* bert = KBert().AllocL();
TPtrC halfOfBert = bert->Des().Left(2); // Unnecessary call to Des()

Tip 7: For API design, use the descriptor base classes as parameters and return values.
In your APIs, use the descriptor base classes TDes and TDesC as parameters and return values. And remember to pass them by reference for efficiency, and never by value. Thus, descriptor parameters should be passed and returned either as const TDesC& for constant descriptors or TDes& when modifiable.

Thus, when defining functions you should always use the abstract base classes as parameters or return values. For example, class RFile defines straightforward file read and write methods as follows:

IMPORT_C TInt Write(const TDesC8& aDes);
IMPORT_C TInt Read(TDes8& aDes) const;

The descriptor to write to the file is a constant descriptor, while to read from the file into a descriptor requires the parameter to be modifiable (the maximum length of the modifiable descriptor determines how much file data can be read into it).

Tip 8: Don't confuse Size() and Length().
The base class TDesC defines both Size() and Length() methods, which are easy to confuse.

Size() returns the number of bytes the descriptor occupies.
Length() returns the number of characters the descriptor contains.

For 8-bit descriptors, where each character occupies a byte, this is the same thing. However, on all releases of Symbian OS since v5u, the native character width has been 16 bits; that is, each character occupies two bytes.

note: Size() always returns a value which is double that of Length().

Tip 9: Beware of calling MaxLength() on the TPtr returned from HBufC::Des()
There's an interesting side effect to calling Des() on an HBufC to return a modifiable descriptor.

Tip 10: Use operator>> and operator<< to internalize and externalize descriptor data. But remember that they can leave!
When externalizing data to a writable stream, or internalizing it from a readable stream, use the operators crafted for this purpose rather than 'rolling your own'.

// Write the contents of aDes to aStream
void CMyClass::ExternalizeL(RWriteStream& aStream, const TDesC& aDes) const
aStream << aDes;

// Read the contents of aStream and create an HBufC
HBufC* CMyClass::InternalizeL(RReadStream& aStream)
{// KMaxLength is defined elsewhere as the maximum length read from the stream
HBufC* heapBuf = HBufC::NewL(aStream, KMaxLength);

1 comment:

uday said...

Very Usefull to clear or Refresh our basics. Really helpfull...

stats counter