Descriptors: Fundamentals of Symbian C++
Descriptors: Fundamentals of Symbian C++
Descriptors: Fundamentals of Symbian C++
Descriptors
Part One
This work is licensed under the Creative Commons Attribution-Share Alike 2.0 UK: England & Wales License.
To view a copy of this license, visit http://creativecommons.org/licenses/bysa/2.0/uk/ or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California,
94105, USA.
Descriptors
Introduction
Introduction
Descriptors are not like standard C++ strings, Java strings or the
MFC CString
• Their underlying memory allocation and cleanup must be managed by
the programmer
Descriptors
Character Size
Symbian OS
• Has been built to support Unicode character sets with wide (16-bit) characters
by default
• The descriptors in early releases of Symbian OS (EPOC) up to Symbian OS v5
had 8-bit native characters
Character Size
Memory Management
Memory Management
Descriptors
The Symbian OS Descriptor Classes
• Know the characteristics of the TDesC, TDes, TBufC, TBuf,
TPtrC, TPtr, RBuf and HBufC descriptor classes
• Understand that the descriptor base classes TDesC and TDes
implement all generic descriptor manipulation code, while the
derived descriptor classes merely add construction and assignment
code specific to their type
• Identify the correct and incorrect use of modifier methods in the
TDesC and TDes classes
• Recognize that there is no HBuf class, but that RBuf can be used
instead as a modifiable dynamically allocated descriptor
Each subclass
• Does not implement its own data access method using virtual function
overriding
• This would add an extra 4 bytes to each derived descriptor object for a
virtual pointer (vptr) to access the virtual function table
• 4 bits of the 4 bytes that store the length of the descriptor object are used to
indicate the class type of the descriptor
TDesC
TBuf<n>
TDesC
TBuf<n>
All the descriptor classes derive from the base class TDesC
• Apart from the literal descriptors (discussed later)
• The T prefix indicates a simple type class
• The C suffix reflects that the class defines a non-modifiable type of
descriptor i.e. constant
TDesC
type iLength
0..3 4..31
4 bytes
TDesC
TBuf<n>
TDesC
TBuf<n>
Pointer descriptors
• Are agnostic about where the memory they point to is actually
stored
• The pointer descriptors themselves are usually stack-based
• But they can be used on the heap for example as a member
variable of a CBase-derived class
TPtrC
TDesC
iLength iPtr “hello world”
4 bytes 4 bytes
• The pointer to the data follows the length word thus the total size of the
descriptor object is two words (8 bytes)
• TPtrC is the equivalent of using const char* when handling strings in C
• The data can be accessed but not modified i.e. the data in the descriptor is
constant
• All the non-modifying operations defined in the TDesC base class are accessible
to objects of type TPtrC
TText8 is a single (8-bit) character, equivalent to const TText8* cString = (TText8*)"abc ...";
unsigned char
...
TPtrC8 memPtr(memoryLocation,length);
TPtr
TDes
TDesC
iLength iMaxLength iPtr “hello world”
4 bytes 4 bytes 4 bytes
The compiler
• Also generates implicit default and copy constructors
• Since they are not explicitly declared protected or private in the
class
A TPtr object
• May be copy-constructed from another modifiable pointer descriptor
• For example by calling the Des() method on a non-modifiable buffer
TBufC<60> buf(KLiteralDes1);
TBufC is described later Copy construction - can TPtr ptr(buf.Des());
...
Length of data to be represented Maximum length to
TInt len = 12;
be representedConstruct a pointer descriptor from a
pointer into memory
TInt maxLen = 32;
TDesC
TBuf<n>
TBufC<n>
TDesC
iLength iBuf “hello world”
4 bytes n*sizeof(char)
In each case
• The new data length must not exceed the length specified in the
template parameter when the buffer was created.
_LIT(KPalindrome, "aabbaa");
Constructed from literal descriptor
TBufC<50> buf1(KPalindrome);
Constructed from buf1
TBufC<50> buf2(buf1);
...
Constructed from a NULL-terminated C string
TBufC<30> buf3((TText16*)"Never odd or even");
TBufC<50> buf4;
Constructed empty length = 0
Copy and replace
buf4 contains data copied from buf1, length modified buf4 = buf1;
buf1 contains data copied from buf3, length modified
buf1 = buf3;
Panic! Max length of buf3 is insufficient for buf2 data
buf3 = buf2;
The TBuf<n> class for modifiable buffer data is a thin template class
TBuf<n>
TDes
TDesC
iLength iMaxLength iBuf “hello world”
4 bytes 4 bytes n*sizeof(char)
_LIT(KPalindrome, "aabbaa");
Constructed from literal descriptor
TBuf<40> buf1(KPalindrome);
Constructed from constant buffer descriptor TBuf<40> buf2(buf1);
Constructed from a NULL-terminated C string
TBuf8<40> buf3((TText8*)"Do Geese see God?");
Constructed empty
length = 0 maximum length = 40
TBuf<40> buf4;
TDesC
TBuf<n>
HBuf
TDesC
iLength iBuf “hello world”
4 bytes n*sizeof(char)
TDesC::Alloc() or TDesC::AllocL()
• May be used - these spawn an HBufC copy of any existing
descriptor
Heap descriptors
• Can be created dynamically to the size required
• But they are not automatically resized
• The buffer must have sufficient memory available for the
modification operation to succeed or a panic will occur
TDesC
TBuf<n>
RBuf
TDes Union of ...
TDesC TUint16* iEPtrType ‘H’ ‘E’ ‘L’ ‘L’ ‘O’
iLength iMaxLength HBufC16* iEBufCPtrType
4 bytes 4 bytes 4 bytes
HBufC
‘H’ ‘E’ ‘L’ ‘L’ ‘O’
It is an R class
• As it manages a heap-based resource and is responsible for freeing
the memory at cleanup time
Descriptors
Part Two
Descriptors
TDesC
TBuf<n>
Each subclass
• Does not implement its own data access method using virtual function
overriding
• This would add an extra 4 bytes to each derived descriptor object for a virtual pointer
(vptr) to access the virtual function table
Descriptors
_LIT(KDes1, "Sixty zippers were quickly picked from the woven jute bag");
TPtrC alpha(KDes1);
TPtrC beta(KDes2);
TBufC<60> buf(KLiteralDes1);
TBufC<100> buf2(KLiteralDes2);
TPtr8 ptr(buf.Des());
ASSERT(ptr.Length()==buf.Length());
Descriptors
An API client
• Should not be constrained to using a TBuf because a particular function
requires it
Function providers
• Should remain agnostic to the type of descriptor passed to them
Descriptors
An HBufC object
• Can also be instantiated using the static NewL() factory methods
specified for the class
• For HBufC16 the methods available are as follows:
An HBufC16 object
• Can also be instantiated using the non leaving method static
New() factory:
This method creates a new heap-based buffer descriptor with maximum length
•
as specified
It does not leave if there is no heap memory available to allocate the
•
descriptor
The caller must compare the returned pointer against NULL to confirm that it
•
has succeeded before dereferencing it
72
Fundamentals of Symbian C++
}
Descriptors
RBuf
RBuf myRBuf;
_LIT(KHelloRBuf, "Hello RBuf!");
myRBuf.Create(KHelloRBuf());
myRBuf.Assign(pointer, maxSizeOfData);
Use and clean up
...
myRBuf.Close();
It is instead an R class
• As it manages a heap-based resource and is responsible for freeing the
memory at cleanup time
• As is usual for other R classes cleanup is performed by calling Close()
If the RBuf
• Was pushed onto the cleanup stack by a call to CleanupClosePushL()
use CleanupStack::PopAndDestroy()
Previously
• It would have been necessary to instantiate an HBufC and then use a
companion TPtr object - constructed by calling Des() on the heap
descriptor
HBufC vs RBuf
Using HBufC for modifiable data
Using RBuf for modifiable data
HBufC* socketName = NULL; RBuf socketName;
// KMaxNameLength is defined elsewhere ...
if(!socketName) if(socketName.Compare(KNullDesC)==0)
{ {
socketName = HBufC::NewL(KMaxNameLength); socketName.CreateL(KMaxNameLength);
} }
// Create writable ’companion’ TPtr
TPtr socketNamePtr(socketName->Des());
message.ReadL(message.Ptr0(), socketNamePtr); message.ReadL(message.Ptr0(), socketName);
Descriptors
return (*iHeapBuffer);
}
Descriptors
Literal Descriptors
• Know how to manipulate literal descriptors and know that those
specified using _L are deprecated
• Specify the difference between literal descriptors using _L and
those using _LIT and the disadvantages of using the former
Literal Descriptors
Literal Descriptors
_LIT macro
• The _LIT macro is preferred for Symbian OS literals since it is the
more efficient type
• It has been used in the sample code throughout these lectures -
typically as follows:
_LIT(KNULLDesC,"");
_LIT8(KNULLDesC8,"");
_LIT16(KNULLDesC16,"");
_L Macro
User::Panic(_L("telephony.dll"), KErrNotSupported);
• For the example above, the string ("telephony.dll") is built into the program
binary as a basic, NULL-terminated string
_L Macro
Descriptors
Descriptor Conversion
• Know how to convert 8-bit descriptors into 16-bit descriptors and
vice versa using the descriptor Copy() method or the
CnvUtfConverter class
• Recognize how to read data from file into an 8-bit descriptor and
then ‘translate’ the data to 16-bit without padding, and vice versa
• Know how to use the TLex class to convert a descriptor to a
number, and TDes::Num() to convert a number to a descriptor
Descriptor Conversion
The Copy()methods
• Copy the data into the descriptor setting its length accordingly
• The methods will panic if the maximum length of the receiving descriptor
is shorter than the incoming data
Descriptor Conversion
It is possible
• To copy a narrow-width descriptor onto a narrow-width
descriptor
• To copy a wide descriptor onto a wide descriptor
It is also possible
• To copy between descriptor widths
• i.e. carrying out an implicit conversion in the process
Wide to Narrow
The Copy() method implemented by TDes8
• Is to copy an incoming wide descriptor into a narrow descriptor
• It strips out alternate characters assuming them to be zeroes - the
data values do not exceed 255 (decimal).
• The Copy() method which copies a narrow descriptor into the data
area is a straight data copy
CAT
TBuf8<3> cat(_L8("CAT"));
D \0 O \0 G \0
NULL characters stripped out in wide-to- TBuf16<3> dog(_L16("DOG"));
narrow descriptor copy
DOG
cat.Copy(dog);
Narrow to Wide
For class TDes16
• An incoming 16-bit descriptor can be copied directly onto the data
area
• The Copy() method that takes an 8-bit descriptor pads each
character with a trailing zero as part of the copy operation
S M ALL
TBuf8<5> small(_L8("SMALL"));
L \0 A \0 R \0 G \0 E \0
NULL characters used to pad narrow- TBuf16<5> large(_L16("LARGE"));
to-wide descriptor copy
S \0 M \0 A \0 L \0 L \0
large.Copy(small);
Copy() methods
_LIT(KTestLex, "54321");
TLex lex(KTestLex());
TInt value = 0;
TInt err = lex.Val(value); // value == 54321 if no error occurred
The Val() function is overloaded for different signed integer types (TInt, TInt8,
•
TInt16, TInt32, TInt64) with or without limit checking
There are also Val() overloads for the unsigned integer types, passing in a radix
•
value (decimal, hexadecimal, binary or octal), and for TRealTLex provides a number
of other API methods for manipulation and parsing
These are documented in the Symbian OS Library in each SDK
A T-class object
• May be packaged whole into a descriptor (“descriptorized”) so it may be
passed in a type-safe manner between threads or processes
It is modifiable
• Functions may be called on it after calling operator() on the TPckgBuf
object to retrieve it
• The package buffer contains a copy of the original object thus only the copy
is modified, the original is unchanged
TPckg<TSample> TPtr
TDes
TDesC
iLength iMaxLength iPtr
TSample theSample
TPckgC<TSample> TPtrC
TDesC
iLength iPtr
TPckBuf<TSample> TBuf<n>
TDes
TDesC
iLength iMaxLength copy of theSample
Literal Descriptors
Descriptor Conversion