Saturday, May 28, 2005

19. How do I use stack-based buffer descriptors?

The stack-based buffer descriptor classes are TBufC<n> and TBuf<n>. They are useful for fixed-size or relatively small strings, say up to the length of a 256-character filename (i.e. where n <=256). As I've previously described in 6. Large stack-based descriptors and in Tip 3, because they are stack-based, and stack space is limited on Symbian OS, if you need a larger buffer, you should use the heap (either by using the heap-based buffer descriptors, or having TBuf or TBufC as member variables of C classes). Being stack-based, these descriptors should also only be used when they have a lifetime that coincides with that of their creator.

The TBuf and TBufC descriptors are somewhat equivalent to char[] in C, but benefit from internal overflow checks. As you've probably guessed by now, they may be constant (TBufC) or modifiable (TBuf).

The layout of the buffer descriptors is such that the string data forms part of the descriptor object itself, and follows the length word for TBufC and the maximum length word for TBuf. You can find a diagram in [reference 5].

TBufC is a thin template class which uses the TInt value to fix the size of the data area. The non-modifiable buffer class is used to hold constant string or binary data. It derives from TBufCBase (which derives from TDesC, and only exists as an inheritance convenience. It should not be used directly). You can find more about thin templates from Chapter 19 of my book, Symbian OS Explained, more details of which are in the References section.

TBufC defines constructors that allow it to be created from a copy of any other descriptor or from a zero-terminated string. They can also be created empty and filled later. This may at first seem unintuitive for a constant buffer, but in fact, while the data itself is constant, the contents of the buffer may be replaced by calling the assignment operator of the class. The replacing data must not exceed the length specified when the buffer was created, or a panic will occur, in both debug and release modes.

TBufC also has a Des() method which returns a modifiable pointer descriptor for the data represented by the buffer. So, while the contents cannot be altered directly by calling functions such as Format() or LowerCase() upon it, it is possible to change the data indirectly by creating a TPtr around the data in the buffer. When the modification occurs, the length of both the pointer descriptor and the constant buffer descriptor it refers to are changed. But remember, it cannot be extended because the descriptor classes do not provide memory management. So, don't call Append() on the TPtr unless you know there's sufficient space!

_LIT(KFred, "Fred");
_LIT(KBert, "Bert");
TBufC<4> fredBuf(KFred); // Constructed from literal descriptor
TPtr ptr(FredBuf.Des()); // max length = 4
ptr = KBert; // fredBuf now containts "Bert"

_LIT(KCyril, "Cyril");
ptr = KCyril; // Panic! KCyril exceeds max length of ptr (=4)

Like TBufC, the corresponding modifiable buffer class, TBuf<n>, is a thin template class, where n defines the maximum length of the descriptor data area. TBuf derives from TBufBase, which itself derives from TDes. TBuf thus inherits the full range of descriptor operations from TDes and TDesC.

Although the buffer is modifiable, as with all descriptors, memory management of the TBuf is your responsibility and it cannot be extended beyond the initial maximum length set on construction. If it needs to expand, you need to make sure that you either make it large enough at compile time (but not too large to waste valuable stack space) or use a descriptor which you can dynamically allocate at runtime. This means using an HBufC heap descriptor, described in 20. How do I use heap-based buffer descriptors?.

Why not removing TBufC concept comlpetely if we can treat them as TBufs by some programmer control?
Why not removing TBufC concept comlpetely, if we can treat them as TBufs by some programmer control?
It's object oriented design. The semantics of the constant descriptor class means that, when handed an object of this type, you know it's supposed to be non-modifiable. This is particularly important for descriptors that are passed back and forth by reference and thus, in theory, are always modifiable. If this wasn't the case, each function would have to be documented to describe whether you can expect the parameter to be changed. Instead, by having the separation, by defining a function that is passed a modifiable descriptor reference, it's then clear that the parameter will change. If it is a TDesC, it's clear that it won't.

Just because you can poke around in an object's internals, doesn't mean you should :o) This is a fairly good rule for C++ in general in fact - it's often possible to do the wrong thing, eg you can cast *down* the inheritance hierarchy if you want, but it's not a good idea (see Scott Meyers Item 39 for an explanation of why).

Hope that answers the question,

Dear Jo.
My name is Diego And I'm from Italy.
Please What is the relation between TText and TBuf.
In c we have a char * alfa ="Hello"
in symbian we have TText8 = ?
And Then how can we pass a ttext to a TBuf.
Thanks and congratulation for your Symbian awaraness.
Hi Diego

We don't really deal with TText in Symbian OS, but use the descriptor classes and literal descriptors instead.

So we write this:

_LIT(KFred, "Fred");
TBufC<4> fredBuf(KFred);

and then use fredBuf

Instead of
char* fredBuf ="Fred";

If you need to look up individual characters inside fredBuf, you use TChar rather than TText - look at the Locate() methods, for example, in TDesC16
What will happen if we pass n to be a
CBased derived class instead of a number as the templated argument?
Post a Comment

<< Home

This page is powered by Blogger. Isn't yours?

WWW Descriptors FAQ